如何返回含借用值的元组?Rust代码报错解析与修复
问题描述
我想从函数中创建并返回headers和statements,其中statements是一个VecDeque,每个元素都借用headers里的值,写的Rust代码如下:
use csv::{Reader, StringRecord}; use std::{collections::VecDeque, error::Error}; fn main() { let (headers, statements) = run().unwrap(); } fn run<'a>() -> Result<(StringRecord, VecDeque<Statement<'a>>), Box<dyn Error>> { let mut rdr = Reader::from_path("input.csv")?; let headers = rdr.headers()?.clone(); let mut statements = VecDeque::new(); for result in rdr.records() { let record = result?; for (i, v) in record.iter().enumerate() { statements.push_back(Statement::new(&headers[i])); } } Ok((headers, statements)) } struct Statement<'a> { label: &'a str, } impl<'a> Statement<'a> { fn new(label: &'a str) -> Self { Statement { label } } }
代码编译报错,报错信息如下:
error[E0515]: 无法返回引用局部变量`headers`的值 --> src/main.rs:21:5 | 17 | statements.push_back(Statement::new(&headers[i])); | ------- 此处借用了`headers` ... 21 | Ok((headers, statements)) | ^^^^^^^^^^^^^^^^^^^^^^^^^ 返回的值引用了当前函数拥有的数据 error[E0505]: 无法移出`headers`,因为它已被借用 --> src/main.rs:21:9 | 8 | fn run<'a>() -> Result<(StringRecord, VecDeque<Statement<'a>>), Box<dyn Error>> { | -- 生命周期`'a`在此处定义 ... 17 | statements.push_back(Statement::new(&headers[i])); | ------- 此处发生对`headers`的借用 ... 21 | Ok((headers, statements)) | ----^^^^^^^-------------- | | | | | 此处发生`headers`的移出操作 | 返回此值要求`headers`需被借用至生命周期`'a` Some errors have detailed explanations: E0505, E0515. For more information about an error, try `rustc --explain E0505`. warning: `rust` (bin "rust") generated 3 warnings error: could not compile `rust` due to 2 previous errors; 3 warnings emitted
报错原因
这两个错误都是Rust所有权与借用规则的约束导致:
- E0515:
statements中的Statement持有headers的引用,但headers是run函数内的局部变量。函数返回时headers会被销毁,statements里的引用会变成悬垂引用,Rust禁止这种不安全行为。 - E0505:
headers被statements借用后,又尝试将headers移动出函数(作为返回值的一部分)。Rust规则要求,存在活跃引用时,不能移动被借用的变量,否则引用会直接失效。
修复方案
方案1:让Statement持有所有权而非引用
将Statement的label字段从&str改为String,让Statement拥有自己的字符串数据,不再依赖外部变量的生命周期:
use csv::{Reader, StringRecord}; use std::{collections::VecDeque, error::Error}; fn main() { let (headers, statements) = run().unwrap(); } fn run() -> Result<(StringRecord, VecDeque<Statement>), Box<dyn Error>> { let mut rdr = Reader::from_path("input.csv")?; let headers = rdr.headers()?.clone(); let mut statements = VecDeque::new(); for result in rdr.records() { let record = result?; for (i, _) in record.iter().enumerate() { // 克隆header字符串,转移所有权给Statement statements.push_back(Statement::new(headers[i].to_string())); } } Ok((headers, statements)) } struct Statement { label: String, } impl Statement { fn new(label: String) -> Self { Statement { label } } }
方案2:用Rc共享所有权(避免字符串克隆)
如果不想克隆字符串,可以用Rc(单线程场景)共享headers的所有权,Statement持有Rc<str>来复用同一个字符串数据:
use csv::{Reader, StringRecord}; use std::{collections::VecDeque, error::Error, rc::Rc}; fn main() { let (headers, statements) = run().unwrap(); } fn run() -> Result<(Vec<Rc<str>>, VecDeque<Statement>), Box<dyn Error>> { let mut rdr = Reader::from_path("input.csv")?; // 将headers转换为Rc<str>的Vec,方便共享引用 let headers = rdr.headers()? .iter() .map(|s| Rc::from(s)) .collect(); let mut statements = VecDeque::new(); for result in rdr.records() { let record = result?; for (i, _) in record.iter().enumerate() { // 克隆Rc仅增加引用计数,不会复制字符串 statements.push_back(Statement::new(Rc::clone(&headers[i]))); } } Ok((headers, statements)) } struct Statement { label: Rc<str>, } impl Statement { fn new(label: Rc<str>) -> Self { Statement { label } } }
内容的提问来源于stack exchange,提问作者hnb
相关产品推荐
相关产品推荐

