How to imagine a general volatile state?

I'm trying to learn Rust, but the only thing I do is continue to hit the wall, trying to explain to me the familiar (to me) Java concepts in my type system. Or try using Haskell concepts, etc.

I want to write a game with Player and many Resource s. Each Resource can belong to one 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; } 

It does not compile, because I cannot specify a resource for the player, at the same time changing it:

 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 

Also, if the Resource belonged to a mutable Player link, I couldn't even have two Resource with the same owner.

What is the way rust resolves such cases?


I simplified my question, and although Shepmaster's answer is the correct answer, it is not what I wanted to get (because what I asked was not what I really wanted to ask). I will try to rephrase it and add more context.

  • Resources are connected in some way - a map of all Resources form a directed graph (un).
  • Each player can own many resources, each resource can belong to one player. The player must be able to get points from the resources that they own. I was thinking of a signature, for example: fn addPoints(&mut self, allResources: &ResourcesMap) -> () .
  • A player can take a resource associated with one of his resources from another player. This may result in the loss of some points for another player.

Problems:

  • How to present such a graph in Rust (possibly a cyclic structure, where each node can point to many nodes)?
  • Original problem: if Resource points to Player , I cannot change the player!

Resource to Player , because a natural way to do this would be to start from some resources of Player A, move through the map to the resource of player B and from that resource to player B to subtract points. It just doesn't seem natural in Rust (at least for me).

+5
source share
2 answers

The cellular network documentation page has pretty good examples. Rust will always try to protect you from bad things (for example, with two mutable links to the same thing). Therefore, it is not quite “easy” as using the built-in Rust links, since you need to perform runtime checks (Rust links are checked at compile time).

The RefCell type exists only for this. It checks the rules of variability at runtime. You will get some overhead memory and computational load, but in the end you will have the same memory security as Rust promises in its compile-time checks.

Your example ported to RefCell is as follows.

 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()); } 

My concern is that what I'm trying to do is try to write Java code in Rust, can this be done in a Rust-way without compromising compile-time security? Avoid this general altered state at all?

You do not sacrifice security during compilation. Rust ensures that you use libraries correctly (at compile time). However, your program may panic at runtime if you use borrow* functions. If you use try_borrow* functions try_borrow* , you can check to see if it succeeded, and if not, do some backup operation.

You can also use the RefCell reference counter for your type ( Rc<RefCell<Player>> ). Then you only need to make sure that you are not creating loops, or your memory will never be freed. This would be much more like Java (although Java automatically finds loops).

+6
source

Each resource can belong to one player.

Make the following types:

 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()); } 

Edit Thanks to @ziggystar, players are only allowed to have a Resource to indicate my source version. Players can now own N resources, but they are still the sole owner of the resource.

+4
source

Source: https://habr.com/ru/post/1212996/


All Articles