Storing a function pointer taking generic parameter slice in struct - rust

I'm in a situation where I need to store away a function that implements a trait into a struct. Here is some reduced code
struct Node<T>{
compute_func: Box<dyn Fn(&[T]) -> T>
}
impl<T: Debug + 'static> OtherHoldingStruct<T> {
pub fn create_node<F: Fn(&[T]->T>(..., _compute_func: F) {
Node {
compute_function: Box::new(_compute_func),
//~~~~ the parameter type `impl Fn(&[T]) -> T` may not live long enough
//~~~~ ...so that the type `impl Fn(&[T]) -> T` will meet its required lifetime bounds rustc(E0310)
}
}
What I gather is that since I'm trying to accept a function type that takes a reference to a slice, the compiler is trying to create some assurances around how the lifetime of the reference to the slice will behave. What i'm not sure of is how to give it that ?
I considered adding a lifetime to create_node
impl<T: Debug + 'static> OtherHoldingStruct<T> {
pub fn create_node<'a, F: Fn(&'a [T]->T>(..., _compute_func: F) {
Node {
compute_function: Box::new(_compute_func),
//~~~~ expected a `std::ops::Fn<(&[T],)>` closure, found `impl Fn(&'a [T]) -> T`
}
}
which then seems to barf at not being able to match closures to the type.

The problem is not the slice reference — it's a lifetime requirement on the function type F itself. If you're going to store the function, then the function itself must be able to live for 'static (unless there's an explicitly permitted shorter lifetime).
The requirement actually in your code causing the compiler error appears because dyn Fn (or any other dyn) has an implicit + 'static bound if you don't specify a different lifetime. Thus, the bounds for F in create_node are Fn(&[T]) -> T but the bounds for compute_function are Fn(&[T]) -> T + 'static, creating the error you saw.
The fix is to add a 'static bound on F:
pub fn create_node<F: Fn(&[T]) -> T + 'static>(_compute_func: F) -> Node<T> {
// ^^^^^^^^^
This bound disallows passing, for example, a closure that captures a non-static reference, which has to be invalid anyway since Node could live indefinitely, unlike that reference.

Related

Inconsistency of lifetime bound requirement when storing closures

When I try to store closures to a HashMap, I come across a lifetime bound requirement reported by the compiler. It seems like an inconsistent requirement.
struct NoBox<C: Fn() -> ()>(HashMap<String, C>);
impl<C> NoBox<C>
where
C: Fn() -> (),
{
fn new() -> NoBox<C> {
NoBox(HashMap::new())
}
fn add(&mut self, str: &str, closure: C) {
self.0.insert(str.to_string(), closure);
}
}
This is Ok. The compiler is happy with it. However, when I try to wrap the closure into a trait object and store it. The compiler imposes a 'static lifetime bound on it.
struct Boxed(HashMap<String, Box<dyn Fn() -> ()>>);
impl Boxed {
fn new() -> Boxed {
Boxed(HashMap::new())
}
fn add<C>(&mut self, str: &str, closure: C)
where
C: Fn() -> ()//add 'static here fix the error
{
self.0.insert(str.to_string(), Box::new(closure)); //error: type parameter C may not live long enough, consider adding 'static lifebound
}
}
According to the complain of the compiler, C may not live long enough. It makes sense to add a 'static bound to it.
But, why the first case without boxing doesn't have this requirement?
To my understanding, if C contains some reference to an early-dropped referent, then store it in NoBox would also cause the invalid-reference problem. For me, it seems like an inconsistency.
NoBox is not a problem because if the function contains a reference to the lifetime, the type will stay contain this lifetime because the function type needs to be specified explicitly.
For example, suppose we're storing a closure that captures something with lifetime 'a. Then the closure's struct will looks like (this is not how the compiler actually desugars closures but is enough for the example):
struct Closure<'a> {
captured: &'a i32,
}
And when specifying it in NoBox, the type will be NoBox<Closure<'a>>, and so we know it cannot outlive 'a. Note this type may never be actually explicitly specified - especially with closures - but the compiler's inferred type still have the lifetime in it.
With Boxed on the other hand, we erase this information, and thus may accidentally outlive 'a - because it does not appear on the type. So the compiler enforces it to be 'static, unless you explicitly specify otherwise:
struct Boxed<'a>(HashMap<String, Box<dyn Fn() + 'a>>);

How do I return a reversed iterator?

I was writing some code where I want to use an iterator, or its reversed version depending on a flag, but the straightforward code gives an error
pub fn eggs<I,T>(iter:I)->Box<dyn Iterator<Item=T>>
where I:Iterator<Item=T>+DoubleEndedIterator
{
Box::new(iter.rev())
}
pub fn bacon<I,T>(iter:I, reverse:bool) -> Box<dyn Iterator<Item=T>>
where I:Iterator<Item=T>+DoubleEndedIterator
{
if reverse {
Box::new(iter.rev())
} else {
Box::new(iter)
}
}
fn main()
{
let pants:String = "pants".into();
eggs(pants.chars());
}
fails to compile:
error[E0310]: the parameter type `I` may not live long enough
--> src/main.rs:5:5
|
2 | pub fn eggs<I,T>(iter:I)->Box<dyn Iterator<Item=T>>
| - help: consider adding an explicit lifetime bound...: `I: 'static`
...
5 | Box::new(iter.rev())
| ^^^^^^^^^^^^^^^^^^^^ ...so that the type `Rev<I>` will meet its required lifetime bounds
With my limited understanding of Rust, I'm not sure where those lifetime bounds are coming from. There aren't any on the Iterator trait, or the Rev struct, and the parameter is being moved.
What is the proper way to declare these sorts of functions given that 'static isn't really an option.
rust playground
This doesn't have to do with .rev() at all, but with returning Box<dyn Iterator>:
// error[E0310]: the parameter type `I` may not live long enough
fn boxed_iter<I, T>(iter: I) -> Box<dyn Iterator<Item = T>>
// - help: consider adding an explicit lifetime bound...: `I: 'static`
where
I: Iterator<Item = T>,
{
Box::new(iter)
// ^^^^^^^^^^^^^^ ...so that the type `I` will meet its required lifetime bounds
}
The reason for this is that trait objects like Box<dyn Trait> have an implicit 'static lifetime if not specified. So when the compiler tries to cast Box<I> to Box<dyn Iterator>, it fails if I is does not also have a 'static lifetime. (There are some more specific rules if the trait contains lifetimes itself; you can read about those in more detail here.)
If you instead want a shorter lifetime, you need to specify it explicitly as Box<dyn 'a + Trait>. So for example:
fn boxed_iter<'a, I, T>(iter: I) -> Box<dyn 'a + Iterator<Item = T>>
where
I: 'a + Iterator<Item = T>,
{
Box::new(iter)
}
Frxstrem's answer is excellent. I just want to add that, if you know that the return value of your function has a specific concrete type, you can use the special impl trait syntax.
In the case of your eggs function, the return type is probably something like Rev<I>. That type, on its own, isn't very illuminating, but we know that such a type exists and the only thing we care about is that it's an iterator, so we can write
pub fn eggs<I,T>(iter:I) -> impl Iterator<Item=T> + DoubleEndedIterator
where I: Iterator<Item=T> + DoubleEndedIterator {
iter.rev()
}
Now the compiler still understands that there is a single concrete type and will act accordingly (no need to box the value or have dynamic dispatch), but we as the programmers still only have to care about the Iterator and DoubleEndedIterator aspects of it. Zero-cost abstraction at its finest.
Your bacon function can't benefit from this, as it could return either an I or a Rev<I> depending on input, so the dynamic dispatch is actually necessary. In that case, you'll need to follow Frxstrem's answer to correctly box your iterator.

Can you use impl Fn to accept arbitrarily-sized closures as arguments?

I have a History type which contains an initial state, a current state, and a list of closures with every change required to compute the current state from the initial state. These are applied with an .apply(...) method which takes a boxed closure, uses it to modify the current state, and adds it to the list. Because I want these to be deterministically reusable they are Fn, not FnMut or FnOnce.
struct History<State: Clone> {
initial: State,
current: State,
updates: Vec<Box<dyn Fn(&mut State)>>,
}
impl<State: Clone> History<State> {
fn apply(&mut self, update: Box<dyn Fn(&mut State)>) {
update(&mut self.current);
self.updates.push(update);
}
}
I am currently taking closures as Box<dyn Fn(&mut State)>, and it works fine:
fn main() {
let mut history = History::<usize> {
initial: 0,
current: 0,
updates: vec![],
};
let delta = 10;
history.apply(Box::new(move |mut value| *value += delta));
println!("{}", history.current);
}
10
This got me thinking about whether it would be possible for a method to accept arbitrary unboxed closures by using impl Trait instead of dyn Trait. In this case, our method could box the closures itself, so the call site would become:
history.apply(move |mut value| *value += delta);
(Please entertain the question of whether this is even possible, even if it's a bad idea in this case.)
I am imagining that each closure site is like a distinct data type, instantiated with the enclosed values each time it's used, so impl Trait could specialize the method for each implicit closure like it does for each explicit type. But I'm not sure whether Rust actually works like that.
However, when I try making the change in the code, I get a new lifetime error:
fn apply(&mut self, update: impl Fn(&mut State)) {
update(&mut self.current);
self.updates.push(Box::new(update));
}
error[E0310]: the parameter type `impl Fn(&mut State)` may not live long enough
--> src/main.rs:10:27
|
10 | self.updates.push(Box::new(update));
| ^^^^^^^^^^^^^^^^
|
note: ...so that the type `impl Fn(&mut State)` will meet its required lifetime bounds
--> src/main.rs:10:27
|
10 | self.updates.push(Box::new(update));
| ^^^^^^^^^^^^^^^^
This confuses me. I'm not sure where there's a reference that could be going bad.
In my head, the entire closure state is now being moved into apply via the impl Fn parameter, then moved into a Box that is owned by self. But it's complaining that I can't move the contents to a box, because I have a potentially-stale reference, not just owned data? Where am I borrowing anything? Why didn't this occur when I boxed the closure directly in main instead of in apply?
Is it possible to use impl Fn to accept an (arbitrarily-sized) closure as an argument? If so, how?
Can you use impl Fn to accept arbitrarily-sized closures as arguments?
Yes.
impl trait in argument position is the exact same as a generic. These are identical:
fn foo1(_: impl Fn(u8) -> i8) {}
fn foo2<F>(_: F)
where
F: Fn(u8) -> i8,
{}
This is in fact the generally preferred way to accept a closure (or many other trait implementations) because it allows the compiler to monomorphize the result and avoids any unneeded indirection.
Compiling your code has this help text (which currently has some rendering glitches):
help: consider adding an explicit lifetime bound `impl Fn(&mut State): 'static`...
|
8 | fn apply(&mut self, update: impl Fn(&mut State): 'static + {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
What it means to say is to add + 'static:
fn apply(&mut self, update: impl Fn(&mut State) + 'static)
This works.
See also:
What makes `impl Trait` as an argument "universal" and as a return value "existential"?
Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?
The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want

Trait which returns iterator with lifetime bounded by the lifetime of an argument

I have a trait which says that any implementation of Foo needs to provide a method bar which returns an object of some type which implements Iterator<Item = u32>:
trait Foo {
type FooIterator: Iterator<Item = u32>;
fn bar(&self) -> FooIterator;
}
For this case, I believe that the default lifetime elision means that the iterator returned by bar is required to live on its own, without being tied to the lifetime of the Foo it is iterating over. User Habnabit on #rust irc suggested the following way to say that the lifetime of the FooIterator is less than the lifetime of the Foo. i.e. it allows the implementation of the FooIterator to keep a reference to the Foo that it comes from:
trait Foo<'a> {
type FooIterator: Iterator<Item = u32> + 'a;
fn bar<'b: 'a>(&'b self) -> Self::FooIterator;
}
What I really want is the case where the function bar takes an additional argument, and the implementation of FooIterator is allowed to keep a reference to both the Foo and the additional argument. i.e. the lifetime of FooIterator is bounded by the lifetime of the Foo and the lifetime of the additional argument.
My literal translation of this idea would be
trait Zip {}
trait Foo<'a, 'c> {
type FooIterator: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'b: 'a, 'd: 'c>(&'b self, &'d Zip) -> Self::FooIterator;
}
But I was told there there is no "good" way to do this. What would be the best way to implement this idiom? What would the above code do exactly?
What you're looking for is associated type constructors, a planned feature that is not yet implemented in Rust. With associated type constructors, your code would look like this:
trait Zip {}
trait Foo {
type FooIterator<'a, 'c>: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'a, 'b: 'a, 'c, 'd: 'c>(&'b self, &'d Zip) -> Self::FooIterator<'a, 'c>;
}
Actually, I'm not sure all those lifetimes are necessary, because a &'a T can be coerced to a &'b T where 'a: 'b. Thus, the following might be good enough:
trait Zip {}
trait Foo {
type FooIterator<'a, 'c>: Iterator<Item = u32> + 'a + 'c;
// Foo.bar() returns an iterator that has a lifetime less than the Foo
fn bar<'a, 'c>(&'a self, &'c Zip) -> Self::FooIterator<'a, 'c>;
}
Depending on how you want to use this trait, you may be able to make it work by implementing it for &'a Struct instead of for Struct, thus "hoisting" the responsibility for finding the right lifetime from the trait into the caller.
Remove the lifetime annotation from the trait and change bar so it takes self, plus another argument of the same lifetime:
trait Foo {
type FooIterator: Iterator<Item = u32>;
fn bar(self, other: Self) -> Self::FooIterator;
}
(Removing 'a from the trait is possible because bar consumes the reference instead of reborrowing it -- self doesn't have to outlive the return value anymore because it's been moved into it.)
Then impl it for a reference of lifetime 'a:
impl<'a> Foo for &'a Vec<u32> {
type FooIterator = ...; // something presumably containing 'a
fn bar(self, other: Self) -> Self::FooIterator {
...
}
}
This works because the compiler can limit the lifetime 'a to one for which the impl applies.
Here's a playground link where bar is basically a wrapper around .chain().
I'm ignoring the Zip trait for now because how to incorporate it depends on what it provides. Instead, I suppose that bar only accepts an argument of the same type as Self. However, you can probably add it as well, maybe using the same technique if you need to.

How do I return a boxed closure from a method that has a reference to the struct?

I have a structure that contains a value and I want to obtain a function that operates on this value:
struct Returner {
val: i32,
}
impl<'a> Returner {
fn get(&'a self) -> Box<Fn(i32) -> i32> {
Box::new(|x| x + self.val)
}
}
This fails compilation:
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
--> src/main.rs:7:18
|
7 | Box::new(|x| x + self.val)
| ^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime 'a as defined on the impl at 5:1...
--> src/main.rs:5:1
|
5 | impl<'a> Returner {
| ^^^^^^^^^^^^^^^^^
= note: ...so that the types are compatible:
expected &&Returner
found &&'a Returner
= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the expression is assignable:
expected std::boxed::Box<std::ops::Fn(i32) -> i32 + 'static>
found std::boxed::Box<std::ops::Fn(i32) -> i32>
This is because the closure borrows self, which is fine by me, because I don't intend to use the obtained function after the struct is destroyed. From what I've gathered so far, there's two ways to make it possible:
Use the move keyword. I don't want to use it because it will take ownership on the object, and want I to use it after it has returned this function.
Explicitly specify the lifetime of the closure to tell the compiler that it has the same lifetime as the struct it was called from.
I think that 2 is the correct way in my situation, but I've not been able to find out how to specify the closure's lifetime. Is there a direct way to do it or I've got it all wrong and it contradicts Rust lifetime logic?
In general, you can specify the lifetime of a boxed trait object by writing Box<Trait + 'a> and analogously for trait objects behind other kinds of pointers (if it's omitted, it defaults to 'static at least in the case of Box). So in this specific case you want the return type Box<(Fn(i32) -> i32) + 'a>.
However, when you do that you will see another error about self not living long enough. The reason is that (without move) the closure will capture a reference to the local variable self. The solution is to use move. This does not move the Returner object, it moves self which is a reference to the Returner object.
In summary:
struct Returner {
val: i32,
}
impl<'a> Returner {
fn get(&'a self) -> Box<Fn(i32) -> i32 + 'a> {
Box::new(move |x| x + self.val)
}
}
As said in an existing answer:
Add a lifetime that ties the lifetime of self to the lifetime of the returned value.
Move the reference to self into the closure.
Since Rust 1.26, you no longer need to return a boxed closure if you are only returning a single type. Instead, you can use impl Trait:
impl Returner {
fn get<'a>(&'a self) -> impl Fn(i32) -> i32 + 'a {
move |x| x + self.val
}
}
See also:
Why is adding a lifetime to a trait with the plus operator (Iterator<Item = &Foo> + 'a) needed?
What is the correct way to return an Iterator (or any other trait)?
Returning a closure from a function
Conditionally iterate over one of several possible iterators

Resources