mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
halfway through ch10.3
This commit is contained in:
parent
724ea9a5fe
commit
9f29d30aec
10
.obsidian/workspace.json
vendored
10
.obsidian/workspace.json
vendored
@ -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"
|
||||
]
|
||||
|
145
Lifetimes.md
145
Lifetimes.md
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user