Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hashmaps3 double borrow #1593

Open
pinzaghi opened this issue Jul 12, 2023 · 1 comment
Open

hashmaps3 double borrow #1593

pinzaghi opened this issue Jul 12, 2023 · 1 comment

Comments

@pinzaghi
Copy link

pinzaghi commented Jul 12, 2023

Hello, I am new with Rust!

Trying to solve the exercise like this:

let team1 = Team {goals_scored: 0, goals_conceded: 0};
let entry1 = scores.entry(team_1_name).or_insert(team1);

let team2 = Team {goals_scored: 0, goals_conceded: 0};
let entry2 = scores.entry(team_2_name).or_insert(team2);

entry1.goals_scored += team_1_score;
entry1.goals_conceded += team_2_score;

entry2.goals_scored += team_2_score;
entry2.goals_conceded += team_1_score;

produces the following error:

Compiling of exercises/hashmaps/hashmaps3.rs failed! Please try again. Here's the output:
error[E0499]: cannot borrow `scores` as mutable more than once at a time
  --> exercises/hashmaps/hashmaps3.rs:46:22
   |
43 |         let entry1 = scores.entry(team_1_name).or_insert(team1);
   |                      ------------------------- first mutable borrow occurs here
...
46 |         let entry2 = scores.entry(team_2_name).or_insert(team2);
   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here
47 |
48 |         entry1.goals_scored += team_1_score;
   |         ----------------------------------- first borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0499`.

which makes sense because I am borrowing 2 mutable references to scores entries. But when I permute the code like this

let team1 = Team {goals_scored: 0, goals_conceded: 0};
let entry1 = scores.entry(team_1_name).or_insert(team1);

entry1.goals_scored += team_1_score;
entry1.goals_conceded += team_2_score;

let team2 = Team {goals_scored: 0, goals_conceded: 0};
let entry2 = scores.entry(team_2_name).or_insert(team2);

entry2.goals_scored += team_2_score;
entry2.goals_conceded += team_1_score;

it compiles and pass the test.

Why this change make it work? I guess the compiler should not be able to distinguish that the entries are different by just looking at the code in both scenarios.

@ryanwhitehouse
Copy link
Contributor

The compiler is smart enough to distinguish that entry1 is no longer being accessed and therefore allows entry2. After the line

let entry2 = scores.entry(team_2_name).or_insert(team2);

You are no longer allowed to access entry1.

For more info see https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html

Note that a reference’s scope starts from where it is introduced and continues through the last time that reference is used. For instance, this code will compile because the last usage of the immutable references, the println!, occurs before the mutable reference is introduced...

The scopes of the immutable references r1 and r2 end after the println! where they are last used, which is before the mutable reference r3 is created. These scopes don’t overlap, so this code is allowed: the compiler can tell that the reference is no longer being used at a point before the end of the scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants