Reputation: 35
pub struct Writer<'a> {
target: &'a mut String,
}
impl<'a> Writer<'a> {
fn indent<'b>(&'b mut self) -> &'a String {
self.target
}
}
Compiling this code results in the following error:
error: lifetime may not live long enough
--> src/bin/main29.rs:19:9
|
17 | impl<'a> Writer<'a> {
| -- lifetime `'a` defined here
18 | fn indent<'b>(&'b mut self) -> &'a String {
| -- lifetime `'b` defined here
19 | self.target
| ^^^^^^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`
Why can't this Rust code be compiled? I thought it would work.
Upvotes: 0
Views: 1029
Reputation: 822
&mut T
is invariant over T
. In your case, T
being &String
. Since both lifetimes are disjoint (i.e &'b mut &'a String
) the compiler will not "shorten" the lifetime 'a
for the function call. Conversely, it wants 'b
to live for atleast as long as 'a
.
The other way to look at it is that you pass mutable reference to a function and you get an output reference. Now the compiler requires assurance that while there already is an active reference to the output data, you cannot mutate the same data through some other "path".
Here's an example of what would happen if we ask the compiler to ignore using unsafe
use std::mem::transmute;
pub struct Writer<'a> {
target: &'a mut String,
}
impl<'a> Writer<'a> {
fn indent<'b>(&'b mut self) -> &'a String {
unsafe {
transmute (&*self.target)
}
}
}
fn main() {
let mut foo_in = String::from("abc");
let mut foo = Writer {
target : &mut foo_in
};
let foo_out = foo.indent(); // ------'a
// |
// mutation while there is // |
// an active reference 'a // |
foo.target.push('d'); // |
// |
dbg!(foo_out); // |
}
With immutable references you don't have to worry because you can have as many overlapping immutable references as you want.
Upvotes: 0
Reputation: 13820
Because any data that belongs to a &'b Writer<'a>
only lives for the shorter of 'a
and 'b'
, but you're trying to return data that lives for 'a
. As the compiler says, this only works if 'b: 'a
.
You could fix this by applying the bound 'b: 'a
, or more simply writing &'a mut self
, but I strongly recommend you not express types of the form &'a T<'a>
; the invariance is incredibly painful to work through. A simpler implementation would be
impl Writer<'_> {
// or better yet, indent(&self) -> &str
fn indent(&mut self) -> &String {
self.target
}
}
This has the advantage that the lifetimes, being anonymous, will correctly be inferred by the compiler. This works because in general, the compiler will prevent you from constructing a &'b Writer<'a>
where 'b
strictly outlives 'a
, since such a type could contain have a dangling reference; therefore there is no need to foist that responsibility onto the function signature as well.
Upvotes: 1