Rust循环创建结构体向量时触发E0382错误,求解决方案
解决Rust中"borrow of moved value"错误(Advent of Code 2022 Day1练习)
我正在学习Rust,通过Advent of Code 2022年第1天的题目练习。我打算创建一个包含Elf结构体的向量,每个Elf结构体里有一个calory_items向量。我尝试从文本文件解析数据,写了如下代码:
use std::io::{self, BufReader}; use std::path::Path; use std::io::prelude::*; use std::fs::File; struct Elf { calory_items: Vec<i32> } fn parse_meal_list(source: &str) -> Vec<Elf> { let file = File::open(source).unwrap(); let file = BufReader::new(file); let mut elves: Vec<Elf> = Vec::new(); let mut elf = Elf { calory_items: Vec::new() }; for line in file.lines() { let line = line.unwrap(); if line.is_empty() { // create new Elf if elf.calory_items.len() > 0 { // in case there are two empty lines elves.push(elf); let mut elf: Elf = Elf { calory_items: Vec::new() }; } } else { // append to current Elf object the calories let calory_item: i32 = line.parse().unwrap(); elf.calory_items.push(calory_item) } } return elves; } fn main() { let input_day_1_source = Path::new("input_files/day_1.txt"); let input_day_1_source = input_day_1_source.to_str().unwrap(); let Elves: Vec<Elf> = parse_meal_list(input_day_1_source); }
但编译器抛出如下错误:
error[E0382]: borrow of moved value: `elf` --> src/main.rs:23:16 | 15 | let mut elf = Elf { | ------- move occurs because `elf` has type `Elf`, which does not implement the `Copy` trait ... 19 | for line in file.lines() { | ------------------------ inside of this loop ... 23 | if elf.calory_items.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ value borrowed here after move 24 | // in case there are two empty lines 25 | elves.push(elf); | --- value moved here, in previous iteration of loop
我阅读了《Rust编程圣经》中关于借用检查器的内容,但仍不知道如何将元素添加到列表并归还所有权,或通过其他方式解决该问题。
错误原因
执行elves.push(elf)时,elf的所有权会被完全转移到向量elves中,原elf变量随即失效。而你在循环的下一次迭代中又试图访问这个已经失去所有权的变量,触发了借用检查器的错误。另外,你在if块内部重新声明的elf变量仅在块内有效,出了块之后外部的elf仍然处于无效状态。
修复方案
方案1:直接重置elf变量
在将elf推入向量后,直接给外部的elf变量赋值一个新的空Elf实例,不需要重新声明变量:
use std::io::{self, BufReader}; use std::path::Path; use std::io::prelude::*; use std::fs::File; struct Elf { calory_items: Vec<i32> } fn parse_meal_list(source: &str) -> Vec<Elf> { let file = File::open(source).unwrap(); let file = BufReader::new(file); let mut elves: Vec<Elf> = Vec::new(); let mut elf = Elf { calory_items: Vec::new() }; for line in file.lines() { let line = line.unwrap(); if line.is_empty() { if !elf.calory_items.is_empty() { elves.push(elf); // 直接重置外部elf变量 elf = Elf { calory_items: Vec::new() }; } } else { let calory_item: i32 = line.parse().unwrap(); elf.calory_items.push(calory_item) } } // 处理文件末尾没有空行结尾的最后一个Elf if !elf.calory_items.is_empty() { elves.push(elf); } elves } fn main() { let input_day_1_source = Path::new("input_files/day_1.txt"); let input_day_1_source = input_day_1_source.to_str().unwrap(); let elves: Vec<Elf> = parse_meal_list(input_day_1_source); }
方案2:使用std::mem::replace
这个方法可以将当前elf的值替换为新的空实例,同时返回原来的elf值,再推入向量:
// 替换空行处理的代码块 if !elf.calory_items.is_empty() { let old_elf = std::mem::replace(&mut elf, Elf { calory_items: Vec::new() }); elves.push(old_elf); }
额外优化建议
- 给
Elf结构体添加计算总卡路里的方法,适配题目后续需求:
impl Elf { fn total_calories(&self) -> i32 { self.calory_items.iter().sum() } }
- 避免滥用
unwrap(),可以让parse_meal_list返回Result<Vec<Elf>, io::Error>,优雅处理文件读取和解析错误。
内容的提问来源于stack exchange,提问作者Yorian
相关产品推荐
相关产品推荐

