You've already forked comprehensive-rust
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:
23
src/concurrency/channels.md
Normal file
23
src/concurrency/channels.md
Normal 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());
|
||||
}
|
||||
```
|
||||
27
src/concurrency/channels/bounded.md
Normal file
27
src/concurrency/channels/bounded.md
Normal 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);
|
||||
}
|
||||
}
|
||||
```
|
||||
27
src/concurrency/channels/unbounded.md
Normal file
27
src/concurrency/channels/unbounded.md
Normal 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);
|
||||
}
|
||||
}
|
||||
```
|
||||
33
src/concurrency/scoped-threads.md
Normal file
33
src/concurrency/scoped-threads.md
Normal 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
|
||||
11
src/concurrency/send-sync.md
Normal file
11
src/concurrency/send-sync.md
Normal 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
|
||||
41
src/concurrency/send-sync/examples.md
Normal file
41
src/concurrency/send-sync/examples.md
Normal 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.
|
||||
9
src/concurrency/send-sync/send.md
Normal file
9
src/concurrency/send-sync/send.md
Normal 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
|
||||
10
src/concurrency/send-sync/sync.md
Normal file
10
src/concurrency/send-sync/sync.md
Normal 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
|
||||
11
src/concurrency/shared_state.md
Normal file
11
src/concurrency/shared_state.md
Normal 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
|
||||
25
src/concurrency/shared_state/arc.md
Normal file
25
src/concurrency/shared_state/arc.md
Normal 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
|
||||
19
src/concurrency/shared_state/example.md
Normal file
19
src/concurrency/shared_state/example.md
Normal 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:?}");
|
||||
}
|
||||
```
|
||||
28
src/concurrency/shared_state/mutex.md
Normal file
28
src/concurrency/shared_state/mutex.md
Normal 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
|
||||
26
src/concurrency/threads.md
Normal file
26
src/concurrency/threads.md
Normal 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`.
|
||||
Reference in New Issue
Block a user