citrus327 / issue-blog-record

2 stars 2 forks source link

Rust学习笔记 - Ownership #19

Closed citrus327 closed 4 years ago

citrus327 commented 4 years ago

Ownership

关于内存和GC的说明

All programs have to manage the way they use a computer’s memory while running. Some languages have garbage collection that constantly looks for no longer used memory as the program runs; in other languages, the programmer must explicitly allocate and free the memory. Rust uses a third approach: memory is managed through a system of ownership with a set of rules that the compiler checks at compile time. None of the ownership features slow down your program while it’s running.

关于stack和heap

All data stored on the stack must have a known, fixed size. Data with an unknown size at compile time or a size that might change must be stored on the heap instead.

关于Rust的GC

{
  let s = String::from("hello"); // s is valid from this point forward
  // do stuff with s
}                                // this scope is now over, and s is no
                                 // longer valid

Rust takes a different path: the memory is automatically returned once the variable that owns it goes out of scope. There is a natural point at which we can return the memory our String needs to the allocator: when s goes out of scope.

Move

let s1 = String::from("hello");
let s2 = s1;

println!("{}, world!", s1); // error

当S1定义后,S2跟S1指向同一个heap variable。

正常情况,gc时,会去gc s1,然后gc s1对应的heap variable。再去gc s2的时候,s2指向的heap variable已经被gc了。就会导致memory corruption。

为了解决这个问题,Rust采用一种叫做move的策略。当s1定义完,s2指向s1的时候,s1自动变成invalid,即不再有效。

针对stack-only data type,这些type会有一个特殊的anotation叫做Copy的Trait,具有copy trait的类型,可以在move之后继续使用。

Here are some of the types that are Copy:

  • All the integer types, such as u32.
  • The Boolean type, bool, with values true and false.
  • All the floating point types, such as f64.
  • The character type, char.
  • Tuples, if they only contain types that are also Copy. For example, (i32, i32) is Copy, but (i32, String) is not.

Drop

当一个变量out of scope,Rust会自动call一个叫drop的function来帮助gc。

fn main() {
    let s = String::from("hello");
    takes_ownership(s);
    println!("{}", s); // error: borrow of moved value: `s`
    let x = 5;                    
    makes_copy(x);             
    println!("{}", x); // fine
} 
fn takes_ownership(some_string: String) {
    println!("{}", some_string);
} 
fn makes_copy(some_integer: i32) { 
    println!("{}", some_integer);
}

第二行定义了s,调用takes_ownership,在第10行打印完后,some_string会out of scope, 因此被drop,

第四行定义了x,integer是拥有Copy Trait Anotation的类型,所以可以继续使用

想要解决第四行这个问题,可以让takes_ownership函数返回一个string,从而达到Transfer Ownership的意义。

fn main() {
    let s = String::from("hello");
    let s = transfer_ownership(s);
    println!("{}", s); // fine
    let x = 5;                    
    makes_copy(x);             
    println!("{}", x); // fine
} 
fn transfer_ownership(some_string: String) -> String {
    println!("{}", some_string);
    return some_string
} 
fn makes_copy(some_integer: i32) { 
    println!("{}", some_integer);
}

Reference and Borrowing

同时上述问题可以使用reference来解决。

fn main() {
    let s = String::from("hello");
    print_string(&s); // 传入引用,而不是move
    println!("{}", s); // fine
    let x = 5;                    
    makes_copy(x);             
    println!("{}", x); // fine
} 
fn print_string(some_string: &String) { // 接受一个&String类型的string
    println!("{}", some_string);
} 

fn makes_copy(some_integer: i32) { 
    println!("{}", some_integer);
}

reference不会被不拥有ownership的scope drop掉,例如print_string不拥有some_string的ownership,那他就不能drop,只有在main函数执行完才会被drop

We call having references as function parameters borrowing

在传入reference的时候,如果想要修改对应reference的值,可以添加mut

fn main() {
    let mut s = String::from("hello");
    println!("original string: {}", s); 
    print_string(&mut s);
    println!("after change in print string: {}", s); // fine
    let x = 5;                    
    makes_copy(x);             
    println!("{}", x); // fine
} 
fn print_string(some_string: &mut String) { // 接受一个&mut String类型的string
    some_string.push_str(", world");
    println!("{}", some_string);
} 

fn makes_copy(some_integer: i32) { 
    println!("{}", some_integer);
}

But mutable references have one big restriction: you can have only one mutable reference to a particular piece of data in a particular scope.

fn main () {
    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s; // error: cannot borrow `s` as mutable more than once at a time

    println!("{}, {}", r1, r2);
}

The benefit of having this restriction is that Rust can prevent data races at compile time.

fn main () {
    let mut s = String::from("hello");

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    println!("{} and {}", r1, r2);
    // r1 and r2 are no longer used after this point

    let r3 = &mut s; // no problem
    println!("{}", r3);
}

Dangling Pointer

fn dangle() -> &String { // dangle returns a reference to a String

    let s = String::from("hello"); // s is a new String

    &s // we return a reference to the String, s
} // Here, s goes out of scope, and is dropped. Its memory goes away.
  // Danger!