100 exercises to learn Rust
This commit is contained in:
112
book/src/07_threads/02_static.md
Normal file
112
book/src/07_threads/02_static.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# `'static`
|
||||
|
||||
If you tried to borrow a slice from the vector in the previous exercise,
|
||||
you probably got a compiler error that looks something like this:
|
||||
|
||||
```text
|
||||
error[E0597]: `v` does not live long enough
|
||||
|
|
||||
11 | pub fn sum(v: Vec<i32>) -> i32 {
|
||||
| - binding `v` declared here
|
||||
...
|
||||
15 | let right = &v[split_point..];
|
||||
| ^ borrowed value does not live long enough
|
||||
16 | let left_handle = thread::spawn(move || left.iter().sum::<i32>());
|
||||
| ------------------------------------------------
|
||||
argument requires that `v` is borrowed for `'static`
|
||||
19 | }
|
||||
| - `v` dropped here while still borrowed
|
||||
```
|
||||
|
||||
`argument requires that v is borrowed for 'static`, what does that mean?
|
||||
|
||||
The `'static` lifetime is a special lifetime in Rust.
|
||||
It means that the value will be valid for the entire duration of the program.
|
||||
|
||||
## Detached threads
|
||||
|
||||
A thread launched via `thread::spawn` can **outlive** the thread that spawned it.
|
||||
For example:
|
||||
|
||||
```rust
|
||||
use std::thread;
|
||||
|
||||
fn f() {
|
||||
thread::spawn(|| {
|
||||
thread::spawn(|| {
|
||||
loop {
|
||||
thread::sleep(std::time::Duration::from_secs(1));
|
||||
println!("Hello from the detached thread!");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the first spawned thread will in turn spawn
|
||||
a child thread that prints a message every second.
|
||||
The first thread will then finish and exit. When that happens,
|
||||
its child thread will **continue running** for as long as the
|
||||
overall process is running.
|
||||
In Rust's lingo, we say that the child thread has **outlived**
|
||||
its parent.
|
||||
|
||||
## `'static` lifetime
|
||||
|
||||
Since a spawned thread can:
|
||||
|
||||
- outlive the thread that spawned it (its parent thread)
|
||||
- run until the program exits
|
||||
|
||||
it must not borrow any values that might be dropped before the program exits;
|
||||
violating this constraint would expose us to a use-after-free bug.
|
||||
That's why `std::thread::spawn`'s signature requires that the closure passed to it
|
||||
has the `'static` lifetime:
|
||||
|
||||
```rust
|
||||
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
where
|
||||
F: FnOnce() -> T + Send + 'static,
|
||||
T: Send + 'static
|
||||
{
|
||||
// [..]
|
||||
}
|
||||
```
|
||||
|
||||
## `'static` is not (just) about references
|
||||
|
||||
All values in Rust have a lifetime, not just references.
|
||||
|
||||
In particular, a type that owns its data (like a `Vec` or a `String`)
|
||||
satisfies the `'static` constraint: if you own it, you can keep working with it
|
||||
for as long as you want, even after the function that originally created it
|
||||
has returned.
|
||||
|
||||
You can thus interpret `'static` as a way to say:
|
||||
|
||||
- Give me an owned value
|
||||
- Give me a reference that's valid for the entire duration of the program
|
||||
|
||||
The first approach is how you solved the issue in the previous exercise:
|
||||
by allocating new vectors to hold the left and right parts of the original vector,
|
||||
which were then moved into the spawned threads.
|
||||
|
||||
## `'static` references
|
||||
|
||||
Let's talk about the second case, references that are valid for the entire
|
||||
duration of the program.
|
||||
|
||||
### Static data
|
||||
|
||||
The most common case is a reference to **static data**, such as string literals:
|
||||
|
||||
```rust
|
||||
let s: &'static str = "Hello world!";
|
||||
```
|
||||
|
||||
Since string literals are known at compile-time, Rust's stores them in a memory
|
||||
region known as ***. *** is part of the executable itself: there is no risk of it
|
||||
being freed during program execution.
|
||||
All references pointing to that region will therefore be valid for as long as
|
||||
the program runs; they satisfy the `'static` contract.
|
||||
|
||||
Reference in New Issue
Block a user