Lifetime issue with Boxed closure inside HashMap that calls struct methods

Hello! I'm having an issue with the lifetime specification of a key binding dynamic I've created in my program. I have a HashMap that maps integers (corresponding to key codes) to closures that call into the main struct (let's call it MainStruct) that comprises the business logic. Since I'm storing references in MainStruct, and key bindings may call methods that manipulate these references, I've come across some issues related to lifetimes. I've boiled down my problem to the following code snippet:

use std::cell::Cell;
use std::collections::HashMap;

use rand::Rng;

#[derive(Debug)]
struct ItemStruct {
    id: u32,
    vec: Vec<u32>,
}

impl ItemStruct {
    pub fn new(id: u32) -> Self {
        Self {
            id,
            vec: Vec::new(),
        }
    }
}

#[derive(Debug)]
struct Buffer<'buffer> {
    pub item: Cell<Option<&'buffer ItemStruct>>,
}

impl<'buffer> Buffer<'buffer> {
    pub fn set(&self, item: &'buffer ItemStruct) {
        self.item.set(Some(item));
    }

    pub fn unset(&self) {
        self.item.set(None);
    }
}

struct MainStruct<'main> {
    items: HashMap<u32, ItemStruct>,
    buffer: Buffer<'main>,
}

impl<'main> MainStruct<'main> {
    pub fn new() -> Self {
        Self {
            items: {
                let mut map = HashMap::new();
                map.insert(0, ItemStruct::new(0));
                map.insert(1, ItemStruct::new(1));
                map.insert(2, ItemStruct::new(2));
                map
            },
            buffer: Buffer {
                item: Cell::new(None),
            },
        }
    }

    pub fn add(&mut self, id: u32) {
        self.items.insert(id, ItemStruct::new(id));
    }

    pub fn get(&self, id: u32) -> Option<&ItemStruct> {
        self.items.get(&id)
    }

    pub fn store_buffer(&'main self, item: Option<&'main ItemStruct>) {
        if let Some(item) = item {
            self.buffer.set(item);
        } else {
            self.buffer.unset();
        }
    }

    pub fn change_buffer(&'main self) {
        let n: u32 = rand::thread_rng().gen_range(0..=2);
        self.store_buffer(self.items.get(&n));
    }

    pub fn main_loop(
        &'main mut self,
        key_bindings: &HashMap<u32, Box<dyn FnMut(&mut MainStruct, u32)>>,
    ) {
        (key_bindings.get(&0).unwrap())(self, 0);
    }
}

fn main() {
    let mut key_bindings: HashMap<u32, Box<dyn FnMut(&mut MainStruct<'_>, u32)>> = HashMap::new();

    key_bindings.insert(
        0,
        Box::new(|m, id| {
            m.store_buffer(m.get(id));
        }),
    );

    let mut main = MainStruct::new();
    main.main_loop(&key_bindings);
}

The issue occurs in main, where I'm inserting a Boxed closure into the HashMap representing the key bindings. The exact error message I'm getting is the following:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:92:15
   |
92 |             m.store_buffer(m.get(id));
   |               ^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 91:18...
  --> src/main.rs:91:18
   |
91 |           Box::new(|m, id| {
   |  __________________^
92 | |             m.store_buffer(m.get(id));
93 | |         }),
   | |_________^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:92:13
   |
92 |             m.store_buffer(m.get(id));
   |             ^
note: but, the lifetime must be valid for the anonymous lifetime #3 defined on the body at 91:18...
  --> src/main.rs:91:18
   |
91 |           Box::new(|m, id| {
   |  __________________^
92 | |             m.store_buffer(m.get(id));
93 | |         }),
   | |_________^
note: ...so that the types are compatible
  --> src/main.rs:92:15
   |
92 |             m.store_buffer(m.get(id));
   |               ^^^^^^^^^^^^
   = note: expected `&MainStruct<'_>`
              found `&MainStruct<'_>`

error: aborting due to previous error

Is there a way to solve this issue? If not, is there another (better) way to organize key bindings such as to achieve the desired functionality? Thanks a lot!

~dxx

You can use Higher-Rank Trait Bounds with some minimal refactoring. Change the type of your key_bindings variable in main to the following:

let mut key_bindings: HashMap<u32, Box<(dyn for<'a> FnMut(&'a mut MainStruct<'a>, u32))>> = HashMap::new();

The next problem you'll run into is the MainStruct::main_loop function you've written, which should be adjusted to take a mutable reference:

pub fn main_loop(
    &'main mut self,
    key_bindings: &mut HashMap<u32, Box<dyn for<'a> FnMut(&'a mut MainStruct<'a>, u32)>>,
) {
    (key_bindings.get_mut(&0).unwrap())(self, 0);
}

Then you'll want to make sure to pass key_bindings by mutable reference in main:

main.main_loop(&mut key_bindings);

After that it should compile without issue.

Hey, thanks a lot for your quick and helpful response! It worked for the boiled down example I had, but trying to implement it in my actual program, I'm unfortunately coming across double borrow issues now. If I slightly update the above example, this reflects the issue I'm now facing:

use std::cell::Cell;
use std::collections::HashMap;

#[derive(Debug)]
struct ItemStruct {
    id: u32,
    vec: Vec<u32>,
}

impl ItemStruct {
    pub fn new(id: u32) -> Self {
        Self {
            id,
            vec: Vec::new(),
        }
    }
}

#[derive(Debug)]
struct Buffer<'buffer> {
    pub item: Cell<Option<&'buffer ItemStruct>>,
}

impl<'buffer> Buffer<'buffer> {
    pub fn set(&self, item: &'buffer ItemStruct) {
        self.item.set(Some(item));
    }

    pub fn unset(&self) {
        self.item.set(None);
    }
}

struct MainStruct<'main> {
    items: HashMap<u32, ItemStruct>,
    buffer: Buffer<'main>,
    running: bool,
}

impl<'main> MainStruct<'main> {
    pub fn new() -> Self {
        Self {
            items: {
                let mut map = HashMap::new();
                map.insert(0, ItemStruct::new(0));
                map.insert(1, ItemStruct::new(1));
                map.insert(2, ItemStruct::new(2));
                map
            },
            buffer: Buffer {
                item: Cell::new(None),
            },
            running: true,
        }
    }

    pub fn get(&self, id: u32) -> Option<&ItemStruct> {
        self.items.get(&id)
    }

    pub fn store_buffer(&'main self, item: Option<&'main ItemStruct>) {
        if let Some(item) = item {
            self.buffer.set(item);
        } else {
            self.buffer.unset();
        }
    }

    pub fn exit(&mut self) {
        self.running = false;
    }

    pub fn main_loop(
        &'main mut self,
        key_bindings: &mut HashMap<u32, Box<dyn for<'a> FnMut(&'a mut MainStruct<'a>, u32)>>,
    ) {
        while self.running {
            (key_bindings.get_mut(&0).unwrap())(self, 0);
        }
    }
}

fn main() {
    let mut key_bindings: HashMap<u32, Box<dyn for<'a> FnMut(&'a mut MainStruct<'a>, u32)>> = HashMap::new();

    key_bindings.insert(
        0,
        Box::new(|m, id| {
            m.store_buffer(m.get(id));
        }),
    );

    let mut main = MainStruct::new();
    main.main_loop(&mut key_bindings);
}

The error I'm getting is now:

error[E0503]: cannot use `self.running` because it was mutably borrowed
  --> src/main.rs:77:15
   |
40 | impl<'main> MainStruct<'main> {
   |      ----- lifetime `'main` defined here
...
77 |         while self.running {
   |               ^^^^^^^^^^^^ use of borrowed `*self`
78 |             (key_bindings.get_mut(&0).unwrap())(self, 0);
   |             --------------------------------------------
   |             |                                   |
   |             |                                   borrow of `*self` occurs here
   |             argument requires that `*self` is borrowed for `'main`

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:78:49
   |
40 | impl<'main> MainStruct<'main> {
   |      ----- lifetime `'main` defined here
...
78 |             (key_bindings.get_mut(&0).unwrap())(self, 0);
   |             ------------------------------------^^^^----
   |             |                                   |
   |             |                                   mutable borrow starts here in previous iteration of loop
   |             argument requires that `*self` is borrowed for `'main`

error: aborting due to 2 previous errors

Do you perhaps know of a way to nicely get around this issue? The example above simply calls a single key binding from the main_loop, but in reality, it matches on incoming X11 events, and then calls different mutable and immutable functions from there. What I expect is happening now is that, since the lifetime for everything is now 'main, I'm getting double borrow errors within main_loop, even across different match arms. Thanks again!

~dxx

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.