Different fields with mutable & immutable reference of a `struct` instance

I'm very confused with the error[E0502]
cannot borrow *self as immutable because it is also borrowed as mutable

This is my code

#[derive(Clone)]
pub struct Cursor {
    pub stop:    bool,
    pub fst_pos: Vec<usize>,
    pub snd_pos: Vec<usize>,
    pub pos_tmp: usize
}

pub fn pos_inc(cur_pos: &mut usize, pos_ser: &Vec<usize>, idx: usize) {
    if idx == 0 { *cur_pos = 0 }
    else { *cur_pos = pos_ser[pos_ser.len() - 1] }
}

impl Cursor {
    pub fn pos_ref(&self) -> &Vec<usize> {
        if self.stop { &self.snd_pos }
        else { &self.fst_pos }
    }
    
    pub fn new() -> Cursor { Cursor { stop   : false,
                                      fst_pos: [0].to_vec(),
                                      snd_pos: [0].to_vec(),
                                      pos_tmp: 0 } }
    
    pub fn cur_inc(&mut self, cur_idx: usize) { pos_inc(&mut self.pos_tmp, self.pos_ref(), cur_idx) }
}

fn main() {
    let mut cursor = Cursor::new();
    cursor.cur_inc(1)
}

I try to change the function pos_ref parameter &self to &mut self, it is useless yet.
cannot borrow *self as mutable more than once at a time

If your method takes &self (or &mut self), that means the function call borrows the entirety of self by definition of the signature. So if something borrows a field of self mutably, you can't call any method that borrows all of self. After all, the signature of pos_ref would allow accessing self.pos_tmp, even if it currently doesn't do that.

Just inline the definition of pos_ref into cur_inc, like this. For longer functions, the proper solution is to explicitly pass a list of fields that they use, instead of being sloppy with the whole of self.

1 Like

So do I.
At present, the only solution for this I could carry out is this. However, this might cause the code duplication. Thanks for your answer.

Again, you can extract the non-overlapping fields in a function, you don't have to inline the definition. But your method is so short that in this case, it basically wouldn't have any advantage. For a method that's used a lot more or is more complex, you would of course want to write a function.

2 Likes

The usual solution is to make the fields explicitly visible to the compiler at call site, in some way.

  • Turn the methods into free functions:
#[derive(Clone)]
pub struct Cursor {
    pub stop:    bool,
    pub fst_pos: Vec<usize>,
    pub snd_pos: Vec<usize>,
    pub pos_tmp: usize
}

pub fn pos_inc(cur_pos: &mut usize, pos_ser: &Vec<usize>, idx: usize) {
    if idx == 0 { *cur_pos = 0 }
    else { *cur_pos = pos_ser[pos_ser.len() - 1] }
}

pub fn pos_ref(stop: bool, fst_pos: &[usize], snd_pos: &[usize]) -> &[usize] {
    if stop { snd_pos } else { fst_pos }
}

impl Cursor {
    pub fn new() -> Cursor { 
        Cursor { 
            stop   : false,
            fst_pos: vec![0],
            snd_pos: vec![0],
            pos_tmp: 0, 
        } 
    }
    
    pub fn cur_inc(&mut self, cur_idx: usize) { 
        pos_inc(&mut self.pos_tmp, pos_ref(self.stop, &self.fst_pos, &self.snd_pos), cur_idx) 
    }
}
  • Or extract the collectively borrowed fields into a new struct:
#[derive(Clone)]
pub struct CursorInner {
    pub stop:    bool,
    pub fst_pos: Vec<usize>,
    pub snd_pos: Vec<usize>,
}

#[derive(Clone)]
pub struct Cursor {
    pub inner: CursorInner,
    pub pos_tmp: usize
}

pub fn pos_inc(cur_pos: &mut usize, pos_ser: &Vec<usize>, idx: usize) {
    if idx == 0 { *cur_pos = 0 }
    else { *cur_pos = pos_ser[pos_ser.len() - 1] }
}

impl CursorInner {
    pub fn pos_ref(&self) -> &[usize] {
        if self.stop { &self.snd_pos } else { &self.fst_pos }
    }
}

impl Cursor {
    pub fn new() -> Cursor { 
        Cursor { 
            inner: CursorInner { 
                stop   : false,
                fst_pos: vec![0],
                snd_pos: vec![0],
            }, 
            pos_tmp: 0, 
        } 
    }
    
    pub fn cur_inc(&mut self, cur_idx: usize) { 
        pos_inc(&mut self.pos_tmp, self.inner.pos_ref(), cur_idx) 
    }
}
  • Or anything in between those options;
  • Finally, you can resort to runtime borrow checking, by wrapping the mutably borrowed fields in RefCell or Mutex. This allows you to borrow fields only by &, with the actual correct usage of mutable borrows delegated to runtime. This option is usually the least preferable, since it has performance penalties and introduces the possibility of runtime panics, but sometimes it's the best option.
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.