Logicrat
Logicrat

Reputation: 4468

Borrow inside a loop

I'm trying to learn Rust after many years of C++. I have a situation where the compiler is complaining about a borrow, and it doesn't seem to matter whether it is mutable or immutable. I don't seem to be able to use self as a parameter inside a loop that start with: for item in self.func.drain(..).

I've tried calling appropriate() as a function:

Self::appropriate(&self,&item,index)

and I have tried it as a method:

self.appropriate(&item,index)

but I get the same message in either case: The function or method appropriate() is intended imply examine the relationship among its parameters and return a bool without modifying anything. How can I call either a function or method on self without violating borrowing rules?

This program is a learning exercise from exercism.org and doesn't include a main() so it won't run but should almost compile except for the error in question. Here's the code I have:

use std::collections::HashMap;

pub type Value = i32;
pub type Result = std::result::Result<(), Error>;

pub struct Forth {
    v: Vec<Value>,
    f: HashMap<String,usize>,
    s: Vec<Vec<String>>,
    func: Vec<String>
}


#[derive(Debug, PartialEq)]
pub enum Error {
    DivisionByZero,
    StackUnderflow,
    UnknownWord,
    InvalidWord,
}

impl Forth {
    pub fn new() -> Forth {
        let mut temp: Vec<Vec<String>> = Vec::new();
        temp.push(Vec::new());
        Forth{v: Vec::<Value>::new(), f: HashMap::new(), s: temp, func: Vec::new()}
    }

    pub fn stack(&self) -> &[Value] {
        &self.v
    }

    pub fn eval(&mut self, input: &str) -> Result {
        self.v.clear();
        self.s[0].clear();

        let mut count = 0;

        {
            let temp: Vec<&str> = input.split(' ').collect();
            let n = temp.len() as i32;
            for x in 0..n as usize {
                self.s[0].push(String::from(temp[x]));
            }
        }

        let mut collecting = false;
        let mut xlist: Vec<(usize,usize)> = Vec::new();
        let mut sx: usize = 0;
        let mut z: i32 = -1;
        let mut x: usize;
        let mut n: usize = self.s[0].len();

        loop {
            count += 1;
            if count > 20 {break;}

            z += 1;
            x = z as usize;
            if x >= n {break;}
            z = x as i32;
            
            let word = &self.s[sx][x];

            if word == ";" {
                if collecting {
                    collecting = false;
                    let index: usize = self.s.len();
                    self.s.push(Vec::<String>::new());
                    for item in self.func.drain(..) {
                        if self.s[index].len() > 0 &&
                            Self::appropriate(&self,&item,index)
                        {
                            let sx = *self.f.get(&self.s[index][0]).unwrap();
                            let n = self.s[sx].len();
                            for x in 1..n as usize {
                                let symbol = self.s[sx][x].clone();
                                self.s[index].push(symbol);
                            }
                        }
                        else {
                            self.s[index].push(item);
                        }
                    }
                    self.f.insert(self.s[index][0].clone(), index);
                    self.func.clear();
                    continue;
                }
                if 0 < xlist.len() {
                    (x, n) = xlist.pop().unwrap();
                    continue;
                }
                return Err(Error::InvalidWord);
            }
            if collecting {
                self.func.push(String::from(word));
                continue;
            }
            if Self::is_op(word) {
                if self.v.len() < 2 {
                    return Err(Error::StackUnderflow);
                }
                let b = self.v.pop().unwrap();
                let a = self.v.pop().unwrap();
                let c = match word.as_str() {
                    "+" => a + b,
                    "-" => a - b,
                    "*" => a * b,
                    "/" => {if b == 0 {return Err(Error::DivisionByZero);} a / b},
                    _ => 0
                };
                self.v.push(c);
                continue;
            }
            match word.parse::<Value>() {
                Ok(value) => { self.v.push(value); continue;},
                _ => {}
            }
            if word == ":" {
                collecting = true;
                self.func.clear();
                continue;
            }
            if word == "drop" {
                if self.v.len() < 1 {
                    return Err(Error::StackUnderflow);
                }
                self.v.pop();
                continue;
            }
            if word == "dup" {
                if self.v.len() < 1 {
                    return Err(Error::StackUnderflow);
                }
                let temp = self.v[self.v.len() - 1];
                self.v.push(temp);
                continue;
            }
            if !self.f.contains_key(word) {
                return Err(Error::UnknownWord);
            }
            xlist.push((sx,n));
            sx = *self.f.get(word).unwrap();
            n = self.s[sx].len();
            z = 0;
        }
        Ok(())
    }

    fn is_op(input: &str) -> bool {
        match input {"+"|"-"|"*"|"/" => true, _ => false}
    }

    fn appropriate(&self, item:&str, index:usize) -> bool
    {
        false
    }

    fn prev_def_is_short(&self, index: usize) -> bool {
        if index >= self.s.len() {
            false
        }
        else {
            if let Some(&sx) = self.f.get(&self.func[0]) {
                self.s[sx].len() == 2
            }
            else {
                false
            }
        }
    }

}

The error message relates to the call to appropriate(). I haven't even written the body of that function yet; I'd like to get the parameters right first. The compiler's complaint is:

As a subroutine call

error[E0502]: cannot borrow `self` as immutable because it is also borrowed as mutable
  --> src/lib.rs:85:47
   |
81 |                     for item in self.func.drain(..) {
   |                                 -------------------
   |                                 |
   |                                 mutable borrow occurs here
   |                                 mutable borrow later used here
...
85 |                             Self::appropriate(&self,&item,index)
   |                                               ^^^^^ immutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.


as a method call

error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
  --> src/lib.rs:85:29
   |
81 |                     for item in self.func.drain(..) {
   |                                 -------------------
   |                                 |
   |                                 mutable borrow occurs here
   |                                 mutable borrow later used here
...
85 |                             self.appropriate(&item,index)
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here

For more information about this error, try `rustc --explain E0502`.

Is there any canonical way to deal with this situation?

Upvotes: 2

Views: 927

Answers (1)

prog-fh
prog-fh

Reputation: 16785

The problem is that self.func.drain() will consume the elements contained in self.func, thus an exclusive (&mut) access is needed on self.func for the entire for loop. If during the iteration you need to pass a reference to self globally, then its func member is potentially accessible while the loop holds an exclusive access to it: Rust forbids that.

Since you use drain() in order to consume all the elements inside self.func, I suggest you swap this vector with an empty one just before the loop, then iterate on this other vector that is not anymore part of self. No copy of the content of the vector is involved here; swap() only deals with pointers.

Here is an over-simplified version of your code, adapted consequently.

struct Forth {
    func: Vec<String>,
}

impl Forth {
    fn eval(&mut self) {
        /*
        for item in self.func.drain(..) {
            self.appropriate(&self);
        }
        */
        let mut func = Vec::new();
        std::mem::swap(&mut self.func, &mut func);
        for item in func.drain(..) {
            let b = self.appropriate();
            println!("{:?} {:?}", item, b);
        }
    }

    fn appropriate(&self) -> bool {
        false
    }
}

fn main() {
    let mut f = Forth {
        func: vec!["aaa".into(), "bbb".into()],
    };
    f.eval();
}

Upvotes: 3

Related Questions