References in struct's giving lifetime conflicts

I am having trouble with the following code snippet

struct BorrowedStruct {
    x: String
}

impl BorrowedStruct {
    fn get_string(&self) -> &str {
        &self.x
    }
}

struct MyStruct<'a> {
    data: &'a mut BorrowedStruct,
}

impl<'a> MyStruct<'a> {
    fn foo(&self) -> &'a str {
        self.data.get_string()
    }
}

The error reports that

note: first, the lifetime cannot outlive the anonymous lifetime defined here...
  --> src/main.rs:16:12
   |
16 |     fn foo(&self) -> &'a str {
   |            ^^^^^
note: ...so that reference does not outlive borrowed content

But the lifetime should have nothing to do with &self. The lifetime should only be tied to 'a. I don't understand why this is incorrect. Can someone help clarify? Am I structuring this wrong?

You can't get the &'long mut out from behind the &'short self, you can only get a &'short (which you can then call get_string with).

Let's say you could, and I did something like this:

fn f<'a>(my_struct: MyStruct<'a>) {
    // I only borrow `my_struct` for some `'short < 'a` amount of time,
    // but get a (longer) `&'a str` back out
    let view_into: &'a str = my_struct.foo();

    // `'short` has expired so we can deconstruct `my_struct`.
    // `data` is a `&'a mut BorrowedStruct`.
    let MyStruct { data } = borrow;

    // I now simultaneously hold a reference to a `&'a str` and an exclusive
    // borrow (`&'a mut BorrowedStruct`) that leads to the same data,
    // and that is undefined behavior (UB)
}

If you write it this way, the returned lifetime will be limited by the &self lifetime, and it compiles:

impl<'a> MyStruct<'a> {
    fn foo(&self) -> &str {
        self.data.get_string()
    }
}
2 Likes

hmm. I don't see how you would have overlapping borrows, because the lifetime of &'a str is tried to the borrow of &'a mut BorrowedStruct. As soon as you consumed the mutable reference (to make data) the str reference should become invalid. Am I thinking about this wrong?

Well, I'm not sure exactly how you're thinking about it. But you're right that once the mutable reference is utilized, any further uses of the str reference are invalid. Moreover, that's something Rust is going to guarantee at compile time (in safe code).

However, this means that the str reference might not be able to be 'a, depending on the rest of the code. This signaure:

    fn foo(&self) -> &'a str { /* ... */ }

Says "I don't care how short the borrow of self is, I can get you a &'a str." The implication is that there's nothing tying self to the returned &str -- and no way to tie the returned value to the mutable reference inside self. (The field might even be private -- opaque to another crate calling foo.)

Rust respects function API boundaries -- if your method declaration says it's possible, Rust is going to treat it so. Thus, within the context of my example f, there's nothing tying the borrow to the inside value that becomes data. Here we can see that if the error within foo is mitigated (by returning some unrelated &str), everything compiles. But if you returned self.data.get_string(), it would be UB!

If Rust accepted your implementation of foo by making f fail, that would mean that the API of the function declaration doesn't "contracturally" matter, that the language has spooky errors at a distance, and that changing method implementations without breaking users would be nigh-on impossible. Instead it makes your implementation fail for not living up to the contract. So if you actually want to return a borrow linked to self, you're going to have to change the function declaration so that it lets the compiler (and users of the method) know that is what is going on.

For completeness, let's see what happens with f when the implementation is changed to foo(&self) -> &str, and if f no longer demands a &'a str. We can see that compilation fails, but now the error is in f. The error is exactly what you intuited: pulling out data invalidated the borrow.


If you really need an 'a specifically for some reason, you may feel that another alternative is:

impl<'a> MyStruct<'a> {
    fn foo(&'a self) -> &'a str {
        self.data.get_string()
    }
}

However, this is a "code smell" and probably not what you actually want. For example, the below function will fail as 'a may be longer than the function body, and you can't create a reference that lasts longer than the underlying object. (You can't even borrow my_struct that long to make the call.)

fn f<'a>(my_struct: MyStruct<'a>) {
    let view_into: &'a str = my_struct.foo();
}

(If you have an actual circumstance you need this in, more context is required before I would know what advice to give you.)


Maybe you're running into Lifetime Misconception #8.

2 Likes

This works ok:

struct BorrowedStruct {
    x: String
}

impl BorrowedStruct {
    fn get_string(&self) -> &str {
        &self.x
    }
}

struct MyStruct<'a> {
    data: &'a mut BorrowedStruct,
}

impl<'a> MyStruct<'a> {
    fn foo(&'a self) -> &'a str {
        self.data.get_string()
    }
}


fn main() {
   let mut bs = BorrowedStruct{ x: "hello".to_string() };
   let ms = MyStruct{ data: &mut bs };
   println!("{}", ms.foo());
  
}

Playground

Starting with my example that fails, this change will work (...compile...) too:

 fn f<'a>(my_struct: MyStruct<'a>) {
-    let view_into: &'a str = my_struct.foo();
+    let view_into: &str = my_struct.foo();
 }

But then you're not actually getting a &'a str out, despite the signature! MyStuct<'a> is covariant, so in this case the compiler actually calls MyStruct::<'short>::(&'short my_struct). Illustration.

If you don't need the 'a, there is no reason to constrain foo in that way. It will just bite you unexpectedly from time to time and probably result in compiler errors that lead you astray. It might also make you think it's an okay idea for &'a mut self, which almost never works (as an exclusive borrow for the lifetime of an object renders the object otherwise unusable).

(There's no way to even name the lifetime of the MyStruct<'_> in your example.)

1 Like

The only question which needs to be answered is where and how you have managed to obtain cryptic message which you are complaining about. Because actual message that compiler shows is quite different:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:17:19
   |
17 |         self.data.get_string()
   |                   ^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
  --> src/main.rs:16:12
   |
16 |     fn foo(&self) -> &'a str {
   |            ^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:17:9
   |
17 |         self.data.get_string()
   |         ^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
  --> src/main.rs:15:6
   |
15 | impl<'a> MyStruct<'a> {
   |      ^^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:17:9
   |
17 |         self.data.get_string()
   |         ^^^^^^^^^^^^^^^^^^^^^^

And compiler, basically, explains what other were telling: you promised to return reference which is valid for 'a, but haven't ensured that &self would live long enough to make that possible so what do you expect?

No. It's tied to two borrows: 'a (because that's how it's defined) but also &self (because your structure is borrowed, itself, of you would consume it, everything works).

And compiler explains that it's not enough. If you would just say hey, &self lives long enough, don't worry, then compiler immediately becomes happy:

struct BorrowedStruct {
    x: String
}

impl BorrowedStruct {
    fn get_string(&self) -> &str {
        &self.x
    }
}

struct MyStruct<'a> {
    data: &'a mut BorrowedStruct,
}

impl<'a, 'b> MyStruct<'a> {
    fn foo(&'b self) -> &'a str where 'b: 'a {
        self.data.get_string()
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.