Skip to content

Ownership

  • Rust 的每个值都有 owner
  • 某一时刻只会有一个 owner
  • owner 离开作用域,值会被丢弃

内存分配

字符串字面量无法在程序运行时指定,因此需要 String 类型,它可以:

  • 在运行时分配内存
  • 不再使用时释放内存

Rust 采用有别于传统 GC 的内存清理方式,离开作用域时通过自动调用 drop 回收内存。类似的策略在 C++ 被称为 Resource Acquisition Is Initialization (RAII)。

Move

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

s1 s2 都离开了作用域,那是否会被清空两次?不会,因为 Owner 从 s1 转移到了 s2,这个时候 s1 已经没用了。

Move 就是“浅复制,并把之前的变量无效化”。Rust 默认情况下不会进行深复制。

Clone

堆数据深复制:

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

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

栈数据(如 u32boolchar 等)自动复制:通过 Copy trait 默认复制栈的值。相关传送:Derivable Traits

传参与 return

传参和 return 也是 Owner 转移

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.
rust
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
}

Borrow

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

    let len = calculate_length(&s1);

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

fn calculate_length(s: &String) -> usize {
    s.len()
}

创建一个 reference,其实跟指针一样,把地址传到函数。如上,s1 仍然有效。函数实际不是 Owner,这个行为称为 Borrow。

那么代价是什么?无法修改数据。那岂不是实用性很低,也不是,加上 mut 就可以改了,但是不加 mut 可以借无数次,否则只能借出 1 次,这个设定主要避免 data race。

Mutable Reference

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

    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

Dangling Pointer

Dangling Pointer(悬空指针)顾名思义便是原来的值已经被丢弃,但指针仍然存在,那么它就指向了一个空无一物(甚至被新值占据)的地方,这就算是 Dangling。

Rust 编译器保证不会出现 dangling pointer,编译器不会允许变量先于指针离开作用域。

The Slice Type

Slice Type 是一种特殊 reference,不拥有 ownership。可以从下图直观看出二者关系:

rust reference

rust slice