Bird
Raised Fist0
Rustprogramming~10 mins

Shared state overview in Rust - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Shared state overview
Start: Create shared state
↓
Multiple threads access shared state
↓
Use synchronization (Mutex, Arc)
↓
Threads read/write safely
↓
Threads finish work
↓
Shared state dropped
This flow shows how shared state is created, accessed safely by multiple threads using synchronization, and then cleaned up.
Execution Sample
Rust
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..3 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }
}
This code creates a shared counter protected by a mutex and shared safely across threads.
Execution Table
StepActionThreadMutex LockCounter ValueOutput/Note
1Create Arc<Mutex> with 0MainUnlocked0Shared state initialized
2Spawn thread 1Thread 1Lock acquired0Thread 1 locks mutex
3Increment counterThread 1Locked1Counter incremented to 1
4Unlock mutexThread 1Unlocked1Thread 1 releases lock
5Spawn thread 2Thread 2Lock acquired1Thread 2 locks mutex
6Increment counterThread 2Locked2Counter incremented to 2
7Unlock mutexThread 2Unlocked2Thread 2 releases lock
8Spawn thread 3Thread 3Lock acquired2Thread 3 locks mutex
9Increment counterThread 3Locked3Counter incremented to 3
10Unlock mutexThread 3Unlocked3Thread 3 releases lock
11All threads joinedMainUnlocked3Final counter value is 3
💡 All threads finished, shared counter safely incremented to 3
Variable Tracker
VariableStartAfter Thread 1After Thread 2After Thread 3Final
counter (Mutex guard)01233
Arc strong count12221
Key Moments - 3 Insights
Why do we need Arc when sharing state between threads?
Arc allows multiple ownership of the shared state safely across threads, as shown in steps 2, 5, and 8 where threads clone the Arc.
What happens if we try to access the counter without locking the mutex?
Without locking, simultaneous access can cause data races. The mutex lock in steps 2, 5, and 8 ensures only one thread accesses the counter at a time.
Why does the counter value increase step by step instead of all at once?
Each thread locks the mutex, increments the counter, then unlocks it before the next thread accesses it, ensuring safe sequential updates (see steps 3, 6, 9).
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the counter value after Thread 2 releases the mutex?
A1
B3
C2
D0
💡 Hint
Check step 7 in the execution table where Thread 2 unlocks the mutex.
At which step does the mutex lock get acquired for the third time?
AStep 6
BStep 8
CStep 3
DStep 10
💡 Hint
Look for the 'Lock acquired' action in the execution table.
If we remove Arc and share Mutex directly, what would happen?
ACompile error due to ownership rules
BRuntime panic due to data race
CCode compiles and runs fine
DCounter never increments
💡 Hint
Arc is needed for multiple ownership across threads, see key moment about Arc.
Concept Snapshot
Shared state in Rust uses Arc for multiple ownership and Mutex for safe access.
Arc clones increase reference count for threads.
Mutex locks prevent data races by allowing one thread at a time.
Threads clone Arc, lock Mutex, modify data, then unlock.
Final shared state is dropped when all owners go out of scope.
Full Transcript
This example shows how Rust manages shared state safely across threads. We start by creating a shared counter wrapped in a Mutex and inside an Arc. The Arc allows multiple threads to own the counter. Each thread clones the Arc to get access. Before changing the counter, a thread locks the mutex to ensure exclusive access. It increments the counter, then unlocks the mutex. This process repeats for each thread. The execution table traces each step: locking, incrementing, unlocking. The variable tracker shows how the counter value changes from 0 to 3. Key moments explain why Arc and Mutex are needed to avoid data races and ownership errors. The visual quiz tests understanding of the counter value at different steps, mutex locking order, and the role of Arc. This approach ensures safe, synchronized shared state in Rust multithreading.

Practice

(1/5)
1. What is the main purpose of using Arc in Rust when working with shared state?
easy
A. To copy data between threads
B. To allow multiple ownership of the same data safely across threads
C. To create a mutable reference to data
D. To lock data so only one thread can access it at a time

Solution

  1. Step 1: Understand what Arc does

    Arc stands for Atomic Reference Counting. It allows multiple parts of a program to own the same data safely, especially across threads.
  2. Step 2: Differentiate from locking mechanisms

    Mutex is used to lock data for safe access, but Arc is about shared ownership, not locking.
  3. Final Answer:

    To allow multiple ownership of the same data safely across threads -> Option B
  4. Quick Check:

    Arc = shared ownership [OK]
Hint: Arc shares ownership safely across threads [OK]
Common Mistakes:
  • Confusing Arc with Mutex
  • Thinking Arc locks data
  • Assuming Arc copies data
2. Which of the following is the correct way to create a thread-safe shared integer using Arc and Mutex in Rust?
easy
A. let data = Mutex::new(Arc::new(5));
B. let data = Mutex::new(5);
C. let data = Arc::new(5);
D. let data = Arc::new(Mutex::new(5));

Solution

  1. Step 1: Understand the order of wrapping

    To share mutable data safely, wrap the data in a Mutex first, then wrap that in an Arc for shared ownership.
  2. Step 2: Check each option

    let data = Arc::new(Mutex::new(5)); correctly wraps 5 in a Mutex, then in an Arc. let data = Mutex::new(Arc::new(5)); reverses the order, which is incorrect. Options C and D miss one of the wrappers.
  3. Final Answer:

    let data = Arc::new(Mutex::new(5)); -> Option D
  4. Quick Check:

    Arc wraps Mutex for shared mutable data [OK]
Hint: Wrap data in Mutex, then Arc for shared mutable state [OK]
Common Mistakes:
  • Reversing Arc and Mutex order
  • Missing Mutex for mutability
  • Using Arc without Mutex for mutable data
3. What will be the output of the following Rust code snippet?
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..3 {
        let counter = Arc::clone(&counter);
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();
            *num += 1;
        });
        handles.push(handle);
    }

    for handle in handles {
        handle.join().unwrap();
    }

    println!("{}", *counter.lock().unwrap());
}
medium
A. 3
B. 0
C. 1
D. Compilation error

Solution

  1. Step 1: Understand the shared counter

    The counter is wrapped in Arc and Mutex, allowing safe shared mutable access across threads.
  2. Step 2: Analyze thread increments

    Three threads each lock the mutex, increment the counter by 1, then unlock. After all threads join, the counter should be 3.
  3. Final Answer:

    3 -> Option A
  4. Quick Check:

    3 threads increment counter once each = 3 [OK]
Hint: Count increments by threads, sum equals output [OK]
Common Mistakes:
  • Forgetting to clone Arc for each thread
  • Ignoring Mutex lock for safe access
  • Assuming race conditions cause wrong output
4. Identify the error in this Rust code that tries to share a vector across threads:
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let data = Arc::new(Mutex::new(vec![1, 2, 3]));
    let data2 = data.clone();

    let handle = thread::spawn(move || {
        data2.lock().unwrap().push(4);
    });

    handle.join().unwrap();
    println!("{:?}", data.lock().unwrap());
}
medium
A. No error, code runs and prints [1, 2, 3, 4]
B. Cannot clone Arc with .clone(), must use Arc::clone()
C. Mutex lock is missing unwrap()
D. Vector cannot be shared across threads

Solution

  1. Step 1: Check Arc cloning

    Using data.clone() on an Arc is valid and clones the pointer safely.
  2. Step 2: Verify Mutex locking and thread usage

    The mutex is locked with unwrap(), vector is safely mutated inside the thread, and join waits for thread completion.
  3. Final Answer:

    No error, code runs and prints [1, 2, 3, 4] -> Option A
  4. Quick Check:

    Arc clone and Mutex lock correct [OK]
Hint: Arc clone works with .clone(), Mutex lock unwrap needed [OK]
Common Mistakes:
  • Thinking Arc::clone() is mandatory
  • Forgetting to unwrap Mutex lock
  • Believing vectors can't be shared across threads
5. You want to safely share a counter between multiple threads and increment it only if the current value is even. Which approach correctly uses Arc and Mutex to achieve this?
hard
A. Use Arc<i32> only, clone Arc for each thread, increment directly
B. Use Mutex<Arc<i32>>, clone Mutex for each thread, increment without locking
C. Wrap the counter in Arc<Mutex<i32>>, clone Arc for each thread, lock mutex, check if even, then increment
D. Use Mutex<i32> only, share by cloning Mutex, increment without Arc

Solution

  1. Step 1: Understand shared mutable state needs

    To share and mutate data safely across threads, use Arc for shared ownership and Mutex for safe mutable access.
  2. Step 2: Analyze each option

    Wrap the counter in Arc<Mutex<i32>>, clone Arc for each thread, lock mutex, check if even, then increment correctly wraps the counter in Arc<Mutex<i32>>, clones Arc for threads, locks mutex before checking and incrementing. Other options misuse order or omit locking.
  3. Final Answer:

    Wrap the counter in Arc<Mutex<i32>>, clone Arc for each thread, lock mutex, check if even, then increment -> Option C
  4. Quick Check:

    Arc + Mutex + lock for safe conditional increment [OK]
Hint: Arc for sharing, Mutex for locking before mutate [OK]
Common Mistakes:
  • Not locking mutex before mutate
  • Using Arc without Mutex for mutability
  • Cloning Mutex instead of Arc
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard