Rust - 所有权和函数

1,711 阅读3分钟

这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

本文主要介绍一下Rust的所有权与函数之间的关系,我们知道将值传递给函数在语义上和给变量赋值时相似的,像函数传递值也可能会移动或者复制,就像变量与数据的两种交互方式一样,下面通过代码示例对Rust所有权和函数之间的关系进行解释说明。

所有权与函数

以下述代码示例对变量何时进入和离开作用域进行说明:

fn main() {
    let s = String::from("hello");  //  变量s进入作用域
    
    move_variable(s);  // s 的值被移动到move_variable函数中
    					// 因此在这里s已经不再有效
    
    let x = 1;  // 变量x进入作用域
    
    makes_copy(x);  // x应该移动到函数中,但是i32类型时copy的,因此x变量的作用域中还可以继续使用x变量
    
} // 这里 x 先移出了作用域,然后是s,但是因为s的值已经被移走,所以对s没有任何操作

fn move_variable(string_str: String) {  // string_str进入作用域
    println!("str is {}", string_str);
}  // string_str移出作用域 Rust调用drop方法回收被string_str占用的内存

fn makes_copy(some_integer: i32) { // some_integer 进入作用域
    println!("{}", some_integer);
} // 这里,some_integer 移出作用域。不会有特殊操作

当尝试在调用 move_variable 后使用 s 时,Rust 会抛出一个编译时错误。这些静态检查使我们免于犯错。小伙伴们可以试试在 main 函数中添加使用 sx 的代码来看看哪里能使用他们,以及所有权规则会在哪里阻止我们这么做。

函数返回值和作用域

函数的返回值也可以转移其所有权,以下述代码为例进行说明:

fn main() {
    let s1 = gives_ownership();         // gives_ownership 将返回值
                                        // 移给 s1

    let s2 = String::from("hello");     // s2 进入作用域

    let s3 = takes_and_gives_back(s2);  // s2 被移动到
                                        // takes_and_gives_back 中,
                                        // 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
  // 所以什么也不会发生。s1 移出作用域并被丢弃

fn gives_ownership() -> String {             // gives_ownership 将返回值移动给
                                             // 调用它的函数

    let some_string = String::from("hello"); // some_string 进入作用域.

    some_string                              // 返回 some_string 并移出给调用的函数
}

// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域

    a_string  // 返回 a_string 并移出给调用的函数
}

变量的所有权总是遵循相同的模式:将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时,其值将通过 drop 被清理掉,除非数据被移动为另一个变量所有。

在每一个函数中都获取所有权并接着返回所有权有些啰嗦。如果我们想要函数使用一个值但不获取所有权该怎么办呢?如果我们还要接着使用它的话,每次都传进去再返回来就有点烦人了,除此之外,我们也可能想返回函数体中产生的一些数据。这种情况下我们可以使用元组来返回多个值:

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() 返回字符串的长度

    (s, length)
}

用官方的话来说这未免有些形式主义,而且这种场景应该很常见。因此,Rust 对此提供了一个功能,叫做 引用references)。

引用这部分知识将会在后续文章中持续更新哦~

结语

文章首发于微信公众号程序媛小庄,同步于掘金

码字不易,转载请说明出处,走过路过的小伙伴们伸出可爱的小指头点个赞再走吧(╹▽╹)