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

问题描述 投票:0回答:1

我仍然很难理解为什么需要

&mut self
来修改我的结构拥有的对象的内部状态。我明白为什么我必须至少使用
&self
,因为我不想消耗
self
,结束它的存在。我也理解为什么如果我正在修改我的结构的字段我必须使用
&mut self
,但我没有。

我有以下结构和实现:

struct Writer {
    obj: json::JsonValue
}

impl<'b> Writer {
    fn from(obj: json::JsonValue) -> Self {
        Self {
            obj
        }
    }

    fn put(&mut self, field_name: &str, value: bool) {
        self.obj.insert(field_name, value);
        //   ^^^- not modifying this, but a field inside "obj"
    }

    fn release_ownership(self) -> json::JsonValue {
        self.obj
    }
}

用法:

writer.put(field_name, true);

使用

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

`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable`

Rust 书 教我:

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

我看不出如何修改这里的结构。我清楚地修改了一个字段(这里

obj
in the struct,但不是struct本身。

JsonValue::insert()

定义中我可以看到
&mut self
,我也不明白,因为它“只”通过object改变
JsonValue::Object::insert()
contents

Rust book 说:

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

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

我的直觉告诉我,我用

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

但是,这似乎是多余的,因为

JsonValue::insert()
已经强制对其内部对象进行可变访问。所以编译器知道,
JsonValue
的实例被可变地借用用于插入并且不能再次借用,直到它被释放。

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

rust struct borrow-checker mutability
1个回答
0
投票

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

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

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

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

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

RefCell
Mutex
RwLock
进行运行时借用检查。

© www.soinside.com 2019 - 2024. All rights reserved.