英文:
Give element in vector access to other elements
问题
以下是代码的翻译部分:
为了更好理解,我正在创建一个简单的游戏引擎。
我的游戏对象都存储在一个向量中,每一帧都会迭代每个对象并进行更新。
问题在于,偶尔一个对象需要访问向量中其他对象的数据,但我不能传递一个不可变/可变引用到向量本身,因为它正在进行可变迭代。
如何允许每个游戏对象访问同一向量中其他游戏对象的字段(位置、旋转等)?
以下是导致问题的循环:
```rust
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_entitys,因为它被借用为可变迭代。
这里有一个简单的示例来展示我的问题。
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>>) {
// 在这里改变self
// 但也要从对象向量中读取
}
}
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 => {}
}
}
}
非常感谢您的任何帮助或我可以使用的替代方法。
英文:
For context, I am creating a simple game engine.
My game objects are all stored in a vector and every frame each object is iterated over and updated.
The problem with this is that occasionally an object needs to have access to the data of other objects in the vector, I can't pass in an immutable/mutable reference to the vector itself because it is being iterated over mutably.
How can i allow each game object to access fields (position, rotation e.t.c.) of other game objects in the same Vector?
here is the loop that is causing issues:
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 => {}
}
}
the compiler doesn't like me giving an immutable reference to self.scene.collidable_entitys because it is borrowed mutably to iterate over.
here is a simple example to show my problem.
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 => {}
}
}
}
any help or alternatives to systems i could use is much appreciated.
答案1
得分: 1
The entity
is mutably borrowed, and then you are trying to borrow the entire collection of entities. That isn't allowed because the entity is now borrowed twice.
Usually–-especially in game engines––this problem is solved by employing the Entity-Component-System (ECS) pattern. It gets around the problem by working with handles to the entities instead of references. ECS is usually very fast and feels like the perfect fit for Rust because it completely eliminates complex borrowing interactions. There is a big list of ECS crates here.
If you didn't want to use ECS, you can still take the idea of using handles or ids instead of references. For example, using the vector index as a temporary id and borrowing in very short scopes, to pull out individual fields that you need.
// 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
}
英文:
The entity
is mutably borrowed, and then you are trying to borrow the entire collection of entities. That isn't allowed because the entity is now borrowed twice.
Usually–-especially in game engines––this problem is solved by employing the Entity-Component-System (ECS) pattern. It gets around the problem by working with handles to the entities instead of references. ECS is usually very fast and feels like the perfect fit for Rust because it completely eliminates complex borrowing interactions. There is a big list of ECS crates here.
If you didn't want to use ECS, you can still take the idea of using handles or ids instead of references. For example, using the vector index as a temporary id and borrowing in very short scopes, to pull out individual fields that you need.
// 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
}
答案2
得分: 1
以下是代码的翻译部分:
第一个解决方案是将向量切分成片段:
use std::vec::Vec;
struct Object {
a: u32,
b: u32,
}
impl Object {
fn new() -> Self {
Self {
a: 0,
b: 0,
}
}
// 这里你可能需要改变对象的类型
fn update<'a>(&'a mut self, objects: impl Iterator<Item = &'a Option<Object>>) {
// 在这里改变 self
// 但也要从对象向量中读取
}
}
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' 是 'o' 的索引
for i in 0..vector.len() {
// 在这里你将向量分成两个可变切片
// (它们必须是可变的,因为你需要从中获取 'o')
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 }
}
// 在这里你不能仅从每个 RefCell 借用,因为其中一个应该已经给出了 &mut self 引用
fn update(&mut self, objects: &Vec<RefCell<Option<Object>>>) {
// 不要这样做
for obj in objects {
// 如果 self 引用向量中的一个元素,这将会导致 PANIC,因为你尝试获取另一个对 self 的引用
if let Some(o) = obj.borrow().deref() {}
}
// 在这里可以使用调试断言来确保你不能只获取其中一个项目(即 self)
debug_assert_eq!(
objects
.iter()
.filter(|refcell| refcell.try_borrow().is_err())
.count(),
1
);
for obj in objects {
// 如果 self 引用向量中的一个元素,这将会导致 PANIC
if let Ok(reference) = obj.try_borrow() {
if let Some(o) = reference.deref() {
// 使用 'o',它是不同于 self 的对象
}
}
// 或者,使用 let-Some-else 代替 if-let-Some
let Ok(reference) = obj.try_borrow() else { continue };
let Some(o) = reference.deref() else { continue };
// 使用 'o'...
}
}
}
fn main() {
println!("Hello, world!");
// 现在向量被填充了一个包装了你的 Option<Object> 并实现了内部可变性的结构,
// 这意味着你将一些检查推迟到运行时,而不是编译时
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())));
// 在这里,你正在循环访问一个不可变引用,因此稍后你可以再次引用相同的向量
for obj in &vector {
// 这里的 obj 是 &RefCell<Option<Object>>
// obj.borrow_mut() 检查是否已经给出了对其中的 Option<Object> 的可变引用。
// 如果已经给出,它会导致 PANIC,如果没有,它将返回一个表示借用的 RefMut<T> 结构
//(该借用在结构被丢弃时结束)
// RefMut<T>.deref_mut() 将 RefMut<T> 解引用为 &mut Option<Object>
if let Some(o) = obj.borrow_mut().deref_mut() {
// 在这里,'o' 是 &mut Object,因为你没有在任何地方可变借用向量
// 因此,在这里你可以进行不可变借用
o.update(&vector);
}
}
}
希望这些翻译对你有所帮助。如果你有任何其他问题,请随时提出。
英文:
One solution is splitting the vector into slices:
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()));
}
}
}
Another solution is making use of interior mutability via 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);
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论