Binding does not live long enough when storing a r

2019-03-03 20:30发布

问题:

I'm new at Rust and still struggling with the borrow checker and getting lifetimes right.

Here's a simple struct I've started to build - it stores collections of command-line argument like things (which can be represented by a --string or a -c or both):

struct OptionMap<'a, T: 'a> {
    name: HashMap<String, &'a T>,
    short_name: HashMap<char, &'a T>,
    options: Vec<T>
}

impl<'a, T: 'a> OptionMap<'a, T> {
    pub fn new() -> OptionMap<'a, T> {
        OptionMap {
            name: HashMap::new(),
            short_name: HashMap::new(),
            options: Vec::new()
        }
    }

    pub fn register(&mut self, name: &OptionName, option: T) {        
        if name.name.is_some() {
            self.name.insert(name.name.unwrap().to_owned(), &option);
        }

        if name.short_name.is_some() {
            self.short_name.insert(name.short_name.unwrap(), &option);
        }

        self.options.push(option);
    }
}

I get two of these errors (one for each of the &option parameters I wrote):

   Compiling glam v0.1.0 (file:///Users/carson/Projects/glam)
src/options.rs:57:66: 57:72 error: `option` does not live long enough
src/options.rs:57                 self.name.insert(name.name.unwrap().to_owned(), &option);
                                                                                   ^~~~~~
src/options.rs:54:62: 66:6 note: reference must be valid for the lifetime 'a as defined on the block at 54:61...
src/options.rs:54     pub fn register(&mut self, name: &OptionName, option: T) {
src/options.rs:55         {
src/options.rs:56             if name.name.is_some() {
src/options.rs:57                 self.name.insert(name.name.unwrap().to_owned(), &option);
src/options.rs:58             }
src/options.rs:59         }
                  ...
src/options.rs:54:62: 66:6 note: ...but borrowed value is only valid for the scope of parameters for function at 54:61
src/options.rs:54     pub fn register(&mut self, name: &OptionName, option: T) {
src/options.rs:55         {
src/options.rs:56             if name.name.is_some() {
src/options.rs:57                 self.name.insert(name.name.unwrap().to_owned(), &option);
src/options.rs:58             }
src/options.rs:59         }
                  ...

I pass a reference to each of the hash maps (so they borrow it) and then pass the option straight to the vector to move it there, so that the option doesn't go out of scope.

It seems like the scope of 'a and the scope of option should be the same to me - since OptionMap is created with lifetime 'a, and T is bound by that lifetime as well, and option gets moved into options at the end of the function. What am I missing? I feel like I'm constantly fighting with lifetimes in Rust, like there's something that hasn't clicked for me yet.

回答1:

I pass a reference to each of the hash maps (so they borrow it) and then pass the option straight to the vector to move it there, so that the option doesn't go out of scope.

Once something is borrowed, you can't move it elsewhere.

If you place an element into a vector and borrow it from there, you can't mutate the vector until the borrow ends.

In other words your current approach won't work.

The simplest solution is probably to store indices into the vector in your hash maps.

Alternatively, it might be possible to design a fancy key that can be compared to both the short and long names, and then you can store the option directly in a single hash map. I say "might" because I'm not sure if this is currently possible.



标签: rust lifetime