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.