RUST Daily Savings: Lifecycle & Scope

life cycle

        The life cycle of a variable is its entire process from creation to destruction.

Scope
        Every variable we declare has a scope. Scope is actually the environment in which variables and values ​​exist. Scope is denoted by a pair of curly braces. For example, using a block expression creates a scope, any expression that begins and ends with curly braces. Additionally, scopes support nesting within each other, and elements of the parent scope can be accessed in child scopes, but not the other way around.

{
    let a = "hello";    ----------+-- a的作用域
    let b = 1;          -----+--b |
    {                        |    |
    let c = true;   ---+--c  |    |
                       |     |    |
                    ---+     |    |
    }                        |    |
                    ---------+    |
                    --------------+
}

        When the scope ends, the variables defined in the scope will run the relevant code to release resources. For data allocated on the stack, it is easy to determine whether a variable persists. For values ​​allocated on the heap, the drop method will be called before the end-of-scope marker }. But here it is implicit to prevent the programmer from forgetting to release the value. The drop method comes from the Drop trait, which is implemented for most heap-allocated types in Rust, to automatically release resources easily.

Lifecycle & Scope

        The life cycle of Rust is based on scope. We can think of the life cycle of variables as its scope; the compiler can automatically identify the life cycle of these variables in the scope, which is convenient for management. In fact, the lifetime is purely a compile-time construct, which helps the compiler determine the scope in which a reference is valid and ensures that it follows the borrowing rules. It keeps track of things like where the references came from, and whether they outlive the borrowed value. Lifecycles in Rust ensure that a reference doesn't outlive the value it points to. The life cycle is not used by the developer, but by the compiler when it uses and infers the validity of the reference.

{
    let a = "hello";    ----------+-- a生命周期
    let b = 1;          -----+--b |
    {                        |    |
    let c = true;   ---+--c  |    |
                       |     |    |
                    ---+     |    |
    }                        |    |
                    ---------+    |
                    --------------+
}

transfer of ownership 

        Since the variable will be released automatically when the scope ends. If we need to continue using it outside its scope, we can either transfer ownership or make a bitwise copy, depending on whether the variable has copy or move semantics.

        If other variables come into scope, ownership changes also occur; either transfer ownership or bitwise copy, depending on whether the variable has copy or move semantics.

{
    let a = "hello".to_string();     
    let b = 1;   
    let c_out;  
    let d_out;        
    {                         
        let c_in = 2;  
        let d_in = "world".to_string();                    
        a;
        b;
        c_out = c_in;
        d_out = d_in;

    }     
    //println!("a:{}",a);     
    println!("b:{}",b);   
    println!("c_out:{}",c_out);
    //println!("c_in:{}",c_in);
    //println!("d_in:{}",d_in);   
    println!("d_out:{}",d_out);   
}

b:1
c_out:2
d_out:world

        If we want to remove the comment for printing a, the following error will occur. After a enters the subscope, the ownership transfer occurs. When the self-action ends, a is released, so we will report an error if we continue to use it.

 If the printing of c_in is removed, the following error will be generated, and the same is true for c_in.

create new scope

  • Scopes can be created using block expressions (braces).
{
    let a = "hello".to_string();     
    let b = 1;   
    let c_out;  
    let d_out;        
    {                         
        let c_in = 2;  
        let d_in = "world".to_string();                    
        a;
        b;
        c_out = c_in;
        d_out = d_in;

    }     
    //println!("a:{}",a);     
    println!("b:{}",b);   
    println!("c_out:{}",c_out);
    //println!("c_in:{}",c_in);
    //println!("d_in:{}",d_in);   
    println!("d_out:{}",d_out);   
}
  • match matches also produce a scope.
  • The for, loop, and while loop statements can all create new scopes.
  • if let blocks and while let blocks also create new scopes.

        These three are similar, let's take match as an example to demonstrate. If t is replaced by Some(6), there is no problem, because t is Option<i32>, which has copy semantics.

fn main(){
    let t = Some("test".to_string());
    match t{
        Some(v) => (),
        None => (),
    }
    //println!("t:{}",t);
}

After removing the comment, the error is as follows, indicating that t has been transferred in Some(v) => (), and then released when the scope ends;

  • The function body itself is a separate scope.

Since String has move semantics, when it is passed as a parameter to the f function, ownership transfer occurs, and an error will occur if it is called again in the main function.

fn f(t:String){
   println!("{:?}",t);
}
fn main(){
    let t = Some("test".to_string());
    f(t);
    //println!("{:?}",t);
}

  • Closure

Closure will create a new scope. For environment variables, there are the following three capture methods:
· For copy semantic types, capture with immutable reference (&T).
· For types with move semantics, perform move semantics (move) to transfer ownership to capture.
· For mutable bindings, capture them with mutable references (&mut) if operations that modify them are included in the closure.


fn main(){
    let t = Some("test".to_string());
    let c = |i: i32|{
     //println!("{:?},{:?}",t,i);
    t;
    };
    c(0);
    //println!("{:?}",t);
}

There is another interesting point here, that is, println!("{:?},{:?}",t,i); If there is only this sentence in the closure; without t;, there will be no transfer of ownership; that is, println does not transfer ownership. When it is called on the surface, t, the actual parameter &t;

Guess you like

Origin blog.csdn.net/xq723310/article/details/130337716