Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
309 views
in Technique[技术] by (71.8m points)

Why does Rust allow mutation through a reference field using an immutable binding?

If I have an immutable variable bound to a struct, Rust will generally not allow me to mutate the fields of the struct, or the fields of owned child structs.

However, if the field is a mutable reference, Rust will allow me to mutate the referred-to object despite my binding being immutable.

Why is this allowed? Is it not inconsistent with Rust's normal rules for immutability?

Rust won't let me do the same through an immutable reference, so an immutable reference has different behavior than an immutable binding.

Code Example:

struct Bar {
    val: i32,
}

struct Foo<'a> {
    val: i32,
    bar: Bar,
    val_ref: &'a mut i32,
}

fn main() {
    let mut x = 5;

    {
        let foo = Foo { 
            val: 6, 
            bar: Bar { val: 15 },
            val_ref: &mut x
        };

        // This is illegal because binding is immutable
        // foo.val = 7;

        // Also illegal to mutate child structures
        // foo.bar.val = 20;

        // This is fine though... Why?
        *foo.val_ref = 10;

        let foo_ref = &foo;

        // Also illegal to mutate through an immutable reference
        //*foo_ref.val_ref = 10;
    }

    println!("{}", x);
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

A short way to explain this is that mutability in references and mutability in variables are orthogonal to each other. The two forms of mutability are related in the sense that we can only mutable borrow something from a mutable variable (or binding). Other than that, each binary combination is possible in Rust:

               reference mutability
            -----------------------------
variable   |     x: &T  |      x: &mut T |
mutability |------------+----------------|
           | mut x: &T  |  mut x: &mut T |
            -----------------------------

We can think of many samples of code exemplifying what can be done with such a variable x. For instance, an immutable variable of a mutable reference can modify one other element, but not itself:

let mut a = 5;
let mut b = 3;
let x: &mut i32 = &mut a;

*x = 10; // ok

x = &mut b; // nope! [E0384]
*x = 6;

Even as a field in a struct, this does not conflict with Rust's safety guarantees. If a variable is immutably bound to a struct value, each of the fields will be immutable as well. In this example:

let mut x = 5;
let foo = Foo { 
    val: 6, 
    bar: Bar { val: 15 },
    val_ref: &mut x
};
*foo.val_ref = 10;

No mutations were applied to foo here: foo.val_ref still points to x. The former can be mutated because it's mutably borrowed. References are borrow-checked independently. The lifetime parameter 'a in Foo enables the compiler to keep track of the borrow.

That second example (shown below) does not work, because from a &Foo, we can only retrieve references to its fields (such as to val_ref: &mut i32). In turn, to prevent aliasing, a &&mut i32 can only be coerced to &i32. One cannot borrow data mutably through an immutable reference.

let foo_ref = &foo;
*foo_ref.val_ref = 10; // error[E0389]

Rust won't let me do the same through an immutable reference. So an immutable reference has different behavior than an immutable binding.

Exactly!

See also:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...