Reputation: 412
The following code creates a list of players then adds references to some of the other players as friends. I can't get this past the borrow-checker. Could anyone explain where I'm going wrong?
fn main() {
let mut players =
vec![Player::new("alice"), Player::new("bob"), Player::new("eve"), Player::new("dave")];
let mut alice = players.get_mut(0).unwrap();
alice.friends.push(&players[1]);
alice.friends.push(&players[2]);
// The list is borrowed by the existence of Alice, how do I
// put Bob and Eve into Alice's group?
let game = Game::new(GameType::Checkers,
"Game #23",
vec![&players[0], &players[1]][..]);
// the trait bound `[&'a Player<'_>]: std::marker::Sized` is not satisfied
}
For a bonus, how do I equally get players into the game?
The rest of the code is as follows:
struct Player<'a> {
name: String,
friends: Vec<&'a Player<'a>>,
}
impl<'a> Player<'a> {
fn new(name: &str) -> Player {
Player {
name: name.into(),
friends: Vec::new(),
}
}
}
enum GameType {
Chess,
Checkers,
SnakesAndLadders,
}
struct Game<'a> {
game: GameType,
name: String,
players: Vec<&'a Player<'a>>,
}
impl<'a> Game<'a> {
fn new(game: GameType, name: &str, players: [&'a Player]) -> Game<'a> {
Game {
game: game,
name: name.into(),
players: players.to_vec(),
}
}
}
Upvotes: 3
Views: 920
Reputation: 432089
I'll assume that you've already searched for the error message and read through some of the 10+ questions and answers about it already, so I'll return the favor and won't waste your time with that. Instead, I'll move on to the problem regarding borrow checking:
error[E0502]: cannot borrow `players` as immutable because it is also borrowed as mutable
--> src/main.rs:40:25
|
39 | let mut alice = players.get_mut(0).unwrap();
| ------- mutable borrow occurs here
40 | alice.friends.push(&players[1]);
| ^^^^^^^ immutable borrow occurs here
...
48 | }
| - mutable borrow ends here
Rust only allows you to have a single mutable reference or one-or-more immutable references to the same value. This code already creates a mutable reference to the vector and is trying to get a second immutable reference.
This must be disallowed because as far as the compiler knows, changing the mutable reference might invalidate the immutable reference. That would lead to memory unsafety, which Rust disallows.
In this case, once you've created your players
vector, you won't be adding any more values to it. The vector itself is immutable, but the components within it want to be mutable. This is a good fit for a RefCell
:
struct Player<'a> {
name: String,
friends: RefCell<Vec<&'a Player<'a>>>,
}
Now a Player
can have their friends modified without the containing element knowing about the mutability.
Then it's just a matter of removing the mutable borrow of the vector and borrowing friends
as mutable instead:
let alice = &players[0];
alice.friends.borrow_mut().push(&players[1]);
alice.friends.borrow_mut().push(&players[2]);
You also have to add a missing lifetime to Game::new
, and it compiles:
use std::cell::RefCell;
struct Player<'a> {
name: String,
friends: RefCell<Vec<&'a Player<'a>>>,
}
impl<'a> Player<'a> {
fn new(name: &str) -> Player {
Player {
name: name.into(),
friends: RefCell::new(Vec::new()),
}
}
}
struct Game<'a> {
players: Vec<&'a Player<'a>>,
}
impl<'a> Game<'a> {
fn new(players: &[&'a Player<'a>]) -> Game<'a> {
Game { players: players.to_vec() }
}
}
fn main() {
let players = vec![
Player::new("alice"),
Player::new("bob"),
Player::new("eve"),
Player::new("dave"),
];
let alice = &players[0];
alice.friends.borrow_mut().push(&players[1]);
alice.friends.borrow_mut().push(&players[2]);
Game::new(&[&players[0], &players[1]]);
}
Your question is very close to Why can't I store a value and a reference to that value in the same struct?, but is subtly different. You are encouraged to read that for highly-related information.
Upvotes: 4