在惯用 Rust 中实现容器元素关系的正确方法
Correct way to implement container-element relationship in idiomatic Rust
我知道为什么 Rust 不喜欢我的代码。但是,我不知道什么是惯用的 Rust 方法来解决这个问题。
我是一名 C# 程序员,虽然我觉得我理解 Rust 的系统,但我认为我的 "old" 解决某些问题的方法在 Rust 中根本不起作用。
这段代码重现了我遇到的问题,它可能看起来不像惯用的 Rust(或者它甚至在 C# 中看起来也不太好):
//a "global" container for the elements and some extra data
struct Container {
elements: Vec<Element>,
global_contextual_data: i32,
//... more contextual data fields
}
impl Container {
//this just calculates whatever I need based on the contextual data
fn calculate_contextual_data(&self) -> i32 {
//This function will end up using the elements vector and the other fields as well,
//and will do some wacky maths with it.
//That's why I currently have the elements stored in the container
}
}
struct Element {
element_data: i32,
//other fields
}
impl Element {
//I need to take a mutable reference to update element_data,
//and a reference to the container to calculate something that needs
//this global contextual data... including the other elements, as previously stated
fn update_element_data(&mut self, some_data: i32, container: &Container) {
self.element_data *= some_data + container.calculate_contextual_data() //do whatever maths I need
}
}
fn main(){
//let it be mutable so I can assign the elements later
let mut container = Container {
elements: vec![],
global_contextual_data: 1
};
//build a vector of elements
let elements = vec![
Element {
element_data: 5
},
Element {
element_data: 7
}
];
//this works
container.elements = elements;
//and this works, but container is now borrowed as mutable
for elem in container.elements.iter_mut() {
elem.element_data += 1; //and while this works
let some_data = 2;
//i can't borrow it as immutable here and pass to the other function
elem.update_element_data(some_data, &container);
}
}
我明白为什么 elem.update_element_data(some_data, &container);
不起作用:我在调用 iter_mut
时已经将其作为可变借用了。也许每个元素都应该引用容器?但是这样我不是有更多的机会打破借贷检查吗?
我认为不可能将我的旧方法应用到这个新系统中。也许我需要重写整个事情。有人能指出我正确的方向吗?我刚开始用 Rust 编程,虽然所有权系统对我来说有点意义,但我应该写的代码 "around" 仍然不是那么清楚。
我遇到了这个问题:
What's the Rust way to modify a structure within nested loops? 这让我深入了解了我的问题。
我重新审视了这个问题,并将问题归结为通过同时为写入和读取借用来共享向量。这是 Rust 所禁止的。我不想使用 unsafe
来规避借用检查器。不过,我想知道我应该复制多少数据?
我的 Element
,它实际上是一个游戏的实体(我正在模拟一个点击游戏)同时具有可变和不可变的属性,我将它们分开了。
struct Entity {
type: EntityType,
starting_price: f64,
...
...
status: Cell<EntityStatus>
}
每次我需要更改实体的状态时,我都需要在 status
字段上调用 get
和 set
方法。 EntityStatus
导出 Clone, Copy
.
我什至可以将字段直接放在结构上并让它们都是 Cell
s 但是使用它们会很麻烦(很多调用 get
和 set
), 所以我选择了更美观的方法。
通过允许自己复制 status
、编辑和 set
它,我可以不可变地借用数组两次(.iter()
而不是 .iter_mut()
)。
我怕copy的时候性能不好,但实际上用opt-level=3
编译后就很不错了。如果出现问题,我可能会将字段更改为 Cell
s 或想出另一种方法。
就在外面做计算:
#[derive(Debug)]
struct Container {
elements: Vec<Element>
}
impl Container {
fn compute(&self) -> i32 {
return 42;
}
fn len(&self) -> usize {
return self.elements.len();
}
fn at_mut(&mut self, index: usize) -> &mut Element {
return &mut self.elements[index];
}
}
#[derive(Debug)]
struct Element {
data: i32
}
impl Element {
fn update(&mut self, data: i32, computed_data: i32) {
self.data *= data + computed_data;
}
}
fn main() {
let mut container = Container {
elements: vec![Element {data: 1}, Element {data: 3}]
};
println!("{:?}", container);
for i in 0..container.len() {
let computed_data = container.compute();
container.at_mut(i).update(2, computed_data);
}
println!("{:?}", container);
}
另一种选择是将 update_element
添加到您的容器中:
#[derive(Debug)]
struct Container {
elements: Vec<Element>
}
impl Container {
fn compute(&self) -> i32 {
let sum = self.elements.iter().map(|e| {e.data}).reduce(|a, b| {a + b});
return sum.unwrap_or(0);
}
fn len(&self) -> usize {
return self.elements.len();
}
fn at_mut(&mut self, index: usize) -> &mut Element {
return &mut self.elements[index];
}
fn update_element(&mut self, index: usize, data: i32) {
let computed_data = self.compute();
self.at_mut(index).update(data, computed_data);
}
}
#[derive(Debug)]
struct Element {
data: i32
}
impl Element {
fn update(&mut self, data: i32, computed_data: i32) {
self.data *= data + computed_data;
}
}
fn main() {
let mut container = Container {
elements: vec![Element {data: 1}, Element {data: 3}]
};
println!("{:?}", container);
for i in 0..container.len() {
let computed_data = container.compute();
container.at_mut(i).update(2, computed_data);
}
println!("{:?}", container);
for i in 0..container.len() {
container.update_element(i, 2);
}
println!("{:?}", container);
}
我知道为什么 Rust 不喜欢我的代码。但是,我不知道什么是惯用的 Rust 方法来解决这个问题。
我是一名 C# 程序员,虽然我觉得我理解 Rust 的系统,但我认为我的 "old" 解决某些问题的方法在 Rust 中根本不起作用。
这段代码重现了我遇到的问题,它可能看起来不像惯用的 Rust(或者它甚至在 C# 中看起来也不太好):
//a "global" container for the elements and some extra data
struct Container {
elements: Vec<Element>,
global_contextual_data: i32,
//... more contextual data fields
}
impl Container {
//this just calculates whatever I need based on the contextual data
fn calculate_contextual_data(&self) -> i32 {
//This function will end up using the elements vector and the other fields as well,
//and will do some wacky maths with it.
//That's why I currently have the elements stored in the container
}
}
struct Element {
element_data: i32,
//other fields
}
impl Element {
//I need to take a mutable reference to update element_data,
//and a reference to the container to calculate something that needs
//this global contextual data... including the other elements, as previously stated
fn update_element_data(&mut self, some_data: i32, container: &Container) {
self.element_data *= some_data + container.calculate_contextual_data() //do whatever maths I need
}
}
fn main(){
//let it be mutable so I can assign the elements later
let mut container = Container {
elements: vec![],
global_contextual_data: 1
};
//build a vector of elements
let elements = vec![
Element {
element_data: 5
},
Element {
element_data: 7
}
];
//this works
container.elements = elements;
//and this works, but container is now borrowed as mutable
for elem in container.elements.iter_mut() {
elem.element_data += 1; //and while this works
let some_data = 2;
//i can't borrow it as immutable here and pass to the other function
elem.update_element_data(some_data, &container);
}
}
我明白为什么 elem.update_element_data(some_data, &container);
不起作用:我在调用 iter_mut
时已经将其作为可变借用了。也许每个元素都应该引用容器?但是这样我不是有更多的机会打破借贷检查吗?
我认为不可能将我的旧方法应用到这个新系统中。也许我需要重写整个事情。有人能指出我正确的方向吗?我刚开始用 Rust 编程,虽然所有权系统对我来说有点意义,但我应该写的代码 "around" 仍然不是那么清楚。
我遇到了这个问题: What's the Rust way to modify a structure within nested loops? 这让我深入了解了我的问题。
我重新审视了这个问题,并将问题归结为通过同时为写入和读取借用来共享向量。这是 Rust 所禁止的。我不想使用 unsafe
来规避借用检查器。不过,我想知道我应该复制多少数据?
我的 Element
,它实际上是一个游戏的实体(我正在模拟一个点击游戏)同时具有可变和不可变的属性,我将它们分开了。
struct Entity {
type: EntityType,
starting_price: f64,
...
...
status: Cell<EntityStatus>
}
每次我需要更改实体的状态时,我都需要在 status
字段上调用 get
和 set
方法。 EntityStatus
导出 Clone, Copy
.
我什至可以将字段直接放在结构上并让它们都是 Cell
s 但是使用它们会很麻烦(很多调用 get
和 set
), 所以我选择了更美观的方法。
通过允许自己复制 status
、编辑和 set
它,我可以不可变地借用数组两次(.iter()
而不是 .iter_mut()
)。
我怕copy的时候性能不好,但实际上用opt-level=3
编译后就很不错了。如果出现问题,我可能会将字段更改为 Cell
s 或想出另一种方法。
就在外面做计算:
#[derive(Debug)]
struct Container {
elements: Vec<Element>
}
impl Container {
fn compute(&self) -> i32 {
return 42;
}
fn len(&self) -> usize {
return self.elements.len();
}
fn at_mut(&mut self, index: usize) -> &mut Element {
return &mut self.elements[index];
}
}
#[derive(Debug)]
struct Element {
data: i32
}
impl Element {
fn update(&mut self, data: i32, computed_data: i32) {
self.data *= data + computed_data;
}
}
fn main() {
let mut container = Container {
elements: vec![Element {data: 1}, Element {data: 3}]
};
println!("{:?}", container);
for i in 0..container.len() {
let computed_data = container.compute();
container.at_mut(i).update(2, computed_data);
}
println!("{:?}", container);
}
另一种选择是将 update_element
添加到您的容器中:
#[derive(Debug)]
struct Container {
elements: Vec<Element>
}
impl Container {
fn compute(&self) -> i32 {
let sum = self.elements.iter().map(|e| {e.data}).reduce(|a, b| {a + b});
return sum.unwrap_or(0);
}
fn len(&self) -> usize {
return self.elements.len();
}
fn at_mut(&mut self, index: usize) -> &mut Element {
return &mut self.elements[index];
}
fn update_element(&mut self, index: usize, data: i32) {
let computed_data = self.compute();
self.at_mut(index).update(data, computed_data);
}
}
#[derive(Debug)]
struct Element {
data: i32
}
impl Element {
fn update(&mut self, data: i32, computed_data: i32) {
self.data *= data + computed_data;
}
}
fn main() {
let mut container = Container {
elements: vec![Element {data: 1}, Element {data: 3}]
};
println!("{:?}", container);
for i in 0..container.len() {
let computed_data = container.compute();
container.at_mut(i).update(2, computed_data);
}
println!("{:?}", container);
for i in 0..container.len() {
container.update_element(i, 2);
}
println!("{:?}", container);
}