rust 给予向量中的元素访问其他元素

nqwrtyyt  于 2023-03-02  发布在  其他
关注(0)|答案(2)|浏览(151)

对于上下文,我正在创建一个简单的游戏引擎。
我的游戏对象都存储在一个矢量中,每一帧每个对象都被迭代和更新。
这样做的问题是,有时候一个对象需要访问向量中其他对象的数据,我不能传入一个不可变/可变的引用到向量本身,因为它是可变地迭代的。
如何允许每个游戏对象访问同一Vector中其他游戏对象的字段(位置、旋转等)?
以下是导致问题的循环:

for entity in self.scene.collidable_entitys {
    match &mut entity.value {
        Some(e) => {
            e.update(&frame_descriptor, &self.input, &self.scene.collidable_entitys);
            entity_count += 1;
        }
        None => {}
    }
}

编译器不喜欢我给self.scene.collidable_entyys一个不可变的引用,因为它被借用来进行迭代。
下面是一个简单例子来说明我的问题。

use std::vec::Vec;

struct Object {
    a: u32,
    b: u32,
}

impl Object {
    fn new() -> Self {
        Self {
            a: 0,
            b: 0,
        }
    }
    
    fn update(&mut self, objects: &Vec<Option<Object>>) {
        // mutate self here
        // but also read from the object vector
    }
}

fn main() {
    println!("Hello, world!");
    let mut vector: Vec<Option<Object>> = Vec::new();
    vector.push(Some(Object::new()));
    vector.push(Some(Object::new()));
    vector.push(Some(Object::new()));

    for obj in vector.as_ref() {
        match obj {
            Some(o) => o.update(&vector),
            None => {}
        }
    }
}

任何帮助或我可以使用的系统的替代方案都非常感谢。

cdmah0mi

cdmah0mi1#

entity是可变借用的,然后你试图借用实体的整个集合。这是不允许的,因为实体现在被借用了两次。
通常--特别是在游戏引擎中--这个问题可以通过使用实体-组件-系统(ECS)模式来解决。它通过处理实体的句柄而不是引用来解决这个问题。ECS通常非常快,感觉非常适合Rust,因为它完全消除了复杂的借用交互。ECS crates here有一个很大的列表。
如果你不想使用ECS,你仍然可以使用句柄或id代替引用,例如,使用向量索引作为临时id,在很短的范围内借用,以提取出你需要的单个字段。

// take a slice instead of Vec so that you can't accidentally change its size, 
// which could invalidate the given obj_index.
fn update_obj(obj_index: usize, objects: &mut [Option<Object>]) {
    let obj = objects[obj_index];
    // etc
}
pn9klfpd

pn9klfpd2#

一种解决方案是将向量分割为切片:

use std::vec::Vec;

struct Object {
    a: u32,
    b: u32,
}

impl Object {
    fn new() -> Self {
        Self {
            a: 0,
            b: 0,
        }
    }
    
    // here you should probably change type of objects
    fn update<'a>(&'a mut self, objects: impl Iterator<Item = &'a Option<Object>>) {
        // mutate self here
        // but also read from the object vector
    }
}

fn main() {
    println!("Hello, world!");
    let mut vector: Vec<Option<Object>> = Vec::new();
    vector.push(Some(Object::new()));
    vector.push(Some(Object::new()));
    vector.push(Some(Object::new()));
    
    // 'i' is index of 'o'
    for i in 0..vector.len() {
        // here you split the vector into two mutable slices
        // (they have to be mutable because you need to get 'o' from them)
        let (objects_left_of_o, o_and_objects_right_of_o) = vector.split_at_mut(i);
        
        if let Some((Some(o), objects_right_of_o)) = o_and_objects_right_of_o.split_first_mut() {
            o.update(objects_left_of_o.iter().chain(objects_right_of_o.iter()));
        }
    }
}

另一个解决方案是通过RefCell利用内部可变性。

use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::vec::Vec;

struct Object {
    a: u32,
    b: u32,
}

impl Object {
    fn new() -> Self {
        Self { a: 0, b: 0 }
    }

    // here you can't just borrow from every refcell, cause one of them should have already given
    // out the &mut self reference
    fn update(&mut self, objects: &Vec<RefCell<Option<Object>>>) {
        // DO NOT DO THIS
        for obj in objects {
            // if self refers to an element in the vector, this will PANIC because you would be trying to get another reference to self
            if let Some(o) = obj.borrow().deref() {}
        }

        // here maybe debug assert that you can't get borrow only one of the items (which is self)
        debug_assert_eq!(
            objects
                .iter()
                .filter(|refcell| refcell.try_borrow().is_err())
                .count(),
            1
        );

        for obj in objects {
            // if self refers to an element in the vector, this will PANIC
            if let Ok(reference) = obj.try_borrow() {
                if let Some(o) = reference.deref() {
                    // use 'o', which is an object different from self
                }
            }

            // alternatively, use let-Some-else instead of if-let-Some
            let Ok(reference) = obj.try_borrow() else { continue };
            let Some(o) = reference.deref() else { continue };

            // use 'o'...
        }
    }
}

fn main() {
    println!("Hello, world!");

    // now the vector gets filled with a structure that wraps your Option<Object> and implements
    // interior mutability which means you push some checks onto runtime instead of compile time
    let mut vector = Vec::new();
    vector.push(RefCell::new(Some(Object::new())));
    vector.push(RefCell::new(Some(Object::new())));
    vector.push(RefCell::new(Some(Object::new())));

    // here you are looping on an immutable reference,
    // so you can reference the same vector later again
    for obj in &vector {
        // here obj is &RefCell<Option<Object>>

        // obj.borrow_mut() checks if it has already given out a mutable reference to Option<Object>
        // inside it. If it has it panics, if it hasn't it will return a RefMut<T> struct that
        // represents the borrow (which ends once the struct gets dropped)

        // RefMut<T>.deref_mut() dereferences RefMut<T> to &mut Option<Object>

        if let Some(o) = obj.borrow_mut().deref_mut() {
            // here 'o' is &mut Object, and because you haven't borrowed the vector mutably anywhere,
            // you can do an immutable borrow here
            o.update(&vector);
        }
    }
}

相关问题