Mutable self while reading from owner object - rust

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.

Related

Idiomatic workarounds or alternatives to the mutate after immutable borrow problems in Rust

One of the main features of Rust is the borrow checker. Now I found a problem that I cannot solve because I need to mutate after immutable borrow. I am looking for workarounds or alternative solutions for what I want to do, preferably idiomatic methods. The code below is a simplified rewrite of something from C++, where it worked. I am calculating something and saving values in a vector for later analysis. The simplest way is to initialize the "Analysis"-struct with a reference of where to look at for later analysis. Once I am finished calculating new values and storing them in the vector I tell the Analysis-object to check it. I would like to know either how to achieve this or in a more general way, if it is at all a good idea. If not, why is it bad design? (the code below is just an example and does not compile as it is.)
struct Analysis {
curve_segment: &[f64],
local_maxima: u32,
local_minima: u32,
// ...
}
impl Analysis {
pub fn new(curve_segment: &[f64]) {
Analysis {
curve_segment,
local_maxima: 0,
local_minima: 0,
// ...
}
}
pub fn check_segment(&mut self) {
for sample in &self.curve_segment {
// check if maximum or minimum change local_maxima/-minima accordingly
}
}
}
fn main() {
let mut segment: Vec<f64> = vec![0.0; 1000];
let analysis = Analysis::new(&segment);
for _ in 0..number_of_segments {
for i in 0..segment.len() {
let some_value = // do some calculation;
segment[i] = some_value; // does not work because segment is already borrowed to 'analysis'.
}
analysis.check_segment();
}
}

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).

Can a type know when a mutable borrow to itself has ended?

I have a struct and I want to call one of the struct's methods every time a mutable borrow to it has ended. To do so, I would need to know when the mutable borrow to it has been dropped. How can this be done?
Disclaimer: The answer that follows describes a possible solution, but it's not a very good one, as described by this comment from Sebastien Redl:
[T]his is a bad way of trying to maintain invariants. Mostly because dropping the reference can be suppressed with mem::forget. This is fine for RefCell, where if you don't drop the ref, you will simply eventually panic because you didn't release the dynamic borrow, but it is bad if violating the "fraction is in shortest form" invariant leads to weird results or subtle performance issues down the line, and it is catastrophic if you need to maintain the "thread doesn't outlive variables in the current scope" invariant.
Nevertheless, it's possible to use a temporary struct as a "staging area" that updates the referent when it's dropped, and thus maintain the invariant correctly; however, that version basically amounts to making a proper wrapper type and a kind of weird way to use it. The best way to solve this problem is through an opaque wrapper struct that doesn't expose its internals except through methods that definitely maintain the invariant.
Without further ado, the original answer:
Not exactly... but pretty close. We can use RefCell<T> as a model for how this can be done. It's a bit of an abstract question, but I'll use a concrete example to demonstrate. (This won't be a complete example, but something to show the general principles.)
Let's say you want to make a Fraction struct that is always in simplest form (fully reduced, e.g. 3/5 instead of 6/10). You write a struct RawFraction that will contain the bare data. RawFraction instances are not always in simplest form, but they have a method fn reduce(&mut self) that reduces them.
Now you need a smart pointer type that you will always use to mutate the RawFraction, which calls .reduce() on the pointed-to struct when it's dropped. Let's call it RefMut, because that's the naming scheme RefCell uses. You implement Deref<Target = RawFraction>, DerefMut, and Drop on it, something like this:
pub struct RefMut<'a>(&'a mut RawFraction);
impl<'a> Deref for RefMut<'a> {
type Target = RawFraction;
fn deref(&self) -> &RawFraction {
self.0
}
}
impl<'a> DerefMut for RefMut<'a> {
fn deref_mut(&mut self) -> &mut RawFraction {
self.0
}
}
impl<'a> Drop for RefMut<'a> {
fn drop(&mut self) {
self.0.reduce();
}
}
Now, whenever you have a RefMut to a RawFraction and drop it, you know the RawFraction will be in simplest form afterwards. All you need to do at this point is ensure that RefMut is the only way to get &mut access to the RawFraction part of a Fraction.
pub struct Fraction(RawFraction);
impl Fraction {
pub fn new(numerator: i32, denominator: i32) -> Self {
// create a RawFraction, reduce it and wrap it up
}
pub fn borrow_mut(&mut self) -> RefMut {
RefMut(&mut self.0)
}
}
Pay attention to the pub markings (and lack thereof): I'm using those to ensure the soundness of the exposed interface. All three types should be placed in a module by themselves. It would be incorrect to mark the RawFraction field pub inside Fraction, since then it would be possible (for code outside the module) to create an unreduced Fraction without using new or get a &mut RawFraction without going through RefMut.
Supposing all this code is placed in a module named frac, you can use it something like this (assuming Fraction implements Display):
let f = frac::Fraction::new(3, 10);
println!("{}", f); // prints 3/10
f.borrow_mut().numerator += 3;
println!("{}", f); // prints 3/5
The types encode the invariant: Wherever you have Fraction, you can know that it's fully reduced. When you have a RawFraction, &RawFraction, etc., you can't be sure. If you want, you may also make RawFraction's fields non-pub, so that you can't get an unreduced fraction at all except by calling borrow_mut on a Fraction.
Basically the same thing is done in RefCell. There you want to reduce the runtime borrow-count when a borrow ends. Here you want to perform an arbitrary action.
So let's re-use the concept of writing a function that returns a wrapped reference:
struct Data {
content: i32,
}
impl Data {
fn borrow_mut(&mut self) -> DataRef {
println!("borrowing");
DataRef { data: self }
}
fn check_after_borrow(&self) {
if self.content > 50 {
println!("Hey, content should be <= {:?}!", 50);
}
}
}
struct DataRef<'a> {
data: &'a mut Data
}
impl<'a> Drop for DataRef<'a> {
fn drop(&mut self) {
println!("borrow ends");
self.data.check_after_borrow()
}
}
fn main() {
let mut d = Data { content: 42 };
println!("content is {}", d.content);
{
let b = d.borrow_mut();
//let c = &d; // Compiler won't let you have another borrow at the same time
b.data.content = 123;
println!("content set to {}", b.data.content);
} // borrow ends here
println!("content is now {}", d.content);
}
This results in the following output:
content is 42
borrowing
content set to 123
borrow ends
Hey, content should be <= 50!
content is now 123
Be aware that you can still obtain an unchecked mutable borrow with e.g. let c = &mut d;. This will be silently dropped without calling check_after_borrow.

How to represent shared mutable state?

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.

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.

Resources