mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-27 16:25:43 +02:00
Improve explanation of lifetimes (#2584)
This approach seems to balance formalism with understanding. That is, it doesn't mention contravariance, but suggests that lifetime annotations in parameters and return values mean "opposite" things. It also leverages the understanding that types must be specified in function signatures, and are used to check types in the function body and at call sites.
This commit is contained in:
parent
715a23e7a8
commit
fd7cb04a62
@ -171,7 +171,7 @@
|
||||
- [Lifetimes](lifetimes.md)
|
||||
- [Lifetime Annotations](lifetimes/lifetime-annotations.md)
|
||||
- [Lifetime Elision](lifetimes/lifetime-elision.md)
|
||||
- [Struct Lifetimes](lifetimes/struct-lifetimes.md)
|
||||
- [Lifetimes in Data Structures](lifetimes/struct-lifetimes.md)
|
||||
- [Exercise: Protobuf Parsing](lifetimes/exercise.md)
|
||||
- [Solution](lifetimes/solution.md)
|
||||
|
||||
|
@ -12,12 +12,14 @@ also be explicit: `&'a Point`, `&'document str`. Lifetimes start with `'` and
|
||||
`'a` is a typical default name. Read `&'a Point` as "a borrowed `Point` which is
|
||||
valid for at least the lifetime `a`".
|
||||
|
||||
Lifetimes are always inferred by the compiler: you cannot assign a lifetime
|
||||
yourself. Explicit lifetime annotations create constraints where there is
|
||||
ambiguity; the compiler verifies that there is a valid solution.
|
||||
Only ownership, not lifetime annotations, control when values are destroyed and
|
||||
determine the concrete lifetime of a given value. The borrow checker just
|
||||
validates that borrows never extend beyond the concrete lifetime of the value.
|
||||
|
||||
Lifetimes become more complicated when considering passing values to and
|
||||
returning values from functions.
|
||||
Explicit lifetime annotations, like types, are required on function signatures
|
||||
(but can be elided in common cases). These provide information for inference at
|
||||
callsites and within the function body, helping the borrow checker to do its
|
||||
job.
|
||||
|
||||
<!-- The multi-line formatting by rustfmt in left_most is apparently
|
||||
intentional: https://github.com/rust-lang/rustfmt/issues/1908 -->
|
||||
@ -56,9 +58,11 @@ Add `'a` appropriately to `left_most`:
|
||||
fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {
|
||||
```
|
||||
|
||||
This says, "given p1 and p2 which both outlive `'a`, the return value lives for
|
||||
at least `'a`.
|
||||
This says there is some lifetime `'a` which both `p1` and `p2` outlive, and
|
||||
which outlives the return value. The borrow checker verifies this within the
|
||||
function body, and uses this information in `main` to determine a lifetime for
|
||||
`p3`.
|
||||
|
||||
In common cases, lifetimes can be elided, as described on the next slide.
|
||||
Try dropping `p2` in `main` before printing `p3`.
|
||||
|
||||
</details>
|
||||
|
@ -23,7 +23,7 @@ fn cab_distance(p1: &Point, p2: &Point) -> i32 {
|
||||
(p1.0 - p2.0).abs() + (p1.1 - p2.1).abs()
|
||||
}
|
||||
|
||||
fn nearest<'a>(points: &'a [Point], query: &Point) -> Option<&'a Point> {
|
||||
fn find_nearest<'a>(points: &'a [Point], query: &Point) -> Option<&'a Point> {
|
||||
let mut nearest = None;
|
||||
for p in points {
|
||||
if let Some((_, nearest_dist)) = nearest {
|
||||
@ -40,7 +40,11 @@ fn nearest<'a>(points: &'a [Point], query: &Point) -> Option<&'a Point> {
|
||||
|
||||
fn main() {
|
||||
let points = &[Point(1, 0), Point(1, 0), Point(-1, 0), Point(0, -1)];
|
||||
println!("{:?}", nearest(points, &Point(0, 2)));
|
||||
let nearest = {
|
||||
let query = Point(0, 2);
|
||||
find_nearest(points, &Point(0, 2))
|
||||
};
|
||||
println!("{:?}", nearest);
|
||||
}
|
||||
```
|
||||
|
||||
@ -49,12 +53,13 @@ fn main() {
|
||||
In this example, `cab_distance` is trivially elided.
|
||||
|
||||
The `nearest` function provides another example of a function with multiple
|
||||
references in its arguments that requires explicit annotation.
|
||||
references in its arguments that requires explicit annotation. In `main`, the
|
||||
return value is allowed to outlive the query.
|
||||
|
||||
Try adjusting the signature to "lie" about the lifetimes returned:
|
||||
|
||||
```rust,ignore
|
||||
fn nearest<'a, 'q>(points: &'a [Point], query: &'q Point) -> Option<&'q Point> {
|
||||
fn find_nearest<'a, 'q>(points: &'a [Point], query: &'q Point) -> Option<&'q Point> {
|
||||
```
|
||||
|
||||
This won't compile, demonstrating that the annotations are checked for validity
|
||||
|
Loading…
x
Reference in New Issue
Block a user