Lifetime errors while folding over a vector with a closure


#1

I’m building a toy interpreter, and I’m trying to fold over a vector while modifying a mutable reference. I’m not too sure how to do this though, I’m receiving lifetime errors when i call thing.eval.

use self::EvalResult::*;

struct Env {}

enum EvalResult<'a> {
    Something(&'a str),
    Nothing,
}

struct Statement {}

impl Statement {
    fn eval<'a, 'b>(&self, env: &'b mut Env) -> EvalResult<'b> {
        unimplemented!()
    }
}

fn eval_statements<'b>(vec: Vec<Statement>, env: &'b mut Env) -> EvalResult<'b> {
    let closure = |acc: EvalResult, thing: &Statement| thing.eval(env);

    vec.iter().fold(Nothing, closure)

    // Using traditional for loops doesn't work as well
    //
    // let mut result = Nothing;

    // for thing in self.vec.iter() {
    //     result = thing.eval(env);
    // }

    // result
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter 'b in function call due to conflicting requirements
  --> src/lib.rs:19:62
   |
19 |     let closure = |acc: EvalResult, thing: &Statement| thing.eval(env);
   |                                                              ^^^^
   |
note: first, the lifetime cannot outlive the lifetime  as defined on the body at 19:19...
  --> src/lib.rs:19:19
   |
19 |     let closure = |acc: EvalResult, thing: &Statement| thing.eval(env);
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/lib.rs:19:67
   |
19 |     let closure = |acc: EvalResult, thing: &Statement| thing.eval(env);
   |                                                                   ^^^
note: but, the lifetime must be valid for the lifetime 'b as defined on the function body at 18:20...
  --> src/lib.rs:18:20
   |
18 | fn eval_statements<'b>(vec: Vec<Statement>, env: &'b mut Env) -> EvalResult<'b> {
   |                    ^^
   = note: ...so that the expression is assignable:
           expected EvalResult<'b>
              found EvalResult<'_>

error: aborting due to previous error

For more information about this error, try `rustc --explain E0495`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

My understanding is that somehow when closure is passed to fold, the compiler adds the following lifetime annotations:

fn anonymous<'a>(acc: EvalResult<'a>, thing: &'a Statement) -> EvalResult<'a> {
}

However, because of thing.eval requiring a different lifetime ('b of env: &'b mut Env), the compiler error happens. How can I work around this and “tie” the result of the closure to 'b instead?


#2

This change compiles. Maybe there is an alternative. It’s due to moving of variable env (can only do in FnOnce) or auto referencing/dereferencing, exactly what the compiler is trying would confuse me.

    fn eval<'a, 'b>(&self, env: &mut &'b mut Env) -> EvalResult<'b> {

fn eval_statements<'b>(vec: Vec<Statement>, mut env: &'b mut Env) -> EvalResult<'b> {
    let closure = |acc: EvalResult, thing: &Statement| thing.eval(&mut env);

#3

I would stay away from explicit lifetimes on struct definitions, because it can get hairy really fast. Instead have EnvResult own everything inside it and only use explicit lifetimes to optimize your code if necessary


#4

I’m very confused here, it seems like it works, but what am I doing wrong in my original implementation? How does setting up more indirection references help to solve the issue?


#5

I see, I’ll try to poke around and see if I can make something happen, thanks for the tip!