Why does this code seem to work, except in this one case?

We have this code, and these test cases:

And for some reason this all seems to work, except for the test case that doesn't. What's going on here?

edit: we were able to solve it with quinedot's hint below Rust Playground

(not sure how to make this work with closures unfortunately, and this code might still have more issues)

I didn't take the time to comprehend the code as a whole, so I can't say if this is truly a fix or not, but this change:

 impl<'ignored, T> Holder<'ignored, T> where T: Opaque {
     /// "Unwraps" the value in this holder, binding its lifetime to a new stack
     /// frame.
     pub fn operate_in<'i, F, R>(pinned_self: Pin<&'i Self>, f: F) -> R
-    where F: for<'a> FnOnce(Pin<&'a T::Kind<'a>>) -> R {
+    where F: FnOnce(Pin<&'ignored T::Kind<'ignored>>) -> R, {

Allows compilation and execution.

(I tried using a fresh function lifetime parameter first, but the compiler then told me it had to be the same as 'ignored, so I tried that.)

1 Like

wouldn't that make it unsound? (then again, it might also be unsound already because of new_with...)

I don't know.

ah yeah, it is unsound (run with miri) Rust Playground

It might be easier to help with this if you could explain

  • how Opaque and Holder are intended to be used in the general case; as well as
  • how you believe your implementation (with ub_check) to be sound.

I've tried digging into this code before, but it's difficult without knowledge of the intent of it all. In particular, an earlier version of ub_check seemed to have possible issues, but I was unable to work around the compiler limitations of GATs to create a proper counterexample.

ub_check's only purpose is to prevent Drop impls that aren't #[may_dangle], because, ideally, Drop is the only time you'd be able to actually observe dangling references in self-referential structs. Indeed, if you impl Drop for any of these, without #[may_dangle], you get an error. It'd be nice if one were able to represent this in the type system (such as by T: '!) but this macro seems good enough for now.

Meanwhile Opaque itself is just a replacement for proper HKTs. This shouldn't need proper HKTs, as you control the whole types, so GATs should be enough.

And Holder doesn't... really do much, as you can probably tell. It mostly just requires Pin and tries to uphold the above invariants (no Drop, ideally no way to leak the self-ref and then dangle it... but clearly that last part isn't working.) But its main purpose is to allow moving a Box<SelfRef> (or, well, a Pin<Box<SelfRef>>... or rather a Pin<Box<Holder<SelfRef>>> but you get the point).

Hmm, this seems to work, unless we missed something? Rust Playground