I) Can I create an unmovable struct without Cell-like wrapper? I suspect the answer is no, because
I can't move referenced object, therefore I can't create reference before I create the unmovable struct
To add reference later, I need a mutable (and therefore unique) reference to the filed to store reference (y in the example)
At the same time I need immutable reference to filed to be referenced, and that contradict thew point above
Is my reasoning correct, or am I missing something?
II) It would seem possible to create an unmovable struct on heap and then pass around pointer to it. However, the naive approach does not work: Rust Playground . Are there any workarounds, or is it some fundamental restriction I didn't think about?
assert_eq!(&ptest.x as *const u32, ptest.y.get().unwrap() as *const u32);
println!("{:?}", &ptest.x as *const u32);
let ptest2: Unmovable = *ptest;
println!("{:?}", ptest2);
assert_eq!(&ptest2.x as *const u32, ptest2.y.get().unwrap() as *const u32);
Type casting is something I stayed away from until now I take it to be "safe unsafe" code, right? As far as I understand now, the issue with naive version is that Rust borrow check for boxed objects on heap are the same for as for object on stack, while at least in theory, the former can be more relaxed, in the same vein as non-lexical lifetimes. Is it correct?
Notes to future readers: _ stands for "please derive type yourself" instruction to compiler, and you need to cast twice (similarly C++ reinterpret_cast does not cast const away, you need const_cast for that).
I didn't spell it out, but you can only move the box, not move out of it. Moving out of the box and putting the Unmovable on stack is essentially the same thing as grabbing a reference to the &'a i32 and then dropping the box. The compiler will no longer prevent this dangling reference, whichever way you destroy the box internals.
There's not much type casting you would do in safe Rust - pretty much scalars (e.g. widening/narrowing primitive casts) and trait objects.
AFAIK, borrowck doesn't care (or even know) about stack vs heap. It understands regions (aka scopes) and loans (borrows). It also understands things like variance and drop rules (although this is technically handled by the dropck, but they work in tandem). The compiler currently does know about Box as a first-class citizen, but I don't think it treats it any differently in the borrowck.