Two mutable borrows from vector - rust

I am following a tutorial on breakout game written in rust and I have simple data structure representing balls on the screen:
pub struct Ball {
rect: Rect,
vel: Vec2,
}
It is stored in vector
let mut balls: Vec<Ball> = Vec::new();
However when I try to calculate ball to ball collision I encounter error:
--> src/main.rs:193:31
|
192 | for ball in balls.iter_mut() {
| ----------------
| |
| first mutable borrow occurs here
| first borrow later used here
193 | for other_ball in balls.iter_mut() {
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
// ball collision with balls
for ball in balls.iter_mut() {
for other_ball in balls.iter_mut() {
if ball != other_ball {
resolve_collision(&mut ball.rect, &mut ball.vel, &other_ball.rect);
}
}
}
My initial approach was to use double iteration, now I know that borrow checker wont allow me to modify vector as it is considered unsafe. Is there a common pattern that I could use to solve this kind of issues?

You can achieve this using split_at_mut. It feels a bit hacky, but works OK. Here's an implementation that gets two different mutable values.
pub fn get_mut2<T>(v: &mut [T], i: usize, j: usize) -> Option<(&mut T, &mut T)> {
if i == j {
return None;
}
let (start, end) = if i < j { (i, j) } else { (j, i) };
let (first, second) = v.split_at_mut(start + 1);
Some((&mut first[start], &mut second[end - start - 1]))
}
pub fn main() {
let mut data = [0, 1, 2, 3, 4, 5, 6, 7];
let (a, b) = get_mut2(&mut data, 3, 6).unwrap();
*a += 10;
*b += 10;
eprintln!("{:?}", data); // [0, 1, 2, 13, 4, 5, 16, 7]
}
There's a working version on the playground.
You'd then need a double loop over your array lengths:
assert!(!a.is_empty());
for i in 0..a.len()-1 {
for j in i..a.len() {
let (ball_i, ball_j) = get_mut2(&mut a, i, j).unwrap();
...
}
}
Note that my loop ensures I only visit each unordered pair once.

You can use RefCell for mutability and iter() instead of iter_mut() so that compiler won't complain that the code borrows the vec twice, e.g.:
struct Ball(u32, u32);
let mut balls = vec![];
balls.push(RefCell::new(Ball(0, 0)));
// push more balls into vec
for b1 in balls.iter() {
for b2 in balls.iter() {
// change attributes of a ball
b1.borrow_mut().0 = 10;
b2.borrow_mut().1 = 20;
}
}

Related

Dynamically create a range in either direction in Rust

I am learning Rust and recently went through an exercise where I had to iterate through numbers that could go in either direction. I tried the below with unexpected results.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
x: i32,
y: i32
}
fn test() {
let p1 = Point { x: 1, y: 8 };
let p2 = Point { x: 3, y: 6 };
let all_x = p1.x..=p2.x;
println!("all_x: {:?}", all_x.clone().collect::<Vec<i32>>());
let all_y = p1.y..=p2.y;
println!("all_y: {:?}", all_y.clone().collect::<Vec<i32>>());
let points: Vec<Point> = all_x.zip(all_y).map(|(x, y)| Point { x, y }).collect();
println!("points: {:?}", points);
}
The output was
all_x: [1, 2, 3]
all_y: []
points: []
After some googling I found an explanation and some old answers which basically amount to use (a..b).rev() as needed.
My question is, how do I do this in a dynamic way? If I use an if...else like so
let all_x = if p1.x < p2.x { (p1.x..=p2.x) } else { (p2.x..=p1.x).rev() };
I get a type error because the else is different than the if
|
58 | let all_x = if p1.x < p2.x { (p1.x..=p2.x) }
| - ------------- expected because of this
| _________________|
| |
59 | | else { (p2.x..=p1.x).rev() };
| |____________^^^^^^^^^^^^^^^^^^^_- `if` and `else` have incompatible types
| |
| expected struct `RangeInclusive`, found struct `Rev`
|
= note: expected type `RangeInclusive<_>`
found struct `Rev<RangeInclusive<_>>`
After trying a bunch of different variations on let all_x: dyn Range<Item = i32>, let all_x: dyn Iterator<Item = i32>, etc, the only way I have managed to do this is by turning them into collections and then back to iterators.
let all_x: Vec<i32>;
if p1.x < p2.x { all_x = (p1.x..=p2.x).collect(); }
else { all_x = (p2.x..=p1.x).rev().collect(); }
let all_x = all_x.into_iter();
println!("all_x: {:?}", all_x.clone().collect::<Vec<i32>>());
let all_y: Vec<i32>;
if p1.y < p2.y { all_y = (p1.y..=p2.y).collect(); }
else { all_y = (p2.y..=p1.y).rev().collect(); }
let all_y = all_y.into_iter();
println!("all_y: {:?}", all_y.clone().collect::<Vec<i32>>());
which provides the desired outcome
all_x: [1, 2, 3]
all_y: [8, 7, 6]
points: [Point { x: 1, y: 8 }, Point { x: 2, y: 7 }, Point { x: 3, y: 6 }]
but is a bit repetitive, inelegant and I'm assuming not very efficient at large numbers. Is there a better way to handle this situation?
NOTE: Sorry for including the Point struct. I could not get my example to work with x1, x2, etc. Probably a different question for a different post lol.
You can dynamically dispatch it. Wrapping them into a Box and returning a dynamic object, an Iterator in this case. For example:
fn maybe_reverse_range(init: usize, end: usize, reverse: bool) -> Box<dyn Iterator<Item=usize>> {
if reverse {
Box::new((init..end).rev())
} else {
Box::new((init..end))
}
}
Playground
The enum itertools::Either can be used to solve the incompatible type error in the if/else statement. A function like get_range_iter below using Either can reduce the code repetition.
use itertools::Either;
fn get_range_iter(start: i32, end: i32) -> impl Iterator<Item=i32> {
if start < end {
Either::Left(start..=end)
} else {
Either::Right((end..=start).rev())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
x: i32,
y: i32
}
fn main() {
let p1 = Point { x: 1, y: 8 };
let p2 = Point { x: 3, y: 6 };
let all_x = get_range_iter(p1.x, p2.x);
let all_y = get_range_iter(p1.y, p2.y);
println!("all_x: {:?}", all_x.collect::<Vec<_>>());
println!("all_y: {:?}", all_y.collect::<Vec<_>>());
}
Playground

Linking Rust objects to one another mutably causes borrowing problems

I have a group of Rust struct instances that represent squares on a game board. Each square knows which other squares can be reached from itself.
Here is my example:
enum Direction {
North,
South,
East,
West,
}
#[derive(Debug)]
struct Square {
x: u16,
y: u16,
}
impl Square {
fn new(x: u16, y: u16) -> Self {
Square { x, y }
}
fn spawn_another_square(&self, d: Direction) -> Self {
let mut new_x = self.x;
let mut new_y = self.y;
match d {
Direction::North => new_y -= 1,
Direction::South => new_y += 1,
Direction::East => new_x += 1,
Direction::West => new_x -= 1,
}
Self::new(new_x, new_y)
}
}
The Square struct has only x and y coordinates in this example, no real data. The struct implements Debug so we can print it, and two functions: a new(), and a spawn_another_square() that returns a new square, with the coordinates of this one, but shifted north, south, east or west as desired.
If we add a main() to test this part, it works.
fn main() {
let northern_square = Square::new(10, 10);
let central_square = northern_square.spawn_another_square(Direction::South);
let western_square = central_square.spawn_another_square(Direction::West);
let eastern_square = central_square.spawn_another_square(Direction::East);
let southern_square = central_square.spawn_another_square(Direction::South);
println!("Northern square: {:?}", northern_square);
println!("Central square: {:?}", central_square);
println!("Western square: {:?}", western_square);
println!("Eastern square: {:?}", eastern_square);
println!("Southern square: {:?}", southern_square);
}
The main first creates the northern_square, using new(), at arbitrary coordinates 10, 10. This is then spawn_another_square()'d into a new central_square that is shifted Direction::South of the first. Then the central square is spawned to make the others. You get the idea.
Next step is to add information about the paths that exist between squares.
#[derive(Debug)]
struct Square {
x: u16,
y: u16,
to_the_north: Option<&Square>,
to_the_south: Option<&Square>,
to_the_east: Option<&Square>,
to_the_west: Option<&Square>,
}
impl Square {
fn new(x: u16, y: u16) -> Self {
Square {
x,
y,
to_the_north: None,
to_the_south: None,
to_the_east: None,
to_the_west: None,
}
}
fn spawn_another_square(&self, d: Direction) -> Self {
let mut new_x = self.x;
let mut new_y = self.y;
match d {
Direction::North => new_y -= 1,
Direction::South => new_y += 1,
Direction::East => new_x += 1,
Direction::West => new_x -= 1,
}
// neighbour squares will be None on new square, even if set on this square
Self::new(new_x, new_y)
}
}
Here we can see four new elements of the struct, to record which square you will find in each of the four directions. The new elements are Option<&Square>, since it is possible for there to be no square at all in some directions, and so that the Square instances can be created before the path information is known. Also, we don't want the squares to own one another, only to point to one another; it wouldn't be possible for east and west both to own central. Also, the new() function has been updated to supply the None values for the new elements.
Of course my example doesn't compile because expected named lifetime parameter, so let's fix that...
#[derive(Debug)]
struct Square<'a> {
x: u16,
y: u16,
to_the_north: Option<&'a Square<'a>>,
to_the_south: Option<&'a Square<'a>>,
to_the_east: Option<&'a Square<'a>>,
to_the_west: Option<&'a Square<'a>>,
}
impl<'a> Square<'a> {
fn new(x: u16, y: u16) -> Self {
Square {
x,
y,
to_the_north: None,
to_the_south: None,
to_the_east: None,
to_the_west: None,
}
}
fn spawn_another_square(&self, d: Direction) -> Self {
let mut new_x = self.x;
let mut new_y = self.y;
match d {
Direction::North => new_y -= 1,
Direction::South => new_y += 1,
Direction::East => new_x += 1,
Direction::West => new_x -= 1,
}
// neighbour squares will be None on new square, even if set on this square
Self::new(new_x, new_y)
}
}
It compiles, but it looks like a lot of extra <'a>s.
Now to try setting some paths, with a new function implemented on the Square struct, and an updated main(). Here's the whole thing:
enum Direction {
North,
South,
East,
West,
}
#[derive(Debug)]
struct Square<'a> {
x: u16,
y: u16,
to_the_north: Option<&'a Square<'a>>,
to_the_south: Option<&'a Square<'a>>,
to_the_east: Option<&'a Square<'a>>,
to_the_west: Option<&'a Square<'a>>,
}
impl<'a> Square<'a> {
fn new(x: u16, y: u16) -> Self {
Square {
x,
y,
to_the_north: None,
to_the_south: None,
to_the_east: None,
to_the_west: None,
}
}
fn spawn_another_square(&self, d: Direction) -> Self {
let mut new_x = self.x;
let mut new_y = self.y;
match d {
Direction::North => new_y -= 1,
Direction::South => new_y += 1,
Direction::East => new_x += 1,
Direction::West => new_x -= 1,
}
Self::new(new_x, new_y) // neighbour squares will be None on new square, even if set on this square
}
fn set_a_path(&mut self, dir: Direction, dest: &'a Square) {
match dir {
Direction::North => self.to_the_north = Some(dest),
Direction::South => self.to_the_south = Some(dest),
Direction::East => self.to_the_east = Some(dest),
Direction::West => self.to_the_west = Some(dest),
};
}
}
fn main() {
let mut northern_square = Square::new(10, 10);
let mut central_square = northern_square.spawn_another_square(Direction::South);
let mut western_square = central_square.spawn_another_square(Direction::West);
let mut eastern_square = central_square.spawn_another_square(Direction::East);
let mut southern_square = central_square.spawn_another_square(Direction::South);
northern_square.set_a_path(Direction::South, &central_square);
println!("Northern square: {:?}", northern_square);
println!("Central square: {:?}", central_square);
println!("Western square: {:?}", western_square);
println!("Eastern square: {:?}", eastern_square);
println!("Southern square: {:?}", southern_square);
}
This works, changing all the square's variables to be mutable, then setting a path with set_a_path() from the northern_square, heading South, to the central_square. This runs OK.
But adding extra paths causes my problem. One more line in main is enough.
central_square.set_a_path(Direction::East, &eastern_square);
This causes the error.
error[E0502]: cannot borrow `central_square` as mutable because it is also borrowed as immutable
--> src/main.rs:51:5
|
49 | northern_square.set_a_path(Direction::South, &central_square);
| --------------- immutable borrow occurs here
50 | // central_square.set_a_path(Direction::North, &northern_square);
51 | central_square.set_a_path(Direction::East, &eastern_square);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
...
54 | println!("Northern square: {:?}", northern_square);
| --------------- immutable borrow later used here
Am I completely on the wrong path? Am I going about this in an 'object oriented' way, and not a sufficiently Rusty way? Or am I close, maybe just missing the odd & ref mut * combination?
The square variables need to be mutable, to add the references to the other squares. I don't think we could create the squares with the paths already coded in, because the first square's neighbours would not have been created in time for it to receive its info.
Quick Fix
For the immediate problem your Q ends with, the fix is very simple. You just need to declare the instance you need to mutate later as mut (see comments in code below).
More Info
Sharing references between instances is fine as long as you don't need to mutate the shared-referenced instances after they've been multiply shared. Or, if you're cloning/copying instances in lieu of sharing references, you'll be okay.
If we were really implementing a game board, we could avoid the need for shared references in the fields of squares by using array/Vec indices instead. If we had a single "board" instance that owned an array or Vec that owned all the squares. Fields like to_the_north could have type usize instead of a reference. Or, some sort of handle object based internally on array position could work.
Also, implementing the board as an array-like type is a type of optimization. With the way data is cached by the processor, "locality" of contiguous data can make sequential access very quick. This would be better than implementing the board as a web of references to heap allocated instances that aren't necessarily contiguous.
If something like the above simple design won't work (array with indices/handles), and if you do intend to share MUTABLE references between these squares, then you'll need to leverage Rust's smart pointer classes/structs. With Rust's borrowing rules, in many cases, the alternatives to using the smart pointers are less than desirable. The usage model for Rust generally expects the use of the smart pointer classes where mutable shared access is needed.
The relevant smart pointer classes are:
Rc - multiple owners; reference counted.
RefCell - single owner; mutable interior.
These are typically nested as Rc<RefCell<...>>. Or if you only need ref counted multiple owner access to an instance (without interior mutability), you'll only need Rc<...>. RefCell<...> can also be used independently where it has only has one owner, and the encapsulated item needs to be modified on demand, despite the object owning the RefCell itself being immutable.
For instances of classes shared between threads, the thread-safe analogs for the above are Arc (vs Rc) and Mutex (vs RefCell).
Caveat - circular references
A Vec/array implementation using indices as handles instead of refs avoids the following issue.
If you were to have square instances holding Rc<...> references to each other, you'll likely get memory cycles. And the reference counts in cycles may not ever decrement to 0, in which case if you're creating board after board, and even if they go out of scope, there may be some lingering heap-allocated ref-counted objects building up over time.
This may not matter if your application only ever creates one single board and uses that for the duration.
Among the solutions for handling this case are use of weak pointers. You can read more on how to deal with potential memory cycles here
fn main()
{
let northern_square = Square::new(10, 10);
// Add 'mut'
let mut central_square = northern_square.spawn_another_square(Direction::South);
let western_square = central_square.spawn_another_square(Direction::West);
let eastern_square = central_square.spawn_another_square(Direction::East);
let southern_square = central_square.spawn_another_square(Direction::South);
// Now this is okay.
central_square.set_a_path(Direction::East, &eastern_square);
println!("Northern square: {:?}", northern_square);
println!("Central square: {:?}", central_square);
println!("Western square: {:?}", western_square);
println!("Eastern square: {:?}", eastern_square);
println!("Southern square: {:?}", southern_square);
}

Understanding clones borrow

I'm fairly new to rust, and still learning the rust ownership model. I'm working on a piece of code where I hold a reference to a point in the data structure. I want to store multiple copies of this data structure, where on each copy the reference point hold different values. I (tried to) solve this by creating a mutable reference to the point inside the data structure, and after each update of the reference create a clone of the original data structure.
I was able to create this simple example that is analogue to what i'm trying to do and produces the same error.
fn main() {
let mut v = vec![1, 1, 1];
let mut c = Vec::new();
for i in &mut v {
*i += 1;
c.push(v.clone());
}
println!("{:?}", v);
}
which produces the following error
error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
--> src/main.rs:107:16
|
105 | for i in &mut v {
| ------
| |
| mutable borrow occurs here
| mutable borrow later used here
106 | *i += 1;
107 | c.push(v.clone());
| ^ immutable borrow occurs here
With some help of the handy rust book i was able to interpret the error message. I believe this tells me that I cannot have both a mutable, and an immutable reference that are alive at the same time. What is (or is there) an rust-idiomatic method of iteratively creating duplicates of a data structure and updating a reference within said data structure?
Edit:
The example above might be too minified and not highlight the actual problem I'm having. What would be the rust-idiomatic way to write something like the example below.
#[derive(Clone, Debug)]
enum Tree {
Leaf,
Node { l: Box<Tree>, r: Box<Tree>, },
}
fn main() {
let mut tree = Tree::Node {
l: Box::new(Tree::Node { l: Box::new(Tree::Leaf), r: Box::new(Tree::Leaf), }),
r: Box::new(Tree::Node { l: Box::new(Tree::Leaf), r: Box::new(Tree::Leaf), }),
};
let augmenting_trees = vec![
Tree::Node { l: Box::new(Tree::Leaf), r: Box::new(Tree::Leaf), },
Tree::Node { l: Box::new(Tree::Node { l: Box::new(Tree::Leaf), r: Box::new(Tree::Leaf), }), r: Box::new(Tree::Leaf), },
Tree::Node { l: Box::new(Tree::Leaf), r: Box::new(Tree::Node { l: Box::new(Tree::Leaf), r: Box::new(Tree::Leaf), }), },
];
let mut trees: Vec<Tree> = Vec::new();
let leaf = find_some_leaf_in_tree(&mut tree);
for augmenting_tree in augmenting_trees {
*leaf = augmenting_tree;
trees.push(tree.clone());
}
println!("trees: {:?}", trees);
}
fn find_some_leaf_in_tree<'a>(tree: &'a mut Tree) -> &'a mut Tree {
match tree {
Tree::Leaf => tree,
Tree::Node { l, .. } => find_some_leaf_in_tree(l),
}
}
What is (or is there) an rust-idiomatic method of iteratively creating duplicates of a data structure and updating a reference within said data structure?
The general answer is “don't use a reference for the purpose you're trying to use a reference”. In this case, you have a vector, so use indices instead of references:
fn main() {
let mut v = vec![1, 1, 1];
let mut c = Vec::new();
for i in 0..v.len() {
v[i] += 1;
c.push(v.clone());
}
dbg!(v, c);
}
Note that this doesn't mean you can't use references at all. For example, the code could be written in terms of a mutable reference to v instead:
fn modify_and_snapshot(v: &mut Vec<u32>) -> Vec<Vec<u32>> {
let mut c = Vec::new();
for i in 0..v.len() {
v[i] += 1;
c.push(v.clone());
}
c
}
fn main() {
let mut v = vec![1, 1, 1];
let c = modify_and_snapshot(&mut v);
dbg!(v, c);
}
The necessary condition is that when you want to snapshot v, you don't have a mutable reference to less than all of v — you can own the whole vector or you can have a mutable reference to the whole vector, but you can't do anything with the whole of the vector while a mutable reference to part of it exists.

Unable to borrow a Vec inside of a closure

I have a vector of Units. I'm trying to construct a Vec<Vec<Unit>> variable, called world. In the process of creating tiles, I need to know what tiles are adjacent that it knows of.
I have a function that returns a vector of points ((usize, usize)) that are adjacent to another point, and I'm converting that to an iterator, mapping over it, and getting the actual unit associated with that position in world if it's there, or the current line which has not yet been committed to world.
I need to access that line inside the closure that map takes, but I also need to access it later.
fn adjacent((x, y): (usize, usize)) -> Vec<(usize, usize)> {
vec![
(x+1, y),
(x-1, y),
(x, y+1),
(x, y-1),
]
}
struct Unit {
pub tiles: Vec<Tile>,
}
#[derive(Copy, Clone)]
enum Tile {
Floor, Wall, Empty
}
fn main() {
let heightmap = vec![
vec![3, 3, 3, 3, 3, 3],
vec![3, 1, 1, 1, 1, 3],
vec![3, 1, 1, 1, 1, 3],
vec![3, 1, 1, 1, 1, 3],
vec![3, 1, 1, 1, 1, 3],
vec![3, 1, 1, 1, 1, 3],
vec![3, 3, 3, 3, 3, 3],
];
let (sx, sy) = (5, 5);
let mut world: Vec<Vec<Unit>> = vec![];
for y in 0..sy {
let mut line: Vec<Unit> = vec![];
for x in 0..sx {
let mut tiles: Vec<Tile> = vec![];
let height = heightmap[y][x];
let adj = adjacent((x, y))
.iter()
.map(|&(x, y)| {
let list = if y > world.len() {
vec![]
} else if y == world.len() {
line
} else {
world[y]
};
if x >= list.len() {
Tile::Empty
} else {
if height as usize >= list[x].tiles.len() {
Tile::Empty
} else {
list[x].tiles[height as usize]
}
}
})
.collect::<Vec<_>>();
for z in 0..(height as isize - 1) {
tiles.push(Tile::Wall);
}
line.push(Unit {
tiles: tiles,
});
}
world.push(line);
}
}
Here is the Rust Playground.
I would have rather used unwrap_or in combination with get and so on but that leads to other errors with temporary values, and that would be more complicated anyway, so the above code, while probably not optimal, is as simple as I can get it while still duplicating the error. I've found out that a lot of the errors I get with Rust is because what I'm doing isn't the best way to go about it, so if there is a more idiomatic way, I would certainly like to know.
I've tried to make Unit cloneable (deriving Clone and Copy) but Rust won't let me for some reason, even though all it is made up of is vectors which are cloneable if their members are.
Here's a smaller example of your problem:
fn main() {
let mut line = vec![1];
let something_to_iterate_over = vec![true, false, true];
for _ in 0..2 {
let _dummy: Vec<_> = something_to_iterate_over
.iter()
.map(|&value| {
let maybe_moved_line = if value { vec![] } else { line };
() // Don't care about the return value
})
.collect();
line.push(2);
}
}
The first part of the error message is:
error[E0382]: capture of moved value: `line`
--> src/main.rs:9:67
|
8 | .map(|&value| {
| -------- value moved (into closure) here
9 | let maybe_moved_line = if value { vec![] } else { line };
| ^^^^ value captured here after move
|
= note: move occurs because `line` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
error[E0382]: use of moved value: `line`
--> src/main.rs:14:9
|
8 | .map(|&value| {
| -------- value moved (into closure) here
...
14 | line.push(2);
| ^^^^ value used here after move
|
= note: move occurs because `line` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
As the error message is attempting to convey, the code no longer has ownership of the variable line after the map call because the ownership has been transferred to the closure.
The code is attempting to give a single Vec to someone else while keeping it for itself, and that's simply not how ownership works. I can't give you my car and then continue to drive it everyday — it's not mine to drive!
The smallest amount of change to get your code to compile is to stop trying to give away the one and only line, but instead to clone it as needed:
Add #[derive(Clone)] to Unit
Clone line and world[y] inside the closure: line.clone(), world[y].clone()
After doing this, the code never gives up ownership of line so it can be cloned whenever needed.

How can I update a value in a mutable HashMap?

Here is what I am trying to do:
use std::collections::HashMap;
fn main() {
let mut my_map = HashMap::new();
my_map.insert("a", 1);
my_map.insert("b", 3);
my_map["a"] += 10;
// I expect my_map becomes {"b": 3, "a": 11}
}
But this raises an error:
Rust 2015
error[E0594]: cannot assign to immutable indexed content
--> src/main.rs:8:5
|
8 | my_map["a"] += 10;
| ^^^^^^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `IndexMut` is required to modify indexed content, but it is not implemented for `std::collections::HashMap<&str, i32>`
Rust 2018
error[E0594]: cannot assign to data in a `&` reference
--> src/main.rs:8:5
|
8 | my_map["a"] += 10;
| ^^^^^^^^^^^^^^^^^ cannot assign
I don't really understand what that means, since I made the HashMap mutable. When I try to update an element in a vector, I get the expected result:
let mut my_vec = vec![1, 2, 3];
my_vec[0] += 10;
println! {"{:?}", my_vec};
// [11, 2, 3]
What is different about HashMap that I am getting the above error? Is there a way to update a value?
Indexing immutably and indexing mutably are provided by two different traits: Index and IndexMut, respectively.
Currently, HashMap does not implement IndexMut, while Vec does.
The commit that removed HashMap's IndexMut implementation states:
This commit removes the IndexMut impls on HashMap and BTreeMap, in
order to future-proof the API against the eventual inclusion of an
IndexSet trait.
It's my understanding that a hypothetical IndexSet trait would allow you to assign brand-new values to a HashMap, and not just read or mutate existing entries:
let mut my_map = HashMap::new();
my_map["key"] = "value";
For now, you can use get_mut:
*my_map.get_mut("a").unwrap() += 10;
Or the entry API:
*my_map.entry("a").or_insert(42) += 10;
Considering:
let mut m = std::collections::HashMap::new();
m.insert("a", 1);
m.insert("b", 3);
let k = "c";
If the key already exists:
m.insert(k, 10 + m[k] );
If the key not exists:
You may update a value of the key:
m.insert(k, 10 + if m.contains_key(k) { m[k] } else { 0 });
Or first insert a key only if it doesn't already exist:
m.entry(k).or_insert(0);
m.insert(k, 200 + m[k]);
Or update a key, guarding against the key possibly not being set:
*m.entry(k).or_insert(0) += 3000;
Finally print the value:
println!("{}", m[k]); // 3210
See:
https://doc.rust-lang.org/std/collections/struct.HashMap.html
I will share my own Answer because I had this issue but I was working with Structs so, that way in my case was a little bit tricky
use std::collections::HashMap;
#[derive(Debug)]
struct ExampleStruct {
pub field1: usize,
pub field2: f64,
}
fn main() {
let mut example_map = HashMap::new();
&example_map.insert(1usize, ExampleStruct { field1: 50, field2: 184.0});
&example_map.insert(6usize, ExampleStruct { field1: 60, field2: 486.0});
//First Try
(*example_map.get_mut(&1).unwrap()).field1 += 55; //50+55=105
(*example_map.get_mut(&6).unwrap()).field1 -= 25; //60-25=35
//Spliting lines
let op_elem = example_map.get_mut(&6);
let elem = op_elem.unwrap();
(*elem).field2 = 200.0;
let op_ok_elem = example_map.get_mut(&1);
let elem = op_ok_elem.unwrap_or_else(|| panic!("This msg should not appear"));
(*elem).field2 = 777.0;
println!("Map at this point: {:?}", example_map);
let op_err_elem = example_map.get_mut(&8);
let _elem = op_err_elem.unwrap_or_else(|| panic!("Be careful, check you key"));
println!("{:?}", example_map);
}
You can play with this on Rust Playground
You can do this with .and_modify
let mut my_map = HashMap::new();
my_map.insert("a", 1);
my_map.entry("a").and_modify(|k| *k += 10);
assert_eq!(my_map[&"a"], 11);

Resources