【问题标题】:Why does Rust allow mutation through a reference field using an immutable binding?为什么 Rust 允许使用不可变绑定通过引用字段进行突变?
【发布时间】:2018-05-01 22:24:17
【问题描述】:

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

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

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

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

代码示例:

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

【问题讨论】:

标签: rust


【解决方案1】:

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

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

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

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

*x = 10; // ok

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

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

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

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

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

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

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

没错!

另请参阅:

【讨论】:

  • This is covered in the first edition of the book,具体为:y is an immutable binding to a mutable reference, which means that you can’t bind y to something else (y = &amp;mut z), but you can mutate the thing that’s bound to y (*y = 5). A subtle distinction.
  • 谢谢!我很好奇为什么我的最后一个例子*foo_ref.val_ref = 10; 是无效的?在这种情况下,我有一个对结构的不可变引用,并且我正在使用它来读取对不同对象的可变引用。如果不可变绑定的其他情况是这样,那为什么不行呢?无论哪种方式,我都不会修改不可变结构的数据。
  • (我想我的整体困惑是它似乎不一致 - 似乎可变性在某些情况下是可传递的,而在其他情况下则不是)
  • @something_clever 实际上,可变性在某种程度上是通过引用继承的:从&amp;Foo 我们只能检索到其字段的不可变引用,而&amp;&amp;mut T 变为&amp;&amp;T。我明天可以更新这个问题的答案。
  • @E_net4 所以我想知道为什么决定通过绑定继承可变性。在我的例子中,我的感觉实际上是 Rust 不应该接受它们中的任何一个。那就是 - 如果我的绑定是不可变的,我不应该能够传递地改变引用,即使我有它们被可变地借用。
猜你喜欢
  • 1970-01-01
  • 2011-03-01
  • 2013-08-22
  • 1970-01-01
  • 1970-01-01
  • 2023-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多