实现带存储闭包的Iterator时遇到的Rust生命周期困惑
Rust文本解析迭代器生命周期问题解决
问题背景
我正在编写一个基于&str的简单文本解析工具,想要实现一个函数:它接受闭包和分隔符字符串,返回一个实现Iterator的类型,每次调用next()时执行闭包并跳过分隔符。迭代项类型由闭包决定,因此把闭包签名泛化为FnMut(&mut Parser) -> Option<T>;由于闭包可能借用Parser存储的字符串,需要添加合适的生命周期标注。
当前代码如下:
struct Parser<'a> { string: &'a str, } impl<'a> Parser<'a> { // ... fn at_end(&self) -> bool { // 判断是否到达字符串末尾 false } fn expect(&mut self, _s: &str) -> &mut Self { // 期望下一部分是`s`并推进字符串指针 // ... self } fn parse_iter<'b, T, F>( &'a mut self, separator: &'b str, f: F, ) -> ParseIter<'a, 'b, T, F> where F: FnMut(&mut Parser<'a>) -> Option<T>, { ParseIter { parser: self, func: f, sep: separator, skip: false, } } } struct ParseIter<'c, 'b, T, F> where F: FnMut(&mut Parser<'c>) -> Option<T>, { parser: &'c mut Parser<'c>, func: F, sep: &'b str, skip: bool, } impl<'c, 'b, T, F> Iterator for ParseIter<'c, 'b, T, F> where F: FnMut(&mut Parser<'c>) -> Option<T>, { type Item = T; fn next(&mut self) -> Option<Self::Item> { if self.parser.at_end() { return None; } if std::mem::replace(&mut self.skip, true) { self.parser.expect(self.sep); } (self.func)(self.parser) // 此处报错! } }
遇到的编译错误:
error: lifetime may not live long enough --> util\src\parser.rs:156:9 | 140 | impl<'a, 'b, T, F> Iterator for ParseIter<'a, 'b, T, F> | -- lifetime `'a` defined here ... 147 | fn next(&mut self) -> Option<Self::Item> { | - let's call the lifetime of this reference `'1` ... 156 | (self.func)(self.parser) | ^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`
我无法理解错误本质:&mut self本身包含生命周期为'a的引用,不可能比'a更久,为何要求它的生命周期超过'a?需要:1. 修改代码使其编译通过的方法;2. 最少需要多少显式生命周期标注。
错误原因分析
核心问题在于你把Parser内部字符串的生命周期和ParseIter持有Parser的可变引用的生命周期绑定成了同一个'a,导致编译器产生了生命周期冲突:
- 闭包要求接收的是
&'a mut Parser<'a>,意味着这个可变引用的生命周期必须和Parser内部字符串的生命周期完全一致; - 但在
next方法中,self.parser是从&mut self(临时生命周期'1)中取出的引用,它的生命周期是'1,远短于'a; - 编译器因此认为
'1必须长于'a,这显然不可能,所以报错。
解决方案
拆分生命周期,将Parser内部字符串的生命周期('a)与ParseIter持有Parser的可变引用的生命周期('c)分开,让闭包只依赖Parser内部字符串的生命周期,而不是绑定到可变引用的生命周期。
修改后的完整代码:
struct Parser<'a> { string: &'a str, } impl<'a> Parser<'a> { fn at_end(&self) -> bool { false } fn expect(&mut self, _s: &str) -> &mut Self { self } // 拆分生命周期:'c是ParseIter持有Parser的可变引用的周期,'a是Parser内部字符串的周期 fn parse_iter<'c: 'a, 'b, T, F>( &'c mut self, separator: &'b str, f: F, ) -> ParseIter<'a, 'c, 'b, T, F> where F: FnMut(&mut Parser<'a>) -> Option<T>, { ParseIter { parser: self, func: f, sep: separator, skip: false, } } } // 明确三个生命周期:'a是字符串周期,'c是Parser可变引用的周期,'b是分隔符周期 struct ParseIter<'a, 'c, 'b, T, F> where 'c: 'a, // 确保可变引用的周期不短于字符串周期 F: FnMut(&mut Parser<'a>) -> Option<T>, { parser: &'c mut Parser<'a>, func: F, sep: &'b str, skip: bool, } impl<'a, 'c, 'b, T, F> Iterator for ParseIter<'a, 'c, 'b, T, F> where 'c: 'a, F: FnMut(&mut Parser<'a>) -> Option<T>, { type Item = T; fn next(&mut self) -> Option<Self::Item> { if self.parser.at_end() { return None; } if std::mem::replace(&mut self.skip, true) { self.parser.expect(self.sep); } // 现在传递的是&mut Parser<'a>,生命周期匹配闭包要求 (self.func)(&mut *self.parser) } }
最少生命周期标注版本
可以进一步简化,利用Rust的生命周期推断,只保留必要的标注:
struct Parser<'a> { string: &'a str, } impl<'a> Parser<'a> { fn at_end(&self) -> bool { false } fn expect(&mut self, _s: &str) -> &mut Self { self } // 仅标注必要的生命周期:'a是字符串周期,'b是分隔符周期 fn parse_iter<'b, T, F>( &mut self, separator: &'b str, f: F, ) -> ParseIter<'a, '_, 'b, T, F> where F: FnMut(&mut Parser<'a>) -> Option<T>, { ParseIter { parser: self, func: f, sep: separator, skip: false, } } } // 只标注必须的生命周期,其余用'_推断 struct ParseIter<'a, 'c, 'b, T, F> where F: FnMut(&mut Parser<'a>) -> Option<T>, { parser: &'c mut Parser<'a>, func: F, sep: &'b str, skip: bool, } impl<'a, 'c, 'b, T, F> Iterator for ParseIter<'a, 'c, 'b, T, F> where F: FnMut(&mut Parser<'a>) -> Option<T>, { type Item = T; fn next(&mut self) -> Option<Self::Item> { if self.parser.at_end() { return None; } if std::mem::replace(&mut self.skip, true) { self.parser.expect(self.sep); } (self.func)(&mut *self.parser) } }
这里用'_替代了可以被推断的生命周期'c,同时确保闭包只依赖Parser<'a>,避免了之前的生命周期绑定冲突。
内容的提问来源于stack exchange,提问作者oisyn
相关产品推荐
相关产品推荐

