从结构中借用字段
当你有一个 component 或 resource,即具有多个字段的大型结构时,有时你想同时借用其中的几个字段,同时可能要求是可变借用。
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>
。