How to convert lifetime of boxed reference without allocating a new box? - rust

Context
Playground, This works:
fn get_owned_box_working<'a>(b: Box<&'a i32>) -> Box<&'static i32> {
Box::new(&42)
}
but this doesn't:
fn get_owned_box_broken<'a>(b: Box<&'a i32>) -> Box<&'static i32> {
*b = &42;
b
}
error[E0308]: mismatched types
--> src/lib.rs:3:5
|
3 | b
| ^ lifetime mismatch
|
= note: expected struct `Box<&'static i32>`
found struct `Box<&'a i32>`
note: the lifetime `'a` as defined here...
--> src/lib.rs:1:25
|
1 | fn get_owned_box_broken<'a>(b: Box<&'a i32>) -> Box<&'static i32> {
| ^^
= note: ...does not necessarily outlive the static lifetime
Question
How come the compiler can't update the lifetime of the existing box from 'a -> 'static when I mutate it?
Is there some way to get this to work without the extra allocation from creating a new box?

How come the compiler can't update the lifetime of the existing box from 'a -> 'static when I mutate it?
From the compiler point of view, you cannot "update" the value from using 'a to using 'static. You are assigning to it, but the type stays the same. Then later you're trying to convert from Box<&'a i32> to Box<&'static i32>, and it rightfully bails.
Is there some way to get this to work without the extra allocation from creating a new box?
Yes, but only using unsafe: Playground
fn get_owned_box_broken<'a>(s: Box<&'a i32>) -> Box<&'static i32> {
unsafe {
let s = Box::into_raw(s);
let s = s.cast::<&'static i32>();
s.write(&42);
Box::from_raw(s)
}
}
However, think carefully if you need that: it is likely you can afford an allocation.

You cannot do that because lifetime is part of the type.
So when you assigning *b = &42;, your left part has type &'a i32 and right part has type &'static i32. Bigger lifetime can be coerced to smaller one so it is OK. But your variable b still have type with lifetime parameter 'a, it doesn't changed after assignment.
When you return b from function, you try to convert Box<&'a i32> to Box<&'static i32>. Since 'static is larger than 'a, it fails.

Related

the parameter type may not live long enough (again?) (in closure)

In trying to compile the code
fn make<DomainType>(foo: fn(DomainType) -> ()) -> Box<dyn Fn(DomainType) -> ()> {
let foo_clone = foo.clone();
let bar = move |input: DomainType| {foo_clone(input)};
Box::new(bar)
}
fn main() {}
The compiler gives
error[E0310]: the parameter type `DomainType` may not live long enough
--> src/main.rs:4:5
|
4 | Box::new(bar)
| ^^^^^^^^^^^^^ ...so that the type `[closure#src/main.rs:3:15: 3:58]` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
1 | fn make<DomainType: 'static>(foo: fn(DomainType) -> ()) -> Box<dyn Fn(DomainType) -> ()> {
| +++++++++
For more information about this error, try `rustc --explain E0310`.
I have been reading through similar questions
Parameter type may not live long enough?
and
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
But in both of those examples, the boxed trait is what needs the added lifetime. In this case it's apparently just the DomainType of the closure trait. I'm quite baffled what to make of this.
My understanding of lifetime variables in function definitions is that they connect the lifetime of the returned type (which I can understand is implicitly static here, or at least the thing inside the box is) to those (perhaps elided) lifetimes of the function parameters. But no function parameter here is a DomainType.
DISCLAIMER: This question goes very deep into Rust compiler specifics, and I'm not 100% sure about everything I say here. Take everything with a grain of salt.
The problem here is that Box<T> means that T is 'static if no lifetime is annotated.
A generic, however, does not mean that. A generic could contain any kind of lifetime. To match the two, you need an explicit lifetime annotation.
Solution 1
You could annotate 'static to the generic, like this:
fn make<DomainType: 'static>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType)> {
Box::new(foo)
}
That, however, would create problems if you do actually encounter the case where the generic carries a lifetime:
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
f(&x);
}
error[E0597]: `x` does not live long enough
--> src/main.rs:13:7
|
13 | f(&x);
| --^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
14 | }
| - `x` dropped here while still borrowed
Solution 2
The proper way here is to solve the initial mismatch by telling the compiler that the lifetimes attached to the generic match the lifetimes contained in the Box.
Like this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
And it now works:
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
f(&x);
}
my_func: abc
Problems
There are still problems with this. I'm unsure how/if they are solvable, though.
The problem is that the resulting Box object will always be bound to a specific lifetime. Look for example at this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn make2() -> Box<dyn Fn(&str)> {
Box::new(|x| println!("closure: {}", x))
}
fn main() {
let x = "abc".to_string();
let f = make(my_func);
let f2 = make2();
let fs = [f2, f];
}
error[E0308]: mismatched types
--> src/main.rs:19:19
|
19 | let fs = [f2, f];
| ^ one type is more general than the other
|
= note: expected trait object `dyn for<'r> Fn(&'r str)`
found trait object `dyn Fn(&str)`
f2 here takes all objects that can be borrowed. f only takes objects that all can be borrowed with the same lifetime, as 'a resolves when make is called, not when f is executed.
Another example is this:
fn make<'a, DomainType: 'a>(foo: fn(DomainType)) -> Box<dyn Fn(DomainType) + 'a> {
Box::new(foo)
}
fn my_func(x: &str) {
println!("my_func: {}", x);
}
fn make2() -> Box<dyn Fn(&str)> {
Box::new(|x| println!("closure: {}", x))
}
fn main() {
let f = make(my_func);
let f2 = make2();
{
let x = "abc".to_string();
f2(&x); // Works
f(&x); // Fails to compile
}
}
error[E0597]: `x` does not live long enough
--> src/main.rs:20:11
|
20 | f(&x); // Fails to compile
| ^^ borrowed value does not live long enough
21 | }
| - `x` dropped here while still borrowed
22 | }
| - borrow might be used here, when `f` is dropped and runs the destructor for type `Box<dyn Fn(&str)>`
|
= note: values in a scope are dropped in the opposite order they are defined
I don't think this is solvable, though. I think generics can't carry generic lifetimes, they can only carry specific lifetimes. And said specific lifetime gets resolved at the point where make() is called.
Although a proper explanation would require someone with a deeper Rust compiler understanding than me. If something like this exists, I'd be interested in it as well. (something like DomainType: for<'a> or similar)
I can see that one of the example's output says it would require a dyn for<'r> Fn(&'r str). I don't know, however, if something like this is annotatable by hand or if it's something internal to the compiler. But that would be what is required - instead of Box<dyn Fn(DomainType) + 'a>, we would need something like Box<dyn for <'r> Fn(DomainType + 'r)>. Again, I don't know if/how this is annotatable, and if it is, I'd also be interested in it.
Could Higher Rank Trait Bounds be what you're looking for?
fn make<F, DomainType>(foo: F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + Clone + 'r
{
let foo_clone = foo.clone();
let bar = move |input: DomainType| {foo_clone(input)};
Box::new(bar)
}
If the purpose of the function is to consume a closure and return it in a Box, I don't think you need the Clone trait. In fact, it doesn't even need to be nested in a new closure. Just directly box it.
fn make<F, DomainType>(foo: F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + 'r
{
Box::new(foo)
}
Unless you want to pass in a reference to a closure that doesn't get consumed:
fn make<F, DomainType>(foo: &F) -> Box<dyn Fn(DomainType)>
where
for<'r> F: Fn(DomainType) + Clone + 'r
{
Box::new(foo.clone())
}

Returning a higher-kinded closure that captures a reference

I'm trying to write a method on a struct that returns a closure. This closure should take a &[u8] with an arbitrary lifetime 'inner as an argument and return the same type, &'inner [u8]. To perform its function, the closure also needs a (shared) reference to a member of the struct &self. Here is my code:
#![warn(clippy::pedantic)]
// placeholder for a large type that I can't afford to clone
struct Opaque(usize);
struct MyStruct<'a>(Vec<&'a Opaque>);
impl<'a> MyStruct<'a> {
pub fn get_func_for_index(
&'a self,
n: usize,
) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
// the reference to the struct member, captured by the closure
let opaque: &'a Opaque = *self.0.get(n)?;
Some(move |i: &[u8]| {
// placeholder: do something with the input using the &Opaque
&i[opaque.0..]
})
}
}
fn main() {
let o1 = Opaque(1);
let o5 = Opaque(5);
let o7 = Opaque(7);
let x = MyStruct(vec![&o1, &o5, &o7]);
let drop_five = x.get_func_for_index(1 /*Opaque(5)*/).unwrap();
let data: Vec<u8> = Vec::from(&b"testing"[..]);
assert_eq!(drop_five(&data[..]), b"ng");
}
If I try to compile it with rustc 1.54.0 (a178d0322 2021-07-26), I get the following error:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> /tmp/lifetimes.rs:16:14
|
16 | &i[opaque.0..]
| ^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 14:14...
--> /tmp/lifetimes.rs:14:14
|
14 | Some(move |i: &[u8]| {
| ______________^
15 | | // placeholder: do something with the input using the &Opaque
16 | | &i[opaque.0..]
17 | | })
| |_________^
note: ...so that reference does not outlive borrowed content
--> /tmp/lifetimes.rs:16:14
|
16 | &i[opaque.0..]
| ^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 6:6...
--> /tmp/lifetimes.rs:6:6
|
6 | impl<'a> MyStruct<'a> {
| ^^
note: ...so that return value is valid for the call
--> /tmp/lifetimes.rs:10:17
|
10 | ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: higher-ranked subtype error
--> /tmp/lifetimes.rs:7:5
|
7 | / pub fn get_func_for_index(
8 | | &'a self,
9 | | n: usize,
10 | | ) -> Option<impl for<'inner> Fn(&'inner [u8]) -> &'inner [u8] + 'a> {
| |_______________________________________________________________________^
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0495`.
It's quite a mouthful and I don't really understand what it's trying to tell me. The first part (first, the lifetime...) makes sense to me, the returned slice must not outlive the closure argument. The second part (but, the lifetime...) however seems weird to me - the + 'a annotation in the method signature refers to the closure itself (because it captures &'a self.foo), not to the value the closure returns.
Is it possible to change the code to model this correctly in rust, or is this construct just not supported at this time?
The problem is here:
Some(move |i: &[u8]| {
Every & has a lifetime on it, explicit or not. What is the lifetime of &[u8]? Clearly it should be "a lifetime chosen by the caller of the closure" (that is, a higher-ranked lifetime). But when the compiler encounters a reference type with a free lifetime parameter, even in the argument list of a closure, it will not assume that the lifetime is higher-ranked. The error message you get about the "anonymous lifetime #1" is the compiler confusedly trying to make a non-higher-ranked lifetime work.
The compiler could, in theory, work "backwards" from the impl Fn in the return type and recognize that the type of the closure needs to have this higher ranked lifetime. It's not quite clever enough to do that in this case, but there is a way to convince it: use a local function with a bounded type parameter to constrain the type of the closure to exactly the bound you want.
pub fn get_func_for_index(
&self, // note 1
n: usize,
) -> Option<impl 'a + for<'inner> Fn(&'inner [u8]) -> &'inner [u8]> { // note 2
// the reference to the struct member, captured by the closure
let opaque: &'a Opaque = *self.0.get(n)?;
// helper function to constrain the closure type
fn check<F: Fn(&[u8]) -> &[u8]>(f: F) -> F { // note 3
f
}
Some(check(move |i| {
// placeholder: do something with the input using the &Opaque
&i[opaque.0..]
}))
}
Playground
Please note the following:
&'a self is too conservative for this function because 'a is the lifetime parameter of the reference self contains, not the lifetime for which self is borrowed. In general, you should almost never write &'a self or &'a mut self where 'a is a named lifetime from an outer scope.
I find the + 'a easy to miss at the end of a long trait, especially a Fn trait with a return type. I recommend fronting the lifetime (putting it first) in cases like this to make clear that it relates more to the impl than to the &'inner [u8]. This is a stylistic choice.
Fn(&[u8]) -> &[u8] is actually exactly the same as for<'inner> Fn(&'inner [u8]) -> &'inner [u8], because the elision rules for Fn traits are the same as for regular functions. Either way is fine; I find the shorter version easier to read.
Similar questions
Expected bound lifetime parameter, found concrete lifetime [E0271]
How to declare a higher-ranked lifetime for a closure argument?
Why does this closure require inlining or `dyn`? What does `dyn` do here?

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.

Why do the lifetimes on a trait object passed as an argument require Higher Ranked Trait Bounds but a struct doesn't?

How are lifetimes handled when there is a trait object passed to a function?
struct Planet<T> {
i: T,
}
trait Spinner<T> {
fn spin(&self, value: T);
}
impl<T> Spinner<T> for Planet<T> {
fn spin(&self, value: T) {}
}
// foo2 fails: Due to lifetime of local variable being less than 'a
fn foo2<'a>(t: &'a Spinner<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
// foo1 passes: But here also the lifetime of local variable is less than 'a?
fn foo1<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
(Playground)
This code results in this error:
error[E0597]: `x` does not live long enough
--> src/main.rs:16:17
|
16 | t.spin(&x);
| ^ borrowed value does not live long enough
17 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 14:5...
--> src/main.rs:14:5
|
14 | fn foo2<'a>(t: &'a Spinner<&'a i32>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The function signature of foo1 is nearly same as foo2. One receiving the reference to struct and the other a trait object.
I read this is where Higher Ranked Trait Bounds comes in. Modifying foo2 as foo2(t: &for<'a> Spinner<&'a i32>) compiles the code, but I don't understand why.
Why won't 'a shrink for x?
Citing the Nomicon:
How on earth are we supposed to express the lifetimes on F's trait bound? We need to provide some lifetime there, but the lifetime we care about can't be named until we enter the body of call! Also, that isn't some fixed lifetime; call works with any lifetime &self happens to have at that point.
Can this please be elaborated?
In short: foo1 compiles because most types are variant over their generic parameters and the compiler can still chose a Spinner impl for t. foo2 doesn't compile because traits are invariant over their generic parameters and the Spinner impl is already fixed.
Some explanation
Let's take a look at a third version of foo:
fn foo3<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
Spinner::<&'a i32>::spin(t, &x);
}
This results in the same error as your foo2. What's going in there?
By writing Spinner::<&'a i32>::spin, we force the compiler to use a specific implementation of the Spinner trait. And the signature of Spinner::<&'a i32>::spin is fn spin(&self, value: &'a i32). Period. The lifetime 'a is given by the caller; foo can't choose it. Thus we have to pass a reference that lives for at least 'a. That's why the compiler error happens.
So why does foo1 compile? As a reminder:
fn foo1<'a>(t: &'a Planet<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
Here, the lifetime 'a is also given by the caller and cannot be chosen by foo1. But, foo1 can chose which impl of Spinner to use! Note that impl<T> Spinner<T> for Planet<T> basically defines infinitely many specific implementations (one for each T). So the compiler also knows that Planet<&'x i32> does implement Spinner<&'x i32> (where 'x is the specific lifetime of x in the function)!
Now the compiler just has to figure out if it can turn Planet<&'a i32> into Planet<&'x i32>. And yes, it can, because most types are variant over their generic parameters and thus Planet<&'a i32> is a subtype of Planet<&'x i32> if 'a is a subtype of 'x (which it is). So the compiler just "converts" t to Planet<&'x i32> and then the Spinner<&'x i32> impl can be used.
Fantastic! But now to the main part: why doesn't foo2 compile then? Again, as a reminder:
fn foo2<'a>(t: &'a Spinner<&'a i32>) {
let x: i32 = 10;
t.spin(&x);
}
Again, 'a is given by the caller and foo2 cannot chose it. Unfortunately, now we already have a specific implementation! Namely Spinner<&'a i32>. We can't just assume that the thing we were passed also implements Spinner<&'o i32> for any other lifetime 'o != 'a! Traits are invariant over their generic parameters.
In other words: we know we have something that can handle references which live at least as long as 'a. But we can't assume that the thing we were handed can also handle lifetimes shorter than 'a!
As an example:
struct Star;
impl Spinner<&'static i32> for Star {
fn spin(&self, value: &'static i32) {}
}
static SUN: Star = Star;
foo2(&SUN);
In this example, 'a of foo2 is 'static. And in fact, Star implements Spinner only for 'static references to i32.
By the way: this is not specific to trait objects! Let's look at this fourth version of foo:
fn foo4<'a, S: Spinner<&'a i32>>(t: &'a S) {
let x: i32 = 10;
t.spin(&x);
}
Same error once again. The problem is, again, that the Spinner impl is already fixed! As with the trait object, we only know that S implements Spinner<&'a i32>, not necessarily more.
HRTB to the rescue?
Using higher ranked trait bounds resolves the issue:
fn foo2(t: &for<'a> Spinner<&'a i32>)
and
fn foo4<S: for<'a> Spinner<&'a i32>>(t: &S)
As it's hopefully clear from the explanation above, this works because we the specific impl of Spinner isn't fixed anymore! Instead, we again have infinitely many impls to choose from (one for each 'a). Thus we can choose the impl where 'a == 'x.

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