能否将结构体的部分作为可变的借用,而将其他部分作为不可变的借用?

9

如果结构体的字段是私有的,是否可能将结构体的一部分作为可变的借用,另一部分作为不可变的借用。

fn main() {
    let mut ecs = EntityComponentSystem::new();

    for e_id in ecs.get_entities_with_component::<Velocity>().unwrap() {
           let components = ecs.get_mut_components(e_id);
           ...
}

impl EntityComponentSystem {
    ...
    pub fn get_entities_with_component<K: Key>(&self) -> Option<&HashSet<u64>> {
        self.entities_with_components.get(&TypeId::of::<K>())
    }

    pub fn get_mut_components(&mut self, entity_id: u64) -> &mut TypeMap {
        let entity = self.entities.get_mut(&entity_id).unwrap();
        &mut entity.components
    }
}

pub struct EntityComponentSystem {
    entities: HashMap<u64, Entity>,                     <------- I would like to modify this.
    entities_with_components: HashMap<TypeId, HashSet<u64>>,   <---- while reading from this!
}

编译器给我以下错误提示:
error[E0502]: cannot borrow `*ecs` as mutable because it is also borrowed as immutable
  --> physics.rs:19:26
   |
18 |     for e_id in ecs.get_entities_with_component::<Velocity>() {
   |                 --- immutable borrow occurs here
19 |         let components = ecs.get_mut_components(*e_id);
   |                          ^^^ mutable borrow occurs here
...
26 |     }
   |     - immutable borrow ends here

我没理解的是,在我们基本上返回了entities_with_components字段的一部分后,get_entities_with_component中的&self引用仍然被借用。只有那部分不应该被借用吗?有没有办法强制执行这个规定?
2个回答

9
你只能将整个结构体作为不可变或可变借用,没有部分借用的概念。当这成为问题时,你可以使用内部可变性,形式为RefCell
pub struct EntityComponentSystem {
    entities: RefCell<HashMap<u64, Entity>>,
    entities_with_components: HashMap<TypeId, HashSet<u64>>,
}

现在您可以将整个结构体作为不可变借用,并独立地将RefCell的内容作为可变借用:
pub fn get_mut_components(&self, entity_id: u64) -> &mut TypeMap {
    let mut entities = self.entities.borrow_mut();
    let entity = entities.get_mut(&entity_id).unwrap();
    &mut entity.components
}

7
严格来说,是有的。你可以对结构体进行部分借用,但是这对于该操作并不适用,因为字段是私有的。这里有一个例子:https://play.rust-lang.org/?gist=fcfcca700ba238287063143754e15c97&version=stable - Timidger

2
不可以,函数不能返回结构体的部分引用并且保留结构体的部分借用状态。
但是你可以像这样返回不可变和可变借用的元组。
#[derive(Debug)]
struct AB(u32, u32);

impl AB {
    fn borrow_parts(&mut self) -> (&u32, &mut u32) {
        (&self.0, &mut self.1)
    }
}

fn main() {
    let mut ab = AB(0, 2);
    {
        let (a, b) = ab.borrow_parts();
        *b = *a;
    }
    println!("{:?}", ab);
}

我不知道在 OP 的情况下你该如何处理,因为他们在循环外进行了一次不可变借用,并在循环内多次进行可变借用。 - interjay
@interjay,如果为HashMap<TypedId, HashSet<u64>>或某个包装类型实现get_mut_components,则不需要多个可变借用来完成此操作。作为borrow_parts结果的一部分,将返回对该包装类型的可变引用。 - red75prime

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接