How do you combine lifetimes in rust? - rust

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)

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.

Related

Lifetime of a closure stored in a struct

I tried to apply the answers of this question to my situation, but failed to do so.
I assume, what I try to do is much simpler.
Before I explain, first a bit of code:
type Coord = (usize, usize);
pub struct SquareIterator {
upper_bounds: Coord,
direction: Coord,
predicate: Box<dyn Fn(Coord) -> bool>,
current: Coord,
}
impl SquareIterator {
pub fn new<P>(
start_square: Coord,
direction: Coord,
board_size: Coord,
predicate: P,
) -> SquareIterator
where
P: Fn(Coord) -> bool,
{
SquareIterator {
upper_bounds: board_size,
direction,
predicate: Box::new(predicate),
current: start_square,
}
}
}
impl Iterator for SquareIterator {
type Item = Coord;
fn next(&mut self) -> Option<Self::Item> {
let next = (
self.current.0 + self.direction.0,
self.current.1 + self.direction.1,
);
if next.0 >= self.upper_bounds.0 || next.1 >= self.upper_bounds.1 {
None
} else if (self.predicate)(next) {
self.current = next;
Some(self.current)
} else {
None
}
}
}
It is about the predicate closure, which is supposed to live as long as the containing struct SquareIterator lives.
Application code, using this looks along the lines of:
struct Board {
// ... some members ...
}
impl Board {
pub fn queen_moves(&self, queen_coord: (u8, Coord)) -> SquareIterator {
SquareIterator::new(queen_coord.1, (0, 1), self.board_size(), |coord| {
self.squares[coord.1][coord.0] == sv::EMPTY
})
}
}
After fiddling for two hours, reading up the lifetimes chapter in the rust book a couple of times, looking at the above mentioned other stackoverflow question, I am still at a loss as to how to teach Rust, that my closure will be alive as long as the iterator...
While I am aware, that there is lifetime stuff missing, the current ccode yields the error:
> error[E0310]: the parameter type `P` may not live long enough
--> amazons-engine/src/lib.rs:33:18
|
33 | predicate: Box::new(predicate),
| ^^^^^^^^^^^^^^^^^^^ ...so that the type `P` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
29 | P: Fn(Coord) -> bool + 'static, {
| +++++++++
For more information about this error, try `rustc --explain E0310`.
So, how to do it?
You need to tell the compiler that SquareIterator might be bound to a specific lifetime. Closures that borrow from their environment aren't 'static, but a struct without a lifetime parameter is assumed to reference nothing (e.g. be 'static) itself. This makes your closure that borrows self incompatible with the implied-static lifetime.
Step one is to tell the compiler that SquareIterator might refer to an external lifetime, and that this lifetime belongs to the closure type:
pub struct SquareIterator<'a> {
upper_bounds: Coord,
direction: Coord,
predicate: Box<dyn 'a + Fn(Coord) -> bool>,
current: Coord,
}
Step two is to adjust the constructor function similarly, which binds together the lifetime of the supplied closure with that of the returned SquareIterator, allowing the compiler to infer 'a for you:
impl SquareIterator<'_> {
pub fn new<'a, P>(
start_square: Coord,
direction: Coord,
board_size: Coord,
predicate: P,
) -> SquareIterator<'a>
where
P: 'a + Fn(Coord) -> bool,
{ ... }
}
Finally, the impl Iterator block needs to include a lifetime, but we can use the "please infer me" lifetime '_ since the implementation doesn't depend on it:
impl Iterator for SquareIterator<'_> { ... }
(Playground, which has some placeholder types/methods to allow it to compile.)

What does a static lifetime of a Fn closure type mean?

The following error goes away if I do as rustc tells me to, and change the bound to
where F: Fn() -> () + 'static
pub struct Struct(Box<dyn Fn() -> ()>);
pub fn example<F>(f: F)
where
F: Fn() -> ()
{
Struct(Box::new(|| ())); // ok
Struct(Box::new(f)); // error: The parameter type `F` may not live long eneough
// help: consider adding an explicit lifetime bound: `F: 'static`
}
However, I just don't understand what 'static means here. It doesn't seem to mean that the closure itself lives forever.
At least, it doesn't seem like the closure lives forever. If I have the closure own some data, that data gets dropped at some point, which makes me suspect the closure is dropped:
pub struct Struct(Box<dyn Fn() -> ()>);
#[derive(Debug)]
struct DropMe;
impl Drop for DropMe {
fn drop(&mut self) {
println!("dropped");
}
}
/// prints:
/// "got DropMe"
/// "got DropMe"
/// "dropped"
/// "end of program"
pub fn main() {
let d = DropMe;
example(move || {
println!("got {:?}", d);
});
println!("end of program")
}
pub fn example<F>(f: F)
where
F: Fn() -> () + 'static
{
let s = Struct(Box::new(f));
s.0();
s.0();
}
Is 'static an upper bound on the lifetime of the closure rather than a lower bound? 'static makes sense as an upper bound, then example would be saying "give me a function that I can hold on to as long as I want", which in practice can be less than the 'static lifetime.
How can I tell when + 'lifetime adds 'lifetime as an upper bound vs. a lower bound?
The Rustonomicon chapter on subtyping+variance doesn't seem to cover this, and I couldn't find the information I was looking for in the Rust Book or Rust Reference.
T: 'a isn't a constraint on the lifetime of instances of T, but of things that T borrows (if any): that is, all borrows in T must outlive 'a.
Thus F: Trait + 'static requires that any borrows in F be for 'static lifetime (or longer, which doesn't exist), regardless that Trait in this case is Fn() -> ().
In your case, the closure takes ownership of d (and borrows the &'static str literal); hence it satisfies F: 'static. But if instead of move || ... the closure merely borrowed d with || ..., then it would not be able to satisfy 'static (as the lifetime of the borrow of d cannot exceed the scope of the call to main).
Yes, 'static is an upper bound.
In fact, all lifetimes constraints are upper bounds. For the callee (that is, whom uses the lifetime).
For the caller (i.e. the provider of the lifetime), on the other hand, they're usually lower bounds: give me something that lives at least as 'static (of course, nothing lives more than 'static, so it actually means "give me something 'static". But it does matter when talking about other lifetimes).
Variance is about changing the caller's respect regarding the lifetime: whether it can pass a longer lifetime (covariance, i.e. a lower bound), a shorter lifetime (contravariance, i.e. an upper bound), or only exactly this lifetime (invariance).
The 'static variant is not obligatory. It seems like it's just what compiler proposes by default.
The two edits below should hopefully illustrate the capture of lifetime of variables from environment.
//rustc 1.62.1 (e092d0b6b)
pub struct Struct<'a>(Box<dyn Fn() -> () + 'a>);
#[derive(Debug)]
struct DropMe;
impl Drop for DropMe {
fn drop(&mut self) {
println!("dropped");
}
}
pub fn main() {
let d = DropMe;
let x = 43;
example(move || {
println!("got {:?} {:?}", d, &x);
});
println!("end of program")
}
pub fn example<'env_borrow, F>(f: F)
where
F: Fn() -> () + 'env_borrow
{
let s = Struct(Box::new(f));
s.0();
s.0();
// drop(s);
println!("end of higher bound func");
}
// rustc 1.62.1 (e092d0b6b)
pub struct Struct<'a>(Box<dyn FnMut() -> () + 'a>);
#[derive(Debug)]
struct DropMe;
impl Drop for DropMe {
fn drop(&mut self) {
println!("dropped");
}
}
pub fn main() {
let d = DropMe;
let mut x = 43;
let y = &mut x;
let closure_boxed = example(move || {
println!("got {:?} , *&mut x = {:?}", d, y);
*y += 1;
});
// drop(x);
// ^ ERROR: try uncomment
drop(closure_boxed);
// ^ ERROR: try comment
println!("x is now {}", x);
println!("end of program")
}
pub fn example<'env_borrow, F>(f: F) -> Struct<'env_borrow>
where
F: FnMut() -> () + 'env_borrow
{
let mut s = Struct(Box::new(f));
s.0();
s.0();
s
}
I believe the issue is that Struct has no generic lifetime parameters, which means the lifetime of one of its instances has no name. The function an instance of Struct boxes (obviously) has to live as long as the instance, but the only way to guarantee that to the compiler is to give it a named lifetime. But because Struct has no lifetime parameters, the only named lifetime that is guaranteed to last sufficient long is 'static.
To fix this, you can give Struct a generic lifetime parameter and then constrain the boxed function to also only live that long.
pub struct Struct<'a>(Box<dyn 'a + Fn() -> ()>);
pub fn example<F>(f: F)
where
F: Fn() -> (),
{
Struct(Box::new(|| ())); // ok
Struct(Box::new(f)); // also ok; 'a will be inferred from the lifetime of f
}

What is the difference between '&self' and '&'a self'?

I recently had an error which was simply resolved by changing
impl<'a> Foo<'a> {
fn foo(&'a self, path: &str) -> Boo<'a> { /* */ }
}
to
impl<'a> Foo<'a> {
fn foo(&self, path: &str) -> Boo { /* */ }
}
which did not make sense according to my understanding, as I thought that the second version is exactly the same as the first with applied lifetime elision.
In case we introduce a new lifetime for the method this seems to be the case according this example from the nomicon.
fn get_mut(&mut self) -> &mut T; // elided
fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded
So what are the differences between this and my first code snipped.
Lifetime 'a in fn foo(&'a self, ...) ... is defined for impl<'a>, that is it is the same for all foo calls.
Lifetime 'a in fn get_mut<'a>(&'a mut self) ... is defined for the function. Different calls of get_mut can have different values for 'a.
Your code
impl<'a> Foo<'a> {
fn foo(&'a self, path: &str) -> Boo<'a> { /* */ }
}
is not the expansion of elided lifetime. This code ties lifetime of borrow &'a self to the lifetime of structure Foo<'a>. If Foo<'a> is invariant over 'a, then self should remain borrowed as long as 'a.
Correct expansion of elided lifetime is
impl<'a> Foo<'a> {
fn foo<'b>(&'b self, path: &str) -> Boo<'b> { /* */ }
}
This code doesn't depend on variance of structure Foo to be able to borrow self for shorter lifetimes.
Example of differences between variant and invariant structures.
use std::cell::Cell;
struct Variant<'a>(&'a u32);
struct Invariant<'a>(Cell<&'a u32>);
impl<'a> Variant<'a> {
fn foo(&'a self) -> &'a u32 {
self.0
}
}
impl<'a> Invariant<'a> {
fn foo(&'a self) -> &'a u32 {
self.0.get()
}
}
fn main() {
let val = 0;
let mut variant = Variant(&val);// variant: Variant<'long>
let mut invariant = Invariant(Cell::new(&val));// invariant: Invariant<'long>
{
let r = variant.foo();
// Pseudocode to explain what happens here
// let r: &'short u32 = Variant::<'short>::foo(&'short variant);
// Borrow of `variant` ends here, as it was borrowed for `'short` lifetime
// Compiler can do this conversion, because `Variant<'long>` is
// subtype of Variant<'short> and `&T` is variant over `T`
// thus `variant` of type `Variant<'long>` can be passed into the function
// Variant::<'short>::foo(&'short Variant<'short>)
}
// variant is not borrowed here
variant = Variant(&val);
{
let r = invariant.foo();
// compiler can't shorten lifetime of `Invariant`
// thus `invariant` is borrowed for `'long` lifetime
}
// Error. invariant is still borrowed here
//invariant = Invariant(Cell::new(&val));
}
Playground link

Mutable versus immutable lifetime

I have a program that I'm not sure how to reason about. I have a concrete lifetime defined on a trait A, and A is parameterized by a type T : A. One of the trait functions refine of A takes a &'a mut self parameter and returns a Vec<T>.
Suppose I have two structs U and V, such that V has a field with type &'a U, and U has a refine defined as:
fn refine(&'a mut self) -> Vec<V<'a>>
If I construct a V directly in the body of U's implementation of refine, the compiler tells me that self doesn't live long enough. However, if I construct a V within an implementation function of U, let's call it make_v, whose signature is:
fn make_v<'a>(&'a self) -> V<'a>
It seems to work OK. I'm confused about how the lifetime requirements of the two are different. I have a rust playground working example, and here it is again for posterity:
trait A<'a, T : A<'a> = Self> {
fn refine(&'a mut self) -> Vec<T>;
}
#[derive(Clone, Debug)]
struct V<'a> {
u: &'a U,
id: usize
}
#[derive(Debug)]
struct U {
id: u64
}
impl U {
fn make_v<'a>(&'a self, i: usize) -> V<'a> {
V { u: &self, id: i }
}
}
impl<'a> A<'a> for V<'a> {
fn refine(&'a mut self) -> Vec<V<'a>> {
vec![self.clone(), self.clone(), self.clone()]
}
}
impl<'a> A<'a, V<'a>> for U {
fn refine(&'a mut self) -> Vec<V<'a>> {
let mut v = Vec::new();
for i in 0..3 {
// This doesn't compile
// v.push(V { u: &self, id: i });
// This does compile...?
v.push(self.make_v(i));
}
v
}
}
fn main() {
let mut u = U { id: 0 };
println!("{:?}", u.refine());
}
v.push(V { u: &self, id: i });
is invoking auto-deref, so ends up as
v.push(V { u: *&self, id: i });
&self is a &'k &'a mut T where 'k is the scope of the &mut pointer (not the pointed-to object). This means that borrows into this are at times restricted.
The self.make_ref version does this differently; as a reborrow. This looks like &*self. In this case the outer reference is into an object with lifetime 'a, so can be of lifetime 'a.
Just writing self in this case will have this handled automatically.
Credit goes to to #fjh for his comment, which clarified things nicely.

How do you write a trait that returns an iterator?

Broadly speaking my goal is this:
For some known type Bar...
Have a trait Foo with a function: get_iterator<T>() -> T where T: Iterator<Item = Bar>
The instance of the iterator borrows the original object Foo is implemented on.
I imagine it working like this:
let mut foo = Foo;
let bar = foo.get_iterator();
foo.mutable_call(); // <-- This fails, because foo is borrowed in bar
for x in bar {
...
}
So, that's the goal, and here's my attempt, which I can't seem to get working:
struct ValsFromT<'a, T: 'a> {
parent:&'a T,
offset: usize,
}
struct Val;
trait HasValsIterator<T> {
fn val_iterator(&self) -> T where T: Iterator<Item = Val>;
}
struct Foo;
impl<'a> Iterator for ValsFromT<'a, Foo> {
type Item = Val;
fn next(&mut self) -> Option<Val> {
return None;
}
}
impl<'a> HasValsIterator<ValsFromT<'a, Foo>> for Foo {
fn val_iterator(&'a self) -> ValsFromT<'a, Foo> {
return ValsFromT {
offset: 0usize,
parent: self
};
}
}
fn takes_vals<T>(instance:T) where T: HasValsIterator<T> {
// ...
}
#[test]
fn test_foo() {
let x = Foo;
takes_vals(x);
}
(playpen: http://is.gd/wys3fx)
We're getting the dreaded concrete/bound lifetime error here, because of trying to return an iterator instance that references self from the trait function:
<anon>:22:3: 27:4 error: method `val_iterator` has an incompatible type for trait:
expected bound lifetime parameter ,
found concrete lifetime [E0053]
<anon>:22 fn val_iterator(&'a self) -> ValsFromT<'a, Foo> {
<anon>:23 return ValsFromT {
<anon>:24 offset: 0usize,
<anon>:25 parent: self
<anon>:26 };
<anon>:27 }
<anon>:22:3: 27:4 help: see the detailed explanation for E0053
Is there some way of doing this?
Unfortunately, Veedrac's suggestion doesn't work directly. You will get the following error if you'd try to use val_iterator() method on instance inside takes_vals():
<anon>:31:25: 31:39 error: the trait `core::iter::Iterator` is not implemented for the type `U` [E0277]
<anon>:31 let iter = instance.val_iterator();
^~~~~~~~~~~~~~
<anon>:31:25: 31:39 help: see the detailed explanation for E0277
<anon>:31:25: 31:39 note: `U` is not an iterator; maybe try calling `.iter()` or a similar method
error: aborting due to previous error
playpen: application terminated with error code 101
This (and some other further errors) requires changing the signature of the function to this one:
fn takes_vals<'a, T: 'a, U: Iterator<Item=Val>+'a>(instance: T) where T: HasValsIterator<'a, U>
However, even this doesn't work yet:
<anon>:31:16: 31:24 error: `instance` does not live long enough
<anon>:31 let iter = instance.val_iterator();
^~~~~~~~
<anon>:30:97: 32:2 note: reference must be valid for the lifetime 'a as defined on the block at 30:96...
<anon>:30 fn takes_vals<'a, T: 'a, U: Iterator<Item=Val>+'a>(instance: T) where T: HasValsIterator<'a, U> {
<anon>:31 let iter = instance.val_iterator();
<anon>:32 }
<anon>:30:97: 32:2 note: ...but borrowed value is only valid for the scope of parameters for function at 30:96
<anon>:30 fn takes_vals<'a, T: 'a, U: Iterator<Item=Val>+'a>(instance: T) where T: HasValsIterator<'a, U> {
<anon>:31 let iter = instance.val_iterator();
<anon>:32 }
Remember that the trait requires that val_iterator() accepts the target by reference with lifetime 'a. This lifetime in this function is an input parameter. However, when val_iterator() is called on instance, the only lifetime which can be specified for the reference is the one of instance which is strictly smaller than any possible 'a as a parameter, because it is a local variable. Therefore, it is not possible to pass instance by value; you can only pass it by reference for lifetimes to match:
fn takes_vals<'a, T: 'a, U: Iterator<Item=Val>+'a>(instance: &'a T) where T: HasValsIterator<'a, U> {
let iter = instance.val_iterator();
}
This works.
I'd like to add that using associated types instead of type parameters would be more correct semantically:
trait HasValsIterator<'a> {
type Iter: Iterator<Item=Val> + 'a;
fn val_iterator(&'a self) -> Self::Iter;
}
impl<'a> HasValsIterator<'a> for Foo {
type Iter = ValsFromT<'a, Foo>;
fn val_iterator(&'a self) -> ValsFromT<'a, Foo> { ... }
}
fn takes_vals<'a, T: 'a>(instance: &'a T) where T: HasValsIterator<'a> {
...
}
I say that this is more correct because the type of the iterator is determined by the implementor, that is, it is "output" type, which are modeled by associated types. As you can see, takes_vals() signature also shrank considerably.
Ideally, HasValsIterator trait should have been defined like this:
trait HasValsIterator {
type Iter<'a>: Iterator<Item=Val> + 'a
fn val_iterator<'a>(&'a self) -> Iter<'a>;
}
This way, val_iterator() would in any situation, including when HasValsIterator implementor is passed by value. However, Rust is not there yet.

Resources