Can I mutate a vector with a borrowed element? - rust

I'm attempting to store a reference to an element of a mutable vector to use later. However, once I mutate the vector, I can no longer use the stored reference. I understand that this is because borrowing reference to the element also requires borrowing a reference to the vector itself. Therefore, the vector cannot be modified, because that would require borrowing a mutable reference, which is disallowed when another reference to the vector is already borrowed.
Here's a simple example
struct Person {
name: String,
}
fn main() {
// Create a mutable vector
let mut people: Vec<Person> = ["Joe", "Shavawn", "Katie"]
.iter()
.map(|&s| Person {
name: s.to_string(),
})
.collect();
// Borrow a reference to an element
let person_ref = &people[0];
// Mutate the vector
let new_person = Person {
name: "Tim".to_string(),
};
people.push(new_person);
// Attempt to use the borrowed reference
assert!(person_ref.name == "Joe");
}
which produces the following error
error[E0502]: cannot borrow `people` as mutable because it is also borrowed as immutable
--> src/main.rs:21:5
|
15 | let person_ref = &people[0];
| ------ immutable borrow occurs here
...
21 | people.push(new_person);
| ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
24 | assert!(person_ref.name == "Joe");
| --------------- immutable borrow later used here
I've also tried boxing the vector elements as suggested here, but that doesn't help. I thought it might allow me to drop the reference to the vector while maintaining a reference to the element, but apparently not.
struct Person {
name: String,
}
fn main() {
// Create a mutable vector
let mut people: Vec<Box<Person>> = ["Joe", "Shavawn", "Katie"]
.iter()
.map(|&s| {
Box::new(Person {
name: s.to_string(),
})
})
.collect();
// Borrow a reference to an element
let person_ref = people[0].as_ref();
// Mutate the vector
let new_person = Box::new(Person {
name: "Tim".to_string(),
});
people.push(new_person);
// Attempt to use the borrowed reference
assert!(person_ref.name == "Joe");
}
This still produces the same error
error[E0502]: cannot borrow `people` as mutable because it is also borrowed as immutable
--> src/main.rs:23:5
|
17 | let person_ref = people[0].as_ref();
| ------ immutable borrow occurs here
...
23 | people.push(new_person);
| ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
26 | assert!(person_ref.name == "Joe");
| --------------- immutable borrow later used here
Is there a way to do this, or am I trying to do something impossible?

I found that using a reference counted smart pointer allows me to accomplish what I'm attempting. It makes sense that a shared ownership is necessary, because otherwise the element reference would become invalid if the original vector were to go out of scope (which would deallocate the element, with or without the Box).
The following code compiles successfully.
use std::rc::Rc;
struct Person {
name: String,
}
fn main() {
// Create a mutable vector
let mut people: Vec<Rc<Person>> = ["Joe", "Shavawn", "Katie"]
.iter()
.map(|&s| {
Rc::new(Person {
name: s.to_string(),
})
})
.collect();
// Borrow a reference to an element
let person_ref = Rc::clone(&people[0]);
// Mutate the vector
let new_person = Rc::new(Person {
name: "Tim".to_string(),
});
people.push(new_person);
// Attempt to use the borrowed reference
assert!(person_ref.name == "Joe");
}
If anyone else has any corrections, improvements or further insight, I'd be glad to hear it. But if not, I feel satisfied with this answer for now.

Related

Fix for first borrow used after second mutable borrow in rust

I have a toy problem where I am trying to push a mutable reference onto a stack. The mutable reference being pushed is obtained from a vector held in the top most element of the stack.
struct Node {
name: String,
children: Vec<Node>,
}
fn main() {
let mut root = Node {
name: String::from("/"),
children: Vec::new(),
};
let mut stack: Vec<&mut Node> = Vec::new();
stack.push(&mut root);
// first mutable borrow occurs here
let top = stack.last_mut().unwrap();
let children = &mut top.children;
for node in children.iter_mut() {
if node.name == "asdf" {
// second mutable borrow occurs here
// FIXME: first borrow later used here
stack.push(node);
break;
}
}
}
I sorta get that the second mutable borrow should only occur after all code referenced in the block by the first mutable borrow is no longer in use. Though I am not certain whether the pattern I am trying to implement is impossible to achieve in Rust, or if there is an alternative that could help here.

Push to Vector in Hashmap

Goal: Create a hashmap, add a vector to the hashmap, push to the vector
Code:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert("first", Vec::new());
let get_option = map.get("first");
match get_option {
None => println!("invalid key"),
Some(v) => v.push("Chris"),
}
}
Error:
error[E0596]: cannot borrow `*v` as mutable, as it is behind a `&` reference
--> src/main.rs:12:20
|
12 | Some(v) => v.push("Chris"),
| - ^^^^^^^^^^^^^^^ `v` is a `&` reference, so the data it refers to cannot be borrowed as mutable
| |
| help: consider changing this to be a mutable reference: `&mut Vec<&str>`
Your hashmap is mutable, but HashMap::get always returns an immutable reference. If you want a mutable reference to an element, use HashMap::get_mut.
let get_option = map.get_mut("first");
match get_option {
None => println!("invalid key"),
Some(v) => v.push("Chris"),
}
You'll see this a lot in Rust: a _mut version and a non-mutable version side-by-side. The idea of having mutability-polymorphic functions (a function whose mut status is a generic argument like a lifetime) has been floated before to the language developers, but it's usually been dismissed as too complicated and not worth the cost.

Linked list in Rust with nodes owned by HashMap

I am trying to construct a bunch of linked-list data-structure in Rust, but different than other examples I've seen, I'd like the actual nodes to be owned by a HashMap. It seems like if a HashMap owns the nodes, then lifetimes shouldn't be an issue and this should allow me to reference the next element in my list by Option<&List> rather than Box<List> (as done in Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time ). However, I am getting mutable/immutable borrow errors.
My minimal example is:
use std::collections::HashMap;
#[derive(Debug, Default)]
struct List<'a> {
data: i32,
child: Option<&'a List<'a>>,
}
fn main() {
let mut map = HashMap::<i32, List>::new();
let data = vec![(1, None), (2, Some(1))];
for (data, child) in data.iter() {
println!("Inserting {:?} with child {:?}", data, child);
let new_node = map.entry(*data).or_insert(List {
data: *data,
child: None,
});
match child {
Some(child_data) => {
map.get(child_data).map(|child_node| {
new_node.child = Some(child_node);
});
}
None => {}
}
}
println!("Data: {:?}", map);
}
Which gives the error
error[E0502]: cannot borrow `map` as mutable because it is also borrowed as immutable
--> src/main.rs:16:24
|
16 | let new_node = map.entry(*data).or_insert(List {
| ^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
22 | map.get(child_data).map(|child_node| {
| --- immutable borrow occurs here
error[E0502]: cannot borrow `map` as immutable because it is also borrowed as mutable
--> src/main.rs:22:17
|
16 | let new_node = map.entry(*data).or_insert(List {
| --- mutable borrow occurs here
...
22 | map.get(child_data).map(|child_node| {
| ^^^ immutable borrow occurs here
23 | new_node.child = Some(child_node);
| -------- mutable borrow later captured here by closure
My question is, is this error a result of me not setting up the List struct correctly, a result of the way I am using HashMap, and how can I fix it?
The error is correct:
If you have a node that refers to itself (e.g. (1, Some(1))), then new_node and child_node will point to the same value. This is illegal as new_node is a mutable (read: exclusive) reference.
HashMap, like Vec, is backed by an array. When the HashMap fills up, it will reallocate this array, invalidating all existing references.
HashMap lets you delete entries. This will cause dangling pointers if a deleted entry is referenced elsewhere.
Here are a few solutions to this problem:
Store the index instead. This is the simplest solution, and is recommended most of the time.
struct List {
data: i32,
child: Option<i32>,
}
Use an append-only collection like elsa::FrozenMap. This only solves problem 3, but Box and Cell should handle the rest:
use elsa::FrozenMap;
struct List<'a> {
data: i32,
child: Cell<Option<&'a List<'a>>>,
}
fn main() {
let map = FrozenMap::<i32, Box<List>>::new();
// ...
}

Why does an immutable borrow in a loop last outside of its lexical scope?

I am stuck on the borrow checker.
pub struct Gamepad {
str: String,
}
pub enum Player {
Human(Gamepad),
Computer,
}
pub struct PlayerData {
pub player: Player, /* actually this should be private */
}
struct Pong {
players: Vec<PlayerData>,
}
fn update_game(_pong: &mut Pong) {}
fn main() {
println!("Hello, world!");
let mut pong = Pong {
players: vec![
PlayerData {
player: Player::Computer,
},
PlayerData {
player: Player::Human(Gamepad {
str: "mydev".to_string(),
}),
},
],
};
game_loop(&mut pong);
}
fn game_loop(pong: &mut Pong) {
let mut vec: Vec<&Gamepad> = Vec::new();
{
for playerdata in pong.players.iter() {
match playerdata.player {
Player::Human(ref gp) => {
if gp.str == "mydev" {
vec.push(gp); //omitting this line of code fixes borrow checker issues
}
}
_ => {}
}
}
}
update_game(pong);
}
playground
This gives:
error[E0502]: cannot borrow `*pong` as mutable because `pong.players` is also borrowed as immutable
--> src/main.rs:52:17
|
41 | for playerdata in pong.players.iter() {
| ------------ immutable borrow occurs here
...
52 | update_game(pong);
| ^^^^ mutable borrow occurs here
53 | }
| - immutable borrow ends here
While I can understand the error to some extent, but coming from a C and Java background, I really struggle to get out of this problem. I am mainly confused why the immutable borrow is not released after the for loop ends. How would you write this in idiomatic Rust?
The error is a bit poorly worded, but I see your problem.
The error says the immutable borrow occurs in the for loop, which isn't quite correct. Instead, it occurs on the line you marked: vec.push(gp).
gp is an immutable reference to an object contained within pong.players. When you exit the loop, there is no immutable reference to pong.players itself, but there is a vector full of references to objects inside that vector.
pong.players : [ a, b, c, d, e]
^ ^ ^ ^ ^
vec : [&a, &b, &c, &d, &e]
Since you have outstanding immutable references to objects within pong.players, Rust has to consider pong.players as "implicitly" immutably borrowed, to ensure that none of its contents are mutated while there is still an immutable reference to that item. Since pong.players is a component of pong and is "implicitly" borrowed, pong itself has to be "implicitly" borrowed immutably as well.
In other words, the borrow of pong in game_loop lasts as such:
fn game_loop(pong: &mut Pong) {
let mut vec: Vec<&Gamepad> = Vec::new(); // <+ `vec`'s lifetime begins here
{ // |
for playerdata in pong.players.iter() { // <+ `pong.players.iter()` temporarily immutably borrows
// | `players` from `pong` for the iterator. `playerdata`
// | is a borrowed portion of `pong.players`.
// | As long as any `playerdata` exists, `pong.players`
// | is immutably borrowed by extension.
match playerdata.player { // <+ `playerdata.player` is a portion of `playerdata`.
Player::Human(ref gp) => { // <+ `gp` is a borrow of an element of `playerdata`.
if gp.str == "mydev" { // |
vec.push(gp); // <! At this point, `gp` is added to `vec`.
// | Since `gp` is inside `vec`, the reference to `gp`
// | is not dropped *until `vec` is dropped.
} // |
} // <- `gp`'s *lexical* lifetime ends here, but it may still
// | be inside `vec`. Any `gp` added to `vec` is still
// | considered borrowed.
_ => {} // |
} // <- `playerdata.player` is not longer lexically borrowed.
// | However, since `gp`, a portion of `playerdata.player`,
// | may still be borrowed, the compiler flags
// | `playerdata.player` as still borrowed.
} // <- `playerdata`'s borrow scope ends here, but since
// | `playerdata.player` may still be borrowed (due to the
// | fact that `vec` may contain references to elements of
// | playerdata.player), `playerdata` is still considered borrowed
} // <- the iterator over `pong.players` is dropped here. But since
// | `playerdata` might still be referenced in `vec`, `pong.players`
// | is still considered borrowed... and since `pong.players` is
// | implicitly borrowed, `pong` is implicitly borrowed.
update_game(pong); // <! When you reach this line, `pong` is implicitly borrowed because
// | there are references to something 'inside' it. Since you can't
// | have an immutable borrow and a mutable borrow at the same time
// | (to ensure you can't change something at the same time another
// | part of the program views it), `update_game(pong)` cannot accept
// | a mutable reference to `pong`.
} // <- At this point, `vec` is dropped, releasing all references to the
// | contents of `pong`. `pong` is also dropped here, because it is the
// | end of the function.
That explains the why. As for the how to solve it: Theoretically, the easiest solution would be to implement Clone on Gamepad (which can be easily done with #[derive(Clone)] if all of Gamepad's fields implement clone; the standard implementation is basically creating a new object by calling .clone on all of the fields of the original) and then use gp.clone() rather than just gp.
This has a (probably negligible) impact on the memory use of your program, but moreover, it can be infeasible if Gamepad uses external-library types that do not implement Clone - you can't implement Clone on those external types, because you don't define Clone or SomeExternalType in your project.
If impl Clone isn't available to you, you may need to refactor your code; reconsider why you need certain mutable or immutable borrows, and remove them if they're unnecessary. If that fails, you might need to look into other types of pointers like Cell, which I'm not qualified to give information about!
If you don't need to keep vec around and do stuff with it after update_game is called, consider this solution:
fn game_loop(pong: &mut Pong) {
{
let mut vec: Vec<&Gamepad> = Vec::new(); // <+ Vec is created
for playerdata in pong.players.iter() { // |
match playerdata.player { // |
Player::Human(ref gp) => { // |
if gp.str == "mydev" { // |
vec.push(gp); // |
} // |
} // |
_ => {} // |
} // |
} // |
for g_pad in vec { // |
// Do something with each gamepad // |
} // |
} // <- `vec` is dropped
// Since `vec` no longer exists, there are no more references
// to the contents of `pong`, and `update_game` can be called.
update_game(pong);
}
Hope this helps.

What lifetimes do I use to create Rust structs that reference each other cyclically?

I'd like to have struct members that know their parent. This is approximately what I'm trying to do:
struct Parent<'me> {
children: Vec<Child<'me>>,
}
struct Child<'me> {
parent: &'me Parent<'me>,
i: i32,
}
fn main() {
let mut p = Parent { children: vec![] };
let c1 = Child { parent: &p, i: 1 };
p.children.push(c1);
}
I tried to appease the compiler with lifetimes without completely understanding what I was doing.
Here's the error message I'm stuck on:
error[E0502]: cannot borrow `p.children` as mutable because `p` is also borrowed as immutable
--> src/main.rs:13:5
|
12 | let c1 = Child { parent: &p, i: 1 };
| - immutable borrow occurs here
13 | p.children.push(c1);
| ^^^^^^^^^^ mutable borrow occurs here
14 | }
| - immutable borrow ends here
That makes some sense, but I'm not at all sure where to go from here.
It is not possible to create cyclic structures with borrowed pointers.
There is not any good way of achieving cyclic data structures at present; the only real solutions are:
Use reference counting with Rc<T> with a cyclic structure with Rc::new and Rc:downgrade. Read the rc module documentation and be careful to not create cyclic structures that use strong references, as these will cause memory leaks.
Use raw/unsafe pointers (*T).

Resources