exercises/06_move_semantics
move_semantics1.rs - 基础移动语义
问题代码
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let vec = vec;
vec.push(88);
vec
}
fn main() {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics1() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
assert_eq!(vec1, vec![22, 44, 66, 88]);
}
}
解题要点
- 错误原因: 函数参数
vec 已经移动到函数内部,不需要重新赋值
- 解决方法: 直接使用参数
vec,去掉多余的重新赋值
正确写法
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
vec.push(88);
vec
}
fn main() {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics1() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
assert_eq!(vec1, vec![22, 44, 66, 88]);
}
}
move_semantics2.rs - 所有权保留
问题代码
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec;
vec.push(88);
vec
}
fn main() {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics2() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
assert_eq!(vec0, [22, 44, 66]);
assert_eq!(vec1, [22, 44, 66, 88]);
}
}
解题要点
- 错误原因:
vec0 被移动到 fill_vec 函数后,在测试中无法再使用
- 解决方法: 传递
vec0 的引用而不是所有权,或者克隆向量
正确写法
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
let mut vec = vec;
vec.push(88);
vec
}
fn main() {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics2() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0.clone());
assert_eq!(vec0, [22, 44, 66]);
assert_eq!(vec1, [22, 44, 66, 88]);
}
}
move_semantics3.rs - 可变绑定
问题代码
fn fill_vec(vec: Vec<i32>) -> Vec<i32> {
vec.push(88);
vec
}
fn main() {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics3() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
assert_eq!(vec1, [22, 44, 66, 88]);
}
}
解题要点
- 错误原因: 函数参数
vec 是不可变的,但 push 方法需要可变引用
- 解决方法: 将参数声明为可变的
let mut vec = vec
正确写法
fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
vec.push(88);
vec
}
fn main() {
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn move_semantics3() {
let vec0 = vec![22, 44, 66];
let vec1 = fill_vec(vec0);
assert_eq!(vec1, [22, 44, 66, 88]);
}
}
move_semantics4.rs - 可变引用冲突
问题代码
fn main() {
}
#[cfg(test)]
mod tests {
#[test]
fn move_semantics4() {
let mut x = Vec::new();
let y = &mut x;
let z = &mut x;
y.push(42);
z.push(13);
assert_eq!(x, [42, 13]);
}
}
解题要点
- 错误原因: 同时存在两个可变引用
y 和 z 指向同一个数据,违反借用规则
- 解决方法: 重新排列代码,确保同一时间只有一个可变引用
正确写法
fn main() {
}
#[cfg(test)]
mod tests {
#[test]
fn move_semantics4() {
let mut x = Vec::new();
let y = &mut x;
y.push(42);
let z = &mut x;
z.push(13);
assert_eq!(x, [42, 13]);
}
}
move_semantics5.rs - 引用与所有权
问题代码
#![allow(clippy::ptr_arg)]
fn get_char(data: String) -> char {
data.chars().last().unwrap()
}
fn string_uppercase(mut data: &String) {
data = data.to_uppercase();
println!("{data}");
}
fn main() {
let data = "Rust is great!".to_string();
get_char(data);
string_uppercase(&data);
}
解题要点
- 错误原因:
get_char 不应该获取所有权,应该使用引用;string_uppercase 的参数类型错误
- 解决方法: 调整函数参数的引用类型
正确写法
#![allow(clippy::ptr_arg)]
fn get_char(data: &String) -> char {
data.chars().last().unwrap()
}
fn string_uppercase(mut data: String) {
data = data.to_uppercase();
println!("{data}");
}
fn main() {
let data = "Rust is great!".to_string();
get_char(&data);
string_uppercase(data);
}
知识点汇总
移动语义基础
- 所有权转移: 当值赋给另一个变量时,所有权会发生移动
- 函数参数: 传递给函数的值会被移动,除非使用引用
- 返回值: 函数返回值可以转移所有权给调用者
可变性与借用
- 可变绑定: 使用
mut 关键字声明可变变量
- 可变引用: 使用
&mut 创建可变引用,用于修改数据
- 借用规则: 同一时间只能有一个可变引用或多个不可变引用
引用类型
- 不可变引用: 使用
&T 创建不可变引用,不转移所有权
- 可变引用: 使用
&mut T 创建可变引用,用于修改数据
- 克隆: 使用
.clone() 方法创建值的深拷贝,保留原始所有权