mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-03-30 17:28:20 +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)
|
- [Lifetimes](lifetimes.md)
|
||||||
- [Lifetime Annotations](lifetimes/lifetime-annotations.md)
|
- [Lifetime Annotations](lifetimes/lifetime-annotations.md)
|
||||||
- [Lifetime Elision](lifetimes/lifetime-elision.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)
|
- [Exercise: Protobuf Parsing](lifetimes/exercise.md)
|
||||||
- [Solution](lifetimes/solution.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
|
`'a` is a typical default name. Read `&'a Point` as "a borrowed `Point` which is
|
||||||
valid for at least the lifetime `a`".
|
valid for at least the lifetime `a`".
|
||||||
|
|
||||||
Lifetimes are always inferred by the compiler: you cannot assign a lifetime
|
Only ownership, not lifetime annotations, control when values are destroyed and
|
||||||
yourself. Explicit lifetime annotations create constraints where there is
|
determine the concrete lifetime of a given value. The borrow checker just
|
||||||
ambiguity; the compiler verifies that there is a valid solution.
|
validates that borrows never extend beyond the concrete lifetime of the value.
|
||||||
|
|
||||||
Lifetimes become more complicated when considering passing values to and
|
Explicit lifetime annotations, like types, are required on function signatures
|
||||||
returning values from functions.
|
(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
|
<!-- The multi-line formatting by rustfmt in left_most is apparently
|
||||||
intentional: https://github.com/rust-lang/rustfmt/issues/1908 -->
|
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 {
|
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
|
This says there is some lifetime `'a` which both `p1` and `p2` outlive, and
|
||||||
at least `'a`.
|
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>
|
</details>
|
||||||
|
@ -23,7 +23,7 @@ fn cab_distance(p1: &Point, p2: &Point) -> i32 {
|
|||||||
(p1.0 - p2.0).abs() + (p1.1 - p2.1).abs()
|
(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;
|
let mut nearest = None;
|
||||||
for p in points {
|
for p in points {
|
||||||
if let Some((_, nearest_dist)) = nearest {
|
if let Some((_, nearest_dist)) = nearest {
|
||||||
@ -40,7 +40,11 @@ fn nearest<'a>(points: &'a [Point], query: &Point) -> Option<&'a Point> {
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let points = &[Point(1, 0), Point(1, 0), Point(-1, 0), Point(0, -1)];
|
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.
|
In this example, `cab_distance` is trivially elided.
|
||||||
|
|
||||||
The `nearest` function provides another example of a function with multiple
|
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:
|
Try adjusting the signature to "lie" about the lifetimes returned:
|
||||||
|
|
||||||
```rust,ignore
|
```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
|
This won't compile, demonstrating that the annotations are checked for validity
|
||||||
|
Loading…
x
Reference in New Issue
Block a user