我正在努力学习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的答案是正确的,但这不是我想得到的答案(因为我问的不是我真正想问的)。
1.资源以某种方式连接-所有资源的Map形成一个(无)有向图。
1.每个玩家可以拥有多个资源,每个资源可以归一个玩家所有,玩家应该可以从自己拥有的资源中获得积分,我想到了一个签名:fn addPoints(&mut self, allResources: &ResourcesMap) -> ()
。
1.玩家可以从另一个玩家手中接管与他们的资源相连的资源。这可能会导致另一个玩家损失一些积分。
问题:
1.如何在Rust中表示这样的图(可能是循环结构,其中每个节点可以从许多节点指向)?
1.原来的问题:如果Resource
指向一个Player
,我不能修改播放器!Resource
s指向Player
,因为--做这样一个操作的自然方法是从玩家A的一些资源开始,通过Map移动到玩家B的资源,然后从该资源移动到玩家B以减去点数。
3条答案
按热度按时间pexxcrt21#
cell documentation page有相当好的例子。Rust总是试图保护你不做坏事(比如对同一个东西有两个可变引用)。因此,它不像使用Rust的内置引用那么“容易”,因为你需要做运行时检查(Rust引用在编译时检查)。
RefCell
类型就是为此而存在的。它在运行时检查可变性规则。你会得到一些内存和计算时间开销,但你最终会得到Rust在编译时检查中承诺的内存安全。移植到
RefCell
的示例如下所示。字符串
我关心的是,如果我尝试用Rust编写Java代码,它能以Rust的方式完成而不牺牲编译时安全性吗?完全避免共享可变状态?
你并没有牺牲编译时的安全性。Rust确保(在编译时)你正确地使用了你的库。但是,如果你使用
borrow*
函数,你的程序可能会在运行时“panic”。如果你使用try_borrow*
函数,你可以检查它是否成功,如果没有,做一些回退操作。你也可以使用一个
RefCell
类型的引用计数框(Rc<RefCell<Player>>
)。然后你只需要确保你没有创建循环,否则你的内存将永远不会被释放。这将更像Java(尽管Java会自动找到循环)。ha5z0ras2#
每个资源可以由一个玩家拥有。
然后让类型执行此操作:
字符串
编辑感谢@ziggystar指出我的原始版本只允许玩家拥有一个
Resource
。现在玩家可以拥有N个资源,但他们仍然是一个资源的唯一所有者。iyr7buue3#
这是一个常见的问题,
Rc<RefCell<T>>
是一个常见的答案。它在小示例中工作正常,或者当你只需要一点点共享时。但是当你的程序状态是一个有循环的图时,Rc
往往会导致内存泄漏,RefCell
往往会在运行时死机。你失去了一些你期望从Rust得到的编译时正确性,而且它也是令人痛苦的冗长。这在游戏中尤其是个问题。对于游戏和一般具有图/关系状态的程序来说,一个更好的方法是使用索引而不是引用。你的例子可能看起来像这样(我将避免假设每个资源都被一个玩家唯一引用,以使解决方案更通用):
字符串
编写游戏是一个很深的主题,我们可以用很多方法来扩展这个例子。我们可能需要为
Player
和Resource
使用不同的索引类型,而不是为所有内容使用usize
,这样我们就不会把它们弄混了。如果我们需要支持删除,我们可能想用HashMap
来代替Vec
。但这只是一个开始。参见Using Rust For Game Development和Object Soup is Made of Indexes。