Thread safety

Traditional programming with threads is very difficult to get right if you allow the different threads to work on the same mutable data, or so-called shared memory. When two or more threads simultaneously change data, then data corruption (also called data racing) can occur due to the unpredictability of the thread's scheduling.

In general, data (or a type) is said to be thread-safe when its contents will not be corrupted by the execution of different threads. Other languages offer no such help, but the Rust compiler simply forbids non-thread-safe situations to occur. The same ownership strategy that we looked at to allow Rust to prevent memory safety errors also enables you to write safe, concurrent programs.

Consider the following program:

// code from Chapter 9/code/not_shared.rs:    
use std::thread; 
use std::time; 
 
fn main() { 
    let mut health = 12; 
    for i in 2..5 { 
        thread::spawn(move || { 
            health *= i; 
        }); 
    } 
 thread::sleep(time::Duration::from_secs(2)); 
    println!("{}", health); // 12 
} 

Our initial health is 12, but there are three fairies that can double, triple, and quadruple our health. We let each of them do that in a different thread, and after the threads are finished we expect a health of 288 (= 12 * 2 * 3 * 4). But after their magic actions, our health is still 12, even if we wait long enough to be sure that the threads are finished. Clearly, the three threads worked on a copy of our variable, not on the variable itself. Rust does not allow the health variable to be shared among the threads to prevent data corruption. In the next section, we will explore how we can use mutable variables that are shared between threads.