Why does this work
#[derive(Debug)]
pub struct Foo<'a,'b> {
s : &'a str,
n : &'b i32
}
#[test]
fn test_struct() {
let f = Foo { s : &"bar" , n : &17 };
println!("{:?}",f);
}
but this doesn't
#[derive(Debug)]
pub enum Bar<'a,'b> {
Baz ( &'a str),
Fub ( &'b i32)
}
#[test]
fn test_struct() {
let b = Bar::Baz(&"Foo");
let c = Bar::Fub(&17);
println!("{:?} {:?}",b,c);
}
The error is (part of a bigger file so ignore line numbers)
src\lib.rs:176:27: 176:29 error: borrowed value does not live long enough
src\lib.rs:176 let c = Bar::Fub(&17);
^~~~~~~~~~~~~~~~~~~~~~
To me it seems like let c = Bar::Fub(&17), the 17 lasts the same life time as the previous line where "Foo" is created on the stack. If I modify it slightly and do
let h = &17;
let c = Bar::Fub(&h);
In which case it's completely clear that h lasts longer than Bar::Fub(). SoI'm not sure how I can can get this to work.
This is a follow up to Lifetime parameters for an enum within a struct
To me it seems like let c = Bar::Fub(&17), the 17 lasts the same life time as the previous line where "Foo" is created on the stack
A string literal always has 'static lifetime and will therefor always live long enough.
I think the issue is that you are hitting is the fact that an enum expression is actually somewhat of a a function call. Somewhat meaning that the lifetime of the argument is ignored in the computation of the Enum's lifetime. The Enum's lifetime apparently is marginally larger, as if you wrote:
let c: Bar;
let x = &17;
c = Bar::Fub(x);
which is already addressed in Scope of addresses: Does not live long enough
In which case it's completely clear that h lasts longer than Bar::Fub().
Yes, the lifetimes are clear here, and it works in the Playpen:
let x = &17;
let c = Bar::Fub(x);
so I'm not sure what you are asking.
Related
I'm having some trouble understanding the compiler error message for this code:
struct A {
b: B,
c: C,
}
struct B {
f: u32,
}
struct C {
f: u32,
}
fn foo(b: &mut B) -> &u32 { &b.f }
fn bar(c: &mut C) -> &u32 { &c.f }
fn quux(z: &mut A) -> u32 { z.b.f }
fn baz(a: &mut A) {
let x = foo(&mut a.b);
let y = bar(&mut a.c);
let z = quux(a); // equivalent to: quux(&mut *a)
println!("{} {}", x, y);
}
error[E0499]: cannot borrow `*a` as mutable more than once at a time
--> src/main.rs:22:18
|
20 | let x = foo(&mut a.b);
| -------- first mutable borrow occurs here
21 | let y = bar(&mut a.c);
22 | let z = quux(a);
| ^ second mutable borrow occurs here
23 | println!("{} {}", x, y);
| - first borrow later used here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `playground` due to previous error
If line 20 is mutably borrowing *a (as the compilation error asserts), then line 21 must also mutably borrow *a (How couldn't it? It's nearly identical to line 20 -- only difference is which field is borrowed), so by my reasoning the code should still fail to compile after deleting line 22; however, the code actually compiles just fine if I delete line 22. The fact that this would then compile seems to contradict the compiler's claim that &mut a.b results in a mutable borrow of *a.
x and y are immutable borrows, so if anything I would expect a compilation error regarding the overlapping scopes of the immutable x/y and the mutable borrow of *a in line 22 (otherwise quux could conceivably mutate a while we're borrowing its fields).
What knowledge am I likely missing that makes this error message confusing to me? If you're not surprised by this message, could you break down how you reason about this?
Some more thinking before I submitted this question:
I suppose the immutable references returned from foo and bar somehow extend the original mutable borrows of a.b and a.c (why that would be necessary, I don't know). If that's the case, maybe the way to see this is that &mut a.b and &mut a.c in turn extend the mutable borrow of *a. The error message says the first mutable borrow of *a occurs on line 20, but maybe the way to see this is:
The true first borrow of *a is when when the caller gives us the &mut A.
While the compiler suggests there's a mutable borrow of *a on line 20, this isn't exactly the case: the mut &a.b expression borrows (*a).b, but merely extends the existing borrow given to us (as opposed to being a new mutable borrow of *a).
The last point, but with respect to mut &a.c.
Insert the aforementioned reasoning of "extending borrows" wrt *a.
The call to quux then mutably borrows the *a a second time.
So maybe what the compilation error calls the initial borrow of *a is actually reporting the location of the expression responsible for extending the original *a borrow?
Line 20 does not conflict with 21 since they borrow different fields within a and therefore cause no collision.
The issue is that foo and bar return references that inherit the lifetime of the mutable reference. Even though they return immutable references, their lifetimes rely on the lifetime of the mutable borrow. So by requiring they live for a period of time, by extension the mutable borrow must do so as well. The compiler always takes the function signature on face value so it can not downgrade the initial mutable reference. If it did not, it could lead to issues if the function was modified later or if it contained unsafe code that relied on consuming a mutable reference for safety.
Here is a counter example where all of the function signatures remain the same. Since the compiler can not make assumptions on how quux and foo will use their values quux may make the references returned by foo or bar invalid.
struct A {
b: B,
c: C,
}
struct B {
f: Option<u32>,
}
struct C {
f: u32,
}
fn foo(b: &mut B) -> &u32 {
b.f.as_ref().unwrap()
}
fn bar(c: &mut C) -> &u32 { &c.f }
fn quux(z: &mut A) -> u32 {
z.b.f = None;
z.c.f
}
fn baz(a: &mut A) {
let x = foo(&mut a.b);
let y = bar(&mut a.c);
let z = quux(a);
println!("{} {}", x, y);
}
There are two issues here. First: why does let x = foo(&mut a.b); prevent you from doing let z = quux(a);, and second why does let x = foo(&mut a.b); not prevent you from doing let y = bar(&mut a.c);?
Why does let x = foo(&mut a.b); prevent you from doing let z = quux(a);?
This is due to the lifetime elision rules. You declared foo as:
fn foo(b: &mut B) -> &u32
Since you use references there must be a lifetime, but since you didn't specify one yourself the compiler assumes that all references have the same lifetime. In other words, the compiler rewrites your code as:
fn foo<'a>(b: &'a mut B) -> &'a u32
Which means that the returned value has the same lifetime as the mutable borrow, so the mutable borrow lasts until the println where you use the return value x, which prevents other borrows of a.b or a as a whole.
But then why does let x = foo(&mut a.b); not prevent you from doing let y = bar(&mut a.c);?
That's because the compiler is smart enough to realize that a.b and a.c are disjoint parts of a, which can be borrowed without conflict (in the same way that you can borrow different local variables simultaneously).
I'm trying to figure out a Rust lifetime issue and after boiling it down a bunch, I realized that I have no idea how I would explicitly annotate the lifetimes of r, x2, and _arr in foo:
struct X<'a> {
_v: &'a mut i32,
}
fn main() {
let mut v1 = 0;
let x1 = X { _v: &mut v1 };
foo(x1);
}
fn foo(x1: X) {
let mut v2 = 1;
let r = &mut v2;
let x2 = X { _v: r };
let _arr = [x1, x2];
}
I'm going to work off of the assumption that the part of your code that got cut off is the following:
struct X<'a> {
_v: &'a mut i32,
}
In that case, let's dissect what's going on in foo a little bit.
fn foo(x1: X) {
// Lives until the end of the function, call its lifetime 'v2
let mut v2 = 1;
// Borrow v2 for some lifetime 'r which is no longer than 'v2
// so r is of type &'r mut i32
let r = &mut v2;
let x2 = X { _v: r }; // x2 is of type X<'r>
let _arr = [x1, x2]; // ??
}
The confusion probably comes from the fact that x1 seems to have an ambiguous lifetime in its type. This is because we didn't explicitly specify the lifetime in x1: X. This is a Rust 2018 idiom, and I personally recommend not doing this. You can add #![deny(rust_2018_idioms)] to the top of your crate root and the compiler will point out these ambiguities to you and force you to be more explicit. What happens here is that the function declaration gets de-sugared to the following:
fn foo<'x1>(x1: X<'x1>) { ... }
Now it is implied that the lifetime 'x1 extends at least through the body of foo, and this makes sense because it kind of has to. If something which lived for 'x1 was freed in the middle of foo (disregarding how something like that could even happen), then that would defeat the point of using lifetimes for memory safety.
So that being said, let's revisit this line:
let _arr = [x1, x2];
We know that x2 is of the type X<'r> and that 'r extends at most to the end of foo (since 'r is no longer than 'v2 and 'v2 extends to the end of foo). Moreover, we know that x1 is of the type X<'x1> and that 'x1 extends at least to the end of foo. This means that 'x1 is at least as long as 'r. Since lifetimes are covariant, this means that X<'x1> is a sub-type of X<'r>, since whenever we require a value of the type X<'r> we can safely substitute in one of type X<'x1> instead. So what happens is _arr is given the type [X<'r>; 2], and upon construction the lifetime on x1 is shortened to 'r.
We can actually test this hypothesis to see if it's correct. What would happen if the compiler wasn't allowed to shorten that lifetime? In other words, what if the lifetime on X was not covariant. If we modify X as follows then its lifetime type parameter is made invariant:
struct X<'a> {
_v: &'a mut i32,
_invariant: PhantomData<fn(&'a ()) -> &'a ()>
}
And sure enough, after adding the _invariant field in the appropriate places to make the code compile, we receive the following error:
|
14 | fn foo<'x1>(x1: X<'x1>) {
| --- lifetime `'x1` defined here
15 | let mut v2 = 1;
16 | let r = &mut v2;
| ^^^^^^^ borrowed value does not live long enough
17 | let x2 = X { _v: r, _invariant: PhantomData };
| - this usage requires that `v2` is borrowed for `'x1`
18 | let _arr = [x1, x2];
19 | }
| - `v2` dropped here while still borrowed
Now how do we know the compiler wasn't extending the lifetime 'r to 'x1 in the first place? Well if it was doing that, then we could modify foo to do the following, which would unequivocally cause a use-after-free:
fn foo<'x1>(x1: X<'x1>) -> &'x1 mut i32 {
let mut v2 = 1;
let r = &mut v2;
let x2 = X { _v: r };
let _arr = [x1, x2];
_arr[1]._v
}
And sure enough if you try the code above it fails to compile with the reason given being that we're returning a reference to a local variable. Moreover, if you try returning _arr[0]._v, then you get the exact same error.
Sub-typing and variance can be pretty hard to grasp and it's not something you need to fully understand to be an effective Rust programmer. Nonetheless, it is very interesting and you can learn more about it here in the Rustonomicon.
The Rust Programming Language says:
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
The function signature now tells Rust that for some lifetime 'a, the
function takes two parameters, both of which are string slices that
live at least as long as lifetime 'a. The function signature also
tells Rust that the string slice returned from the function will live
at least as long as lifetime 'a. In practice, it means that the
lifetime of the reference returned by the longest function is the
same as the smaller of the lifetimes of the references passed in.
These relationships are what we want Rust to use when analyzing this
code.
I don't get why it says:
In practice, it means that the lifetime of the reference returned by
the longest function is the same as the smaller of the lifetimes of
the references passed in.
Note the word "smaller". For both parameters and returned values, we specified 'a which is the same. Why does the book say "smaller"? If that was the case, we would have different specifiers(a', b').
It's important to note that in the example, whatever is passed as x and y do not have to have identical lifetimes.
Let's rework the example to use &i32 which makes for easier demonstrations:
fn biggest<'a>(x: &'a i32, y: &'a i32) -> &'a i32 {
if x > y {
x
} else {
y
}
}
Now, given the following example:
fn main() {
let bigger;
let a = 64;
{
let b = 32;
bigger = biggest(&a, &b);
}
dbg!(bigger);
}
We have 3 different lifetimes:
bigger which lives until the end of main
a which lives until the end of main
b which lives until the end of its block
Now lets take the description apart
the lifetime of the reference returned by the [...] function
In our case this would be bigger, which lives until the end of main
smaller of the lifetimes of the references passed in
We passed in a, which gets dropped at the end of main, and b, which gets dropped at the end of its block. Since b gets dropped first, it is the "smaller" lifetime.
And sure enough, the compiler yells at us:
error[E0597]: `b` does not live long enough
--> src/main.rs:7:30
|
7 | bigger = biggest(&a, &b);
| ^^ borrowed value does not live long enough
8 | }
| - `b` dropped here while still borrowed
9 |
10 | dbg!(bigger);
| ------ borrow later used here
But if we move bigger inside the block, and hence reduce its lifetime, the code compiles:
fn main() {
let a = 64;
{
let b = 32;
let bigger = biggest(&a, &b);
dbg!(bigger);
}
println!("This works!");
}
Now a and b still have different lifetimes but the code compiles since the the smaller of both lifetimes (b's) is the same as bigger's lifetime.
Did this help illustrate what "smaller" lifetime means?
I can't call Foo::new(words).split_first() in the following code
fn main() {
let words = "Sometimes think, the greatest sorrow than older";
/*
let foo = Foo::new(words);
let first = foo.split_first();
*/
let first = Foo::new(words).split_first();
println!("{}", first);
}
struct Foo<'a> {
part: &'a str,
}
impl<'a> Foo<'a> {
fn split_first(&'a self) -> &'a str {
self.part.split(',').next().expect("Could not find a ','")
}
fn new(s: &'a str) -> Self {
Foo { part: s }
}
}
the compiler will give me an error message
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:8:17
|
8 | let first = Foo::new(words).split_first();
| ^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| creates a temporary which is freed while still in use
9 |
10 | println!("{}", first);
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
If I bind the value of Foo::new(words) first, then call the split_first method there is no problem.
These two methods of calling should intuitively be the same but are somehow different.
Short answer: remove the 'a lifetime for the self parameter of split_first: fn split_first(&self) -> &'a str (playground).
Long answer:
When you write this code:
struct Foo<'a> {
part: &'a str,
}
impl<'a> Foo<'a> {
fn new(s: &'a str) -> Self {
Foo { part: s }
}
}
You are telling the compiler that all Foo instances are related to some lifetime 'a that must be equal to or shorter than the lifetime of the string passed as parameter to Foo::new. That lifetime 'a may be different from the lifetime of each Foo instance. When you then write:
let words = "Sometimes think, the greatest sorrow than older";
Foo::new(words)
The compiler infers that the lifetime 'a must be equal to or shorter than the lifetime of words. Barring any other constraints the compiler will use the lifetime of words, which is 'static so it is valid for the full life of the program.
When you add your definition of split_first:
fn split_first(&'a self) -> &'a str
You are adding an extra constraint: you are saying that 'a must also be equal to or shorter than the lifetime of self. The compiler will therefore take the shorter of the lifetime of words and the lifetime of the temporary Foo instance, which is the lifetime of the temporary. #AndersKaseorg's answer explains why that doesn't work.
By removing the 'a lifetime on the self parameter, I am decorrelating 'a from the lifetime of the temporary, so the compiler can again infer that 'a is the lifetime of words, which is long enough for the program to work.
Foo::new(words).split_first() would be interpreted roughly as
let tmp = Foo::new(words);
let ret = tmp.split_first();
drop(tmp);
ret
If Rust allowed you to do this, the references in ret would point [edit: would be allowed by the type of split_first to point*] into the now dropped value of tmp. So it’s a good thing that Rust disallows this. If you wrote the equivalent one-liner in C++, you’d silently get undefined behavior.
By writing the let binding yourself, you delay the drop until the end of the scope, thus extending the region where it’s safe to have these references.
For more details, see temporary lifetimes in the Rust Reference.
* Edit: As pointed out by Jmb, the real problem in this particular example is that the type
fn split_first(&'a self) -> &'a str
isn’t specific enough, and a better solution is to refine the type to:
fn split_first<'b>(&'b self) -> &'a str
which can be abbreviated:
fn split_first(&self) -> &'a str
This conveys the intended guarantee that the returned references do not point into the Foo<'a> (only into the string itself).
This code fails as expected at let c = a; with compile error "use of moved value: a":
fn main() {
let a: &mut i32 = &mut 0;
let b = a;
let c = a;
}
a is moved into b and is no longer available for an assignment to c. So far, so good.
However, if I just annotate b's type and leave everything else alone:
fn main() {
let a: &mut i32 = &mut 0;
let b: &mut i32 = a;
let c = a;
}
the code fails again at let c = a;
But this time with a very different error message: "cannot move out of a because it is borrowed ... borrow of *a occurs here: let b: &mut i32 = a;"
So, if I just annotate b's type: no move of a into b, but instead a "re"-borrow of *a?
What am I missing?
Cheers.
So, if I just annotate b's type: no move of a into b, but instead a "re"-borrow of *a?
What am I missing?
Absolutely nothing, as in this case these two operations are semantically very similar (and equivalent if a and b belong to the same scope).
Either you move the reference a into b, making a a moved value, and no longer available.
Either you reborrow *a in b, making a unusable as long as b is in scope.
The second case is less definitive than the first, you can show this by putting the line defining b into a sub-scope.
This example won't compile because a is moved:
fn main() {
let a: &mut i32 = &mut 0;
{ let b = a; }
let c = a;
}
But this one will, because once b goes out of scope a is unlocked:
fn main() {
let a: &mut i32 = &mut 0;
{ let b = &mut *a; }
let c = a;
}
Now, to the question "Why does annotating the type of b change the behavior ?", my guess would be:
When there is no type annotation, the operation is a simple and straightforward move. Nothing is needed to be checked.
When there is a type annotation, a conversion may be needed (casting a &mut _ into a &_, or transforming a simple reference into a reference to a trait object). So the compiler opts for a re-borrow of the value, rather than a move.
For example, this code is perflectly valid:
fn main() {
let a: &mut i32 = &mut 0;
let b: &i32 = a;
}
and here moving a into b would not make any sense, as they are of different type. Still this code compiles: b simply re-borrows *a, and the value won't be mutably available through a as long as b is in scope.
To complement #Levans's answer on the specific question "Why does annotating the type change the behaviour?":
When you don't write the type, the compiler performs a simple move. When you do put the type, the let statement becomes a coercion site as documented in "Coercion sites":
Possible coercion sites are:
let statements where an explicit type is given.
In the present case the compiler performs a reborrow coercion, which is a special case of coercion going from &mut to &mut, as explained in this issue comment on GitHub.
Note that reborrowing in general and reborrow coercion in particular are currently poorly documented. There is an open issue on the Rust Reference to improve that point.