In regards, to the Low-Level Concurrency in Rust from Mara Bos.

https://github.com/alwinsDen/Rust-Atomics-and-Locks refer this for all runnable code.


CHAPTER 1 - Basics of Rust Concurrency.

Threads in Rust:

The Thread execution begins from the Main thread in Rust. An example:

use std::thread;
fn main(){
    thread::spawn(f);
    thread::spawn(f);
    println!("Hello from the main thread!")
}
fn f(){
    println!("Hello from another thread!");
    let id = thread::current().id();
    println!("This here is my thread id! {id:?}");
}
use std::thread;
fn main(){
    let t1 = thread::spawn(f);
    let t2 = thread::spawn(f);
    println!("Hello from the main thread!");
    t1.join().unwrap();
    t2.join().unwrap();
}
fn f(){
    println!("Hello from another thread!");
    let id = thread::current().id(); //here the this function is exited before completion
    //because the main thread func has finished.
    println!("This here is my thread id! {id:?}");
    //we add JoinHandle to combat aforementioned issue
}

Even after adding join() handler for waiting the Thread execution, there are times the order is still messed up like below (This order keeps changing between runs).

Even after adding join() handler for waiting the Thread execution, there are times the order is still messed up like below (This order keeps changing between runs).

USING CLOSURES INSTEAD OF FUNC():

Example 1:

let numbers = vec![1,2,3,4];
    thread::spawn(move||{
        //numbers in captured here.
        //move key word moves the owner ship to the closure.
        for n in numbers{
            println!("{n}");
        }
    }).join().unwrap();
fn snip3(){
    let numbers = Vec::from_iter(0..=100);
    let t = thread::spawn(move ||{
        let len = numbers.len();
        let sum = numbers.into_iter().sum::<usize>();
        return sum/len;
    });
    let average = t.join().unwrap();
    println!("{average}");
}
fn snip4(){
    let t = thread::Builder::new().spawn(||{
        return 43i32;
    }).unwrap();
    let tst = t.join().unwrap();
    println!("{tst}");
}

SCOPED THREAD:

It allows us to spawn threads that cannot outlive the scope of the closure we pass to that function, making it possible to safely borrow local variables.

fn snip5(){
    let numbers = vec![1,2,3,5,6,3];
    thread::scope(|s|{
        s.spawn(||{
            println!("{}",numbers.len());
        });
        s.spawn(||{
            for n in &numbers{
                println!("{n}");
            }
        });
    });
}
//!!!THIS CODE FAILS.
let mut numbers = vec![1,2,3,5,6,3];
    thread::scope(|s|{
        s.spawn(||{
            numbers.push(43);
        });
        s.spawn(||{
            numbers.push(23); //ERROR!
        });
    });
    println!("{numbers:?}")
}

SHARED OWNERSHIP AND REFERENCE COUNTING.

Statics: