Reputation: 11875
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?
Upvotes: 0
Views: 428
Reputation: 168998
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.)
Upvotes: 2