为什么 Rust 允许使用不可变绑定通过引用字段进行突变? [英] Why does Rust allow mutation through a reference field using an immutable binding?

查看:44
本文介绍了为什么 Rust 允许使用不可变绑定通过引用字段进行突变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我有一个绑定到结构体的不可变变量,Rust 通常不允许我改变结构体的字段,或拥有的子结构体的字段.

然而,如果该字段是可变引用,Rust 允许我改变被引用的对象,尽管我的绑定是不可变的.

为什么允许这样做?是不是不符合 Rust 的正常不变性规则?

Rust 不会让我通过不可变引用来做同样的事情,因此不可变引用与不可变绑定具有不同的行为.

代码示例:

struct Bar {价值:i32,}struct Foo<'a>{价值:i32,酒吧:酒吧,val_ref: &'a mut i32,}fn 主(){让 mut x = 5;{让 foo = foo {价值:6,酒吧:酒吧{val:15},val_ref: &mut x};//这是非法的,因为绑定是不可变的//foo.val = 7;//改变子结构也是非法的//foo.bar.val = 20;//这很好……为什么?*foo.val_ref = 10;让 foo_ref = &foo;//通过不可变引用进行变异也是非法的//*foo_ref.val_ref = 10;}println!("{}", x);}

解决方案

一个简短的解释是引用中的可变性和变量中的可变性是正交的其他.这两种形式的可变性是相关的,因为我们只能从可变变量(或绑定)中可变地借用一些东西.除此之外,每个二进制组合在 Rust 中都是可能的:

 参考可变性-----------------------------变量 |x: &T |x: &mut T |可变性|------------+----------------||mut x: &T |mut x: &mut T |-----------------------------

我们可以想到许多代码示例来举例说明使用这样的变量 x 可以做什么.例如,可变引用的不可变变量可以修改另一个元素,但不能修改其自身:

让 mut a = 5;让 mut b = 3;让 x: &mut i32 = &mut a;*x = 10;//好的x = &mut b;//不![E0384]*x = 6;

即使作为结构体中的字段,这也不与 Rust 的安全保证相冲突.如果变量不可变地绑定到结构值,则每个字段也将是不可变的.在这个例子中:

让 mut x = 5;让 foo = foo {价值:6,酒吧:酒吧{val:15},val_ref: &mut x};*foo.val_ref = 10;

没有对 foo 应用任何改变:foo.val_ref 仍然指向 x.前者可以改变,因为它是可变借用的.引用是独立借用检查的.Foo 中的生命周期参数 'a 使编译器能够跟踪借用.

第二个示例(如下所示)不起作用,因为从 &Foo 中,我们只能检索对其字段的引用(例如对 val_ref: &mut i32).反过来,为了防止混叠,&&mut i32 只能被强制转换为 &i32.不能通过不可变引用可变地借用数据.

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

<块引用>

Rust 不会让我通过不可变引用来做同样的事情.因此,不可变引用与不可变绑定具有不同的行为.

没错!

另见:

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);
}

解决方案

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:

这篇关于为什么 Rust 允许使用不可变绑定通过引用字段进行突变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆