likes
comments
collection
share

【Rust 中级教程】 10 所有权(3)

作者站长头像
站长
· 阅读数 30

0x00 开篇

上一篇文章介绍了所有权在变量间赋值的转移操作,以及详细对比了与其它语言的异同。本篇文章将继续介绍另外两种转移操作——向函数传递值从函数返回值本篇文章的阅读时间大约 8 分钟

0x01 转移——向函数传递值

先上代码:

fn main() {
    let name = String::from("ZhangSan");
    print_name(name);
    println!("{}", name);
}


fn print_name(name: String) {
    println!("My name is {}", name);
}

如果用目前大部分的主流代码的思想去看上面的代码,都会认为没有问题。但是在 Rust 中,它则会编译失败。

【Rust 中级教程】 10 所有权(3)

失败的错误与上一篇文章介绍的变量赋值一样。当我们把外部的变量传递到函数内部时,外部变量的所有权将会转移到函数内部。此时,外部将无法再次使用该变量了。j简易示意过程如下:

【Rust 中级教程】 10 所有权(3)

向函数传值所有权转移 简易示意图

代码释义:

fn main() {
    // 1. 创建 name,name所有权在 main 函数作用域内
    let name = String::from("ZhangSan");
    // 2.
    // 2.1 将 name 以参数形式传递到 print_name 函数中
    // 2.2 name 离开 main 函数作用域,所有权转移到函数内部
    print_name(name);


    // 5.接下来 将无法再使用 name 变量
    // name 变为无效,下面的代码报错
    // println!("{}", name);
	
    // 运行结果
    // My name is ZhangSan
}


fn print_name(name: String) {
    // 3. name 进入 print_name 作用域, 外部将无法访问 name 变量
    println!("My name is {}", name);
} // 4. 函数结束,作用域失效,内存释放,name 变量也被释放

0x02 转移——从函数返回值

理解了向函数传递值的过程,那么从函数返回值也是同样的道理。

【Rust 中级教程】 10 所有权(3)

从函数返回值所有权转移 简易示意图

示例代码:

fn get_name() -> String {
    // 1. 函数内创建 name 变量
    let name = String::from("LiSi");
    return String::from("My name is ") + name.as_str();
} // 2. 作用域结束,内存释放,name释放,将返回值的所有权转移至调用者




fn main() {
	// 3. get_name 返回值的所有权转移至 name
    let name = get_name();
    // 4. 打印name
    println!("{}", name);
    
    // 运行结果
    // My name is LiSi
}

0x03 转移与流程控制

再来了解一些其它场景中使用转移。

 条件语句【Rust 中级教程】 10 所有权(3)

在条件语句中使用转移。由于分支语句肯定会执行一个分支,因此我们可以在每个分支中去使用变量,但只会在一个分支发生转移。

fn print_dance(name: String) {
    println!("{} 喜欢跳舞", name);
}


fn print_swim(name: String) {
    println!("{} 喜欢游泳", name);
}




fn main() {
	let x = 3 % 2;
    let name = String::from("小明");
    if x == 0 {
        print_dance(name)
    } else {
        print_swim(name)
    }
    
    // 无法再次使用 name
    // print_dance(name);
    
    // 运行结果
    // 小明 喜欢游泳


}
循环语句

由于循环体的特殊性,当第二次运行循环体时,上一次循环已经发生转移。所以下面的代码将会编译错误。

fn main() {
    let name = String::from("rust");
    for _ in 1..4 {
        // 下面的代码 是错误的
        // print_dance(name);
    }
}
模式匹配

模式匹配与分支语句类似,不再赘述了,直接看下代码吧。

fn main() {
    // 【匹配】
    let x = 3 % 2;
    let name = String::from("小明");
    match x {
        0 => {
            print_dance(name);
        }
        1 => {
            print_swim(name);
        }
        _ => {
            println!("{}", name);
        }
    };
    // 下面的代码 是错误的
    // println!("{}", name);
    
    // 运行结果
    // 小明 喜欢游泳
}

0x04 小结

有关 Rust 转移的特性基本已经介绍完了。我相信读完文章的你没有感觉到它有多难,只是觉得这样的设计很新奇。总结下 Rust 所有权的几个核心特点吧:

  • 所有权机制仅针对在堆上分配的数据,不包含基本类型。
  • 变量与值的关系是绑定关系,变量拥有值的所有权。反过来讲。每一个值都存在一个所有者每块内存空间都存在一个所有者
  • 当某一个变量离开当前所属的作用域后,变量将变为无效。
  • 转移通常发生在变量间的赋值、向函数传值、从函数返回值这三大场景。
  • 从目前来讲,所有的值有且只有一个所有者。