3

I have been working on a simple lexer in rust. However, I have ran into the error[E0502]: cannot borrow 'a_rule' as immutable because it is also borrowed as mutable problem. I have checked other answers and I can't seem to find the reason.

pub struct Rule<'a> {
    selector: &'a str,
}

impl<'a> Rule<'a> {
    pub fn new(selector: &'a str) -> Self {
        Self {
            selector
        }
    }

    pub fn get_selector(&'a self) -> &'a str {
        self.selector
    }

    pub fn set_selector(&'a mut self, selector: &'a str) {
        self.selector = selector
    }
}

#[cfg(test)]
mod tests {
    use super::Rule;

    #[test]
    fn set_selector_test() {
        let mut a_rule = Rule::new(".foo");
        a_rule.set_selector(".bar");

        assert_eq!(a_rule.get_selector(), ".bar")
    }
}

Error:

error[E0502]: cannot borrow `a_rule` as immutable because it is also borrowed as mutable
  --> src/lib.rs:30:20
   |
28 |         a_rule.set_selector(".bar");
   |         ------ mutable borrow occurs here
29 | 
30 |         assert_eq!(a_rule.get_selector(), ".bar")
   |                    ^^^^^^
   |                    |
   |                    immutable borrow occurs here
   |                    mutable borrow later used here

(Playground)

I would also like to use the opportunity to ask if it is recommended or not to use java like get and set methods or just set the members within a struct as public.

Please feel free to call out any other dumb mistakes.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Zana
  • 53
  • 6
  • Mistakenly using a `'a` from an outer scope where a fresh lifetime is sufficient is a common mistake. I think this is a duplicate of [Linking the lifetimes of self and a reference in method](https://stackoverflow.com/questions/30273850/linking-the-lifetimes-of-self-and-a-reference-in-method), or possibly one of the other questions linked from there. – trent Apr 09 '20 at 13:05
  • Actually, [Mutable borrow in loop](https://stackoverflow.com/q/46393890/3650362) and [Cannot borrow as mutable more than once at a time in one code - but can in another very similar](https://stackoverflow.com/a/31067272/3650362) are probably more to the point. – trent Apr 09 '20 at 13:09

1 Answers1

3

You’ve tied the lifetime of your rule to the lifetime of your string by making get_selector and set_selector take &'a self/&'a mut self, but that’s not the correct relationship between them. You can produce &'a strs without needing your self to live that long (or be borrowed mutably for that long) because self.selector is already an &'a str.

Remove the 'a on the self references:

pub fn get_selector(&self) -> &'a str {
    self.selector
}

pub fn set_selector(&mut self, selector: &'a str) {
    self.selector = selector;
}

(but do you need this getter and setter at all? consider immutability!)

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • The entire lifetime seems screwy though, logically the setter should take an str which lives longer than self, and the getter should return an str which is borrowed from self. Seems to me this'd only ever work with `'static` at which point you're better off just typing it as `'static` no? – Masklinn Apr 09 '20 at 13:54
  • @Masklinn The `&str` doesn't have to be `'static`, it just has to live (at least) as long as `'a` -- that's the whole point of having lifetime annotations, no? If the reference had to be `'static` you wouldn't need to annotate `Rule` at all. – trent Apr 09 '20 at 15:28
  • Can anyone explain the relationship between the compiler's borrow error and the lifetimes? The compilers seems to be complaining about having an immutable borrow when you already have a mutable borrow. But the fix was to change lifetimes... – drootang Apr 09 '20 at 16:03
  • 1
    @drootang Lifetime annotations are how you tell the compiler how long borrows should last for. Making the mutable borrow last longer, that is, making it outlive `'a`, causes it to overlap with the immutable borrow. Removing that constraint allows the compiler to make the borrow shorter and no longer overlap. – trent Apr 09 '20 at 16:43
  • @trentcl technically yes but practically since it's storing the reference in a struct there's a pretty low chance you're going to write any useful code using non-`'static` strings. – Masklinn Apr 09 '20 at 18:07