halfway through ch10.3

This commit is contained in:
darkicewolf50 2025-02-04 12:12:39 -07:00
parent 724ea9a5fe
commit 9f29d30aec
2 changed files with 150 additions and 5 deletions

View File

@ -4,11 +4,11 @@
"type": "split",
"children": [
{
"id": "9cf638edb21842e3",
"id": "64904eb93f53e8e0",
"type": "tabs",
"children": [
{
"id": "3025ec3e871a2841",
"id": "caf0233e624d6c1c",
"type": "leaf",
"state": {
"type": "markdown",
@ -164,8 +164,10 @@
"command-palette:Open command palette": false
}
},
"active": "3025ec3e871a2841",
"active": "caf0233e624d6c1c",
"lastOpenFiles": [
"2025-02-04.md",
"Lifetimes.md",
"Generic Types Traits and Lifetimes.md",
"data_types.md",
"Collection of Common Data Structs.md",
@ -190,10 +192,8 @@
"Variables.md",
"Vector.md",
"Reducing.md",
"crates.io.md",
"does_not_compile.svg",
"Untitled.canvas",
"Packages and Crates.md",
"Good and Bad Code/Commenting Pratices",
"Good and Bad Code"
]

View File

@ -173,3 +173,148 @@ Here is an example to an `i32` reference
One annotation by itself has no meaning because cannot be a relationship to another (or multiple) reference(s)
## Lifetime Annotations in Function Signatures
To use lifetime annotations in a function's signature you must first declare the generic lifetime parameter inside `<>` between the function name and the parameter list (just like generics)
In this case the constraint we have is that we want the returned reference to be valid as long as both parameters are valid
This will be the relationship between the lifetime of the parameters and the return reference.
Here is this implemented with the name `'a` as the lifetime, it is added to each reference in the function signature
```rust
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
```
This code should compile now with the use of lifetime annotations
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 signature also tells Rust that the string slice returned from the function will live at least as long as life `'a`.
What this means is that the smaller of the lifetime returned by the `longest` function is the same as the smaller of the lifetime values referred to by the function arguments
These relationships are what we want Rust to use when analyzing this code
Lifetime annotations go in the signature NOT the body of the function
Lifetime annotations are part of the contract of the function, like types in the signature
This makes the lifetime analysis easier on the Rust compiler
If there is a problem in the signature it makes it easier to identify and express in an error as well, and give clear solutions
Here is another example of the use of the `longest` function
```rust
fn main() {
let string1 = String::from("long string is long");
{
let string2 = String::from("xyz");
let result = longest(string1.as_str(), string2.as_str());
println!("The longest string is {result}");
}
}
```
In this example the lifetime of the return value of the `longest` function is the same as `string2` which means that after the inner scope or `{}` the reference is no longer valid
This is both due to `string2`, `result` (which stores the reference) and the function signature states that it only lives as long as the shortest function
The value reference in `result` is `long string is long` and the program will print `The longest string is long string is long`
In this example the program will not compile
```rust
fn main() {
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
println!("The longest string is {result}");
}
```
This is due to `string2` not living long enough for the reference in `result` to be used while in a valid state
Here is the error it would provide
```
$ cargo run
Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0597]: `string2` does not live long enough
--> src/main.rs:6:44
|
5 | let string2 = String::from("xyz");
| ------- binding `string2` declared here
6 | result = longest(string1.as_str(), string2.as_str());
| ^^^^^^^ borrowed value does not live long enough
7 | }
| - `string2` dropped here while still borrowed
8 | println!("The longest string is {result}");
| -------- borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `chapter10` (bin "chapter10") due to 1 previous error
```
This also states that `string2` would need to be valid until the `print!` macro. Hence why it states that it doesn't live long enough
Even though the reference in this case is to `string1` the compiler and the function signature states that the lifetime of the return value is the same as the shortest lifetime
## Thinking in Terms of Lifetimes
The way that you need to specify lifetime parameters depends on what your function is doing
For example if this was your function
```rust
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
x
}
```
This function's return reference's lifetime is the same as `x` so the returned reference only lives as long as what is passed into `x`
The return reference lifetime has no relations to the `y` lifetime
Any lifetime MUST have another relationship to another reference
Any lifetime in a function signature's return value MUST relate to AT LEAST one parameter
If it doesn't it would create a dangling reference, this is because the value would go out of scope at the end of the function
Here is an example of `longest` that creates both a dangling reference and an invalid lifetime
```rust
fn longest<'a>(x: &str, y: &str) -> &'a str {
let result = String::from("really long string");
result.as_str()
}
```
Here is the error
```
$ cargo run
Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0515]: cannot return value referencing local variable `result`
--> src/main.rs:11:5
|
11 | result.as_str()
| ------^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `result` is borrowed here
For more information about this error, try `rustc --explain E0515`.
error: could not compile `chapter10` (bin "chapter10") due to 1 previous error
```
The problem is that `result` goes out of scope and gets cleaned up at the end of the function.
We also try to return a reference at the end of the function to `result`
There is no way we can specify lifetime parameters that would change the dangling reference in this case
Rust will not let you create a dangling reference
The fix for this would be to transfer ownership out of the function
## Lifetime Annotations in Struct Definitions