Unsafe {} blocks change borrowck semantics in subtle ways


I have a struct which wraps a raw pointer and I’m trying to implement std::ops::Deref for it.

pub struct Archive_ref {
    pub pointer: *const ArchiveOpaqueType

What I have discovered is that depending on where the unsafe block is, the code either passes or fails borrowck.

This impl gives an error: borrowed value does not live long enough. https://is.gd/wGKJQG

impl ::std::ops::Deref for Archive_ref {
    type Target = ActualArchiveType;
    fn deref(&self) -> &Self::Target {
         &(unsafe { *self.pointer }).0

This impl works fine: https://is.gd/13Jl8h

impl ::std::ops::Deref for Archive_ref {
    type Target = ActualArchiveType;
    fn deref(&self) -> &Self::Target {
         unsafe { &(*self.pointer).0 }

Why do unsafe blocks affect this?

Edit: a third example shows that a normal {} block breaks the impl, just like the unsafe {} block: https://is.gd/6tlfF1

List of things to be explained in the documentation?

It’s not due to unsafe. It’s borrow vs move.

Observe that

  • &foo.0 borrows from foo, while
  • &{foo}.0 first moves foo and then borrows the moved value, which is a temporary.

See this playground link for an example.


Actually, I should add that you can still do it your way, minimizing the scope of the unsafe by moving a reference to the data within the unsafe block:

&(unsafe { &*self.pointer }).0

Here, you are moving a borrowed reference and .0 auto-derefs that reference.
See the second example in this playground link.


Ah okay that’s a very helpful explanation thank you… Do you think this is perhaps worth looking at as part of the learning curve discussion going on at the moment? (https://internals.rust-lang.org/t/roadmap-2017-productivity-learning-curve-and-expressiveness/4097)

Like, I understand why this happens after being given an explanation, but… It feels as if there could be a way to make this simpler (maybe just an improvement of the error message for this particular case - no change of language semantics involved)