Calling a method within a iter_mut()


#1

Hi Everyone,

I’m new to learning Rust, so sorry if this is an obvious question. I’ve made this code example where I’m trying to call a method iplus1 to modify elements in a vector kvec. I can accomplish this with foo where I iterate over the index of the vector, and I can do it in bar or baz where I inline the contents of iplus1. But how can I combine the two where I use an iterator and call the method? The methods badbar or badbaz don’t compile. What would the preferred approach be in a case like this? Using foo just seems really ugly.

Thanks! Craig

fn main() {

    #[derive(Debug)]
    struct A {
        i : i64,
        kvec : Vec<i64>
    }

    impl A {

        // some method
        fn iplus1(&self) -> i64 {
            self.i + 1
        }

        // this works using the index
        fn foo(&mut self) {
            for j in 0..self.kvec.len() {
                self.kvec[j] += self.iplus1();
            }
        }

        // so does this 
        // where we inline the contents of iplus1
        fn bar(&mut self) {
            for kref in &mut self.kvec {
                *kref += self.i + 1;
            }
        }

        // and this works with iter_mut()
        fn baz(&mut self) {
            for kref in self.kvec.iter_mut() {
                *kref += self.i + 1;
            }
        }

        // but how do we do it with an iterator
        // and the method call?


        // this doesn't compile
        // cannot borrow `*self` as immutable because 
        // `self.kvec` is also borrowed as mutable
        fn badbar(&mut self) {
            for kref in &mut self.kvec {
                *kref += self.iplus1();
            }
        }

        // cannot borrow `*self` as immutable because 
        // `self.kvec` is also borrowed as mutable
        fn badbaz(&mut self) {
            for kref in self.kvec.iter_mut() {
                *kref += self.iplus1();
            }
        }

    }

    let mut a = A { i : 1, kvec : vec![1,2,3]};

    a.foo();
    a.bar();
    a.baz();
    a.badbar();
    a.badbaz();

    println!("{:?}", a);
}

#2

In cases like this, where an iplus1 style method returns a Copy type and doesn’t do any internal mutation per call, the canonical answer would be to store its return value in a local and use the local inside the loop.

In other cases, you could move the i field into a separate struct and borrow them independently. So essentially:

struct A {
    i: B,
    kvec : Vec<i64>,
}

impl A {
fn badbaz(&mut self) {
            for kref in self.kvec.iter_mut() {
                *kref += self.i.iplus1();
            }
        }
}

struct B(i64);

impl B {
  fn iplus1(&self) -> i64 { ... }
}

This is silly for the example in hand but is appropriate for more involved cases.

The overarching principle/technique behind this is splitting data across types and leveraging the compiler understanding disjoint borrows: allowing to borrow different fields in parallel.


#3

Thanks very much for your answer. As you say, you could just factor the call out of the loop in this particular case, but I was just trying to give a simple example. Your main suggestion looks like just what I needed. It is very counterintuitive to me that wrapping the value of i in a struct B makes the compiler happy, so thanks for the suggestion.