Difference between "Self" and elided lifetime - rust

Don't know how to describe, but here is the minimal reproducing snippet (also on playground):
struct Ctx(Vec<Box<dyn Fn(&mut MockCtx)>>);
struct MockCtx<'a>(&'a mut Ctx);
impl MockCtx<'_> {
// this works
fn push<F: 'static + Fn(&mut MockCtx)>(&mut self, f: F) {
self.0.0.push(Box::new(f));
}
}
trait Push {
fn push<F: 'static + Fn(&mut Self)>(&mut self, f: F);
}
impl Push for MockCtx<'_> {
// fn push<F: 'static + Fn(&mut Self)>(&mut self, f: F) { (1)
fn push<F: 'static + Fn(&mut MockCtx)>(&mut self, f: F) { // (2)
MockCtx::push(self, f)
}
}
If I take (1), compiler reports
expected a `Fn<(&mut MockCtx<'_>,)>` closure, found `F`
and recommand to add explicit lifetime to restrict. If I do so or simply take (2), compiler reports
impl has stricter requirements than trait
The truth is I do not understand the problem from starting point... I understand what is lifetime and simple "outlive" rules, and to me compiler should have no worry about 'a for these code so far, because that can only be a thing when these Box<dyn Fn> actually get called.
Thanks for any replying and explaining!

This is understandably a little opaque, because several lifetimes are elided. It should become a little more clear if we write those out.
Let's first look at the container:
struct Ctx(Vec<Box<dyn Fn(&mut MockCtx)>>);
This is short for:
struct Ctx(Vec<Box<dyn for<'a, 'b> Fn(&'a mut MockCtx<'b>) + 'static>>);
The 'static requires that any closure you put in the container must own its data. It cannot refer to other data that depends on a lifetime. The elided lifetime is 'static because Box has no lifetime parameters.
The construct for<'a, 'b> Fn(&'a mut MockCtx<'b>) is a higher rank trait bound, or HRTB for short. This means that any closure you put in the container must be able to accept a mutable reference with any lifetime to a MockCtx<'_> with any lifetime parameter. Note for later that there are two lifetime parameters here.
Now, let's go to the impl:
impl MockCtx<'_> {
fn push<F: 'static + Fn(&mut MockCtx)>(&mut self, f: F);
}
This is short for:
impl MockCtx<'_> {
fn push<F: 'static + for<'a, 'b> Fn(&'a mut MockCtx<'b>)>(&mut self, f: F);
}
The requirements on the closure are the same as for the container. No conflicts. But note that the closure must accept types other than Self. It must accept a mutable reference to a MockCtx<'_> with a different lifetime.
Now let's go to the trait:
trait Push {
fn push<F: 'static + Fn(&mut Self)>(&mut self, f: F);
}
This is short for:
trait Push {
fn push<F: 'static + for<'a> Fn(&'a mut Self)>(&mut self, f: F);
}
This is going to conflict. A closure given to Push can only be called with a reference to Self, of any lifetime. But a closure that we store in our container must accept some types other than Self. There is a whole range of Self-like types that have different lifetime parameters that the closure needs to accept for our container.
Now, we can explain why the two attempts failed. #1 fails because the closure in the trait and the trait impl can only accept Self, but we're attempting to store it in a container that holds closures that can accept MockCtx<'_> of any lifetime. #2 fails because the trait only requires the closure accept Self, but our trait impl tries to accept a more restrictive set of closures that can accept MockCtx<'_> for any lifetime.
There are several ways to fix this, but they all amount to fixing this disagreement on the bounds for the argument of the closure.
One way is to explicitly accept the full range of types in the closure bound. You have to mention types other than Self. So, for example, you can mention the concrete types:
trait Push {
fn push<F: 'static + Fn(&mut MockCtx<'_>)>(&mut self, f: F);
}
See here.
Using a nightly feature of trait aliases, you could use a common name for all the requirements:
trait MockFn = 'static + Fn(&mut MockCtx<'_>);
trait Push {
fn push<F: MockFn>(&mut self, f: F);
}
See here.
Using a nightly feature of generic associated types, you could also make the trait slightly more general:
trait Push {
type SelfLike<'a>;
fn push<F: 'static + Fn(&mut Self::SelfLike<'_>)>(&mut self, f: F);
}
See here.
Alternatively, as #Jmb says in the comments, you can make the closure a generic parameter on the trait:
trait Push<F> {
fn push(&mut self, f: F);
}
See here.
This is a relatively clean approach. It is asserted in the comments that this is useless, but I don't think it is.

Related

Rust lifetimes for implementing a trait on nested slices

I want to create a wrapper around (nested) slices for easy operations on multidimensional data, owned by a different struct.
The most basic version of the mutable version of my slice wrapper might look like this:
struct MySliceMut<'a> {
data: Vec<&'a mut [f32]>,
}
impl<'a, 'b> MySliceMut<'a> {
fn get(&'b mut self) -> &'a mut [&'b mut [f32]] {
self.data.as_mut_slice()
}
}
Now if I want to implement a trait, for instance AddAssign, Rust does not seem to infer the lifetime of &mut self from the implementing type. The compiler complains that &mut self might outlive 'a:
impl<'a> AddAssign<MySlice<'a>> for MySliceMut<'a> { // lifetime 'a
fn add_assign(&mut self, rhs: MySlice<'a>) { // lifetime '1
let a = self.get(); // lifetime may not live long enough, '1 must outlive 'a
let b = rhs.get();
// do inplace addition here
}
}
Full Code - Rust Playground
I tried to figure out the issue with the lifetimes, but can't find it. Would the trait impl require any additional annotations?
struct MySlice<'a> {
data: Vec<&'a [f32]>,
}
impl<'a, 'b> MySlice<'a> {
fn get(&'b self) -> &'a [&'b [f32]] {
self.data.as_slice()
}
}
Problem with your code is that fn get(&'b self) returns variable with wrong lifetime. Associated lifetime 'a of MySlice<'a> is lifetime of inner slice. Associated lifetime 'b of fn get(...) is lifetime of the self. So I guess the function probably should return &'b [&'a [f32]] instead.
-- Edited --
Make sure to change fn get(...) of MySliceMut either.

Why do I need 'static lifetime here and how to fix it?

I have taken off all extra irrelevant code to come to this clean one to reproduce the error I am getting:
trait EventsHandler{}
struct Engine{}
struct Request{}
struct RequestHandler<'a> {
r: &'a mut Request
}
impl<'a> EventsHandler for RequestHandler<'a>{}
impl Engine {
pub fn exec(&mut self, script: &str, handler: Box<dyn EventsHandler>){ }
}
pub fn handle_request(script: &str, r: &mut Request) {
let r_h:RequestHandler =RequestHandler {r};
let handler = Box::new(r_h);
let mut engine = Engine{};
engine.exec(script, handler);
}
and the error is:
error: explicit lifetime required in the type of `r`
label: lifetime `'static` required
The error is pointing to the parameter 'handler' in the last line of code.
As far as I can see everything seems live long enough. I can't see why there is a need for static and how I can go and fix that need or rewrite the code to avoid that requirement.
Following Rust's lifetime elision rules for trait objects, a Box<dyn Trait> is in many cases shorthand for Box<dyn Trait + 'static>. The meaning of the lifetime 'a in Box<dyn Trait + 'a> is that all lifetime parameters of the type implementing Trait outlive 'a (see the reference).
The 'static can be relaxed by adding an explicit lifetime to the trait object. At minimum, this will entail an additional lifetime parameter on Engine::exec:
impl Engine {
pub fn exec<'b>(&mut self, script: &str, handler: Box<dyn EventsHandler + 'b>){ }
}
See an amended code listing on the playground
trait EventsHandler{}
struct Engine{}
pub struct Request{}
pub struct RequestHandler<'a> {
r: &'a mut Request
}
impl<'a> EventsHandler for RequestHandler<'a>{}
impl<'a> Engine {
pub fn exec(&'a mut self, script: &'a str, handler: Box<dyn EventsHandler + 'a>){ }
}
pub fn handle_request<'a>(script: &'a str, r: &'a mut Request){
let r_h:RequestHandler =RequestHandler {r};
let handler = Box::new(r_h);
let mut engine = Engine{};
engine.exec(script, handler);
}
I really don't know how to explain but this is what I did by following the tip in the error message. it can compile now.
Hope someone else can give a better explanation. :)
or you may take a look at: Box with a trait object requires static lifetime?
Though trait objects like dyn EventsHandler erase the type at runtime, they still need to have information about the lifetime of the type so that it can be used in the type system. You can specify the lifetime explicitly with dyn EventsHandler + 'lifetime, but it can also be elided, in which case Rust uses the following rule:
If the trait object is used as a type argument of a generic type then the containing type is first used to try to infer a bound.
If there is a unique bound from the containing type then that is the default
If there is more than one bound from the containing type then an explicit bound must be specified
If neither of those rules apply, then the bounds on the trait are used:
If the trait is defined with a single lifetime bound then that bound is used.
If 'static is used for any lifetime bound then 'static is used.
If the trait has no lifetime bounds, then the lifetime is inferred in expressions and is 'static outside of expressions.
(Source: Lifetime elision, Rust reference)
In this case, the containing type Box<_> has no lifetimes, the trait EventsHandler has no lifetime bounds, and the type Box<dyn EventsHandler> is used in a function signature (so outside of any expressions), so the lifetime is inferred as 'static. In other words, Box<dyn EventsHandler>, in this code, is equivalent to Box<dyn EventsHandler + 'static> by the above rules, and can only contain values with a 'static lifetime, which RequestHandler<'a> is not.
If you want your Box<dyn EventsHandler> to be able to contain values with a shorter lifetime than 'static, you should add an explicit lifetime:
pub fn exec<'h>(&mut self, script: &str, handler: Box<dyn EventsHandler + 'h>)
// ^^^^ ^^^^^
// alternatively:
pub fn exec(&mut self, script: &str, handler: Box<dyn EventsHandler + '_>)
// ^^^^^

How to create an `Iterable` trait for references in Rust?

I'm trying to create a trait that captures the iter function in slice as well as VecDeque, BTreeMap and HashMap. I'd like the implementer of this trait to be able to specify and implement their own iterator type, but it looks like this iterator type must have a lifetime argument, and that cannot be given as an associated type.
In more detail, here's what I wish was possible in Rust:
trait RefIterable<T>
where for<'a> (T: 'a) => (Self::Iter<'a>: Iterator<Item = &'a T>)
{
type Iter; // Has kind (lifetime -> type)
fn refs<'a>(&'a self) -> Self::Iter<'a>
}
If this was possible, the implementation could look like this
impl RefIterable<T> for Vec<T> {
type Iter<'a> = std::slice::Iter<'a, T>; // This is not valid Rust code.
fn refs<'a>(&'a self) -> std::slice::Iter<'a, T> {
self.as_slice().iter()
}
}
I'm still relatively new to Rust, so I'm asking if there's already a way to do this that I'm not aware of, or if there's a nice workaround for this situation. I'd imagine that this situation is not very rare.
(Using Box<dyn 'a + Iterator<Item = &'a T>> is my current workaround, but that prevents some optimization from happening.)
Edit:
EvilTak's answer is probably the best thing we can do right now. The ability to combine all possible lifetimes together with the condition T: 'a into one unparametrized trait seems to be unsupported by Rust as of today.
Add the lifetime parameter to the trait instead, which allows you to use it in the associated type Iter's bound:
trait RefIterable<'a> {
type Item: 'a;
type Iter: Iterator<Item = &'a Self::Item>; // Has kind (lifetime -> type)
fn refs(&'a self) -> Self::Iter;
}
The Item: 'a bound is required to let the compiler know that the references (&'a Self::Item) do not outlive the type (Self::Item).
I have modified RefIterable to make it follow Iterator's convention of using an associated type to specify the type of the items that are iterated over for the same reason as the one behind Iterator's usage of an associated type.
Implementations are pretty straightforward:
impl<'a, T: 'a> RefIterable<'a> for Vec<T> {
type Item = T;
type Iter = std::slice::Iter<'a, T>;
fn refs(&'a self) -> std::slice::Iter<'a, T> {
self.as_slice().iter()
}
}
Playground

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.

Resources