Rust学习笔记之Ownership

Rust使用ownership系统来管理内存,编译器会检查这些ownership系统的规则,一旦违反这些规则,则编译不通过

0. 堆和栈

编译期大小可知的数据,放在栈上

编译器大小不定,数据放在堆上,对应的指针放在栈上

1. Ownership的规则

  1. Rust中每个值都有一个owner

  2. 任何时间有且仅有一个owner

  3. owner离开作用域之后,对应的值将会被删掉

2. 作用域

Rust中变量离开作用域之后,会被调用drop方法

2.1 变量作用域

fn main() {
    {                      // s is not valid here, it’s not yet declared
        let s = "hello";   // s is valid from this point forward

        // do stuff with s
    }                      // this scope is now over, and s is no longer valid
}

上面的代码注释详细给出了对应的变量作用域

2.2 heap对象的赋值操作

图片.png

下面的赋值操作有个问题是,当s1,s2都离开作用域的时候,其对应的同一块内存会被释放两次,造成错误

let s1 = String::from("hello");
let s2 = s1;  // s1赋值完之后,就不再有用了

println!("{}, world!", s1);  // 到这一行没法编译

2.2.1 move != 浅拷贝

由于在Rust中,赋值完之后,并且失效了前一个变量,则不能称其为浅拷贝,而称其为move

图片.png

2.2.2 clone=深拷贝

由于以上的原因,Rust不会主动去深拷贝数据,则需要我们显式的声明,见下面的代码:

fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();

    println!("s1 = {}, s2 = {}", s1, s2);
}

2.3 stack对象的赋值操作

fn main() {
    let x = 5;
    let y = x;

    println!("x = {}, y = {}", x, y);
}

由于上述变量在编译器大小可知,则都是存储在栈上,没有深拷贝和浅拷贝的区别,可以看作是深拷贝

2.4 Ownership与函数

在Rust中,将一个值传递给函数,类似于将其赋值给一个变量

fn main() {
    let s = String::from("hello");  // s comes into scope

    takes_ownership(s);             // s's value moves into the function...
                                    // ... and so is no longer valid here

    let x = 5;                      // x comes into scope

    makes_copy(x);                  // x would move into the function,
                                    // but i32 is Copy, so it's okay to still
                                    // use x afterward

} // Here, x goes out of scope, then s. But because s's value was moved, nothing
  // special happens.

fn takes_ownership(some_string: String) { // some_string comes into scope
    println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing
  // memory is freed.

fn makes_copy(some_integer: i32) { // some_integer comes into scope
    println!("{}", some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.

明白了上面heap对象和stack对象的赋值操作,上述代码可以看出:

  1. s在之后就无法再用了,因为当其被传入函数的时候,就已经不是它自己了,除非是把自己clone一份

  2. x还可以再用,因为是一个深拷贝

2.5 函数返回值

赋值情况也是类似的道理

fn main() {
    let s1 = gives_ownership();         // gives_ownership moves its return
                                        // value into s1

    let s2 = String::from("hello");     // s2 comes into scope

    let s3 = takes_and_gives_back(s2);  // s2 is moved into
                                        // takes_and_gives_back, which also
                                        // moves its return value into s3
} // Here, s3 goes out of scope and is dropped. s2 was moved, so nothing
  // happens. s1 goes out of scope and is dropped.

fn gives_ownership() -> String {             // gives_ownership will move its
                                             // return value into the function
                                             // that calls it

    let some_string = String::from("yours"); // some_string comes into scope

    some_string                              // some_string is returned and
                                             // moves out to the calling
                                             // function
}

// This function takes a String and returns one
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
                                                      // scope

    a_string  // a_string is returned and moves out to the calling function
}

如果我们想再拿回之前传入的值,在没有references的情况下,只能再返回出来

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

    let (s2, len) = calculate_length(s1);

    println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() returns the length of a String

    (s, length)
}

Ref:

  1. https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html#what-is-ownership

你可能感兴趣的:(Rust学习笔记之Ownership)