Changing one attribute of a structure while iterating over another attribute

I have a structure that has 2 Vec

s. I want to be able to iterate over one while changing the other. Here's a sample program:

use std::slice;

struct S {
    a: Vec<i32>,
    b: Vec<i32>
}

impl S {
    fn a_iter<'a>(&'a self) -> slice::Iter<i32>  {
        self.a.iter()
    }
    fn a_push(&mut self, val: i32) {
        self.a.push(val);
    }
    fn b_push(&mut self, val: i32) {
        self.b.push(val);
    }
}

fn main() {
    let mut s = S { a: Vec::new(), b: Vec::new() };
    s.a_push(1);
    s.a_push(2);
    s.a_push(3);

    for a_val in s.a_iter() {
        s.b_push(a_val*2);
    }
}

      

But there is this compiler error:

$ rustc iterexample.rs 
iterexample.rs:28:9: 28:10 error: cannot borrow `s` as mutable because it is also borrowed as immutable
iterexample.rs:28         s.b_push(a_val*2);
                           ^
note: in expansion of for loop expansion
 iterexample.rs:26:5: 29:6 note: expansion site
iterexample.rs:26:18: 26:19 note: previous borrow of `s` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `s` until the borrow ends
iterexample.rs:26     for a_val in s.a_iter() {
                                   ^
note: in expansion of for loop expansion
iterexample.rs:26:5: 29:6 note: expansion site
iterexample.rs:29:6: 29:6 note: previous borrow ends here
iterexample.rs:26     for a_val in s.a_iter() {
iterexample.rs:27         println!("Looking at {}", a_val);
iterexample.rs:28         s.b_push(a_val*2);
iterexample.rs:29     }
                      ^
note: in expansion of for loop expansion
iterexample.rs:26:5: 29:6 note: expansion site
error: aborting due to previous error

      

I understand what the compiler is complaining about. I borrowed self

in the for loop because I am still iterating over it.

Conceptually there should be a way to do this, though. I am only changing s.b

and not changing what I iterate over ( s.a

). Is there anyway to write my program to demonstrate this separation and allow this program to compile?

This is a simplified example of a larger program, so I need to keep the overall structure the same (a structure that has some things, one of which will be renamed and the other will be updated).

+3


source to share


4 answers


I think I solved this problem with macros. If I use the following code, it works:

use std::slice;

struct S {
    a: Vec<i32>,
    b: Vec<i32>
}

impl S {
    fn a_push(&mut self, val: i32) {
        self.a.push(val);
    }
}

macro_rules! a_iter {
    ($x: expr) => {
        { $x.a.iter() }
    }
}

macro_rules! b_push {
    ($x: expr, $val: expr) => {
        $x.b.push($val);
    }
}

fn main() {
    let mut s = S { a: Vec::new(), b: Vec::new() };
    s.a_push(1);
    s.a_push(2);
    s.a_push(3);

    let iter = a_iter!(s);

    for a_val in iter {
        println!("Looking at {}", a_val);
        b_push!(s, a_val*2);
    }
}

      



Here I moved the code a_iter

and b_push

in the macro. When the code is compiled, the macro will expand and it is as if we are not using an abstraction function. However, for writing code, this functionality is abstracted.

I'm not sure if this is a good or bad idea.

0


source


You can remove this error if you use s.a.it

instead s.a_iter()

. Your current solution does not work because the returned iterator from s.a_iter()

stores a reference s

that has the same lifetime s

, and therefore, until this reference is saved, you will not be able to take something internally as mutable.In s

particular, this is because:

the compiler stops at the function call boundary when evaluating shared parameters

(lifetime in your case)

There is a good answer here, which contains a full explanation of a very similar problem: cannot treat `self.x` as immutable because` * self` is also borrowed as mutable

Edit



A possible solution could be an operation inside s

instead of taking the iterator out of s

. You can define in a s

method like this:

fn foreach_in_a_push_to_b<F>(&mut self, func: F) where F : Fn(&i32) -> i32 {
    for a_val in self.a.iter() {
        self.b.push(func(a_val));
    }
}

      

and then

s.foreach_in_a_push_to_b(|&x| x * 2);

      

+4


source


The main problem is that the check validation does not have enough information to prove that your code is safe; it stops at function boundaries. You can work around this by writing a method that splits the link so that the compiler actually has the information it needs:

struct S {
    a: Vec<i32>,
    b: Vec<i32>
}

impl S {
    fn a_push(&mut self, val: i32) {
        self.a.push(val);
    }
    fn split_a_mut_b<'a>(&'a mut self) -> (&'a Vec<i32>, &'a mut Vec<i32>) {
        (&self.a, &mut self.b)
    }
}

fn main() {
    let mut s = S { a: Vec::new(), b: Vec::new() };
    s.a_push(1);
    s.a_push(2);
    s.a_push(3);

    let (a, b) = s.split_a_mut_b();

    for a_val in a.iter() {
        b.push(a_val*2);
    }
}

      

The clues here are that the split_a_mut_b

compiler can prove that the two roles do not overlap. Another pattern you can use that saves more of the original API is to temporarily parse this value into mutable and immutable parts:

use std::slice;

#[derive(Debug)]
struct S {
    a: Vec<i32>,
    b: Vec<i32>
}

impl S {
    fn a_iter(&self) -> slice::Iter<i32>  {
        self.a.iter()
    }
    fn a_push(&mut self, val: i32) {
        self.a.push(val);
    }
    fn b_push(&mut self, val: i32) {
        self.b.push(val);
    }
    fn split_a_mut_b<F, R>(&mut self, f: F) -> R
    where F: FnOnce(&Self, &mut Self) -> R {
        use std::mem::swap;

        // Break off the mutable part(s) (or the immutable parts if there
        // are less of those).
        let mut temp = S { a: vec![], b: vec![] };
        swap(&mut self.b, &mut temp.b);

        // Call the closure.
        let result = f(self, &mut temp);

        // Glue the value back together.
        swap(&mut self.b, &mut temp.b);

        result
    }
}

fn main() {
    let mut s = S { a: Vec::new(), b: Vec::new() };
    s.a_push(1);
    s.a_push(2);
    s.a_push(3);

    s.split_a_mut_b(|imm, muta| {
        for a_val in imm.a_iter() {
            muta.b_push(a_val*2);
        }
    });

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

      

It's not terribly ineffective; this method represents absolutely no heap activity; we just drag the pointers around.

+2


source


With raw pointers, you can enumerate the structure into a second variable - Rust will treat them as two different variables and let you borrow the immutable part without complaint.

let s_alias = &s as *const S;
let a_iter = unsafe { (*s_alias).a_iter() };

for a_val in a_iter {
    s.b_push(a_val*2);
}

      

Playground

I welcome a second opinion on this, but I don't see how it might cause memory safety issues in this example, at least.

0


source







All Articles