将对象修改为结构体中的字段时,为什么要在方法中使用 &mut self

try*_*lly 1 struct mutability rust borrow-checker

我仍然很难理解为什么&mut self需要修改我的结构所拥有的对象的内部状态。我明白为什么我必须使用至少&self,因为我不想消费self,结束它的存在。我也明白为什么我必须使用&mut self如果我修改结构的字段,但我不这样做。

\n

我有以下结构和实现:

\n
struct Writer {\n    obj: json::JsonValue\n}\n\nimpl<\'b> Writer {\n    fn from(obj: json::JsonValue) -> Self {\n        Self {\n            obj\n        }\n    }\n\n    fn put(&mut self, field_name: &str, value: bool) {\n        self.obj.insert(field_name, value);\n        //   ^^^- not modifying this, but a field inside "obj"\n    }\n\n    fn release_ownership(self) -> json::JsonValue {\n        self.obj\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

用法:

\n
writer.put(field_name, true);\n
Run Code Online (Sandbox Code Playgroud)\n

使用&self而不是&mut self我会得到一个编译器错误:

\n
`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable`\n
Run Code Online (Sandbox Code Playgroud)\n

Rust教会了我:

\n
\n

如果我们想更改我们\xe2\x80\x99调用该方法的实例作为该方法的一部分,我们\xe2\x80\x99d使用 &mut self 作为第一个参数。

\n
\n

我不明白如何修改这里的结构。我明确修改了struct 中的字段(此处obj),但没有修改结构本身。

\n

在我可以看到的定义JsonValue::insert()&mut self中,我也不理解,因为它“仅”改变了through的内容objectJsonValue::Object::insert()

\n

Rust书中说:

\n
\n

请注意,整个实例必须是可变的;Rust 不允许我们仅将某些字段标记为可变。

\n
\n

如果我想修改字段值,我可以理解这一点,但如果字段是指向已修改的其他结构的指针,我就不能理解这一点。

\n

我的直觉告诉我,我限定的 with&mut不是指对数据的直接引用,而是指沿着所有指针的整个路径或最终引用。如果是这样的话:为什么会这样?

\n

然而,这似乎是多余的,因为JsonValue::insert()已经强制对其内部对象进行可变访问。因此编译器知道, 的实例JsonValue是可变借用的用于插入,并且不能再次借用,直到它被释放。

\n

作业:阅读这本书并进行搜索

\n

caf*_*e25 5

访问链上的所有内容都必须声明为可变引用,以确保无法获得对同一对象的两个可变引用。考虑一下:

struct ContainsMutable<'a>(&'a mut i8);

fn main() {
    let mut a = 99;
    let cm = ContainsMutable(&mut a);
    let cmref1 = &cm;
    let cmref2 = &cm;
    // now if both references allowed mutable access to the pointee
    // `a` we'd have a problem because this would be allowed
    std::thread::scope(|s| {
        s.spawn(|| {
            *cmref1.0 = 55;
        });
        s.spawn(|| {
            *cmref2.0 = 31;
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

因此,我们永远不能允许对共享引用背后的某些内容(我们通过路径上某处的共享引用访问的内容)进行可变访问,因为我们可以轻松地以这种方式创建对同一对象的可变引用。

然而,这似乎是多余的,因为 JsonValue::insert() 已经强制对其内部对象进行可变访问。因此编译器知道,JsonValue 的实例是可变借用的用于插入,并且不能再次借用,直到它被释放。

假设编译器可以在编译时预测函数何时被调用并不允许这样做,但它不能,或者借用检查是在运行时完成的,但事实并非如此,借用检查是在编译时静态完成的。您可以使用RefCell,Mutex或进行运行时借用检查RwLock

  • 没有共享引用总是意味着可能同时存在其他引用,这就是 Rust 中借用的工作原理,编译器并不总是有如此清晰的目标路径。可变引用是我们程序员告诉编译器该引用不共享的方式,因此可以安全地进行变异。 (2认同)