Lifetime of a closure stored in a struct - rust

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.)

Related

Issues setting lifetimes in functions of Traits

I have a trait Atom that has many associated types, of which one is an owned version OP and the other is a borrow version O of essentially the same data. I have a function to_pow_view that creates a view from an owned version and I have an equality operator.
Below is an attempt:
pub trait Atom: PartialEq {
// variants truncated for this example
type P<'a>: Pow<'a, R = Self>;
type OP: OwnedPow<R = Self>;
}
pub trait Pow<'a>: Clone + PartialEq {
type R: Atom;
}
#[derive(Debug, Copy, Clone)]
pub enum AtomView<'a, R: Atom> {
Pow(R::P<'a>),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum OwnedAtom<R: Atom> {
Pow(R::OP),
}
pub trait OwnedPow {
type R: Atom;
fn some_mutable_fn(&mut self);
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a>;
// compiler said I should add 'a: 'b
fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
if self.to_pow_view().eq(&other) {
self.some_mutable_fn();
}
}
}
impl<R: Atom> OwnedAtom<R> {
// compiler said I should add 'a: 'b, why?
pub fn eq<'a: 'b, 'b>(&'a self, other: AtomView<'b, R>) -> bool {
let a: AtomView<'_, R> = match self {
OwnedAtom::Pow(p) => {
let pp = p.to_pow_view();
AtomView::Pow(pp)
}
};
match (&a, &other) {
(AtomView::Pow(l0), AtomView::Pow(r0)) => l0 == r0,
}
}
}
// implementation
#[derive(Debug, Copy, Clone, PartialEq)]
struct Rep {}
impl Atom for Rep {
type P<'a> = PowD<'a>;
type OP = OwnedPowD;
}
#[derive(Debug, Copy, Clone, PartialEq)]
struct PowD<'a> {
data: &'a [u8],
}
impl<'a> Pow<'a> for PowD<'a> {
type R = Rep;
}
struct OwnedPowD {
data: Vec<u8>,
}
impl OwnedPow for OwnedPowD {
type R = Rep;
fn some_mutable_fn(&mut self) {
todo!()
}
fn to_pow_view<'a>(&'a self) -> <Self::R as Atom>::P<'a> {
PowD { data: &self.data }
}
}
fn main() {}
This code gives the error:
27 | fn test<'a: 'b, 'b>(&'a mut self, other: <Self::R as Atom>::P<'b>) {
| -- lifetime `'b` defined here
28 | if self.to_pow_view().eq(&other) {
| ------------------
| |
| immutable borrow occurs here
| argument requires that `*self` is borrowed for `'b`
29 | self.some_mutable_fn();
| ^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
I expect this to work, since the immutable borrow should be dropped right after the eq function evaluates.
Something in the setting of the lifetimes is wrong in this code, already in the equality function eq: I would expect that there is no relation between 'a and 'b; they should just live long enough to do the comparison. However, the compiler tells me that I should add 'a: 'b and I do not understand why. The same thing happened for the function test.
These problems lead me to believe that the lifetimes in to_pow_view are wrong, but no modification I tried made it work (except for removing the 'a lifetime on &'a self, but then the OwnedPowD does not compile anymore).
Link to playground
Can someone help understand what is going on?
Here's the point: you constrained Pow to be PartialEq. However, PartialEq is PartialEq<Self>. In other words, Pow<'a> only implements PartialEq<Pow<'a>> for the same 'a.
This is usually the case for any type with lifetime and PartialEq, so why does it always work but not here?
It usually works because if we compare T<'a> == T<'b>, the compiler can shrink the lifetimes to the shortest of the two and compare that.
However, Pow is a trait. Traits are invariant over their lifetime, in other words, it must stay exactly as is, not longer nor shorter. This is because they may be used with invariant types, for example Cell<&'a i32>. Here's an example of how it could be exploited if this was allowed:
use std::cell::Cell;
struct Evil<'a> {
v: Cell<&'a i32>,
}
impl PartialEq for Evil<'_> {
fn eq(&self, other: &Self) -> bool {
// We asserted the lifetimes are the same, so we can do that.
self.v.set(other.v.get());
false
}
}
fn main() {
let foo = Evil { v: Cell::new(&123) };
{
let has_short_lifetime = 456;
_ = foo == Evil { v: Cell::new(&has_short_lifetime) };
}
// Now `foo` contains a dangling reference to `has_short_lifetime`!
dbg!(foo.v.get());
}
The above code does not compile, because Evil is invariant over 'a, but if it would, it would contain UB in safe code. For that reason, traits, that may contain types such as Evil, are also invariant over their lifetimes.
Because of that, the compiler cannot shrink the lifetime of other. It can shrink the lifetime of self.to_pow_view() (in test(), eq() is similar), because it doesn't really shrink it, it just picks a shorter lifetime for to_pow_view()'s self. But because PartialEq is only implemented for types with the same lifetime, it means that the Pow resulting from self.to_pow_view() must have the same lifetime of other. Because of that, (a) 'a must be greater than or equal to 'b, so we can pick 'b out of it, and (b) by comparing, we borrow self for potentially whole 'a, because it may be that 'a == 'b and therefore the comparison borrows self for 'a, so it is still borrowed immutably while we borrow it mutably for some_mutable_fn().
Once we understood the problem, we can think about the solution. Either we require that Pow is covariant over 'a (can be shrinked), or we require that it implements PartialEq<Pow<'b>> for any lifetime 'b. The first is impossible in Rust, but the second is possible:
pub trait Pow<'a>: Clone + for<'b> PartialEq<<Self::R as Atom>::P<'b>> {
type R: Atom;
}
This triggers an error, because the automatically-derived PartialEq does not satisfy this requirement:
error: implementation of `PartialEq` is not general enough
--> src/main.rs:73:10
|
73 | impl<'a> Pow<'a> for PowD<'a> {
| ^^^^^^^ implementation of `PartialEq` is not general enough
|
= note: `PartialEq<PowD<'0>>` would have to be implemented for the type `PowD<'a>`, for any lifetime `'0`...
= note: ...but `PartialEq` is actually implemented for the type `PowD<'1>`, for some specific lifetime `'1`
So we need to implement PartialEq manually:
impl<'a, 'b> PartialEq<PowD<'b>> for PowD<'a> {
fn eq(&self, other: &PowD<'b>) -> bool {
self.data == other.data
}
}
And now it works.

How can I return an impl Iterator that has multiple lifetimes?

I'm having trouble with lifetimes on an impl trait. I'm trying to get the following code to work:
struct Foo<'op, Input> {
op: Box<dyn Fn(Input) -> i32 + 'op>,
}
impl<'op, Input> Foo<'op, Input> {
fn new<Op>(op: Op) -> Foo<'op, Input>
where
Op: Fn(Input) -> i32 + 'op,
{
Foo { op: Box::new(op) }
}
fn apply<'input_iter, InputIter>(
self,
input_iter: InputIter,
) -> impl Iterator<Item = i32> + 'op + 'input_iter
where
InputIter: IntoIterator<Item = Input> + 'input_iter,
{
input_iter.into_iter().map(move |input| (self.op)(input))
}
}
(Playground)
This is giving me the following error:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/lib.rs:20:36
|
20 | input_iter.into_iter().map(move |input| (self.op)(input))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime 'op as defined on the impl at 5:6...
--> src/lib.rs:5:6
|
5 | impl<'op, Input> Foo<'op, Input> {
| ^^^
= note: ...so that the types are compatible:
expected Foo<'_, _>
found Foo<'op, _>
note: but, the lifetime must be valid for the lifetime 'input_iter as defined on the method body at 13:14...
--> src/lib.rs:13:14
|
13 | fn apply<'input_iter, InputIter>(
| ^^^^^^^^^^^
note: ...so that return value is valid for the call
--> src/lib.rs:16:10
|
16 | ) -> impl Iterator<Item = i32> + 'op + 'input_iter
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Here's my understanding of the lifetimes involved. Foo owns an op, which is a closure that may have a reference in it somewhere, so it may have a bound lifetime. This is denoted by 'op, and Foo is constrained such that it can't outlive it. So far, so good.
In apply(), the idea is that we want to consume an input_iter and self and return an iterator of each element in input_iter mapped using self.op. input_iterator may also contain references, so it may have its own lifetime bounds, denoted by 'input_iter.
What I want is to return an iterator that takes ownership of both self and input_iter. In doing so, it would have to take on both of their lifetime parameters to ensure that it didn't outlast either the input_iter references or the op references. I thought impl Iterator<Item = i32> + 'op + 'input_iter would accomplish this, but I seem to have taken a wrong turn somewhere.
It's also weird that it's complaining about the closure. I understand that the closure can't outlive 'op, because it takes ownership of the operator and its references. That makes perfect sense. What I don't understand is why it needs to live as long as 'input_iter. The closure and the input iterator shouldn't care about each other at all; the only thing connecting them is that they both have the same owner (the output iterator).
What am I missing here?
Lifetime parameters don't always represent the exact lifetime of an object (or of a borrow). Let's consider this example:
fn main() {
let a = 3;
let b = 5;
let c = min(&a, &b);
println!("{:?}", c);
}
fn min<'a>(a: &'a i32, b: &'a i32) -> &'a i32 {
if a < b {
a
} else {
b
}
}
Here, min takes two reference arguments with the same lifetime. However, we call it with borrows of different variables, which have different lifetimes. So why does this code compile?
The answer is subtyping and variance. Larger lifetimes are subtypes of shorter lifetimes; conversely, shorter lifetimes are supertypes of larger lifetimes. In the example above, the compiler has to find one lifetime that is compatible with both input lifetimes. The compiler does this by finding the common supertype of both lifetimes (i.e. the shortest lifetime that contains both). This is called unification.
Back to your problem. It seems like the compiler doesn't handle multiple lifetime bounds on impl Trait too well at the moment. However, there's no real need to have two lifetime bounds: one is enough. The compiler will shorten this single lifetime if necessary to satisfy the requirements of both self and input_iter.
struct Foo<'op, Input> {
op: Box<dyn Fn(Input) -> i32 + 'op>,
}
impl<'op, Input> Foo<'op, Input> {
fn new<Op>(op: Op) -> Foo<'op, Input>
where
Op: Fn(Input) -> i32 + 'op,
{
Foo { op: Box::new(op) }
}
fn apply<InputIter>(
self,
input_iter: InputIter,
) -> impl Iterator<Item = i32> + 'op
where
Input: 'op,
InputIter: IntoIterator<Item = Input> + 'op,
{
input_iter.into_iter().map(move |input| (self.op)(input))
}
}
fn main() {
let y = 1;
let foo = Foo::new(|x| x as i32 + y);
let s = "abc".to_string();
let sr: &str = &*s;
let bar = sr.chars();
let baz: Vec<_> = foo.apply(bar).collect();
println!("{:?}", baz);
}
Try moving the lines in main around to convince yourself that the lifetimes are indeed unified.
At this point, calling the lifetime 'op is somewhat misleading; it might as well be called 'a.

How do I implement std::ops::Index to mutate the value being indexed? [duplicate]

This question already has answers here:
Implementing Index trait to return a value that is not a reference
(2 answers)
Closed 5 years ago.
I want to learn how to use the Index trait for my toy roguelike but can't even get it to work in a useless, dummy scenario.
use std::ops::Index;
// Dummy struct that wraps i32
#[derive(Clone, Copy, Debug)]
pub struct Integer {
pub val: i32,
}
impl Integer {
fn new(num: i32) -> Self {
Integer { val: num }
}
}
// Using the index operator on an Integer should add the index to the Integer's value.
// let i = Integer::new(20);
// i[20];
// The above code should make it so i.val == 40
impl Index<Integer> for Integer {
type Output = i32;
// The error is in the following line:
fn index(&self, to_add: Integer) -> &Self::Output {
self.val + to_add.val;
}
}
// The code causes an error before it reaches this
fn main() {
let mut i = Integer::new(20);
let mut n = Integer::new(30);
println!("i[20] is: {:?}", i[n]);
}
I get this error:
error[E0308]: mismatched types
--> src/main.rs:23:55
|
23 | fn index(&self, to_add: Integer) -> &Self::Output {
| _______________________________________________________^
24 | | self.val + to_add.val;
25 | | }
| |_____^ expected &i32, found ()
|
= note: expected type `&i32`
found type `()`
I don't really know what I'm talking about but I guess that the value dies before it reaches the end of the function or something like that? I don't yet fully understand lifetimes.
I know this looks like a "NO IDEA WHAT IM DOING FIX PLS" question, but I'd love to know what I'm doing wrong here so I can learn from it.
Editor's note: This answer applied to the original question before the OP completely changed it. It is no longer applicable to the posted question.
The Index documentation says, emphasis mine:
The Index trait is used to specify the functionality of indexing operations like container[index] when used in an immutable context.
You are attempting to mutate the value, which is simply not possible. You can also tell this from the signature of index:
pub trait Index<Idx>
where
Idx: ?Sized,
{
type Output: ?Sized;
fn index(&self, index: Idx) -> &Self::Output;
}
There's no mut anywhere here; you cannot "implement" a trait with a completely different signature! You also cannot change the type of one of the arguments (in this case, self).
Bluntly put, Index is the wrong trait to use. Additionally, idiomatic Rust users would be really upset if you did implement the code this way; we generally aren't of the "reuse an operator for a completely different meaning" crowd.
Instead, this should just be a function with a name:
impl Integer {
fn increment(&mut self, to_add: i32) {
self.val += to_add;
}
}
Alternatively, you could implement DerefMut:
use std::ops::{Deref, DerefMut};
impl Deref for Integer {
type Target = i32;
fn deref(&self) -> &Self::Target { &self.val }
}
impl DerefMut for Integer {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.val }
}
And then use it like
let mut i = Integer::new(20);
*i += 20;
println!("i[20] is: {:?}", i);

How do you combine lifetimes in 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.

What does a lifetime reference when declared in a trait implementation?

I'm learning about named lifetimes in Rust, and I'm having trouble understanding what they represent when they are used in the implementation of a trait. Specifically, I'm having trouble understanding this piece of code from libserialize/hex.rs. I've removed some comments for brevity's sake.
pub trait ToHex {
fn to_hex(&self) -> ~str;
}
static CHARS: &'static[u8] = bytes!("0123456789abcdef");
impl<'a> ToHex for &'a [u8] {
fn to_hex(&self) -> ~str {
let mut v = slice::with_capacity(self.len() * 2);
for &byte in self.iter() {
v.push(CHARS[(byte >> 4) as uint]);
v.push(CHARS[(byte & 0xf) as uint]);
}
unsafe {
str::raw::from_utf8_owned(v)
}
}
}
I understand the 'static lifetime in the CHARS definition, but I'm stumped on the lifetime defined in the ToHex implementation. What do named lifetimes represent in the implementation of a trait?
In that particular case—not much. &[u8] is not a completely specified type because the lifetime is missing, and implementations must be for fully specified types. Thus, the implementation is parameterised over the arbitrary (for the generic parameter is unconstrained) lifetime 'a.
In this case, you don't use it again. There are cases where you will, however—when you wish to constrain a function argument or return value to the same lifetime.
You can then write things like this:
impl<'a, T> ImmutableVector<'a, T> for &'a [T] {
fn head(&self) -> Option<&'a T> {
if self.len() == 0 { None } else { Some(&self[0]) }
}
…
}
That means that the return value will have the same lifetime as self, 'a.
Incidentally, just to mess things up, the lifetime could be written manually on each function:
impl<'a, T> ImmutableVector<'a, T> for &'a [T] {
fn head<'a>(&'a self) -> Option<&'a T> {
if self.len() == 0 { None } else { Some(&self[0]) }
}
…
}
… and that demonstrates that having to specify the lifetime of the type that you are implementing for is just so that the type is indeed fully specified. And it allows you to write a little bit less for all the functions inside that use that lifetime.

Resources