为何Rust会报‘borrowed value does not live long enough’错误?
问题场景
我用Rust编写读取Excel表格数据并填充对象的程序,为了将Excel每行转换为Test对象,我为Test实现了From<std::slice::Iter<'_, calamine::DataType>> trait,代码如下:
use calamine::{open_workbook, DataType, Reader, Xlsx}; use std::convert::From; use std::path::Path; use std::slice::Iter; #[derive(Debug)] pub struct Test { pub field1: String, pub field2: String, pub field3: String, } impl From<Iter<'_, calamine::DataType>> for Test { fn from(mut val: Iter<'_, calamine::DataType>) -> Self { Test { field1: val.next().expect("failed").to_string(), field2: val.next().expect("failed").to_string(), field3: val.next().expect("failed").to_string(), } } } fn read_from_excel_sheet<'a, 'b, T: From<Iter<'a, calamine::DataType>>, P: AsRef<Path>>( wb_path: P, ws_name: &'b str, ) { let mut workbook: Xlsx<_> = open_workbook(wb_path).expect("Cannot open wb"); if let Some(Ok(range)) = workbook.worksheet_range(ws_name) { for row in range.rows().skip(1) { let new_item = T::from(row.iter()); } } }
编译错误
编译时出现如下生命周期错误:
error[E0597]: `range` does not live long enough --> src/main.rs:31:20 | 25 | fn read_from_excel_sheet<'a,'b, T: From<Iter<'a, calamine::DataType>>, P: AsRef<Path>>(wb_path: P, ws_name: &'b str) { | -- lifetime `'a` defined here ... 29 | if let Some(Ok(range)) = workbook.worksheet_range(ws_name) { | ----- binding `range` declared here 30 | 31 | for row in range.rows().skip(1) { | ^^^^^^^^^^^^ borrowed value does not live long enough 32 | 33 | let new_item = T::from(row.iter()); | ------------------- argument requires that `range` is borrowed for `'a` ... 36 | } | - `range` dropped here while still borrowed
我的疑惑
我能理解错误提示的内容,但不清楚range的生命周期为何会影响程序。range确实会在if块结束时被销毁,但此时没有任何引用指向它——new_item在每次迭代中创建并销毁,且Test的所有字段都是拥有所有权的String,完全不依赖任何外部数据。
可行的解决方案
经过多次尝试,我修改了函数的泛型约束,编译成功通过,但不清楚生效原因:
fn read_from_excel_sheet<'b, T: for<'a> From<Iter<'a, calamine::DataType>>, P: AsRef<Path>>( wb_path: P, ws_name: &'b str, ) { let mut workbook: Xlsx<_> = open_workbook(wb_path).expect("Cannot open wb"); if let Some(Ok(range)) = workbook.worksheet_range(ws_name) { for row in range.rows().skip(1) { let new_item = T::from(row.iter()); } } }
原因解析
原代码的问题
原函数中声明了固定的生命周期'a,并要求T实现From<Iter<'a, calamine::DataType>>。编译器会认为,T的From实现必须使用这个全局的'a生命周期,但range的生命周期仅局限在if代码块内,远短于'a的作用域,因此编译器判定range被提前销毁但仍有引用依赖,触发错误。
修改后代码的原理
修改后的代码使用了高阶生命周期(HRTB,Higher-Rank Trait Bounds):for<'a> From<Iter<'a, calamine::DataType>>。这个约束的意思是:T必须能对任意生命周期'a都实现From<Iter<'a, calamine::DataType>>。
这样编译器就不会绑定到某个固定的'a,而是允许每次迭代时使用当前row的短生命周期——正好匹配我们的场景:Test的From实现只是读取迭代器中的数据,转换为拥有所有权的String,不会保留任何对原始数据的引用,完全可以适配任意短生命周期的迭代器。
内容的提问来源于stack exchange,提问作者JUSEOK KO

