Immutable reference "chains" to a mutable reference which mislead borrow rules

The title seem not very clear about what I met, I wrote an example code:

struct Foo {
    i: i32,
}

impl Foo {
    fn new() -> Self {
        Self {
            i: 0
        }
    }
}

fn main() {
    let mut vec = Vec::new();
    vec.resize_with(3, || {
        Foo::new()
    });

    let mut vec2 = Vec::new();


    for i in 0..3 {
        for foo in &mut vec {
            foo.i = i;

            // let foo2 = unsafe { transmute::<&mut Foo, &Foo>(foo) };
            let foo2 = &*foo;
            vec2.push(foo2);
        }
    }
}

As you can see, I want to take a mutable reference of foo to do some stuff, and save an immutable reference of foo into another vector.

If I use a normal way to "cast" a mutable reference into immutable, the compiler will complain:

error[E0499]: cannot borrow `vec` as mutable more than once at a time
  --> src/main.rs:27:20
   |
27 |         for foo in &mut vec {
   |                    ^^^^^^^^ `vec` was mutably borrowed here in the previous iteration of the loop

and if I use transmute to cast it, the compile will pass.

I got a feeling that even if I take a immutable reference foo by using let foo2 = &*foo;, it somehow "chains" or "relates" to the mutable foo, witch relates to the mutable vec, which invalid the borrow rules.

So how can I do this safely? Or, what could possibly be wrong if I use transmute?

Your outer loop is the problem -- each time you hit the inner loop you're mutably borrowing vec again, which invalidates all your stored refernces in vec2. If you need to do this, you'll have to use pointers or such to avoid aliasing (UB).

If you can do (whatever it is) without looping, that would work -- e.g. try this change.

-    for i in 0..3 {
+    {
+        let i = 13;
1 Like

Good point of "each time you hit the inner loop you're mutably borrowing vec again, which invalidates all your stored refernces in vec2 .", I think this is the answer of "what could possibly be wrong if I use transmute ?", I didn't consider this thoroughly, and the code is a bad design now.

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.