Search code examples
rustborrow-checkerborrow

Cannot borrow `*self`. How to make the fn work without string cloning?


struct Foo {
    stack: Vec<String>,
}

impl Foo {
    pub fn bar(&mut self) {
        // find condition here is for example only.
        // position in the stack is important.
        if let Some(s) = self.stack.iter().find(|x| x.is_ascii()) {
            self.baz(s.as_str());
        }
    }

    fn baz(&mut self, _input: &str) {
       // mutating of `self.stack` and some other fields.
    }
}
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
 --> src/main.rs:8:13
  |
7 |         if let Some(s) = self.stack.last() {
  |                          ----------------- immutable borrow occurs here
8 |             self.baz(s.as_str());
  |             ^^^^^---^^^^^^^^^^^^
  |             |    |
  |             |    immutable borrow later used by call
  |             mutable borrow occurs here

I don't want to clone a string every time. How to get it working with borrowed string?

And yes, I really need the &mut self here.


Solution

  • You can wrap your strings in Rc that way you can cheaply clone the Rc and have something owned so you don't reference the original struct:

    use std::rc::Rc;
    struct Foo {
        stack: Vec<Rc<String>>,
    }
    
    impl Foo {
        pub fn bar(&mut self) {
            if let Some(s) = self
                .stack
                .iter()
                .find(|x| x.is_ascii())
                .map(Rc::clone)
            {
                self.baz(s.as_str());
            }
        }
        // …
    }
    

    For mutable access to the underlying strings you can use Rc::get_mut or wrap further in RefCell or similar.


    Solution to the original underspecified question:

    The most straight forward solution is to just remove the String from Foo for the baz call:

    
    struct Foo {
        stack: Vec,
    }
    
    impl Foo {
        pub fn bar(&mut self) {
            if let Some(s) = self.stack.pop() {
                self.baz(s.as_str());
                self.stack.push(s);
            }
        }
    
        fn baz(&mut self, _input: &str) {}
    }