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 中,它则会编译失败。
失败的错误与上一篇文章介绍的变量赋值一样。当我们把外部的变量传递到函数内部时,外部变量的所有权将会转移到函数内部。此时,外部将无法再次使用该变量了。j简易示意过程如下:
向函数传值所有权转移 简易示意图
代码释义:
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 转移——从函数返回值
理解了向函数传递值的过程,那么从函数返回值也是同样的道理。
从函数返回值所有权转移 简易示意图
示例代码:
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 转移与流程控制
再来了解一些其它场景中使用转移。
条件语句
在条件语句中使用转移。由于分支语句肯定会执行一个分支,因此我们可以在每个分支中去使用变量,但只会在一个分支发生转移。
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 所有权的几个核心特点吧:
- 所有权机制仅针对在堆上分配的数据,不包含基本类型。
- 变量与值的关系是绑定关系,变量拥有值的所有权。反过来讲。每一个值都存在一个所有者。每块内存空间都存在一个所有者。
- 当某一个变量离开当前所属的作用域后,变量将变为无效。
- 转移通常发生在变量间的赋值、向函数传值、从函数返回值这三大场景。
从目前来讲,所有的值有且只有一个所有者。