我正在努力学习Rust,但我唯一要做的就是不停地试图将熟悉(对我而言)Java概念塞进其类型系统中。或者尝试使用Haskell概念等等。
我想用Player
和许多Resource
来编写游戏。每个Resource
都可以由一个Player
拥有:
struct Player {
points: i32,
}
struct Resource<'a> {
owner: Option<&'a Player>,
}
fn main() {
let mut player = Player { points: 0 };
let mut resources = Vec::new();
resources.push(Resource {
owner: Some(&player),
});
player.points = 30;
}
它无法编译,因为我无法将资源指向播放器,同时修改它:
error[E0506]: cannot assign to `player.points` because it is borrowed
--> src/main.rs:15:5
|
13 | owner: Some(&player),
| ------ borrow of `player.points` occurs here
14 | });
15 | player.points = 30;
| ^^^^^^^^^^^^^^^^^^ assignment to borrowed `player.points` occurs here
此外,如果Resource
拥有对Player
的可变引用,我甚至不能拥有两个拥有相同所有者的Resource
。
解决此类案件的Rust方式是什么?
我过度简化了我的问题,虽然Shepmaster的答案是正确的答案,但这不是我想要的(因为我问的不是我真正想问的问题)。我会尝试改写它并添加更多上下文。
fn addPoints(&mut self, allResources: &ResourcesMap) -> ()
。问题:
Resource
指向Player
,我无法修改播放器! Resource
指向Player
因为 - 执行此类操作的自然方式是从玩家A的某些资源开始,将地图移动到玩家的B资源并从该资源移动让玩家B减去积分。它在Rust中似乎并不自然(至少对我而言)。
答案 0 :(得分:7)
cell documentation page有很好的例子。 Rust总是试图保护你不要做坏事(比如对同一件事有两个可变引用)。因此它并不像#34;容易&#34;使用Rust的内置引用,因为您需要进行运行时检查(在编译时检查Rust引用)。
仅存在RefCell
类型。它在运行时检查可变性规则。您将获得一些内存和计算时间开销,但最终会得到Rust在其编译时检查中所承诺的相同内存安全性。
移植到RefCell
的示例如下所示。
use std::cell::RefCell;
struct Player {
points: i32,
}
// the lifetime is still needed to guarantee that Resources
// don't outlive their player
struct Resource<'a> {
owner: &'a RefCell<Player>,
}
impl<'a> Resource<'a> {
fn test(&self) -> i32 {
self.owner.borrow().points
}
}
fn main() {
let player = RefCell::new(Player { points: 0 });
let mut resources = Vec::new();
resources.push(Resource { owner: &player });
player.borrow_mut().points = 30;
println!("{:?}", resources[0].test());
}
我担心的是,如果我尝试做的是尝试在Rust中编写Java代码,是否可以在不牺牲编译时安全的情况下以Rust方式完成?完全避免共享的可变状态?
您不会牺牲编译时安全性。 Rust确保(在编译时)您正确使用库。但是,如果使用borrow*
函数,您的程序可能会在运行时恐慌。如果您使用try_borrow*
函数,则可以检查它是否成功,如果没有,则执行一些回退操作。
您还可以将RefCell
的引用计数框用于您的类型(Rc<RefCell<Player>>
)。然后你只需要确保不创建循环,或者永远不会释放你的记忆。这将更像Java(尽管Java会自动查找周期)。
答案 1 :(得分:4)
每个资源都可以由一个玩家拥有。
然后让类型执行:
struct Player {
points: i32,
resources: Vec<Resource>,
}
struct Resource {
gold: i32,
}
fn main() {
let player1 = Player {
points: 30,
resources: vec![Resource { gold: 54 }],
};
let player2 = Player {
points: 50,
resources: vec![Resource { gold: 99 }],
};
// If you really need an array of all the resources...
// Although this seems like you should just ask the Player to do something
let mut resources: Vec<_> = vec![];
resources.extend(player1.resources.iter());
resources.extend(player2.resources.iter());
}
编辑感谢@ziggystar指出我的原始版本允许玩家只有一个Resource
。现在玩家可能拥有N个资源,但他们仍然是资源的唯一所有者。