I have the following scenario in a Rust project:
struct Foo<'a> {
stuff: &'a i32,
}
thus I'm telling the compiler that my Foo
struct holds a reference to something and I have to give it a lifetime to make that work, fine.
Now, I declare this:
type FooFunc<'a> = &'a dyn Fn(&'a mut Foo<'a>) -> bool;
const funcs: [FooFunc; 4] = [
&|f| { *f.stuff = 0; false },
&|f| { *f.stuff = 1; true },
&|f| { *f.stuff = 2; true },
&|f| { *f.stuff = 3; true },
];
and try to call a closure from that const array inside a "method" of Foo
:
impl<'a> Foo<'a> {
fn bar(&mut self, i: usize) -> bool {
funcs[i](self)
}
}
Reasoning about lifetimes, this should be fine since the self
reference to the Foo
struct has a lifetime 'a
(which sure thing doesn't outlive 'static
, that is the lifetime of the closures) so the closures should be able to receive self
as a parameter without any issue.
And the borrow checker sort of agrees with this, but still reports an error I don't understand:
error[E0308]: mismatched types
--> src/main.rs:7:18
|
7 | funcs[i](self)
| ^^^^ lifetime mismatch
|
= note: expected type `&'static mut Foo<'static>`
found type `&'static mut Foo<'a>`
note: the lifetime 'a as defined on the impl at 5:6...
--> src/main.rs:5:6
|
5 | impl<'a> Foo<'a> {
| ^^
= note: ...does not necessarily outlive the static lifetime
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/main.rs:7:18
|
7 | funcs[i](self)
| ^^^^
|
= note: ...the reference is valid for the static lifetime...
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5
--> src/main.rs:6:5
|
6 | / fn bar(&mut self, i: usize) -> bool {
7 | | funcs[i](self)
8 | | }
| |_____^
It's telling me (in the note
tags) that the lifetime 'a as defined on the impl at 5:6 does not necessarily outlive the static lifetime
and that the reference is valid for the static lifetime but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5
. While a compiler giving these kind of error messages is amazing, I still don't get what the problem is.
Talking about the second note
(which is the one that sort of (tries to) explain the issue), I can't understand if the borrowed content is meant to be the const array or the self reference, but in either case:
if the "borrowed content" is the const array, why would that be a problem even if an anonymous lifetime that matches the function body has been assigned to it? The closure just has to operate on the reference that gets passed as a parameter and return by transferring ownership of the return value, and not by returning a reference with some weird lifetime that could cause problems.
if the "borrowed content" is the self reference, again, why would that be the problem? Okay, the self reference is no more
'a
, but it's another one (let's call it'b
) which is "included" in'a
, thus shouldn't outlive'static
, right?
Sure thing I'm missing something here, any help would be appreciated.
Note: all the code in this post is just a stripped down version of the "architecture" I'm trying to implement - of course the logic you see in the example code could be easily implemented in a way simple manner, but that's not what I need. I would like to have a table of operations to do on my struct (thus the idea to make the elements closures that accept a &mut
reference) and run these operations from a "method" of my struct. The actual type in the actual project is instead [Option<[FooFunc<'a>; 6]>; 256]
, so we're talking a pretty big bi-dimensional "table", which I imagine would become quite unpleasant to implement with match
statements, especially considering I reuse FooFunc
s very often.
_
to phase out the lifetime in the type and that the lifetime associated to the type itself might have been the problem. I guess the lesson to learn is to try working with as few lifetimes specifiers as possible? – gdelazzari