从结构中借用字段

当你有一个 componentresource,即具有多个字段的大型结构时,有时你想同时借用其中的几个字段,同时可能要求是可变借用。

struct MyThing {
    a: Foo,
    b: Bar,
}

fn my_system(mut q: Query<&mut MyThing>) {
    for thing in q.iter_mut() {
        helper_func(&thing.a, &mut thing.b); // ERROR!
    }
}

fn helper_func(foo: &Foo, bar: &mut Bar) {
    // do something
}

这会导致编译器出现关于借用冲突的错误:

error[E0502]: cannot borrow `thing` as mutable because it is also borrowed as immutable
    |
    |         helper_func(&thing.a, &mut thing.b); // ERROR!
    |         -----------  -----         ^^^^^ mutable borrow occurs here
    |         |            |
    |         |            immutable borrow occurs here
    |         immutable borrow later used by call

解决办法是使用 "reborrow" 方式,这是 Rust 编程中一个通用但并不显而易见的技巧:

        // add this at the start of the for loop, before using `thing`:
        let thing = &mut *thing;

注意,这一行会触发 变更检测。即使你在此后没有修改数据,该组件也会被标记为已更改。

解释

Bevy 通常让你通过特殊的封装类型(如 Res<T>ResMut<T>Mut<T>(当获取可变组件的时候)来访问你的数据。) 这可以让 Bevy 跟踪对数据的访问。

这些是 "智能指针" 类型,使用 Rust 的 Deref 特性来解除对数据的引用。它们通常可以无缝地工作以致于你甚至不会注意到它们。

然而,在某种意义上,它们对编译器是不透明的。当你可以直接访问该结构时,Rust 语言允许单独借用该结构的字段,但当它被包裹在另一种类型中时,字段就无法借用。

上面显示的 "reborrow" 技巧,有效地将包装器转换为一个普通的 Rust 引用。*thing 通过 DerefMut 解除对包装器的引用,然后通过 &mut 以可变方式借用它。现在你可访问的数据是 &mut MyStuff 而不是 Mut<MyStuff>