4

This code works fine (Playground):

struct F<'a> {
    x: &'a i32,
}

impl<'a> F<'a> {
    fn get<'b>(&'b self) -> &'a i32 {
        self.x
    }
}

fn main() {
    let x = 3;
    let y = F { x: &x };
    let z = y.get();
}

But when I change x to be a mutable reference instead (Playground):

struct Foo<'a> {
    x: &'a mut i32,  // <-- `mut` added
}

impl<'a> Foo<'a> {
    fn get(&self) -> &'a i32 {
        self.x
    }
}

fn main() {
    let mut x = 3;              // <-- `mut` added
    let y = Foo { x: &mut x };  // <-- `mut` added
    let z = y.get();
}

I get this error:

error[E0312]: lifetime of reference outlives lifetime of borrowed content...
 --> src/main.rs:7:9
  |
7 |         self.x
  |         ^^^^^^
  |
note: ...the reference is valid for the lifetime 'a as defined on the impl at 5:6...
 --> src/main.rs:5:6
  |
5 | impl<'a> Foo<'a> {
  |      ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5
 --> src/main.rs:6:5
  |
6 | /     fn get(&self) -> &'a i32 {
7 | |         self.x
8 | |     }
  | |_____^

Why does that happen? As far as I see it, nothing about the lifetimes has changed: all values/references still live exactly as long as in the first code snippet.

3
  • I don't know exactly, but I think it is important that the second example fails as you couldn't mutate f's x anymore. Commented Jan 2, 2017 at 10:47
  • Note: f should be F, and you can call y.get() directly... and I really like this question! Commented Jan 2, 2017 at 11:56
  • Wow, took me some time but I think I finally understand exactly what was going on here and going down this rabbit hole was really cool. Thanks for the question again! Commented Jan 2, 2017 at 12:57

1 Answer 1

7

Why does the Rust compiler reject this implementation of get? Because it allows:

The following is a perfectly reasonable main, assuming that get compiles:

fn main() {
    let mut x = 3;

    let y = Foo { x: &mut x };
    let a = y.get();
    let b = y.x;

    println!("{} {}", a, b);
}

Yet if get were to compile, this would be fine:

  • a does not borrow y because the lifetime is different
  • b "consumes" y (moving from y.x) but we do not reuse it after

So everything is fine, except that we now have a &i32 and &mut i32 both pointing to x.

Note: to make it compile, you can use unsafe inside of get: unsafe { std::mem::transmute(&*self.x) }; scary, eh?


At the heart of the borrow-checking algorithm is the cornerstone on which Rust's memory safety is built:

Aliasing XOR Mutability

Rust achieves memory safety without garbage collection by guaranteeing that whenever you are modifying something, no observer can have a reference inside that something that could become dangling.

This, in turns, lets us interpret:

  • &T as an aliasing reference; it is Copy
  • &mut T as a unique reference; it is NOT Copy, as it would violate uniqueness, but it can be moved

This difference saved us here.

Since &mut T cannot be copied, the only way to go from &mut T to &T (or &mut T) is to perform a re-borrowing: dereference and take a reference to the result.

This is done implicitly by the compiler. Doing it manually makes for a somewhat better error message:

fn get<'b>(&'b self) -> &'a i32 {
    &*self.x
}
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
 --> <anon>:7:9
  |
7 |         &*self.x
  |         ^^^^^^^^
  |
help: consider using an explicit lifetime parameter as shown: fn get(&'a self) -> &'a i32
 --> <anon>:6:5
  |
6 |     fn get<'b>(&'b self) -> &'a i32 {
  |     ^

Why cannot it infer a lifetime? Because lifetime of the re-borrow is limited by 'b but we are requiring a 'a and there's no relationship between the two!

By the way, this is what is saving us from blunder here, because it ensures that the instance Foo must be borrowed while the result lives (preventing us to use a mutable reference via Foo::x).

Following the compiler hint, and returning &'b i32 works... and prevents the above main from compiling:

impl<'a> Foo<'a> {
    fn get<'b>(&'b self) -> &'b i32 {
        &*self.x
    }
}

fn main() {
    let mut x = 3;

    let y = Foo { x: &mut x };
    let a = y.get();
    let b = y.x;

    println!("{} {}", a, b);
}
error[E0505]: cannot move out of `y.x` because it is borrowed
  --> <anon>:16:9
   |
15 |     let a = y.get();
   |             - borrow of `y` occurs here
16 |     let b = y.x;
   |         ^ move out of `y.x` occurs here

However it lets the first main compile without issue:

fn main() {
    let mut x = 3;

    let y = Foo { x: &mut x };
    let z = y.get();

    println!("{}", z);
}

Prints 3.

Sign up to request clarification or add additional context in comments.

5 Comments

Anywhere documented about the "dereference and take a reference to the result" when assigning from &mut T to &T?
@updogliu: Not that I know of; but I haven't read the latest version of the Rust book, etc...
let mut_ref_s = &mut String::from("this"); let sss: &mut String = mut_ref_s; //reborrow from mut_ref_s dummy(mut_ref_s); // moving newly reborrowed tmp from mut_ref_s let ss = mut_ref_s; // moving mut_ref_s; Anyone helps to explain when the compiler would choose to reborrow or move a mutable reference. Thanks!
Cause I find very different MIR generated for ss and sss here. play.rust-lang.org/…
@ZeGao: As you may have noticed, comments just don't support code snippets well; please hope on the chat (chat.stackoverflow.com/rooms/62927/rust) or post a new question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.