Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
317 additions
and
203 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// These two functions have essentially identical signatures | ||
// because the compiler implicitly adds the lifetimes to | ||
// the first. | ||
fn elide_input(x: &i32) { | ||
println!("`elide_input`: {}", x) | ||
} | ||
fn annotated_input<'a>(x: &'a i32) { | ||
println!("`annotated_input`: {}", x) | ||
} | ||
|
||
// Similarly, lifetimes are added implicitly to the first. | ||
fn elide_pass(x: &i32) -> &i32 { x } | ||
fn annotated_pass<'a>(x: &'a i32) -> &'a i32 { x } | ||
|
||
fn main() { | ||
let x = 3; | ||
|
||
elide_input(&x); | ||
annotated_input(&x); | ||
|
||
println!("`elide_pass`: {}", elide_pass(&x)); | ||
println!("`annotated_pass`: {}", annotated_pass(&x)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
Some lifetime patterns are overwelmingly common and so they may be elide | ||
(dropped) and the borrow checker will implicitly add them. Elision exists | ||
solely because these patterns are common; saving typing and easing legibility. | ||
|
||
This section is brief and not comprehensive. See [lifetime elision][elision] | ||
in the book for a more comprehensive treatment. | ||
|
||
{elision.play} | ||
|
||
### See also: | ||
|
||
[elision][elision] | ||
|
||
[elision]: http://doc.rust-lang.org/book/lifetimes.html#lifetime-elision |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,34 @@ | ||
struct Book { | ||
// `String` is a heap allocated string | ||
title: String, | ||
author: String, | ||
year: i32, | ||
// `print_refs` takes two references to `i32` which have different | ||
// lifetimes `'a` and `'b`. These two lifetimes must both outlive | ||
// the function `print_refs`. | ||
fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) { | ||
println!("x is {} and y is {}", x, y); | ||
|
||
// Suppose this `drop` worked. Then `print_refs` would outlive | ||
// `x` and `four` from `main()` would refer to erased data. | ||
// These two things go hand in hand and so both are banned. | ||
// | ||
//drop(*x); | ||
// ERROR: cannot move out of borrowed content | ||
} | ||
|
||
fn get_title<'a>(book: &'a Book) -> &'a str { | ||
&book.title | ||
// `failed_borrow` takes no references and returns nothing but has | ||
// a single lifetime `'a` which must outlive the function. | ||
fn failed_borrow<'a>() { | ||
let _x = 12; | ||
|
||
// Attempting to use the lifetime `'a` as an explicit type | ||
// annotation inside the function will fail because the | ||
// lifetime `'a` doesn't match the lifetime that `y` has. | ||
// `y` starts inside `failed_borrow` and so it is smaller. | ||
// | ||
//let y: &'a i32 = &_x; | ||
// ERROR: `_x` does not live long enough | ||
} | ||
|
||
fn main() { | ||
let geb = Book { | ||
// construct a `String` from a reference to a string (`&'static str`) | ||
// by copying of the data | ||
author: "Douglas Hofstadter".to_string(), | ||
title: "Godel, Escher, Bach".to_string(), | ||
year: 1979, | ||
}; | ||
|
||
let title: &str = get_title(&geb); | ||
let (four, nine) = (4, 9); | ||
|
||
println!("I just read {}", title); | ||
print_refs(&four, &nine); | ||
failed_borrow(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,30 @@ | ||
When writing functions that return references, lifetimes must be explicitly | ||
annotated. These functions are generic and we must tell the compiler what is | ||
the relationship between the lifetimes of the objects that appear in the | ||
arguments and the output. | ||
The borrow checker utilizes explicit lifetime annotation to reason about | ||
how long references should be valid. Failure to annotate lifetimes[^1] is akin | ||
to banning the borrow checker from validating borrows and so accordingly, | ||
annotation is mandatory. | ||
|
||
Let's illustrate with an example: we want a function that returns a reference | ||
to the title field of a Book struct. The most generic function that we could | ||
write would look like this: | ||
Since lifetimes *currently* have no explicit type or name associated with them, | ||
usage will require generics (similar to [closures][anonymity]). Somewhat | ||
peculiarly, lifetime annotation has a second additional meaning. `foo<'a, 'b>` | ||
states: | ||
|
||
1. `'a` and `'b` will represent names for lifetimes with non-specifiable | ||
(generic) types. | ||
2. The lifetime of `foo` may not exceed either lifetimes `'a` or `'b`. | ||
|
||
Explicit annotation of a type has the form: `&'a T` where `'a` has already | ||
been introduced. | ||
|
||
{explicit.play} | ||
|
||
The compiler can't tell how `'a` and `'b` are related, so we must supply this | ||
information. The answer here is that `'a = 'b`, the reason is that the title | ||
field will be destroyed when the book gets destroyed (same way with the | ||
creation time), therefore the title field has the same lifetime as the book. | ||
[^1]: [elision][elision] implicitly annotates lifetimes and so is different. | ||
|
||
### See also: | ||
|
||
[generics][generics] and [closures][closures] | ||
|
||
|
||
[anonymity]: /fn/closures/anonymity.html | ||
[closures]: /fn/closures.html | ||
[elision]: http://doc.rust-lang.org/nightly/book/lifetimes.html#lifetime-elision | ||
[generics]: /generics.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,50 +1,46 @@ | ||
#[derive(Debug)] | ||
struct Triplet { | ||
one: i32, | ||
two: i32, | ||
three: i32, | ||
// One input reference with lifetime `'a` which must live | ||
// longer than the function. This restricts the function | ||
// from ever being able to consume the input. | ||
fn print_one<'a>(x: &'a i32) { | ||
println!("`print_one`: x is {}", x); | ||
} | ||
|
||
impl Triplet { | ||
// First attempt: No explicit lifetimes | ||
// The compiler infers that the field and the struct have the same lifetime | ||
fn mut_one(&mut self) -> &mut i32 { | ||
&mut self.one | ||
} | ||
|
||
// Second attempt: We explicitly annotate the lifetimes on all the | ||
// references | ||
// Error! The compiler doesn't know what is the relationship between the | ||
// lifetime `structure` and the lifetime `field` | ||
//fn mut_two<'structure, 'field>(&'structure mut self) -> &'field mut i32 { | ||
//&mut self.two | ||
//} | ||
// TODO ^ Try uncommenting this method | ||
|
||
// Third attempt: We think! What is the relationship between the lifetimes? | ||
// Clearly `'field` *can't* outlive `'structure`, because the field will be | ||
// destroyed when the struct gets destroyed | ||
// If the fields get destroyed along with the struct, then that means that | ||
// both the struct and its field have the same lifetime! | ||
// Ok, so we need to tell the compiler that `'structure` = `'field` | ||
// We can use a shorter name for the lifetime, it's common to use a single | ||
// letter lifetime, let's use `'s`, because it's the first letter of | ||
// structure | ||
fn mut_three<'s>(&'s mut self) -> &'s mut i32 { | ||
&mut self.three | ||
} | ||
// Mutable references are possible with lifetimes as well. | ||
fn add_one<'a>(x: &'a mut i32) { | ||
*x += 1; | ||
} | ||
|
||
fn main() { | ||
let mut triplet = Triplet { one: 1, two: 2, three: 3 }; | ||
|
||
println!("Before: {:?}", triplet); | ||
// Multiple elements with different lifetimes. This would | ||
// be equally acceptable if both references had the same | ||
// lifetime `'a`. | ||
fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) { | ||
println!("`print_multi`: x is {}, y is {}", x, y); | ||
} | ||
|
||
*triplet.mut_one() = 0; | ||
println!("After: {:?}", triplet); | ||
// This is invalid. An `i32` would be created, a reference | ||
// would be created, then immediately the data would be | ||
// dropped leaving a reference to invalid data to be returned. | ||
// | ||
// The reason the problem is caught is because of the restriction | ||
// `<'a>` imposes: `'a` must live longer than the function. | ||
//fn invalid_output<'a>() -> &'a i32 { &7 } | ||
|
||
// Use mutable reference to modify the original struct | ||
*triplet.mut_three() = 0; | ||
// While returning references without input is banned, returning | ||
// references that have been passed in are perfectly acceptable. | ||
// One restriction is the correct lifetime must be returned. | ||
fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 { x } | ||
|
||
println!("After: {:?}", triplet); | ||
fn main() { | ||
let x = 7; | ||
let y = 9; | ||
|
||
print_one(&x); | ||
print_multi(&x, &y); | ||
|
||
let z = pass_x(&x, &y); | ||
print_one(z); | ||
|
||
let mut t = 3; | ||
add_one(&mut t); | ||
print_one(&t); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,15 @@ | ||
Explicit lifetimes are necessary when functions return references. Our case | ||
study will be returning a reference to one of the fields of a struct. | ||
Functions with lifetimes have a few different valid forms. Ignoring | ||
[elision][elision] for the time being, the rules for function parameters are: | ||
|
||
* any reference *must* have an annotated lifetime. | ||
* any reference being returned *must* have the same lifetime as an input. | ||
|
||
{fn.play} | ||
|
||
### See also: | ||
|
||
[functions][fn] | ||
|
||
|
||
[elision]: http://doc.rust-lang.org/nightly/book/lifetimes.html#lifetime-elision | ||
[fn]: /fn.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,13 @@ | ||
The compiler enforces valid borrowing using its borrow checker. To accomplish | ||
this, it keeps track of the scope of blocks. | ||
A *lifetime* is a construct the compiler (also called the borrow checker) | ||
uses to ensure all borrows are valid. Specifically, the lifetime refers to | ||
the span which starts when the variable is created and ends when the variable | ||
is destroyed. Borrowing (via `&` for example) also creates new lifetimes. | ||
|
||
The lifetime of an object starts when the object is created and ends when it | ||
goes out of scope (i.e. it gets destroyed, because of the RAII discipline). | ||
A borrow is valid as long as the borrow ends before (inside) the lender is | ||
destroyed. As you can see below, the lifetime of a variable is directly related | ||
to the scope in which it was created: | ||
|
||
A lifetime looks like this: `'burrito`, which reads as: "the lifetime burrito". | ||
{lifetime.play} | ||
|
||
All references actually have a type signature of the form `&'a T`, where | ||
`'a` is the lifetime of the *referenced* object. The compiler takes care of | ||
inserting the lifetime part `'a` so we can simply type annotate references with | ||
`&T`. | ||
|
||
For example: | ||
|
||
```rust | ||
let integer: int = 5; | ||
let ref_to_int: &int = &integer; | ||
``` | ||
|
||
* `integer` has lifetime `'i` (it could be any other name, like `'foo`) | ||
* `ref_to_int` has lifetime `'r` (references also have lifetimes!) | ||
* `ref_to_int` type signature actually is `&'i int` (the compiler inserts the | ||
`'i` for us) | ||
* The type signature `&'i int` reads as: | ||
* `&`: reference to an | ||
* `int`: integer with | ||
* `'i`: lifetime `i` (`i` is the lifetime of `integer`!) | ||
|
||
Because the compiler keeps track of the lifetime of referenced objects in the | ||
type system, it can avoid several memory bugs. | ||
|
||
Haven't grokked what a lifetime is yet? Don't dismay! See the next page. | ||
You may have noted that no names or types are assigned to label lifetimes. | ||
This restricts how lifetimes will be able to be used as we will see. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Lifetimes are annotated with a lines denoting | ||
// when each variable is created and destroyed: | ||
fn main() { | ||
let i = 3; // Lifetime for `i` starts. ───────┐ | ||
// │ | ||
{ // │ | ||
let borrow = &i; // Borrow starts. ──────┐│ | ||
// ││ | ||
println!("Borrowed `i`: {}", borrow); // ││ | ||
} // Borrow ends. ───────────────────────────┘│ | ||
// │ | ||
} // Lifetime ends. ────────────────────────────┘ |
Oops, something went wrong.