Rust中Cow含嵌套引用时生命周期报错原因及无侵入修复咨询
Rust生命周期问题分析
示例代码
mod fail { use std::borrow::Cow; pub struct C<'a>(Cow<'a, [&'a ()]>); struct CWrapper<'a>(&'a C<'a>); pub fn foo(c: &C) { let _ = CWrapper(c); } } mod ok_std_cow { use std::borrow::Cow; pub struct C<'a>(Cow<'a, [()]>); struct CWrapper<'a>(&'a C<'a>); pub fn foo(c: &C) { let _ = CWrapper(c); } } mod ok_my_cow { enum MyCow<'a> { Borrowed(&'a [&'a ()]), Owned(Vec<&'a ()>), } pub struct C<'a>(MyCow<'a>); struct CWrapper<'a>(&'a C<'a>); pub fn foo(c: &C) { let _ = CWrapper(c); } }
编译错误信息
error: lifetime may not live long enough --> src/lib.rs:8:26 | 7 | pub fn foo(c: &C) { | - - let's call the lifetime of this reference `'1` | | | has type `&fail::C<'2>` 8 | let _ = CWrapper(c); | ^ this usage requires that `'1` must outlive `'2` | help: consider introducing a named lifetime parameter | 7 | pub fn foo<'a>(c: &'a C<'a>) { | ++++ ++ ++++
问题解答
1. 为什么fail::foo会触发生命周期不足的错误?
核心原因是fail::C<'a>的生命周期不变性:
- 在
fail模块中,C<'a>内部是Cow<'a, [&'a ()]>,Cow的元素类型是&'a (),意味着元素的生命周期和C自身的生命周期'a完全绑定。这导致整个C<'a>类型对生命周期'a是不变的——你不能把C<'long>安全地当成C<'short>来用,因为里面的元素引用&'long ()无法被缩短为&'short ()。 - 当
foo函数接收&C时,编译器会推断出两个独立的生命周期:引用本身的生命周期'1,以及C内部的生命周期'2,也就是参数类型是&'1 C<'2>。 - 而
CWrapper<'a>要求输入是&'a C<'a>,也就是引用的生命周期必须和C内部的生命周期完全一致。但编译器无法保证'1 >= '2('2可能比'1更长),因此抛出生命周期不足的错误。
对比另外两个正常编译的模块:
ok_std_cow中,C<'a>内部是Cow<'a, [()]>,元素是无生命周期的单元类型,C<'a>对'a是协变的——可以安全地将C<'long>缩短为C<'short>,因此&'1 C<'2>能被自动转换为&'1 C<'1>,满足CWrapper的要求。ok_my_cow中,自定义的MyCow<'a>虽然也用了&'a [&'a ()],但Rust对自定义枚举的生命周期协变推断更宽松,没有std库中Cow的隐藏约束,因此编译器允许这种转换。
2. 能否在不修改foo签名、C泛型列表、不重新实现Cow的前提下修复错误?
可以,只需要修改CWrapper的定义,解除引用生命周期和C内部生命周期的绑定:
mod fixed { use std::borrow::Cow; pub struct C<'a>(Cow<'a, [&'a ()]>); // 改为两个独立的生命周期参数 struct CWrapper<'a, 'b>(&'a C<'b>); pub fn foo(c: &C) { let _ = CWrapper(c); } }
原来的CWrapper<'a>(&'a C<'a>)强制要求两个生命周期完全一致,修改为CWrapper<'a, 'b>(&'a C<'b>)后,编译器可以分别推断引用的生命周期'a和C内部的生命周期'b,不需要两者相等,自然就能通过编译。
这个方案完全符合要求:没有修改foo的函数签名,没有改动C的泛型列表,也没有重新实现Cow。
内容的提问来源于stack exchange,提问作者Sprite
相关产品推荐
相关产品推荐

