Problem with Lifetimes when storing closure into variable

I have the following working code (minimal example):

#[derive(Debug)]
struct Item {
    x: i32
}

fn main() {
    let mut items = vec![Item {x: 1}, Item {x: 2}, Item {x: 3}];
    do_processing(&mut items, |items| {
        dbg!(items);
    });
}

fn do_processing<F>(items: &mut Vec<Item>, progress_closure: F)
        where F: Fn(&Vec<Item>) {
    for i in 0..items.len() {
        let item = &mut items[i];
        item.x += 1;
        progress_closure(items);
    }
}

I now want to move the progress closure into a variable, because I want to reuse it.

fn main() {
    let mut items = vec![Item {x: 1}, Item {x: 2}, Item {x: 3}];
    let progress_closure = |items| {
        dbg!(items);
    };
    do_processing(&mut items, progress_closure);
}

This fails to compile, probably due to problems inferring the right type for the closure.

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:22:5
   |
22 |     do_processing(&mut items, progress_closure);
   |     ^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(&'2 Vec<Item>)` must implement `FnOnce<(&'1 Vec<Item>,)>`, for any lifetime `'1`...
   = note: ...but it actually implements `FnOnce<(&'2 Vec<Item>,)>`, for some specific lifetime `'2`

I therefore added lifetime annotations to do_processing:

fn do_processing<'a, F>(items: &'a mut Vec<Item>, progress_closure: F)
        where F: Fn(&'a Vec<Item>) {
    for i in 0..items.len() {
        let item = &mut items[i];
        item.x += 1;
        progress_closure(items);
    }
}

However now I get the error that item is still borrowed at the time of the progress_closure call. I would assume that the borrow on item would end after the previous line, but apparently this is not the case.

error[E0502]: cannot borrow `*items` as mutable because it is also borrowed as immutable
  --> src/main.rs:45:25
   |
42 | fn do_processing<'a, F>(items: &'a mut Vec<Item>, progress_closure: F)
   |                  -- lifetime `'a` defined here
...
45 |         let item = &mut items[i];
   |                         ^^^^^ mutable borrow occurs here
46 |         item.x += 1;
47 |         progress_closure(items);
   |         -----------------------
   |         |                |
   |         |                immutable borrow occurs here
   |         argument requires that `*items` is borrowed for `'a

Can somebody help me fix this situation?

Rust closure is somewhat bad at inferencing hrtb when stored into a variable. You can somewhat workaround this problem like this:

#[derive(Debug)]
struct Item {
    x: i32,
}

fn main() {
    let mut items = vec![Item { x: 1 }, Item { x: 2 }, Item { x: 3 }];
    let progress_closure = ensure_bound(|items| {
        dbg!(items);
    });
    do_processing(&mut items, progress_closure);
}

fn ensure_bound<F>(f: F) -> F
where
    F: Fn(&Vec<Item>),
{
    f
}

fn do_processing<F>(items: &mut Vec<Item>, progress_closure: F)
where
    F: Fn(&Vec<Item>),
{
    for i in 0..items.len() {
        let item = &mut items[i];
        item.x += 1;
        progress_closure(items);
    }
}

This is correct, the problem is inferring the argument type of the closure. Note that closure type inference is a bit finicky in general; it works best in cases like your original code, when the closure is directly passed to a generic function expecting some type F with a clear F: Fn… bound.

Note that the arguably easiest way to fix your code in this case is something like

fn main() {
    let mut items = vec![Item {x: 1}, Item {x: 2}, Item {x: 3}];
-   let progress_closure = |items| {
+   let progress_closure = |items: &_| {
        dbg!(items);
    };
    do_processing(&mut items, progress_closure);
}

Changing the type of do_processing is not going to help. The type was already correct, you can only make it worse; the problem was type inference doing some wrong decisions before even considering the type of do_processing. For some reason (this is why I’m saying “closure type inference is a bit finicky”), when you write

    let progress_closure = |items| {
        dbg!(items);
    };

type inference immediately decides that progress_closure is not going to be generic over the lifetime anymore. And do_processing expects Fn(&Vec<Item>), which is equivalent to something like for<'a> Fn(&'a Vec<Item>), a so-called “higher ranked trait bound”.

If you write

    let progress_closure = |items: &_| {
        dbg!(items);
    };

so that type inference knows ahead of time that some reference-type is involved as the closure arguments, is for some reason (why? well.. I can only re-state, “closure type inference is a bit finicky”) type inference now doesn’t prematurely (and incorrectly) rule out the possibility of the closure being generic over that reference’s lifetime anymore.


The reason why do_processing needs a progress_closure argument that’s generic over the lifetime of the &Vec<Item> is because the argument that’s going to be passed to it is a very short re-borrow of the mutable &mut Vec<Item>; every call to progress_closure(items) has a new lifetime so to speak, so it makes sense that progress_closure needs to be generic; also the lifetime is contained within the loop body (and thus within the body of do_processing). If you switch to a fixed lifetime argument do_processing<'a, F>, then

  • the lifetime 'a is always longer than the body of do_processing (this is a basic property that’s always true for lifetime arguments of functions)
  • the caller can decide what lifetime a lifetime argument is, not the callee, even though within do_processing you need to pass a reference with a very specific lifetime to progress_closure, i.e. the lifetime needs to be chosen by the definition of do_processing, i.e. the callee

Thanks a lot for your answers! Indeed rewriting the closure definition to

let progress_closure = |items: &_| {
    dbg!(items);
};

worked for me.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.