It's ok to Miri because it checks the actual layouts the compiler chose to use for your compilation, not whether what you're doing is stable to allowed layout changes.
You should put #[repr(C)] on the three structs to say that you're depending on them having consistent layouts.
rustc doesn't currently randomize field ordering for repr(Rust) structs, but it's allowed to. (Just like how it didn't used to reorder fields in repr(Rust) structs, but now it does.)
EDIT: bjorn3 points out below that there's a nightly flag that can randomize layout now!
means the self is uniquely borrowed by the returned &mut FooProjection2 reference. So the self is not usable while the returned reference is alive. You don't need to do anything more within the function body to prevent coexistent.
This was my intention, I just wasn't sure because:
Normally you'd be returning a reference to some inner part of self, not a cast of self to a new type, and the borrow checker would enforce not using the outer &mut while inner still exists.
Lexically they both exist before the "transfer" happens while still inside the method.
Yup I was only thinking for satisfying the borrow checker.
This feels a little weird just because I'd expect self to stick around for the duration of a method (and it does from the POV of calling code), but it makes sense.
I think it's kinda hard to say -- the first borrow is arguably over, as &mut*p is technically a fresh borrow of unbound lifetime, which is then constrained to the same lifetime as the input by the function signature when it's returned.
I'm interpreting the borrows here as the lifetimes basically, so the &mut self borrow has to be the same as that of the returned reborrow, as per the function signature. The fact that self is dead doesn't mean the borrow wasn't for that long (though it may be crucial in avoiding aliasing). The fact that the borrowed-thing got reborrowed doesn't either. The returned borrow can be completely unrelated and your input borrow still has to be long enough.