This code:
struct Foo<'a> {
value: Option<&'a int>,
parent: Option<&'a Foo<'a>>
}
impl<'a> Foo<'a> {
fn bar<'a, 'b, 'c: 'a + 'b>(&'a self, other:&'b int) -> Foo<'c> {
return Foo { value: Some(other), parent: Some(self) };
}
}
fn main() {
let e = 100i;
{
let f = Foo { value: None, parent: None };
let g:Foo;
{
g = f.bar(&e);
}
// <--- g should be valid here
}
// 'a of f is now expired, so g should not be valid here.
let f2 = Foo { value: None, parent: None };
{
let e2 = 100i;
let g:Foo;
{
g = f2.bar(&e2);
}
// <--- g should be valid here
}
// 'b of e2 is now expired, so g should not be valid here.
}
Fails to compile with the error:
<anon>:8:30: 8:35 error: cannot infer an appropriate lifetime due to conflicting requirements
<anon>:8 return Foo { value: Some(other), parent: Some(self) };
^~~~~
<anon>:7:3: 9:4 note: consider using an explicit lifetime parameter as shown: fn bar<'a, 'b>(&'a self, other: &'b int) -> Foo<'b>
<anon>:7 fn bar<'a, 'b, 'c: 'a + 'b>(&'a self, other:&'b int) -> Foo<'c> {
<anon>:8 return Foo { value: Some(other), parent: Some(self) };
<anon>:9 }
(playpen: http://is.gd/vAvNFi )
This is obviously a contrived example, but it is something I want to do occasionally.
So...
1) How do you combine lifetimes? (ie. Return a Foo which has a lifetime of at least 'a or 'b, which ever is shorter)
2) Is there some way to write tests to asset lifetime compile failures? (eg. try to compile a #[test] that uses the function in the wrong way and fails with a lifetime error)
Lifetimes are what the Rust compiler uses to keep track of how long references are valid for. Checking references is one of the borrow checker's main responsibilities. Lifetimes help the borrow checker ensure that you never have invalid references.
Static items have the static lifetime, which outlives all other lifetimes in a Rust program. Static items may be placed in read-only memory if the type is not interior mutable. Static items do not call drop at the end of the program.
The <> is used to declare lifetimes. This says that bar has one lifetime, 'a. Rust has two main types of strings: &str and String . The &str are called 'string slices' . A string slice has a fixed size, and cannot be mutated.
Rust uses lifetime parameters to avoid such potential run-time errors. Since the compiler doesn't know in advance whether the if or the else block will execute, the code above won't compile and an error message will be printed that says, “a lifetime parameter is expected in compare 's signature.”
The bounds 'c: 'a + 'b
means that 'c
is at least as long as 'a
and as long as 'b
. However, in this case, the Foo
value is valid for exactly the shortest of 'a
and 'b
: as soon as the data behind either reference goes out of scope the whole Foo
must be invalidated. (This is saying that data that is valid for 'c
is valid in the union of 'a
and 'b
.)
In more concrete terms, say 'b = 'static
, then 'c: 'a + 'static
means that 'c
must also be 'static
, so the return value would be Foo<'static>
. This is clearly not right as it would be "upgrading" the limited 'a
self
reference into one that lasts forever.
The correct behaviour here is taking the intersection of the lifetimes: the Foo
is only valid while both function parameters are valid. The intersection operation is just labelling the references with the same name:
fn bar<'a>(&'a self, other: &'a int) -> Foo<'a>
Remove all the limetime parameters on bar
and use the 'a
lifetime parameter from the impl
instead.
impl<'a> Foo<'a> {
fn bar(&'a self, other:&'a int) -> Foo<'a> { // '
return Foo { value: Some(other), parent: Some(self) };
}
}
'a
will be inferred by the compiler to be the smallest lifetime in which all references are valid.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With