Borrowing self property


#1

Hello,

I’m trying to run this code

struct AccessControlList<'a> {
    info: HashMap<&'a str, Vec<&'a str>>
}

impl <'a>AccessControlList<'a> {
    fn define(& mut self, ability: &'a str, role: &'a str) {
        let option = self.info.get_mut(ability);

        if option.is_some() {
            let roles = option.unwrap();

            roles.push(role)
        } else {
            self.info.insert(ability, vec![role]);
        }
    }

    fn new() -> AccessControlList<'a> {
        AccessControlList {
            info: HashMap::new()
        }
    }
}

But when I compile, I have a error saying that I’m borrowing self.info more than once at a time. I do not understand why…

Can you explain me please?


#2

This is lexical borrowing, which is described here: http://smallcultfollowing.com/babysteps/blog/2016/04/27/non-lexical-lifetimes-introduction/

You can use something like this to avoid it:

self.info.entry(ability).or_insert_with(|| vec![]).push(role);

#3

Thank you, I found it here stackoverflow.

Really not easy :smiley: I will read your link.


#4

A way to think of it is that since option is a mutable referense, as long as it is in scope, you have the ability to mutate self.info through it. For real code, I recommend the way @vitalyd mentions. But for playing around, you can try to limit the scope of it. Maybe a bit ugly like this:

fn define(&mut self, ability: &'a str, role: &'a str) {
    {
       let option = self.info.get_mut(ability);

        if option.is_some() {
            let roles = option.unwrap();

            roles.push(role)
            return;
        }
    } // this ends the block containing option

    // So this should work:
    self.info.insert(ability, vec![role]);
}

Or less ugly, like this:

fn define(&mut self, ability: &'a str, role: &'a str) {
    if let Some(roles) = self.info.get_mut(ability) {
        roles.push(role)
        return;
    }
    self.info.insert(ability, vec![role]);
}

But then, there is the Entry api, which is great. Here’s the code @vitalyd wrote again, for emphasis:

fn define(&mut self, ability: &'a str, role: &'a str) {
    self.info.entry(ability).or_insert_with(|| vec![]).push(role);
}

#5

What’s nice about the Entry API is that not only does it sidestep the lexical lifetime issue, it’s also a nice ergonomic one-liner. So from an educational perspective, it’s good to understand the issue (or current limitation, really), but otherwise just use the Entry.