A question about &,pointer and ref

How to make the First change with the change of the Second, that is, the fIrst store is a pointer to the Second pointer

    let mut mut_tuple = (Box::new(5u32), 3u32);
    {
        let (ref mut first, ref mut second) = mut_tuple;
        *first = Box::new(*second);
        *second = 2u32;
    }
    println!("tuple is {:?}", mut_tuple);

You cannot use a Box for this. A box always owns the thing it points at, but the target of second is owned by mut_tuple, so it cannot be owned by the box.


You might attempt to use a mutable reference, which does not need to own its target:

fn main() {
    let mut mut_tuple = (&mut 5u32, 3u32);
    {
        let (ref mut first, ref mut second) = mut_tuple;
        *first = second;
        *second = 2u32;
    }
    println!("tuple is {:?}", mut_tuple);
}

but it fails:

error[E0506]: cannot assign to `*second` because it is borrowed
 --> src/main.rs:6:9
  |
5 |         *first = second;
  |                  ------ borrow of `*second` occurs here
6 |         *second = 2u32;
  |         ^^^^^^^^^^^^^^ assignment to borrowed `*second` occurs here
7 |     }
8 |     println!("tuple is {:?}", mut_tuple);
  |                               --------- borrow later used here

This is because mutable references always have exclusive access to the thing they point to for the duration in which the reference is live. A reference is live at least from its creation until its last use, and the creation is at the *first = second; line, and the last is use the println! line. This means that accessing second directly between those two is not allowed, because it would be in contradiction with the fact that first has exclusive access at that point.


Now, immutable references don't require that the access is exclusive. So maybe we can use that?

fn main() {
    let mut mut_tuple = (&5u32, 3u32);
    {
        let (ref mut first, ref second) = mut_tuple;
        *first = second;
        *second = 2u32;
    }
    println!("tuple is {:?}", mut_tuple);
}

this also fails:

error[E0594]: cannot assign to `*second`, which is behind a `&` reference
 --> src/main.rs:6:9
  |
4 |         let (ref mut first, ref second) = mut_tuple;
  |                             ---------- help: consider changing this to be a mutable reference: `ref mut second`
5 |         *first = second;
6 |         *second = 2u32;
  |         ^^^^^^^^^^^^^^ `second` is a `&` reference, so the data it refers to cannot be written

Immutable references must be immutable for the entire duration in which it is live. Therefore, we cannot modify second while the immutable reference in first is alive.


So how can we fix this? Well, there's a special type called Cell that can let us do this:

use std::cell::Cell;
fn main() {
    let mut mut_tuple = (&Cell::new(5u32), Cell::new(3u32));
    {
        let (ref mut first, ref second) = mut_tuple;
        *first = second;
        second.set(2u32);
    }
    println!("tuple is {:?}", mut_tuple);
}
tuple is (Cell { value: 2 }, Cell { value: 2 })

Why does this work? Well, the Cell type is special in the following way: Even if the contents change, the cell is not considered to have been modified. This means that we can actually modify it, even though we only have an immutable reference to the cell.

To read more, see:

  1. The documentation for std::cell::Cell.
  2. This article on Cell: Temporarily opt-in to shared mutation

Note that Cell is a simple version of RefCell, which you may have heard of.

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.