I have two types: Lexer
and SFunction
.
SFunction
stands for stateful function and is definined like so:
struct SFunction {
f: Option<Box<FnMut() -> SFunction>>,
}
The important part is that any SFunction
references a closure that returns an SFunction
.
Now I want to have these functions carry state by each affecting the same Lexer
. This means that each of these SFunctions
has to have a lifetime that depends on a specific Lexer
.
Here's some more code if you want to get more of a sense of what I'm doing with this:
impl Lexer {
fn lex(&mut self) {
self.sfunction(Lexer::lexNormal).call()
}
fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
SFunction::new(Box::new(|| f(self)))
// SFunction { f: Some(Box::new(move ||f(self))) }
}
fn lexNormal(&mut self) -> SFunction {
return SFunction::empty()
}
}
(Here’s a full version of the code in the Rust playground.)
How do I specify this lifetime requirement in the code?
The compiler errors I'm getting say "cannot infer an appropriate lifetime for capture of self
by closure due to conflicting requirements". I'm pretty sure the "conflicting requirements" here is that a Box
type assumes the lifetime to be 'static
. I could do something like Box<FnMut() -> SFunction + 'a>
where 'a
is a lifetime defined by the Lexer it depends upon, but I'm not sure how to define such an 'a
.
Thanks for your help!
The problem is in this line:
SFunction::new(Box::new(|| f(self)))
Here, self
is a reference to a Lexer
, but there's no guarantee that the lexer will live long enough. In fact, it needs to live for the 'static
lifetime! Without a lifetime specified, a boxed trait object will use the 'static
lifetime. Said in code, these two declarations are equivalent:
<Box<FnMut() -> SFunction>>
<Box<FnMut() -> SFunction> + 'static>
And you can make your code compile (in an unsatisfactory way) by restricting it to accept only references that will live for the 'static
lifetime:
fn lex(&'static mut self) {
self.sfunction(Lexer::lex_normal).call()
}
fn sfunction(&'static mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
SFunction::new(Box::new(move || f(self)))
}
Of course, it's very doubtful that you will have a Lexer
with the static lifetime, as that would mean that it's lexing static data, which wouldn't be very useful. That means we need to include lifetimes in your trait object... as you suggested.
Ultimately what helped to see the problem was to restructure your closure a bit:
fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
SFunction::new(Box::new(move || {
// f(self)
let s2 = self;
let f2 = f;
f2(s2)
}))
}
Compiling this produces an error that points to what seems to be the real problem:
<anon>:31:22: 31:26 error: cannot move out of captured outer variable in an `FnMut` closure [E0507]
<anon>:31 let s2 = self;
^~~~
<anon>:31:17: 31:19 note: attempting to move value to here
<anon>:31 let s2 = self;
^~
<anon>:31:17: 31:19 help: to prevent the move, use `ref s2` or `ref mut s2` to capture value by reference
I believe this is because a FnMut
closure may be called multiple times, which would mean that the reference enclosed in the closure would need to be copied around, which would be bad news as &mut
references should be unique.
All together, this code works:
struct SFunction<'a> {
f: Option<Box<FnOnce() -> SFunction<'a> + 'a>>,
}
impl<'a> SFunction<'a> {
fn new(f: Box<FnOnce() -> SFunction<'a> + 'a>) -> SFunction<'a> {
SFunction {
f: Some(f),
}
}
fn empty() -> SFunction<'a> {
SFunction {
f: None,
}
}
fn call(self) { }
}
struct Lexer;
impl Lexer {
fn lex(&mut self) {
self.sfunction(Lexer::lex_normal).call()
}
fn sfunction(&mut self, f: fn(&mut Lexer) -> SFunction) -> SFunction {
SFunction::new(Box::new(move || f(self)))
}
fn lex_normal<'z>(&'z mut self) -> SFunction<'z> {
SFunction::empty()
}
}
fn main() {
let mut l = Lexer;
l.lex()
}
I hope my explanation is right and that the changed code still suits your use case!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With