Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does Rust prevent data races when the owner of a value can read it while another thread changes it?

The Rust book states the following in References and borrowing

We also cannot have a mutable reference while we have an immutable one. Users of an immutable reference don’t expect the values to suddenly change out from under them! However, multiple immutable references are okay because no one who is just reading the data has the ability to affect anyone else’s reading of the data.

But the owner can read the data, while another thread changes the value through a mutable borrow, right? Don't we have then the usual problem all over again, or where do I misunderstand the underlying concept?

like image 369
Julian Hofer Avatar asked Mar 05 '23 03:03

Julian Hofer


1 Answers

the owner can read the data, while another thread changes the value through a mutable borrow, right?

This is incorrect, and it does not even matter whether multiple threads are involved or not.

You can see for yourself in the following example (Playground). Here is a string value x being modified while retaining a reference to the same value y:

let mut x = "123".to_string();
let y = &mut x;

x.push_str("456");

println!("y = {}", y);

This fails to compile:

error[E0499]: cannot borrow `x` as mutable more than once at a time
 --> src/main.rs:5:5
  |
3 |     let y = &mut x;
  |             ------ first mutable borrow occurs here
4 |     
5 |     x.push_str("456");
  |     ^ second mutable borrow occurs here
6 | 
7 |     println!("y = {}", y);
  |                        - first borrow later used here

By the time we try to call push_str, a method which receives &mut self, a new mutable reference to the value is assumed to be built on the spot. Since x was already borrowed in that scope, this is illegal.

Now, you could even think about reassigning instead of calling a method expecting &mut self. Still, fat chance:

let mut x = "123".to_string();
let y = &mut x;

x = "321".to_string();

println!("y = {}", y);
error[E0506]: cannot assign to `x` because it is borrowed
 --> src/main.rs:5:5
  |
3 |     let y = &mut x;
  |             ------ borrow of `x` occurs here
4 |     
5 |     x = "321".to_string();
  |     ^ assignment to borrowed `x` occurs here
6 | 
7 |     println!("y = {}", y);
  |                        - borrow later used here

A borrowed value cannot be reassigned either.

To summarize, the owner of the value is still constrained by whichever kind of borrow was made. If the value was immutably borrowed, the owner can also have immutable access, but never can the value be written to or moved for as long as the borrow is in place. When the value was mutably borrowed, the owner cannot do anything with that value until that reference is dropped.

like image 200
E_net4 stands with Ukraine Avatar answered May 17 '23 01:05

E_net4 stands with Ukraine