1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-12-23 23:12:52 +02:00

Publish Comprehensive Rust 🦀

This commit is contained in:
Martin Geisler
2022-12-21 16:36:30 +01:00
commit c212a473ba
252 changed files with 8047 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
# Channels
Rust channels have two parts: a `Sender<T>` and a `Receiver<T>`. The two parts
are connected via the channel, but you only see the end-points.
```rust,editable
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
tx.send(10).unwrap();
tx.send(20).unwrap();
println!("Received: {:?}", rx.recv());
println!("Received: {:?}", rx.recv());
let tx2 = tx.clone();
tx2.send(30).unwrap();
println!("Received: {:?}", rx.recv());
}
```

View File

@@ -0,0 +1,27 @@
# Bounded Channels
Bounded and synchronous channels make `send` block the current thread:
```rust,editable
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::sync_channel(3);
thread::spawn(move || {
let thread_id = thread::current().id();
for i in 1..10 {
tx.send(format!("Message {i}")).unwrap();
println!("{thread_id:?}: sent Message {i}");
}
println!("{thread_id:?}: done");
});
thread::sleep(Duration::from_millis(100));
for msg in rx.iter() {
println!("Main: got {}", msg);
}
}
```

View File

@@ -0,0 +1,27 @@
# Unbounded Channels
You get an unbounded and asynchronous channel with `mpsc::channel()`:
```rust,editable
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let thread_id = thread::current().id();
for i in 1..10 {
tx.send(format!("Message {i}")).unwrap();
println!("{thread_id:?}: sent Message {i}");
}
println!("{thread_id:?}: done");
});
thread::sleep(Duration::from_millis(100));
for msg in rx.iter() {
println!("Main: got {}", msg);
}
}
```

View File

@@ -0,0 +1,33 @@
# Scoped Threads
Normal threads cannot borrow from their environment:
```rust,editable,compile_fail
use std::thread;
fn main() {
let s = String::from("Hello");
thread::spawn(|| {
println!("Length: {}", s.len());
});
}
```
However, you can use a [scoped thread][1] for this:
```rust,editable
use std::thread;
fn main() {
let s = String::from("Hello");
thread::scope(|scope| {
scope.spawn(|| {
println!("Length: {}", s.len());
});
});
}
```
[1]: https://doc.rust-lang.org/std/thread/fn.scope.html

View File

@@ -0,0 +1,11 @@
# `Send` and `Sync`
How does Rust know to forbid shared access across thread? The answer is in two traits:
* [`Send`][1]: a type `T` is `Send` if it is safe to move a `T` across a thread
boundary.
* [`Sync`][2]: a type `T` is `Sync` if it is safe to move a `&T` across a thread
boundary.
[1]: https://doc.rust-lang.org/std/marker/trait.Send.html
[2]: https://doc.rust-lang.org/std/marker/trait.Sync.html

View File

@@ -0,0 +1,41 @@
# Examples
## `Send + Sync`
Most types you come across are `Send + Sync`:
* `i8`, `f32`, `bool`, `char`, `&str`, ...
* `(T1, T2)`, `[T; N]`, `&[T]`, `struct { x: T }`, ...
* `String`, `Option<T>`, `Vec<T>`, `Box<T>`, ...
* `Arc<T>`: Explicitly thread-safe via atomic reference count.
* `Mutex<T>`: Explicitly thread-safe via internal locking.
* `AtomicBool`, `AtomicU8`, ...: Uses special atomic instructions.
The generic types are typically `Send + Sync` when the type parameters are
`Send + Sync`.
## `Send + !Sync`
These types can be moved to other threads, but they're not thread-safe.
Typically because of interior mutability:
* `mpsc::Sender<T>`
* `mpsc::Receiver<T>`
* `Cell<T>`
* `RefCell<T>`
## `!Send + Sync`
These types are thread-safe, but they cannot be moved to another thread:
* `MutexGuard<T>`: Uses OS level primitives which must be deallocated on the
thread which created them.
## `!Send + !Sync`
These types are not thread-safe and cannot be moved to other threads:
* `Rc<T>`: each `Rc<T>` has a reference to an `RcBox<T>`, which contains a
non-atomic reference count.
* `*const T`, `*mut T`: Rust that there are special lifetime considerations for the
pointer.

View File

@@ -0,0 +1,9 @@
# `Send`
> A type `T` is [`Send`][1] if it is safe to move a `T` value to another thread.
The effect of moving ownership to another thread is that _destructors_ will run
in that thread. So the question is when you can allocate a value in one thread
and deallocate it in another.
[1]: https://doc.rust-lang.org/std/marker/trait.Send.html

View File

@@ -0,0 +1,10 @@
# `Sync`
> A type `T` is [`Sync`][1] if it is safe to access a `T` value from multiple
> threads at the same time.
More precisely, the definitions is
> `T` is `Sync` if and only if `&T` is `Send`
[1]: https://doc.rust-lang.org/std/marker/trait.Sync.html

View File

@@ -0,0 +1,11 @@
# Shared State
Rust uses the type system to enforce synchronization of shared data. This is
primarily done via two types:
* [`Arc<T>`][1], atomic reference counted `T`: handled sharing between threads and
takes care to deallocate `T` when the last thread exists,
* [`Mutex<T>`][2]: ensures mutual exclusion for to the `T` value.
[1]: https://doc.rust-lang.org/std/sync/struct.Arc.html
[2]: https://doc.rust-lang.org/std/sync/struct.Mutex.html

View File

@@ -0,0 +1,25 @@
# `Arc`
[`Arc<T>`][1] allows shared read-only access via its `clone` method:
```rust,editable
use std::thread;
use std::sync::Arc;
fn main() {
let v = Arc::new(vec![10, 20, 30]);
let mut handles = Vec::new();
for _ in 1..5 {
let v = v.clone();
handles.push(thread::spawn(move || {
let thread_id = thread::current().id();
println!("{thread_id:?}: {v:?}");
}));
}
handles.into_iter().for_each(|h| h.join().unwrap());
println!("v: {v:?}");
}
```
[1]: https://doc.rust-lang.org/std/sync/struct.Arc.html

View File

@@ -0,0 +1,19 @@
# Example
Let us see `Arc` and `Mutex` in action:
```rust,editable,compile_fail
use std::thread;
// use std::sync::{Arc, Mutex};
fn main() {
let mut v = vec![10, 20, 30];
let handle = thread::spawn(|| {
v.push(10);
});
v.push(1000);
handle.join().unwrap();
println!("v: {v:?}");
}
```

View File

@@ -0,0 +1,28 @@
# `Mutex`
[`Mutex<T>`][1] ensures mutual exclusion _and_ allows mutable access to `T`
behind a read-only interface:
```rust,editable
use std::sync::Mutex;
fn main() {
let v: Mutex<Vec<i32>> = Mutex::new(vec![10, 20, 30]);
println!("v: {:?}", v.lock().unwrap());
{
let v: &Mutex<Vec<i32>> = &v;
let mut guard = v.lock().unwrap();
guard.push(40);
}
println!("v: {:?}", v.lock().unwrap());
}
```
Notice how we have a [`impl<T: Send> Sync for Mutex<T>`][2] blanket
implementation.
[1]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
[2]: https://doc.rust-lang.org/std/sync/struct.Mutex.html#impl-Sync-for-Mutex%3CT%3E
[3]: https://doc.rust-lang.org/std/sync/struct.Arc.html

View File

@@ -0,0 +1,26 @@
# Threads
Rust threads work similarly to threads in other languages:
```rust,editable
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 1..10 {
println!("Count in thread: {i}!");
thread::sleep(Duration::from_millis(5));
}
});
for i in 1..5 {
println!("Main thread: {i}");
thread::sleep(Duration::from_millis(5));
}
}
```
* Threads are all daemon threads, the main thread does not wait for them.
* Thread panics are independent of each other.
* Panics can carry a payload, which can be unpacked with `downcast_ref`.