How to represent shared mutable state? - rust

I'm trying to learn Rust, but the only thing I do is keep hitting the wall trying to shoehorn familiar (to me) Java concepts into its type system. Or try to shoehorn Haskell concepts, etc.
I want to write a game with a Player and many Resources. Each Resource can be owned by 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 doesn't compile, because I can't have the resource point to player, while at the same time modifying 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
Moreover, if the Resource owned a mutable reference to Player, I couldn't even have two Resources with the same owner.
What is the Rust way to solve such cases?
I oversimplified my question, and while Shepmaster's answer is a correct answer to it, it's not what I wanted to get (because what I asked was not what I really wanted to ask). I'll try to rephrase it and add more context.
The resources are connected in some way - the map of all
resources forms a (un)directed graph.
Each player can own many resources, each resource can be owned by one player. The player should be able to get points from resources they own. I thought of a signature like: fn addPoints(&mut self, allResources: &ResourcesMap) -> ().
The player can take over a resource connected to one of their resources from another player. It could result in some points loss for the other player.
Problems:
How to represent such graph in Rust (a possibly cyclic structure, where each node can be pointed to from many nodes)?
The original problem: if the Resource points to a Player, I can't modify the player!
Resources point to Player because - the natural way to do such an operation would be to start from some of Player A's resources, move through the map to a player's B resource and from that resource to player B to subtract the points. It just doesn't seem natural in Rust (at least for me).

The cell documentation page has rather good examples. Rust will always try to protect you from doing bad things (like having two mutable references to the same thing). Therefor it's not quite as "easy" as using Rust's built-in references, since you need to do runtime-checking (Rust references are checked at compile-time).
The RefCell type exists just for that. It checks the mutability rules at runtime. You will get some memory and computation-time overhead, but you end up with the same memory-safety that Rust promises in it's compile-time checks.
Your example ported to RefCell looks like the following.
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, if what I'm trying to do is trying to write Java code in Rust, can it be done in a Rust-way without sacrificing compile time safety? Avoid that shared mutable state at all?
You are not sacrificing compile-time-safety. Rust makes sure (at compile-time) that you are using your libraries correctly. Still, your program might panic at runtime if you use the borrow* functions. If you use the try_borrow* functions instead, you can check if it succeeded and if not, do some fallback operation.
You can also use a reference counted box of a RefCell to your type (Rc<RefCell<Player>>). Then you only need to make sure that you do not create cycles, or your memory will never be freed. This would be much more Java like (although Java automatically finds cycles).

Each Resource can be owned by one Player.
Make the types do that then:
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 for pointing out my original version allowed players to only have one Resource. Now players may own N resources, but they still are the only owner of a resource.

Related

Rust mutable container of immutable elements?

With Rust, is it in general possible to have a mutable container of immutable values?
Example:
struct TestStruct { value: i32 }
fn test_fn()
{
let immutable_instance = TestStruct{value: 123};
let immutable_box = Box::new(immutable_instance);
let mut mutable_vector = vec!(immutable_box);
mutable_vector[0].value = 456;
}
Here, my TestStruct instance is wrapped in two containers: a Box, then a Vec. From the perspective of a new Rust user it's surprising that moving the Box into the Vec makes both the Box and the TestStruct instance mutable.
Is there a similar construct whereby the boxed value is immutable, but the container of boxes is mutable? More generally, is it possible to have multiple "layers" of containers without the whole tree being either mutable or immutable?
Is there a similar construct whereby the boxed value is immutable, but the container of boxes is mutable? More generally, is it possible to have multiple "layers" of containers without the whole tree being either mutable or immutable?
Not really. You could easily create one (just create a wrapper object which implements Deref but not DerefMut), but the reality is that Rust doesn't really see (im)mutability that way, because its main concern is controlling sharing / visibility.
After all, for an external observer what difference is there between
mutable_vector[0].value = 456;
and
mutable_vector[0] = Box::new(TestStruct{value: 456});
?
None is the answer, because Rust's ownership system means it's not possible for an observer to have kept a handle on the original TestStruct, thus they can't know whether that structure was replaced or modified in place[1][2].
If you want to secure your internal state, use visibility instead: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8a9346072b32cedcf2fccc0eeb9f55c5
mod foo {
pub struct TestStruct { value: i32 }
impl TestStruct {
pub fn new(value: i32) -> Self { Self { value } }
}
}
fn test_fn() {
let immutable_instance = foo::TestStruct{value: 123};
let immutable_box = Box::new(immutable_instance);
let mut mutable_vector = vec!(immutable_box);
mutable_vector[0].value = 456;
}
does not compile because from the point of view of test_fn, TestStruct::value is not accessible. Therefore test_fn has no way to mutate a TestStruct unless you add an &mut method on it.
[1]: technically they could check the address in memory and that might tell them, but even then it's not a sure thing (in either direction) hence pinning being a thing.
[2]: this observability distinction is also embraced by other languages, for instance the Clojure language largely falls on the "immutable all the things" side, however it has a concept of transients which allow locally mutable objects

Rust borrow checker in a battl simulation engine

Okay, I have Combatants which battle on a Battlefield. My intuition for which things go on which place is a little off. It's pretty close to being a game, but I am now stuck.
I want the Battlefield to have a tick() function, which allows all Combatants to take a decision, like attacking another of the opposite team or moving closing to one if no one is in range. I'm having issues making the borrow checker happy in doing this.
Here's a minimal version which has all the problems of my code.
struct Combatant{
current_health: i16,
max_health: i16
}
struct Battlefield{
combatants: Vec<Combatant>
}
impl Combatant {
fn attack(&self, other: &mut Combatant) {
other.current_health -= 3;
}
}
impl Battlefield {
fn tick(&mut self) {
let target = &mut self.combatants[0];
for combatant in &self.combatants {
combatant.attack(target);
}
}
}
cargo check returns
error[E0502]: cannot borrow `self.combatants` as immutable because it is also borrowed as mutable
--> src/main.rs:20:26
|
19 | let target = &mut self.combatants[0];
| --------------- mutable borrow occurs here
20 | for combatant in &self.combatants {
| ^^^^^^^^^^^^^^^^ immutable borrow occurs here
21 | combatant.attack(target);
| ------ mutable borrow later used here
How can I design this function (or more like, this whole scenario, heh) to make it work in Rust?
Since in your scenario you need to simultaneously have a mutable reference and an immutable reference on two elements in the same container, I think you need the help of interior mutability.
This will check at run-time that the same element is not accessed simultaneously through a mutable (.borrow_mut()) and immutable (.borrow()) reference (otherwise it panics).
Obviously, you have to ensure that by yourself (which is quite ugly since we have to compare pointers!).
Apparently, it is necessary to reach pointers because references cannot be compared (the self argument of std::cmp::PartialEq::eq() will be dereferenced). The documentation of std::ptr::eq() (which should probably be used here) shows the difference between comparing references and comparing pointers.
struct Combatant {
current_health: i16,
max_health: i16,
}
struct Battlefield {
combatants: Vec<std::cell::RefCell<Combatant>>,
}
impl Combatant {
fn attack(
&self,
other: &mut Combatant,
) {
other.current_health -= 3;
}
}
impl Battlefield {
fn tick(&mut self) {
let target_cell = &self.combatants[0];
let target = &*target_cell.borrow();
for combatant_cell in &self.combatants {
let combatant = &*combatant_cell.borrow();
// if combatant as *const _ != target as *const _ {
if !std::ptr::eq(combatant, target) {
let target_mut = &mut *target_cell.borrow_mut();
combatant.attack(target_mut);
}
}
}
}
Note that this interior mutability was bothering me at first, and seemed like « bending the rules » because I was reasoning essentially in terms of « immutable becoming suddenly mutable » (like const-casting in C++), and the shared/exclusive aspect was only the consequence of that.
But the answer to the linked question explains that we should think at first in terms of shared/exclusive access to our data, and then the immutable/mutable aspect is just the consequence.
Back to your example, the shared access to the Combatants in the vector seems essential because, at any time, any of them could access any other.
Because the consequence of this choice (shared aspect) is that mutable accesses become almost impossible, we need the help of interior mutability.
This is not « bending the rules » because strict checking is done on .borrow()/.borrow_mut() (small overhead at this moment) and the obtained Ref/RefMut have lifetimes allowing usual (static) borrow-checking in the portion of code where they appear.
It is much safer than free immutable/mutable accesses we could perform with other programming languages.
For example, even in C++ where we could consider target as const (reference/pointer to const) while iterating on the non-const combatants vector, one iteration can accidentally mutate the target that we consider as const (reference/pointer to const means « I won't mutate it », not « it cannot be mutated by anyone »), which could be misleading. And with other languages where const/mut do not even exist, anything can be mutated at any time (except for objects which are strictly immutable, like str in Python, but it becomes difficult to manage objects with states that could change over time, like current_health in your example).
The problem is this: When you iterate over the combatants, that requires an immutable borrow of all the combatants in that Vec. However, one of them, combatants[0] is already borrowed, and it's a mutable borrow.
You cannot, at the same time, have a mutable and an immutable borrow to the same thing.
This prevents a lot of logic errors. For example, in your code, if the borrow was actually allowed, you'd actually have combatants[0] attack itself!
So what to do? In the specific example above, one thing you could do is use the split_first_mut method of vec, https://doc.rust-lang.org/std/vec/struct.Vec.html#method.split_first_mut
let (first, rest) = self.combatants.split_first_mut();
if let Some(first) = first {
if let Some(rest) = rest {
for combatant in rest {
combatant.attack(first);
}
}
}
You can also use split_at_mut to only iterate on the other elements:
fn tick(&mut self) {
let idx = 0;
let (before, after) = self.combatants.split_at_mut (idx);
let (target, after) = after.split_at_mut (1);
let target = &mut target[0];
for combatant in before {
combatant.attack(target);
}
for combatant in after {
combatant.attack(target);
}
}
Playground
Note that this will panic if idx >= len (self.combatants).

Mutable self while reading from owner object

I have one object that owns another. The owned object has a mutating method that depends on non-mutating methods of its owner. The architecture (simplified as much as possible) looks like this:
struct World {
animals: Vec<Animal>,
}
impl World {
fn feed_all(&mut self) {
for i in 0..self.animals.len() {
self.animals[i].feed(self);
}
}
}
struct Animal {
food: f32,
}
impl Animal {
fn inc_food(&mut self) {
self.food += 1.0;
}
fn feed(&mut self, world: &World) {
// Imagine this is a much more complex calculation, involving many
// queries to world.animals, several loops, and a bunch of if
// statements. In other words, something so complex it can't just
// be moved outside feed() and pass its result in as a pre-computed value.
for other_animal in world.animals.iter() {
self.food += 10.0 / (other_animal.food + self.food);
}
}
}
fn main() {
let mut world = World {
animals: Vec::with_capacity(1),
};
world.animals.push(Animal { food: 0.0 });
world.feed_all();
}
The above does not compile. The compiler says:
error[E0502]: cannot borrow `*self` as immutable because `self.animals` is also borrowed as mutable
--> src/main.rs:8:34
|
8 | self.animals[i].feed(self);
| ------------ ^^^^- mutable borrow ends here
| | |
| | immutable borrow occurs here
| mutable borrow occurs here
I understand why that error occurs, but what is the idiomatic Rust way to do this?
Just to be clear, the example code is not real. It's meant to present the core problem as simply as possible. The real application I'm writing is much more complex and has nothing to do with animals and feeding.
Assume it is not practical to pre-compute the food value before the call to feed(). In the real app, the method that's analogous to feed() makes many calls to the World object and does a lot of complex logic with the results.
You'd want to compute the argument first in a form that doesn't alias self, then pass that in. As it stands, it seems a little strange that an animal decides how much food it's going to eat by looking at every other animal... regardless, you could add a method Animal::decide_feed_amount(&self, world: &World) -> f32. You can call that safely (&self and &World are both immutable, so that's OK), store the result in a variable, then pass that to Animal::feed.
Edit to address your Edit: well, you're kinda screwed, then. Rust's borrow checker is not sophisticated enough to prove that the mutations you make to the Animal cannot possibly interfere with any possible immutable access to the containing World. Some things you can try:
Do a functional-style update. Make a copy of the Animal you want to update so that it has its own lifetime, update it, then overwrite the original. If you duplicate the whole array up front, this gives you what is effectively an atomic update of the whole array.
As someone who worked on a simulator for like half a decade, I wish I'd done something like that instead of mutating updates. sigh
Change to Vec<Option<Animal>> which will allow you to move (not copy) an Animal out of the array, mutate it, then put it back (see std::mem::replace). Downside is that now everything has to check to see if there's an animal in each position of the array.
Put the Animals inside Cells or RefCells, which will allow you to mutate them from immutable references. It does this by performing dynamic borrow checking which is infinitely slower (no checks vs. some checks), but is still "safe".
Absolute last resort: unsafe. But really, if you do that, you're throwing all your memory safety guarantees out the window, so I wouldn't recommend it.
In summary: Rust is doing the right thing by refusing to compile what I wrote. There's no way to know at compile time that I won't invalidate the data I'm using. If I get a mutable pointer to one animal, the compiler can't know that my read-only access to the vector isn't invalidated by my mutations to that particular animal.
Because this can't be determined at compile time, we need some kind of runtime check, or we need to use unsafe operations to bypass the safety checks altogether.
RefCell is the way to go if we want safety at the cost of runtime checks. UnsafeCell is at least one option to solve this without the overhead, at the cost of safety of course.
I've concluded that RefCell is preferable in most cases. The overhead should be minimal. That's especially true if we're doing anything even moderately complex with the values once we obtain them: The cost of the useful operations will dwarf the cost of RefCell's checks. While UnsafeCell might be a little faster, it invites us to make mistakes.
Below is an example program solving this class of problem with RefCell. Instead of animals and feeding, I chose players, walls, and collision detection. Different scenery, same idea. This solution is generalizable to a lot of very common problems in game programming. For example:
A map composed of 2D tiles, where the render state of each tile depends on its neighbors. E.g. grass next to water needs to render a coast texture. The render state of a given tile updates when that tile or any of its neighbors changes.
An AI declares war against the player if any of the AI's allies are at war with the player.
A chunk of terrain is calculating its vertex normals, and it needs to know the vertex positions of the neighboring chunks.
Anyway, here's my example code:
use std::cell::RefCell;
struct Vector2 {x: f32, y: f32}
impl Vector2 {
fn add(&self, other: &Vector2) -> Vector2 {
Vector2 {x: self.x + other.x, y: self.y + other.y}
}
}
struct World {
players: Vec<RefCell<Player>>,
walls: Vec<Wall>
}
struct Wall;
impl Wall {
fn intersects_line_segment(&self, start: &Vector2, stop: &Vector2) -> bool {
// Pretend this actually does a computation.
false
}
}
struct Player {position: Vector2, velocity: Vector2}
impl Player {
fn collides_with_anything(&self, world: &World, start: &Vector2, stop: &Vector2) -> bool {
for wall in world.walls.iter() {
if wall.intersects_line_segment(start, stop) {
return true;
}
}
for cell in world.players.iter() {
match cell.try_borrow_mut() {
Some(player) => {
if player.intersects_line_segment(start, stop) {
return true;
}
},
// We don't want to collision detect against this player. Nor can we,
// because we've already mutably borrowed this player. So its RefCell
// will return None.
None => {}
}
}
false
}
fn intersects_line_segment(&self, start: &Vector2, stop: &Vector2) -> bool {
// Pretend this actually does a computation.
false
}
fn update_position(&mut self, world: &World) {
let new_position = self.position.add(&self.velocity);
if !Player::collides_with_anything(self, world, &self.position, &new_position) {
self.position = new_position;
}
}
}
fn main() {
let world = World {
players: vec!(
RefCell::new(
Player {
position: Vector2 { x: 0.0, y: 0.0},
velocity: Vector2 { x: 1.0, y: 1.0}
}
),
RefCell::new(
Player {
position: Vector2 { x: 1.1, y: 1.0},
velocity: Vector2 { x: 0.0, y: 0.0}
}
)
),
walls: vec!(Wall, Wall)
};
for cell in world.players.iter() {
let player = &mut cell.borrow_mut();
player.update_position(&world);
}
}
The above could be altered to use UnsafeCell with very few changes. But again,I think RefCell is preferable in this case and in most others.
Thanks to #DK for putting me on the right track to this solution.

Referring to Traits of generic objects seems impossible

Consider the following simple structs:
struct Monster {
// ...
}
struct Player {
// ...
}
struct Missile {
// ...
//target: ???,
}
When writing game logic, it is very common to have objects refer to each other. In the example above, we have the structs Monster, Player and Missile to illustrate the kinds of interactions needed.
Imagine we have the following traits: Position and Think. All the above structs implements Position, and all except Missile implements Think.
The first thing to note is that Missile is a homing missile: It stores a target and whenever the game updates Missile objects, it will move it toward it's target.
As far as I can tell, it is impossible to sensibly store the target of this Missile in Rust.
Obviously, the missile doesn't own its target. It just wants to access its Position trait. The game objects must be shared, through Rc or Gc. But it's not like the Missile can just store a Weak<???> reference to something with a Position trait. A Box<Position> means consuming whatever object had that trait. A Box<Any> does not allow downcasting to traits.
Making Missile<T> and storing the target as Weak<T> doesn't help. How would those Missile<T> be stored? In one Collection<T> for each kind of object the missile targets? Game objects will need to be more generic, and the dreadful Box<Any> seems inevitable.
I'm rather new to Rust. It baffles my mind that this is straight out impossible. Surely, I must be missing something?
I'm also pretty new to Rust, but here's what I've found. You can use the std::rc::Rc<T> struct in combination with Box. To have a mutable shared references, you need to wrap boxed item in a std::cell::RefCell.
So, your player, monster, missile example would go something like this:
use std::cell::RefCell;
use std::rc::Rc;
trait Position {
fn position(&mut self);
}
struct Monster;
impl Position for Monster {
fn position(&mut self) {
println!("Rawr I am getting the monster's position");
}
}
struct Player {
x: i32,
}
impl Position for Player {
fn position(&mut self) {
println!("Getting player's position {}", self.x);
self.x += 1;
}
}
struct Missile {
target: Rc<RefCell<Box<Position>>>,
}
fn main() {
// Create some stuff
let player = Rc::new(RefCell::new(Box::new(Player{x: 42}) as Box<Position>));
let monster = Rc::new(RefCell::new(Box::new(Monster) as Box<Position>));
// Our missile: initial target - monster
let mut missile = Missile{target: monster};
// Should be a monster
missile.target.borrow_mut().position();
// Redirect missile to player
missile.target = player.clone();
// Should be a player
missile.target.borrow_mut().position();
// Show that it is in fact a reference to the original player
player.borrow_mut().position();
}
However, it is usually possible to design entity systems that don't require shared references, and this is considered to be more idiomatic Rust. If your entity system is really complex, I'd recommend using an Entity Component System.
EDIT: Improved code and information, and removed some inaccurate information.

Referencing a containing struct in Rust (and calling methods on it)

Editor's note: This code example is from a version of Rust prior to 1.0 and is not syntactically valid Rust 1.0 code. Updated versions of this code produce different errors, but the answers still contain valuable information.
I'm trying to write a container structure in Rust where its elements also store a reference to the containing container so that they can call methods on it. As far as I could figure out, I need to do this via Rc<RefCell<T>>. Is this correct?
So far, I have something like the following:
struct Container {
elems: ~[~Element]
}
impl Container {
pub fn poke(&mut self) {
println!("Got poked.");
}
}
struct Element {
datum: int,
container: Weak<RefCell<Container>>
}
impl Element {
pub fn poke_container(&mut self) {
let c1 = self.container.upgrade().unwrap(); // Option<Rc>
let mut c2 = c1.borrow().borrow_mut(); // &RefCell
c2.get().poke();
// self.container.upgrade().unwrap().borrow().borrow_mut().get().poke();
// -> Error: Borrowed value does not live long enough * 2
}
}
fn main() {
let container = Rc::new(RefCell::new(Container{ elems: ~[] }));
let mut elem1 = Element{ datum: 1, container: container.downgrade() };
let mut elem2 = Element{ datum: 2, container: container.downgrade() };
elem1.poke_container();
}
I feel like I am missing something here. Is accessing the contents of a Rc<RefCell<T>> really this difficult (in poke_container)? Or am I approaching the problem the wrong way?
Lastly, and assuming the approach is correct, how would I write an add method for Container so that it could fill in the container field in Element (assuming I changed the field to be of type Option<Rc<RefCell<T>>>? I can't create another Rc from &mut self as far as I know.
The long chain of method calls actually works for me on master without any changes, because the lifetime of "r-values" (e.g. the result of function calls) have changed so that the temporary return values last until the end of the statement, rather than the end of the next method call (which seemed to be how the old rule worked).
As Vladimir hints, overloadable dereference will likely reduce it to
self.container.upgrade().unwrap().borrow_mut().poke();
which is nicer.
In any case, "mutating" shared ownership is always going to be (slightly) harder to write in Rust that either single ownership code or immutable shared ownership code, because it's very easy for such code to be memory unsafe (and memory safety is the core goal of Rust).

Resources