| ## Validating References with Lifetimes |
| |
| Lifetimes are another kind of generic that we’ve already been using. Rather than |
| ensuring that a type has the behavior we want, lifetimes ensure that references |
| are valid as long as we need them to be. |
| |
| One detail we didn’t discuss in the |
| [“References and Borrowing”][references-and-borrowing]<!-- ignore --> section in |
| Chapter 4 is that every reference in Rust has a _lifetime_, which is the scope |
| for which that reference is valid. Most of the time, lifetimes are implicit and |
| inferred, just like most of the time, types are inferred. We must annotate types |
| only when multiple types are possible. In a similar way, we must annotate |
| lifetimes when the lifetimes of references could be related in a few different |
| ways. Rust requires us to annotate the relationships using generic lifetime |
| parameters to ensure the actual references used at runtime will definitely be |
| valid. |
| |
| Annotating lifetimes is not a concept most other programming languages have, so |
| this is going to feel unfamiliar. Although we won’t cover lifetimes in their |
| entirety in this chapter, we’ll discuss common ways you might encounter lifetime |
| syntax so you can get comfortable with the concept. |
| |
| ### Preventing Dangling References with Lifetimes |
| |
| The main aim of lifetimes is to prevent _dangling references_, which cause a |
| program to reference data other than the data it’s intended to reference. |
| Consider the program in Listing 10-16, which has an outer scope and an inner |
| scope. |
| |
| <Listing number="10-16" caption="An attempt to use a reference whose value has gone out of scope"> |
| |
| ```rust,ignore,does_not_compile |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-16/src/main.rs}} |
| ``` |
| |
| </Listing> |
| |
| > Note: The examples in Listing 10-16, 10-17, and 10-23 declare variables |
| > without giving them an initial value, so the variable name exists in the outer |
| > scope. At first glance, this might appear to be in conflict with Rust’s having |
| > no null values. However, if we try to use a variable before giving it a value, |
| > we’ll get a compile-time error, which shows that Rust indeed does not allow |
| > null values. |
| |
| The outer scope declares a variable named `r` with no initial value, and the |
| inner scope declares a variable named `x` with the initial value of `5`. Inside |
| the inner scope, we attempt to set the value of `r` as a reference to `x`. Then |
| the inner scope ends, and we attempt to print the value in `r`. This code won’t |
| compile because the value that `r` is referring to has gone out of scope before |
| we try to use it. Here is the error message: |
| |
| ```console |
| {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-16/output.txt}} |
| ``` |
| |
| The error message says that the variable `x` “does not live long enough.” The |
| reason is that `x` will be out of scope when the inner scope ends on line 7. But |
| `r` is still valid for the outer scope; because its scope is larger, we say that |
| it “lives longer.” If Rust allowed this code to work, `r` would be referencing |
| memory that was deallocated when `x` went out of scope, and anything we tried to |
| do with `r` wouldn’t work correctly. So how does Rust determine that this code |
| is invalid? It uses a borrow checker. |
| |
| ### The Borrow Checker |
| |
| The Rust compiler has a _borrow checker_ that compares scopes to determine |
| whether all borrows are valid. Listing 10-17 shows the same code as Listing |
| 10-16 but with annotations showing the lifetimes of the variables. |
| |
| <Listing number="10-17" caption="Annotations of the lifetimes of `r` and `x`, named `'a` and `'b`, respectively"> |
| |
| ```rust,ignore,does_not_compile |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-17/src/main.rs}} |
| ``` |
| |
| </Listing> |
| |
| Here, we’ve annotated the lifetime of `r` with `'a` and the lifetime of `x` with |
| `'b`. As you can see, the inner `'b` block is much smaller than the outer `'a` |
| lifetime block. At compile time, Rust compares the size of the two lifetimes and |
| sees that `r` has a lifetime of `'a` but that it refers to memory with a |
| lifetime of `'b`. The program is rejected because `'b` is shorter than `'a`: the |
| subject of the reference doesn’t live as long as the reference. |
| |
| Listing 10-18 fixes the code so it doesn’t have a dangling reference and it |
| compiles without any errors. |
| |
| <Listing number="10-18" caption="A valid reference because the data has a longer lifetime than the reference"> |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-18/src/main.rs}} |
| ``` |
| |
| </Listing> |
| |
| Here, `x` has the lifetime `'b`, which in this case is larger than `'a`. This |
| means `r` can reference `x` because Rust knows that the reference in `r` will |
| always be valid while `x` is valid. |
| |
| Now that you know what the lifetimes of references are and how Rust analyzes |
| lifetimes to ensure references will always be valid, let’s explore generic |
| lifetimes of parameters and return values in the context of functions. |
| |
| ### Generic Lifetimes in Functions |
| |
| We’ll write a function that returns the longer of two string slices. This |
| function will take two string slices and return a single string slice. After |
| we’ve implemented the `longest` function, the code in Listing 10-19 should print |
| `The longest string is abcd`. |
| |
| <Listing number="10-19" file-name="src/main.rs" caption="A `main` function that calls the `longest` function to find the longer of two string slices"> |
| |
| ```rust,ignore |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-19/src/main.rs}} |
| ``` |
| |
| </Listing> |
| |
| Note that we want the function to take string slices, which are references, |
| rather than strings, because we don’t want the `longest` function to take |
| ownership of its parameters. Refer to the |
| [“String Slices as Parameters”][string-slices-as-parameters]<!-- ignore --> |
| section in Chapter 4 for more discussion about why the parameters we use in |
| Listing 10-19 are the ones we want. |
| |
| If we try to implement the `longest` function as shown in Listing 10-20, it |
| won’t compile. |
| |
| <Listing number="10-20" file-name="src/main.rs" caption="An implementation of the `longest` function that returns the longer of two string slices but does not yet compile"> |
| |
| ```rust,ignore,does_not_compile |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-20/src/main.rs:here}} |
| ``` |
| |
| </Listing> |
| |
| Instead, we get the following error that talks about lifetimes: |
| |
| ```console |
| {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-20/output.txt}} |
| ``` |
| |
| The help text reveals that the return type needs a generic lifetime parameter on |
| it because Rust can’t tell whether the reference being returned refers to `x` or |
| `y`. Actually, we don’t know either, because the `if` block in the body of this |
| function returns a reference to `x` and the `else` block returns a reference to |
| `y`! |
| |
| When we’re defining this function, we don’t know the concrete values that will |
| be passed into this function, so we don’t know whether the `if` case or the |
| `else` case will execute. We also don’t know the concrete lifetimes of the |
| references that will be passed in, so we can’t look at the scopes as we did in |
| Listings 10-17 and 10-18 to determine whether the reference we return will |
| always be valid. The borrow checker can’t determine this either, because it |
| doesn’t know how the lifetimes of `x` and `y` relate to the lifetime of the |
| return value. To fix this error, we’ll add generic lifetime parameters that |
| define the relationship between the references so the borrow checker can perform |
| its analysis. |
| |
| ### Lifetime Annotation Syntax |
| |
| Lifetime annotations don’t change how long any of the references live. Rather, |
| they describe the relationships of the lifetimes of multiple references to each |
| other without affecting the lifetimes. Just as functions can accept any type |
| when the signature specifies a generic type parameter, functions can accept |
| references with any lifetime by specifying a generic lifetime parameter. |
| |
| Lifetime annotations have a slightly unusual syntax: the names of lifetime |
| parameters must start with an apostrophe (`'`) and are usually all lowercase and |
| very short, like generic types. Most people use the name `'a` for the first |
| lifetime annotation. We place lifetime parameter annotations after the `&` of a |
| reference, using a space to separate the annotation from the reference’s type. |
| |
| Here are some examples: a reference to an `i32` without a lifetime parameter, a |
| reference to an `i32` that has a lifetime parameter named `'a`, and a mutable |
| reference to an `i32` that also has the lifetime `'a`. |
| |
| ```rust,ignore |
| &i32 // a reference |
| &'a i32 // a reference with an explicit lifetime |
| &'a mut i32 // a mutable reference with an explicit lifetime |
| ``` |
| |
| One lifetime annotation by itself doesn’t have much meaning because the |
| annotations are meant to tell Rust how generic lifetime parameters of multiple |
| references relate to each other. Let’s examine how the lifetime annotations |
| relate to each other in the context of the `longest` function. |
| |
| ### Lifetime Annotations in Function Signatures |
| |
| To use lifetime annotations in function signatures, we need to declare the |
| generic _lifetime_ parameters inside angle brackets between the function name |
| and the parameter list, just as we did with generic _type_ parameters. |
| |
| We want the signature to express the following constraint: the returned |
| reference will be valid as long as both the parameters are valid. This is the |
| relationship between lifetimes of the parameters and the return value. We’ll |
| name the lifetime `'a` and then add it to each reference, as shown in Listing |
| 10-21. |
| |
| <Listing number="10-21" file-name="src/main.rs" caption="The `longest` function definition specifying that all the references in the signature must have the same lifetime `'a`"> |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-21/src/main.rs:here}} |
| ``` |
| |
| </Listing> |
| |
| This code should compile and produce the result we want when we use it with the |
| `main` function in Listing 10-19. |
| |
| The function signature now tells Rust that for some lifetime `'a`, the function |
| takes two parameters, both of which are string slices that live at least as long |
| as lifetime `'a`. The function signature also tells Rust that the string slice |
| returned from the function will live at least as long as lifetime `'a`. In |
| practice, it means that the lifetime of the reference returned by the `longest` |
| function is the same as the smaller of the lifetimes of the values referred to |
| by the function arguments. These relationships are what we want Rust to use when |
| analyzing this code. |
| |
| Remember, when we specify the lifetime parameters in this function signature, |
| we’re not changing the lifetimes of any values passed in or returned. Rather, |
| we’re specifying that the borrow checker should reject any values that don’t |
| adhere to these constraints. Note that the `longest` function doesn’t need to |
| know exactly how long `x` and `y` will live, only that some scope can be |
| substituted for `'a` that will satisfy this signature. |
| |
| When annotating lifetimes in functions, the annotations go in the function |
| signature, not in the function body. The lifetime annotations become part of the |
| contract of the function, much like the types in the signature. Having function |
| signatures contain the lifetime contract means the analysis the Rust compiler |
| does can be simpler. If there’s a problem with the way a function is annotated |
| or the way it is called, the compiler errors can point to the part of our code |
| and the constraints more precisely. If, instead, the Rust compiler made more |
| inferences about what we intended the relationships of the lifetimes to be, the |
| compiler might only be able to point to a use of our code many steps away from |
| the cause of the problem. |
| |
| When we pass concrete references to `longest`, the concrete lifetime that is |
| substituted for `'a` is the part of the scope of `x` that overlaps with the |
| scope of `y`. In other words, the generic lifetime `'a` will get the concrete |
| lifetime that is equal to the smaller of the lifetimes of `x` and `y`. Because |
| we’ve annotated the returned reference with the same lifetime parameter `'a`, |
| the returned reference will also be valid for the length of the smaller of the |
| lifetimes of `x` and `y`. |
| |
| Let’s look at how the lifetime annotations restrict the `longest` function by |
| passing in references that have different concrete lifetimes. Listing 10-22 is a |
| straightforward example. |
| |
| <Listing number="10-22" file-name="src/main.rs" caption="Using the `longest` function with references to `String` values that have different concrete lifetimes"> |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-22/src/main.rs:here}} |
| ``` |
| |
| </Listing> |
| |
| In this example, `string1` is valid until the end of the outer scope, `string2` |
| is valid until the end of the inner scope, and `result` references something |
| that is valid until the end of the inner scope. Run this code and you’ll see |
| that the borrow checker approves; it will compile and print |
| `The longest string |
| is long string is long`. |
| |
| Next, let’s try an example that shows that the lifetime of the reference in |
| `result` must be the smaller lifetime of the two arguments. We’ll move the |
| declaration of the `result` variable outside the inner scope but leave the |
| assignment of the value to the `result` variable inside the scope with |
| `string2`. Then we’ll move the `println!` that uses `result` to outside the |
| inner scope, after the inner scope has ended. The code in Listing 10-23 will not |
| compile. |
| |
| <Listing number="10-23" file-name="src/main.rs" caption="Attempting to use `result` after `string2` has gone out of scope"> |
| |
| ```rust,ignore,does_not_compile |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-23/src/main.rs:here}} |
| ``` |
| |
| </Listing> |
| |
| When we try to compile this code, we get this error: |
| |
| ```console |
| {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-23/output.txt}} |
| ``` |
| |
| The error shows that for `result` to be valid for the `println!` statement, |
| `string2` would need to be valid until the end of the outer scope. Rust knows |
| this because we annotated the lifetimes of the function parameters and return |
| values using the same lifetime parameter `'a`. |
| |
| As humans, we can look at this code and see that `string1` is longer than |
| `string2`, and therefore, `result` will contain a reference to `string1`. |
| Because `string1` has not gone out of scope yet, a reference to `string1` will |
| still be valid for the `println!` statement. However, the compiler can’t see |
| that the reference is valid in this case. We’ve told Rust that the lifetime of |
| the reference returned by the `longest` function is the same as the smaller of |
| the lifetimes of the references passed in. Therefore, the borrow checker |
| disallows the code in Listing 10-23 as possibly having an invalid reference. |
| |
| Try designing more experiments that vary the values and lifetimes of the |
| references passed in to the `longest` function and how the returned reference is |
| used. Make hypotheses about whether or not your experiments will pass the borrow |
| checker before you compile; then check to see if you’re right! |
| |
| ### Thinking in Terms of Lifetimes |
| |
| The way in which you need to specify lifetime parameters depends on what your |
| function is doing. For example, if we changed the implementation of the |
| `longest` function to always return the first parameter rather than the longest |
| string slice, we wouldn’t need to specify a lifetime on the `y` parameter. The |
| following code will compile: |
| |
| <Listing file-name="src/main.rs"> |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-08-only-one-reference-with-lifetime/src/main.rs:here}} |
| ``` |
| |
| </Listing> |
| |
| We’ve specified a lifetime parameter `'a` for the parameter `x` and the return |
| type, but not for the parameter `y`, because the lifetime of `y` does not have |
| any relationship with the lifetime of `x` or the return value. |
| |
| When returning a reference from a function, the lifetime parameter for the |
| return type needs to match the lifetime parameter for one of the parameters. If |
| the reference returned does _not_ refer to one of the parameters, it must refer |
| to a value created within this function. However, this would be a dangling |
| reference because the value will go out of scope at the end of the function. |
| Consider this attempted implementation of the `longest` function that won’t |
| compile: |
| |
| <Listing file-name="src/main.rs"> |
| |
| ```rust,ignore,does_not_compile |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-09-unrelated-lifetime/src/main.rs:here}} |
| ``` |
| |
| </Listing> |
| |
| Here, even though we’ve specified a lifetime parameter `'a` for the return type, |
| this implementation will fail to compile because the return value lifetime is |
| not related to the lifetime of the parameters at all. Here is the error message |
| we get: |
| |
| ```console |
| {{#include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-09-unrelated-lifetime/output.txt}} |
| ``` |
| |
| The problem is that `result` goes out of scope and gets cleaned up at the end of |
| the `longest` function. We’re also trying to return a reference to `result` from |
| the function. There is no way we can specify lifetime parameters that would |
| change the dangling reference, and Rust won’t let us create a dangling |
| reference. In this case, the best fix would be to return an owned data type |
| rather than a reference so the calling function is then responsible for cleaning |
| up the value. |
| |
| Ultimately, lifetime syntax is about connecting the lifetimes of various |
| parameters and return values of functions. Once they’re connected, Rust has |
| enough information to allow memory-safe operations and disallow operations that |
| would create dangling pointers or otherwise violate memory safety. |
| |
| ### Lifetime Annotations in Struct Definitions |
| |
| So far, the structs we’ve defined all hold owned types. We can define structs to |
| hold references, but in that case we would need to add a lifetime annotation on |
| every reference in the struct’s definition. Listing 10-24 has a struct named |
| `ImportantExcerpt` that holds a string slice. |
| |
| <Listing number="10-24" file-name="src/main.rs" caption="A struct that holds a reference, requiring a lifetime annotation"> |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-24/src/main.rs}} |
| ``` |
| |
| </Listing> |
| |
| This struct has the single field `part` that holds a string slice, which is a |
| reference. As with generic data types, we declare the name of the generic |
| lifetime parameter inside angle brackets after the name of the struct so we can |
| use the lifetime parameter in the body of the struct definition. This annotation |
| means an instance of `ImportantExcerpt` can’t outlive the reference it holds in |
| its `part` field. |
| |
| The `main` function here creates an instance of the `ImportantExcerpt` struct |
| that holds a reference to the first sentence of the `String` owned by the |
| variable `novel`. The data in `novel` exists before the `ImportantExcerpt` |
| instance is created. In addition, `novel` doesn’t go out of scope until after |
| the `ImportantExcerpt` goes out of scope, so the reference in the |
| `ImportantExcerpt` instance is valid. |
| |
| ### Lifetime Elision |
| |
| You’ve learned that every reference has a lifetime and that you need to specify |
| lifetime parameters for functions or structs that use references. However, we |
| had a function in Listing 4-9, shown again in Listing 10-25, that compiled |
| without lifetime annotations. |
| |
| <Listing number="10-25" file-name="src/lib.rs" caption="A function we defined in Listing 4-9 that compiled without lifetime annotations, even though the parameter and return type are references"> |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-25/src/main.rs:here}} |
| ``` |
| |
| </Listing> |
| |
| The reason this function compiles without lifetime annotations is historical: in |
| early versions (pre-1.0) of Rust, this code wouldn’t have compiled because every |
| reference needed an explicit lifetime. At that time, the function signature |
| would have been written like this: |
| |
| ```rust,ignore |
| fn first_word<'a>(s: &'a str) -> &'a str { |
| ``` |
| |
| After writing a lot of Rust code, the Rust team found that Rust programmers were |
| entering the same lifetime annotations over and over in particular situations. |
| These situations were predictable and followed a few deterministic patterns. The |
| developers programmed these patterns into the compiler’s code so the borrow |
| checker could infer the lifetimes in these situations and wouldn’t need explicit |
| annotations. |
| |
| This piece of Rust history is relevant because it’s possible that more |
| deterministic patterns will emerge and be added to the compiler. In the future, |
| even fewer lifetime annotations might be required. |
| |
| The patterns programmed into Rust’s analysis of references are called the |
| _lifetime elision rules_. These aren’t rules for programmers to follow; they’re |
| a set of particular cases that the compiler will consider, and if your code fits |
| these cases, you don’t need to write the lifetimes explicitly. |
| |
| The elision rules don’t provide full inference. If there is still ambiguity as |
| to what lifetimes the references have after Rust applies the rules, the compiler |
| won’t guess what the lifetime of the remaining references should be. Instead of |
| guessing, the compiler will give you an error that you can resolve by adding the |
| lifetime annotations. |
| |
| Lifetimes on function or method parameters are called _input lifetimes_, and |
| lifetimes on return values are called _output lifetimes_. |
| |
| The compiler uses three rules to figure out the lifetimes of the references when |
| there aren’t explicit annotations. The first rule applies to input lifetimes, |
| and the second and third rules apply to output lifetimes. If the compiler gets |
| to the end of the three rules and there are still references for which it can’t |
| figure out lifetimes, the compiler will stop with an error. These rules apply to |
| `fn` definitions as well as `impl` blocks. |
| |
| The first rule is that the compiler assigns a lifetime parameter to each |
| parameter that’s a reference. In other words, a function with one parameter gets |
| one lifetime parameter: `fn foo<'a>(x: &'a i32)`; a function with two parameters |
| gets two separate lifetime parameters: `fn foo<'a, 'b>(x: &'a i32, |
| y: &'b i32)`; |
| and so on. |
| |
| The second rule is that, if there is exactly one input lifetime parameter, that |
| lifetime is assigned to all output lifetime parameters: |
| `fn foo<'a>(x: &'a i32) |
| -> &'a i32`. |
| |
| The third rule is that, if there are multiple input lifetime parameters, but one |
| of them is `&self` or `&mut self` because this is a method, the lifetime of |
| `self` is assigned to all output lifetime parameters. This third rule makes |
| methods much nicer to read and write because fewer symbols are necessary. |
| |
| Let’s pretend we’re the compiler. We’ll apply these rules to figure out the |
| lifetimes of the references in the signature of the `first_word` function in |
| Listing 10-25. The signature starts without any lifetimes associated with the |
| references: |
| |
| ```rust,ignore |
| fn first_word(s: &str) -> &str { |
| ``` |
| |
| Then the compiler applies the first rule, which specifies that each parameter |
| gets its own lifetime. We’ll call it `'a` as usual, so now the signature is |
| this: |
| |
| ```rust,ignore |
| fn first_word<'a>(s: &'a str) -> &str { |
| ``` |
| |
| The second rule applies because there is exactly one input lifetime. The second |
| rule specifies that the lifetime of the one input parameter gets assigned to the |
| output lifetime, so the signature is now this: |
| |
| ```rust,ignore |
| fn first_word<'a>(s: &'a str) -> &'a str { |
| ``` |
| |
| Now all the references in this function signature have lifetimes, and the |
| compiler can continue its analysis without needing the programmer to annotate |
| the lifetimes in this function signature. |
| |
| Let’s look at another example, this time using the `longest` function that had |
| no lifetime parameters when we started working with it in Listing 10-20: |
| |
| ```rust,ignore |
| fn longest(x: &str, y: &str) -> &str { |
| ``` |
| |
| Let’s apply the first rule: each parameter gets its own lifetime. This time we |
| have two parameters instead of one, so we have two lifetimes: |
| |
| ```rust,ignore |
| fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &str { |
| ``` |
| |
| You can see that the second rule doesn’t apply because there is more than one |
| input lifetime. The third rule doesn’t apply either, because `longest` is a |
| function rather than a method, so none of the parameters are `self`. After |
| working through all three rules, we still haven’t figured out what the return |
| type’s lifetime is. This is why we got an error trying to compile the code in |
| Listing 10-20: the compiler worked through the lifetime elision rules but still |
| couldn’t figure out all the lifetimes of the references in the signature. |
| |
| Because the third rule really only applies in method signatures, we’ll look at |
| lifetimes in that context next to see why the third rule means we don’t have to |
| annotate lifetimes in method signatures very often. |
| |
| ### Lifetime Annotations in Method Definitions |
| |
| When we implement methods on a struct with lifetimes, we use the same syntax as |
| that of generic type parameters shown in Listing 10-11. Where we declare and use |
| the lifetime parameters depends on whether they’re related to the struct fields |
| or the method parameters and return values. |
| |
| Lifetime names for struct fields always need to be declared after the `impl` |
| keyword and then used after the struct’s name because those lifetimes are part |
| of the struct’s type. |
| |
| In method signatures inside the `impl` block, references might be tied to the |
| lifetime of references in the struct’s fields, or they might be independent. In |
| addition, the lifetime elision rules often make it so that lifetime annotations |
| aren’t necessary in method signatures. Let’s look at some examples using the |
| struct named `ImportantExcerpt` that we defined in Listing 10-24. |
| |
| First we’ll use a method named `level` whose only parameter is a reference to |
| `self` and whose return value is an `i32`, which is not a reference to anything: |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-10-lifetimes-on-methods/src/main.rs:1st}} |
| ``` |
| |
| The lifetime parameter declaration after `impl` and its use after the type name |
| are required, but we’re not required to annotate the lifetime of the reference |
| to `self` because of the first elision rule. |
| |
| Here is an example where the third lifetime elision rule applies: |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-10-lifetimes-on-methods/src/main.rs:3rd}} |
| ``` |
| |
| There are two input lifetimes, so Rust applies the first lifetime elision rule |
| and gives both `&self` and `announcement` their own lifetimes. Then, because one |
| of the parameters is `&self`, the return type gets the lifetime of `&self`, and |
| all lifetimes have been accounted for. |
| |
| ### The Static Lifetime |
| |
| One special lifetime we need to discuss is `'static`, which denotes that the |
| affected reference _can_ live for the entire duration of the program. All string |
| literals have the `'static` lifetime, which we can annotate as follows: |
| |
| ```rust |
| let s: &'static str = "I have a static lifetime."; |
| ``` |
| |
| The text of this string is stored directly in the program’s binary, which is |
| always available. Therefore, the lifetime of all string literals is `'static`. |
| |
| You might see suggestions to use the `'static` lifetime in error messages. But |
| before specifying `'static` as the lifetime for a reference, think about whether |
| the reference you have actually lives the entire lifetime of your program or |
| not, and whether you want it to. Most of the time, an error message suggesting |
| the `'static` lifetime results from attempting to create a dangling reference or |
| a mismatch of the available lifetimes. In such cases, the solution is to fix |
| those problems, not to specify the `'static` lifetime. |
| |
| ## Generic Type Parameters, Trait Bounds, and Lifetimes Together |
| |
| Let’s briefly look at the syntax of specifying generic type parameters, trait |
| bounds, and lifetimes all in one function! |
| |
| ```rust |
| {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-11-generics-traits-and-lifetimes/src/main.rs:here}} |
| ``` |
| |
| This is the `longest` function from Listing 10-21 that returns the longer of two |
| string slices. But now it has an extra parameter named `ann` of the generic type |
| `T`, which can be filled in by any type that implements the `Display` trait as |
| specified by the `where` clause. This extra parameter will be printed using |
| `{}`, which is why the `Display` trait bound is necessary. Because lifetimes are |
| a type of generic, the declarations of the lifetime parameter `'a` and the |
| generic type parameter `T` go in the same list inside the angle brackets after |
| the function name. |
| |
| ## Summary |
| |
| We covered a lot in this chapter! Now that you know about generic type |
| parameters, traits and trait bounds, and generic lifetime parameters, you’re |
| ready to write code without repetition that works in many different situations. |
| Generic type parameters let you apply the code to different types. Traits and |
| trait bounds ensure that even though the types are generic, they’ll have the |
| behavior the code needs. You learned how to use lifetime annotations to ensure |
| that this flexible code won’t have any dangling references. And all of this |
| analysis happens at compile time, which doesn’t affect runtime performance! |
| |
| Believe it or not, there is much more to learn on the topics we discussed in |
| this chapter: Chapter 18 discusses trait objects, which are another way to use |
| traits. There are also more complex scenarios involving lifetime annotations |
| that you will only need in very advanced scenarios; for those, you should read |
| the [Rust Reference][reference]. But next, you’ll learn how to write tests in |
| Rust so you can make sure your code is working the way it should. |
| |
| [references-and-borrowing]: ch04-02-references-and-borrowing.html#references-and-borrowing |
| [string-slices-as-parameters]: ch04-03-slices.html#string-slices-as-parameters |
| [reference]: ../reference/index.html |