跳转至

所有权与函数

1. 概述

在语义上,将值传递给函数和把值赋给变量是类似的:将值传递给函数将会发生移动或复制。如下示例

fn main() {
    // 声明String类型变量
    let s = String::from("Hello world");
    // s 被移动到函数里面
    take_ownership(s); // 此行以后,s 不再有效

    // x 是一个i32类型的整数,等于 5
    let x = 5;
    // 因为 i32 实现了 copy trait,所以往函数里传的 x 是一个拷贝
    makes_copy(x); // 此行代码以后,x 依然有效

    println!("x: {}", x)
} // main函数执行完成后,x和s都离开了作用域,因为 s 已经被移动到了 take_ownership 函数里面,所以不在执行内存释放操作

fn take_ownership(some_string: String) {
    println!("{}", some_string)
} // 执行完毕后,some_string 离开了作用域,内存被释放

fn makes_copy(some_number: i32) {
    println!("{}", some_number)
} // 执行完毕后,some_number 离开了作用域,拷贝的值被释放

再看以下的代码

fn main() {
    // s1 变量从 gives_ownershio 函数中获得所有权
    let s1 = gives_ownership();

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

    // s2 将所有权转移给 takes_and_gives_back 函数,并返回一个 String 类型的数据,将所有权转移给 s3
    let s3 = takes_and_gives_back(s2);
}

fn gives_ownership() -> String {
    let some_string = String::from("hello");
    some_string
}

fn takes_and_gives_back(a_string: String) -> String {
    a_string
}

一个变量的所有权总是遵循同样的模式:

  • 把一个值赋给其他变量时就会发生移动

  • 当一个包含 heap 数据的变量离开作用域,他的值就会被 drop 函数清理,除非数据的所有权移动到另一个变量上

2. 让函数使用某个值,但不获得所有权

当我们将变量赋给函数时,所有权会丢失,在函数的最后再将所有权返回,即可实现 调用处保留了参数的所有权

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

    // 在main函数里将s1的所有权移动给calculate_length函数,在 calculate_length 再将所有权移交回来
    let (s1, len) = calculate_length(s1);

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

fn calculate_length(s: String) -> (String, usize) {
    let length = s.len();

    // calculate_length 函数再将s的所有权移动给 调用它的函数
    (s, length)
}

但以上这种做法我们不得不将参数传进函数里,再将参数返回回来,而Rust中的“引用”特性可以完美地解决这个问题。