rust


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.
Let's say I want to write a game, there is a Player and many Resources. Each Resource can be owned by one Player.
struct Player<'a> {
points: i32
}
struct Resource<'a> {
owner: Option<&'a Player<'a>>
}
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. 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?
EDIT:
I oversimplified my question, and while Shepmaster's answer is a correct 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.
Say 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 he owns. I thought of a signature like: fn addPoints(&mut self, allResources: &ResourcesMap) -> ().
The player can take over a resource connected to one of his 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)?
Here is the original problem. If the Resource points to a Player, I can't modify the player!
Here is the reason for Resource to point to Player - the natural way to do such 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
}
struct Resource<'a> { //'
// the lifetime is still needed to guarantee that Resources don't outlive their player
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());
}
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 Links

Result has no method called “unwrap()”?
Wrapping a Rust struct in a C++ class
How to create a String directly?
Acessing data from a global struct, gives error “borrowed value does not live long enough”
captured variable does not outlive the enclosing closure [duplicate]
PI constant is ambiguous
Calling an impl method from another impl method
compilation of openssl-sys fails with `openssl/hmac.h: No such file or directory`
Indexing a String
Importing mio::tcp::TcpStream but get std::net::tcp::TcpStream
It is possible to always have Cargo show warnings?
Copy files to the target directory after build
Is it ok to return in main?
Why is this trait/implementation incompatible - bound lifetime vs concrete lifetime
Cannot move out of `req` because it is borrowed
remove duplicates from vector of custom struct

Categories

HOME
php
powerbi
class
facebook-messenger
bokeh
kendo-dropdown
ios-simulator
yql
portable-class-library
anchor
cocotb
selection
hspi
amazon-emr
android-externalstorage
pm2
google-awareness
texas-instruments
multi-upload
animated-gif
jstree
webviewclient
onchange
adapter
dnsmasq
region
nic
credit-card
toad
commonsware-cwac
kendo-listview
payeezy
amazon-kms
cloudera-quickstart-vm
aurelia-http-client
recovery
stat
doctrine-extensions
docker-ucp
lightning-workbench
phppgadmin
sequence-diagram
network-protocols
rustdoc
gawk
ternary-operator
okio
ndk-build
p6spy
openseadragon
cdo.message
unsigned
powerpoint-2013
accessibilityservice
vimperator
psd
android-mapview
ngcordova
modern.ie
gflags
mxe
python-c-extension
fat32
adwords-apiv201402
nidaqmx
commenting
thruway
sql-function
hints
flv
sly-scroller
mongo-jackson-mapper
oscilloscope
smartfox
system-requirements
gitx
kobold2d
navigationcontroller
sigar
motodev-studio
radscheduler
data-dump
hungarian-notation
chunking
explicit
ccnet-config
raw-data
remote-working

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App