Rust中rusqlite Statement借用值生命周期不足问题排查
31 | let mut mapped_rows = get_mapped_rows(&mut get_stmt);
| ^^^^^^^^^^^^^ borrowed value does not live long enough
...
| } |
|---|
get_stmt dropped here while still borrowed |
borrow might be used here, when get_stmt is dropped and runs the Drop code for type Statement |
--- # 问题2:传入两个不同Statement的生命周期冲突 最初尝试向函数传入两个不同的`Statement`,代码如下: ```rust fn get_rows<'stmt>( i: u32, get_stmt_0: &'stmt mut Statement<'_>, get_stmt_1: &'stmt mut Statement<'_>, ) -> Rows<'stmt> { let stmt = if i < 42 { get_stmt_0 } else { get_stmt_1 }; stmt .query(params![0, 0]) .unwrap() }
编译器报错:
error: lifetime may not live long enough --> src/main.rs:13:9 | 9 | get_stmt_0: &'stmt mut Statement<'_>, | ---------- has type `&mut Statement<'1>` 10 | get_stmt_1: &'stmt mut Statement<'_>, | ---------- has type `&mut Statement<'2>` ... 13 | get_stmt_0 | ^^^^^^^^^^ assignment requires that `'1` must outlive `'2` | = note: requirement occurs because of a mutable reference to `Statement<'_>` = note: mutable references are invariant over their type parameter error: lifetime may not live long enough --> src/main.rs:15:9 | 9 | get_stmt_0: &'stmt mut Statement<'_>, | ---------- has type `&mut Statement<'1>` 10 | get_stmt_1: &'stmt mut Statement<'_>, | ---------- has type `&mut Statement<'2>` ... 15 | get_stmt_1 | ^^^^^^^^^^ assignment requires that `'2` must outlive `'1` | = note: requirement occurs because of a mutable reference to `Statement<'_>` = note: mutable references are invariant over their type parameter
尝试引入额外生命周期参数'a并添加约束,仍得到相同错误:
fn get_rows<'stmt, 'a>( i: u32, get_stmt_0: &'a mut Statement<'_>, get_stmt_1: &'a mut Statement<'_>, ) -> Rows<'stmt> where 'a: 'stmt { let stmt = if i < 42 { get_stmt_0 } else { get_stmt_1 }; stmt .query(params![0, 0]) .unwrap() }
针对问题1的修复
问题出在函数签名中错误地将Statement的内部生命周期(与创建它的Connection绑定)和引用的生命周期(对Statement可变引用的存活时间)绑定为同一个'stmt。这种绑定会让编译器认为返回的迭代器需要和Statement的内部生命周期完全一致,而Statement的Drop逻辑可能会在迭代器未销毁时执行,从而触发生命周期冲突。
修改函数签名,将两个生命周期分开(或直接让编译器自动推导):
fn get_mapped_rows(get_stmt: &mut Statement<'_>) -> Peekable<MappedRows<'_, impl FnMut(&Row<'_>) -> Result<i64, Error>>> { get_stmt .query(params![0, 0]) .unwrap() .mapped(|row| { Ok(row.get(0)?) }) .peekable() }
或者显式声明两个独立的生命周期参数,让编译器明确它们的关系:
fn get_mapped_rows<'a, 'b>(get_stmt: &'a mut Statement<'b>) -> Peekable<MappedRows<'a, impl FnMut(&Row<'_>) -> Result<i64, Error>>> { // 函数内容不变 }
这样编译器就能正确推导:迭代器的生命周期仅依赖于对Statement的引用生命周期,而Statement的内部生命周期只需长于引用生命周期即可,避免了Drop阶段的冲突。
针对问题2的修复
错误原因是编译器将两个Statement的内部生命周期推导为不同的'1和'2,而可变引用的不变性要求赋值给同一个变量的两个类型必须完全一致,因此编译器要求两个生命周期互相包含,这显然不可能。
修复方法是显式指定两个Statement的内部生命周期为同一个(因为它们都来自同一个Connection,内部生命周期本来就相同):
fn get_rows<'stmt, 'conn>( i: u32, get_stmt_0: &'stmt mut Statement<'conn>, get_stmt_1: &'stmt mut Statement<'conn>, ) -> Rows<'stmt> { let stmt = if i < 42 { get_stmt_0 } else { get_stmt_1 }; stmt.query(params![0, 0]).unwrap() }
通过将两个Statement的内部生命周期绑定到同一个'conn,编译器确认它们的类型一致,即可正常通过编译。不需要额外的where约束,因为Rows<'stmt>的生命周期已经由引用的'stmt参数保证足够长。
内容的提问来源于stack exchange,提问作者Marcus Johansson

