Anonymous objects contain both mutable and immutable references

The Rules of References
Let’s recap what we’ve discussed about references:

  • At any given time, you can have either one mutable reference or any number of immutable references.
  • References must always be valid.

I saw this question: How to make a Rust mutable reference immutable?
Here is code:

fn main() {
    let data = &mut vec![1, 2, 3];
    let x = &*data;
    println!("{:?}-{:p}, {:?}-{:p}", x, x, data, data);  // same address
}

Output:

It seems that the anonymous object(vec![1, 2, 3]) contain both mutable reference(data) and immutable reference(x, *data is anonymous object vec![1, 2, 3]).

But, when I tried to assign the vec![1, 2, 3] to variable v in following code, I got an error:

fn main() {
    let v = vec![1, 2, 3];
    let data = &mut v;
    let x = &*data;
    println!("{:?}-{:p}, {:?}-{:p}", x, x, data, data);
}

Ouput:

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `v` as mutable, as it is not declared as mutable
 --> src/main.rs:5:16
  |
4 |     let v = vec![1, 2, 3];
  |         - help: consider changing this to be mutable: `mut v`
5 |     let data = &mut v;
  |                ^^^^^^ cannot borrow as mutable

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` due to previous error

What is the difference between these? Please help me. Thanks. :smiling_face:

t

The first example is utilizing temporary lifetime extension: If you take a reference to a temporary value, then you immediately store it in a variable using let, then you can keep using that reference until the end of a block. Otherwise, temporary references only last until the end of the statement. Therefore, the first example is effectively doing this behind the scenes:

fn main() {
    let mut __temporary = vec![1, 2, 3];
    let data = &mut __temporary;
    let x = &*data;
    println!("{:?}-{:p}, {:?}-{:p}", x, x, data, data);
}

This is nearly the same as your second example with v, but note that __temporary is declared with let mut instead of just let. This is because of a certain rule of the language: if you take an &mut reference to a variable, it must have been declared with let mut. That is what the compiler error means by, "cannot borrow v as mutable, as it is not declared as mutable". As it states, changing the first statement to let mut v = vec![1, 2, 3]; fixes the issue.

3 Likes

Thanks :hugs:, I understand " if you take an &mut reference to a variable, it must have been declared with let mut ."
But I have another question. Why the __temporary can contain both mutable and immutable references. Here, I use the std::any::type_name function to get the variable type:

use std::any::type_name;
fn type_of<T>(_: T) -> &'static str {
    type_name::<T>()
}

fn main() {
    let mut __temporary = vec![1, 2, 3];
    let data = &mut __temporary;
    let x = &*data;
    println!("{:?}-{:p}, {:?}-{:p}", x, x, data, data);
    println!("{} , {}", type_of(x), type_of(data));
}

And output:

[1, 2, 3]-0x7ffca285bbe8, [1, 2, 3]-0x7ffca285bbe8
&alloc::vec::Vec<i32> , &mut alloc::vec::Vec<i32>

It might violate the rule of references: At any given time, you can have either one mutable reference or any number of immutable references. (The Rust Programming Language)

And then, I do some test:

  1. do not use immutable references can compile successfully
fn main() {
    let mut __temporary = vec![1, 2, 3];
    let data = &mut __temporary;
    let x = &*data;
    println!("{:?}-{:p}, {:?}-{:p}", x, x, data, data);
    // use mutable reference
    data.push(3);
    println!("data: {:?}-{:p}", data, data);
    // do not use immutable references can compile successfully
    // println!("x: {:?}-{:p}", x, x);
}
  1. use immutable references can't compile
fn main() {
    let mut __temporary = vec![1, 2, 3];
    let data = &mut __temporary;
    let x = &*data;
    println!("{:?}-{:p}, {:?}-{:p}", x, x, data, data);
    // use mutable reference
    data.push(3);
    println!("data: {:?}-{:p}", data, data);
    // use immutable references can't compile
    println!("x: {:?}-{:p}", x, x);
}

Compile Error:

error[E0502]: cannot borrow `*data` as mutable because it is also borrowed as immutable
  --> src/main.rs:7:5
   |
4  |     let x = &*data;
   |             ------ immutable borrow occurs here
...
7  |     data.push(3);
   |     ^^^^^^^^^^^^ mutable borrow occurs here
...
10 |     println!("x: {:?}-{:p}", x, x);
   |                              - immutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` due to previous error

Now, it seems that compiler allow you to define both mutable and immutable references on an object by this way(let x = &*data;), but prohibits the use of both mutable and immutable references.

Rust does allow overlapping reference when the other reference is created from the mutable reference. However in this case, the mutable reference is temporarily marked inactive until the last use of the derived reference, which prevents you from using the mutable reference in a mutable way while the immutable reference exists. If the derived reference was also mutable, the original mutable reference would not be usable at all (mutably or immutably) while inactive.

3 Likes

Thanks, you are right :wink:

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.