1. Fearless concurrency
2. panthom type
Copyright By PowCoder代写 加微信 powcoder
Resource: [TRPL](https://doc.rust-lang.org/book/ch16-00-concurrency.html) and [RBE](https://doc.rust-lang.org/rust-by-example/generics/phantom.html)
# Fearless concurrency
*Concurrent programming*, where different parts of a program execute independently, and *parallel programming*, where different parts of a program execute at the same time, are becoming increasingly important as more computers take advantage of their *multiple processors*.
In rust, the ownership and type systems are a powerful set of tools to help manage memory safety *and* concurrency problems.
## Using threads to run code simultaneously
In most current operating systems, an executed program’s code is run in a *process*, and the operating system manages multiple processes at once. Within your program, you can also have independent parts that run simultaneously. The features that run these independent parts are called *threads*.
Multi-threaded task can lead to the follwoing problems:
* Race conditions, where threads are accessing data or resources in an inconsistent order
* Deadlocks, where two threads are waiting for each other to finish using a resource the other thread has, preventing both threads from continuing
* Bugs that happen only in certain situations and are hard to reproduce and fix reliably
Rust std provides 1:1 threading, meaning one operating system thread per one language thread. There are crates that provide M:N threading in rust, such as [futures](https://docs.rs/futures/0.3.5/futures/index.html),but we will focus on the theading methods in Rust std.
### Creating a with spawn
Creating threads in rust is easy, we call the `thread::spawn()` function and pass it a closure containing the code we want to run in the new thread.
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 1..10 {
println!(“hi number {} from the spawned thread!”, i);
thread::sleep(Duration::from_millis(1));
for i in 1..5 {
println!(“hi number {} from the main thread!”, i);
thread::sleep(Duration::from_millis(1));
// handle.join().unwrap();
The threads will probably take turns, but that isn’t guaranteed: it depends on how your operating system schedules the threads. In this run, the main thread printed first, even though the print statement from the spawned thread appears first in the code. And even though we told the spawned thread to print until i is 9, it only got to 5 before the main thread shut down.
We can fix the problem of the spawned thread not getting to run, or not getting to run completely, by saving the return value of `thread::spawn` in a variable. The return type of `thread::spawn` is `JoinHandle`. A `JoinHandle` is an owned value that, when we call the join method on it, will wait for its thread to finish.
Notice that the position where you put the `join()` at can affect whether or not your threads run at the same time.
### Using move Closures with Threads
we can use the `move` keyword before the parameter list of a closure to force the closure to take ownership of the values it uses in the environment.
To use data from the main thread in the spawned thread, the spawned thread’s closure must capture the values it needs.
use std::thread;
fn main() {
let v = vec![1, 2, 3];
let handle = thread::spawn(/*move*/ || {
println!(“Here’s a vector: {:?}”, v);
// drop(v); // oh no!
handle.join().unwrap();
Rust infers how to capture `v`, and because `println!` only needs a reference to `v`, the closure tries to borrow `v`. However, there’s a problem: Rust can’t tell how long the spawned thread will run, so it doesn’t know if the reference to `v` will always be valid . So, we can use `move` to solve this problem. However, this also means we cannot use `v` anymore after calling `spawn`!
## Using Message Passing to Transfer Data Between Threads
Here’s the idea in a slogan from the [Go language documentation](https://golang.org/doc/effective_go.html#concurrency): “Do not communicate by sharing memory; instead, share memory by communicating.”
One major tool Rust has for accomplishing message-sending concurrency is the *channel*, a programming concept that Rust’s standard library provides an implementation of. You can imagine a channel in programming as being like a channel of water, such as a stream or a river. If you put something like a rubber duck or boat into a stream, it will travel downstream to the end of the waterway.
We create a new channel using the `mpsc::channel` function; `mpsc` stands for *multiple producer, single consumer*.
In short, the way Rust’s standard library implements channels means a channel can have multiple sending ends that produce values but only one receiving end that consumes those values. Imagine multiple streams flowing together into one big river: everything sent down any of the streams will end up in one river at the end. We’ll start with a single producer for now, but we’ll add multiple producers when we get this example working.
The `mpsc::channel` function returns a tuple, the first element of which is the sending end and the second element is the receiving end. The abbreviations tx and rx are traditionally used in many fields for transmitter and receiver respectively, so we name our variables as such to indicate each end. We’re using a let statement with a pattern that destructures the tuples.
Let’s move the transmitting end into a spawned thread and have it send one string so the spawned thread is communicating with the main thread. This is like putting a rubber duck in the river upstream or sending a chat message from one thread to another.
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
// (transmitter, receiver)
let (tx, rx) = mpsc::channel();
// clone the transmitter so that each child thread gets one
let tx1 = tx.clone();
thread::spawn(move || {
let vals = vec![
String::from(“one rubber duck in river 1”),
String::from(“two rubber ducks in river 1”),
String::from(“three rubber ducks in river 1”),
String::from(“four rubber ducks in river 1”),
// putting a rubber duck in the river upstream
for val in vals {
// send() returns a Result
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
// send takes the ownership of val
// println!(“{}”,val); ERROR!
thread::spawn(move || {
let vals = vec![
String::from(“one rubber duck in river 2”),
String::from(“two rubber ducks in river 2”),
String::from(“three rubber ducks in river 2”),
String::from(“four rubber ducks in river 2”),
// putting a rubber duck in the river upstream
for val in vals {
tx1.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
// println!(“{}”,val); ERROR!
// rx blocks the main thread!
// it implements the Iterator trait
for received in rx {
println!(“Got: {}”, received);
Notice that the spawned thread needs to own the transmitting end of the channel to be able to send messages through the channel. The `send` tries to the value to the receiver end and returns a `Result
## Shared-State Concurrency
In a way, channels in any programming language are similar to single ownership, because once you transfer a value down a channel, you should *no longer use* that value. Shared memory concurrency is like *multiple ownership*: multiple threads can access the same memory location at the same time.
What does this remind you? `Rc` right? The value stored in a `Rc` can have multiple ownership.
### Using Mutexes to Allow Access to Data from One Thread at a Time
The secret is `Mutex` smart pointer. Mutex is an abbreviation for *mutual exclusion*, as in, a mutex allows only one thread to access some data at any given time. Like `RefCell`, `Mutex` provides interior mutability.
To access the data in a mutex, a thread *must first signal that it wants access* by asking to acquire the mutex’s lock. The lock is a data structure that is part of the mutex that keeps track of who currently has exclusive access to the data. Therefore, the mutex is described as guarding the data it holds via the locking system. When using mutexes, you need to remember that
* You must attempt to acquire the lock before using the data.
* When you’re done with the data that the mutex guards, you must unlock the data so other threads can acquire the lock.
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
let mut num = m.lock().unwrap();
} \\ unlock
println!(“m = {:?}”, m);
As with many types, we create a `Mutex
The call to `lock` would fail if another thread holding the lock panicked. In that case, no one would ever be able to get the lock, so we’ve chosen to `unwrap` and have this thread panic if we’re in that situation.
After we’ve acquired the lock, we can treat the return value, named num in this case, as a mutable reference to the data inside. The type system ensures that we acquire a lock before using the value in m: `Mutex
As you might suspect, `Mutex
After dropping the lock, we can print the mutex value and see that we were able to change the inner `i32` to 6.
### Sharing a `Mutex
As `move` will take the ownership of `Mutex` if we use it directly in `spawn`. So if we want to share `Mutex
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
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!(“Result: {}”, *counter.lock().unwrap());
### Extensible Concurrency with the Sync and Send Traits
As we said before, the behavior the the types in Rust are defined by traits. So, what are the traits that make `Arc
– The `Send` marker trait indicates that ownership of values of the type implementing Send can be transferred between threads. Almost every Rust type is Send, but there are some exceptions, including `Rc
– The `Sync` marker trait indicates that it is safe for the type implementing Sync to be referenced from multiple threads. Primitive types are Sync, and types composed entirely of types that are Sync are also Sync. `Rc
Implementing `Sync` and `Send` needs unsafe rust. If you are interesting in doing it, [The Rustonomicon](https://doc.rust-lang.org/nomicon/index.html) is a good resource.
# Marker types
Marker types don’t have any methods to implement. They’re just useful for enforcing invariants related to concurrency. `Send` and `Sync` are marker traits.
## Panthom types
In rust, if you define a generic type, for exmaple when defining `Slice<'a, T:'a>`, you have to use `’a` and `T` somewhere in your type, such as specifying the type of the fields.
As a smart coder, if you know that the underlying data is only valid for the lifetime `’a`, so Slice should not outlive `’a`, but there is no uses of the lifetime `’a` in your struct, you can tell the compiler to act as if the `Slice` struct contained a reference `&’a T`:
use std::marker::PhantomData;
struct Slice<'a, T: 'a> {
start: *const T,
end: *const T,
phantom: PhantomData<&'a T>,
fn borrow_vec
let ptr = vec.as_ptr();
start: ptr,
end: unsafe { ptr.add(vec.len()) },
phantom: PhantomData,
If you define a lifetime parameter `’a` in your type, you need to use it
### Unused type parameters
A phantom type parameter is one that doesn’t show up at runtime, but is checked statically (and only) at compile time.
Data types can use extra generic type parameters to act as markers or to perform type checking at compile time. These extra parameters hold no storage values, and have no runtime behavior.
use std::marker::PhantomData;
// A phantom tuple struct which is generic over `A` with hidden parameter `B`.
#[derive(PartialEq)] // Allow equality test for this type.
struct PhantomTuple(A,PhantomData);
// A phantom type struct which is generic over `A` with hidden parameter `B`.
#[derive(PartialEq)] // Allow equality test for this type.
struct PhantomStruct { first: A, phantom: PhantomData }
// Note: Storage is allocated for generic type `A`, but not for `B`.
// Therefore, `B` cannot be used in computations.
fn main() {
// Here, `f32` and `f64` are the hidden parameters.
// PhantomTuple type specified as `
let _tuple1: PhantomTuple
// PhantomTuple type specified as `
let _tuple2: PhantomTuple
// Type specified as `
let _struct1: PhantomStruct
first: ‘Q’,
phantom: PhantomData,
// Type specified as `
let _struct2: PhantomStruct
first: ‘Q’,
phantom: PhantomData,
// Compile-time Error! Type mismatch so these cannot be compared:
//println!(“_tuple1 == _tuple2 yields: {}”,
// _tuple1 == _tuple2);
// Compile-time Error! Type mismatch so these cannot be compared:
//println!(“_struct1 == _struct2 yields: {}”,
// _struct1 == _struct2);
#### An example of using Panthom type
When adding two numbers, we need to make sure that those two numbers are in the same type. If we want to implement the add trait for our own type, we also want to do that.
The `Add` trait is defined as
// This construction would impose: `Self + RHS = Output`
// where RHS defaults to Self if not specified in the implementation.
pub trait Add
type Output;
fn add(self, rhs: RHS) -> Self::Output;
// `Output` must be `T` so that `T + T = T`.
impl Add for T {
type Output = T;
but what if we have a struct and want to implement the `Add` trait for it, and at the same time we want to ensure that the type of the two sides match?
use std::ops::Add;
use std::marker::PhantomData;
/// Create void enumerations to define unit types.
#[derive(Debug, Clone, Copy)]
enum Inch {}
#[derive(Debug, Clone, Copy)]
enum Mm {}
/// `Length` is a type with phantom type parameter `Unit`,
/// and is not generic over the length type (that is `f64`).
/// `f64` already implements the `Clone` and `Copy` traits.
#[derive(Debug, Clone, Copy)]
struct Length
/// The `Add` trait defines the behavior of the `+` operator.
impl
type Output = Length
// add() returns a new `Length` struct containing the sum.
fn add(self, rhs: Length
// `+` calls the `Add` implementation for `f64`.
Length(self.0 + rhs.0, PhantomData)
fn main() {
// Specifies `one_foot` to have phantom type parameter `Inch`.
let one_foot: Length
// `one_meter` has phantom type parameter `Mm`.
let one_meter: Length
// `+` calls the `add()` method we implemented for `Length
// Since `Length` implements `Copy`, `add()` does not consume
// `one_foot` and `one_meter` but copies them into `self` and `rhs`.
let two_feet = one_foot + one_foot;
let two_meters = one_meter + one_meter;
// Addition works.
println!(“one foot + one_foot = {:?} in”, two_feet.0);
println!(“one meter + one_meter = {:?} mm”, two_meters.0);
// Nonsensical operations fail as they should:
// Compile-time Error: type mismatch.
//let one_feter = one_foot + one_meter;
Adding a field of type `PhantomData
If your struct does not in fact own the data of type `T`, it is better to use a reference type, like `PhantomData<&'a T>` (ideally) or `PhantomData<*const T>` (if no lifetime applies), so as not to indicate ownership.
程序代写 CS代考 加微信: powcoder QQ: 1823890830 Email: powcoder@163.com