In learning the Rust lifecycle, as a developer, it is difficult to understand why there is such a design. To realize the role of the lifecycle, try to understand why Rust designs the lifecycle.
The role of the function lifecycle
Must first understand what is the role of the life cycle, life cycle parameters of the function is to prevent the occurrence of dangling references, dangling references has released, which means that when the source data and references to the source data still exist, the this reference was become dangling references, have such problems, in C and C + + requires developers to ensure that won’t happen dangling references manually, Rust, on the other hand, is known for security, so the compiler has to make sure this doesn’t happen.
The timing of the dangling reference
To ensure that this problem does not occur, you need to know when a dangling reference will occur, and then verify those situations.
For functions, function at the end of the execution, will clear the scope of data, and dangling references occurs in the position of the return value, if the function returns a reference, but refer to the source data is destroyed when the cleaning scope, at this point a dangling references, has taken place in the actual coding, is more complicated, What if the returned reference refers to the source data from the function argument? In this case, it is not immediately clear whether dangling references will occur. Now let’s look at two situations where problems can occur.
The return value comes from a variable created in the function body.
fn foo() - > &String {
let s = String::new();
&s
}
Copy the code
Variables created in the body of a function are destroyed at the end of the function call, so the &s returned is a dangling reference.
The return value is derived from the parameter, but it is not clear which one is returned.
fn longer(s1: &str, s2: &str) - > &str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
Copy the code
In the above case, rust is not sure at compile time whether the final return value will be S1 or S2 because of the judgment conditions in the code.
Dangling references can occur when we don’t know the parameter’s life cycle. Consider the following:
let a = String::from("a");
let c;
{
let b = String::from("bb");
// At compile time, rust can only know to call longer
// But it is not clear whether the return value is &a or &b
c = longer(&a, &b);
// If &b is returned, &b is bound to c
// But b is destroyed after the scope ends,
// So c becomes a dangling reference
}
println!("{}", c);
Copy the code
The solution to this is that rust knows at compile time the lifetime of the value returned after longer is called, which is the lifetime of c above. Rust does not know the lifetime of longer, but to prevent the dangling pointer at run time, it can only report an error at compile time: “Unclear about the return value lifecycle of the longer function.”
Tips for improving the compiler
Wouldn’t it be nice if the Rust compiler could explicitly warn developers about which variable’s life cycle is problematic? For example, the compiler can say something like: “C variable life is not long enough.”
How to implement clear error message
In order to generate an error message, rust must be aware of the lifetime of longer c at compile time. If c is bound to &b, then the lifetime of C is the lifetime of B, so it should be able to generate an error message.
How do I determine the return value lifecycle
How can RUST be made aware of the lifecycle of longer’s return value at compile time? If we could map the parameter life cycle to the return value life cycle for a function, rust would know the return value life cycle, such as when we define the function above: The return value of this function has the same lifetime as the two parameters.
fn longer<'a>(s1: &'a str, s2: &'a str) - > &'a str {
if s1.len() > s2.len() {
s1
} else {
s2
}
}
Copy the code
- As with generics, all lifecycles used in the function are first declared after the function name
- Identify the life cycle in each parameter separately
- Identifies the life cycle of the return value
Once the identification is complete, it is immediately obvious that the life cycle of the return value is the same as that of the two parameters, meaning that the return value could be either of the two parameters. With the relationship between parameters and return values, Rust can do code verification at compile time:
- Checksum: a declaration requiring the contents of the function body to conform to the life cycle, the return value &a does not come from the argument, and the compilation fails:
fn longer<'a>(s1: &'a str, s2: &'a str) - > &'a str {
let a = String::new();
&a
}
Copy the code
The following example assigns its own life cycle to each parameter. The return value is required to return the life cycle ‘a, but s2 (‘b ‘) is returned, which also fails:
fn longer<'a.'b>(s1: &'a str, s2: &'b str) - > &'a str {
s2
}
Copy the code
- Checksum two, which requires that the parameters passed to a function when called conform to the life cycle declaration, returns to the original example:
let a = String::from("a");
let c;
{
let b = String::from("bb");
// The life cycle of b is not long enough
c = longer(&a, &b);
}
println!("{}", c);
Copy the code
Because we marked on the life cycle of the function signature that the two parameters and the return value are the same life cycle, and in the above code, the life cycle of &B is obviously shorter than that of &A, so the error was reported. The error message clearly expressed that the life cycle of B is not long enough.
A final word to conclude:
The life cycle is the function that, in order to check for dangling references, requires that the parameter passed conform to the specified life cycle, as long as this is done, no dangling references are generated.