带生命周期的可变方法与后续引用的编译错误分析及解决方案咨询
咱们一步步拆解这个问题,先搞清楚两个编译错误的根源,再找到靠谱的解决办法。
第一个编译错误的原因
你最初的do_stuff方法用的是&mut self,编译器会给这个可变引用分配一个临时生命周期'1——也就是这个方法调用持续的那段时间。而你的subroutine方法要求传入的迭代器元素是(&'a u8, &'a u8),这里的'a是Foo结构体定义时绑定的生命周期(和borrowed字段的生命周期一致)。
但问题在于:zipped是从self.owned_one和self.owned_two迭代出来的,这两个Vec的引用生命周期是'1(因为它们是从&'1 mut Foo<'a>里取出来的),而subroutine要求它们的生命周期必须是'a。显然'1比'a短得多('a是整个Foo实例的生命周期,'1只是方法调用的一瞬间),所以编译器才会报错说'1必须比'a长,这显然做不到。
改成&'a mut self后为啥又报错?
当你把do_stuff的参数改成&'a mut self,相当于明确告诉编译器:这个可变借用的生命周期和Foo实例的'a生命周期完全一样长。也就是说,调用foo.do_stuff()之后,foo被可变借用了,而且这个借用会一直持续到foo的生命周期结束(也就是main函数的末尾)。
这直接违反了Rust的核心借用规则:同一时间不能同时存在可变借用和不可变借用。所以后面的let _out = &foo.output就会触发编译错误——此时foo还被可变借用着呢,自然不能再做不可变借用。
正确的解决方案
核心思路是:subroutine根本不需要和Foo的'a生命周期绑定,它只需要迭代器的两个元素拥有相同的生命周期就行。所以我们可以给subroutine单独定义一个生命周期参数,让它和临时引用的生命周期关联,而不是复用Foo的'a。
修改后的代码如下:
struct Foo<'a> { borrowed: &'a u8, owned_one: Vec<u8>, owned_two: Vec<u8>, output: usize } impl<'a> Foo<'a> { fn do_stuff(&mut self) { self.output = self.owned_one.len(); let zipped = self.owned_one.iter().zip(self.owned_two.iter()); Self::subroutine(&zipped); } // 给subroutine添加独立的生命周期参数'b,让迭代器元素的生命周期与它绑定 fn subroutine<'b, Arg: Iterator<Item=(&'b u8, &'b u8)>>(_zipped: &Arg) {} } fn main() { let num = 0u8; let mut foo = Foo { borrowed: &num, owned_one: vec![0], owned_two: vec![1], output: 0 }; foo.do_stuff(); let _out = &foo.output; }
这样修改后,编译器会在do_stuff调用subroutine时,自动推断'b是self的临时生命周期'1,既满足了subroutine对迭代器元素生命周期的要求,又不会把self的可变借用延长到整个'a生命周期。后面的&foo.output就能正常编译了。
总结一下
- 不要随便把方法的
self生命周期和结构体的生命周期绑定,除非你真的需要让借用持续整个结构体的生命周期 - 当方法需要处理临时引用时,给方法单独定义生命周期参数,让编译器自行推断合适的生命周期,这才是更灵活的做法
备注:内容来源于stack exchange,提问作者Chowlett

