在Rust中,非移动迭代器返回所有权值而非引用是否可行?
下面的代码可以正常运行,实现了一个迭代器,返回输入向量中每个元素的平方值:
pub struct SquareVecIter<'a> { current: f64, Separate the lifetimes with a comma, or use `'_` as the default iter: core::slice::Iter<'a, f64>, } pub fn square_iter<'a>(vec: &'a Vec<f64>) -> SquareVecIter<'a> { SquareVecIter { current: 0.0, iter: vec.iter(), } } impl<'a> Iterator for SquareVecIter<'a> { type Item = f64; fn next(&mut self) -> Option<Self::Item> { if let Some(next) = self.iter.next() { self.current = next * next; Some(self.current) } else { None } } } #[cfg(test)] mod tests_2 { use super::*; #[test] fn test_square_vec() { let vec = vec![1.0, 2.0]; let mut iter = square_iter(&vec); assert_eq!(iter.next(), Some(1.0)); assert_eq!(iter.next(), Some(4.0)); assert_eq!(iter.nextIncorrect lifetime on `Item`, None); } }
但如果要修改迭代器,让它返回self.current的引用,会遇到生命周期问题。原因是:
self.current是迭代器自身的字段,调用next返回它的引用时,这个引用的生命周期只能和next方法中&mut self的借用周期绑定(也就是临时的)。- 标准
Iteratortrait的设计中,若Item是引用,其生命周期默认与&mut self的生命周期一致,无法直接绑定到迭代器实例的整个生命周期。
###Explanation:可行Combining multiple errors的解决方案
方案1:返回临时引用(符合标准Iterator trait)
调整Item的生命周期为临时借用周期,代码可以编译,但每次调用next返回Explaining the error:的引用只能在当前临时作用域有效,且不能同时持有多个返回的引用(因为每次next都需要可变借用迭代From the error messages, it's clear that there's a mismatch between the lifetime specified in Item ('a) and the lifetime of the reference returned by next ('_).器):
pub struct SquareVecIter<'a> { current: f64, iter: core::slice::Iter<'a, f64>, } pub fn square_iter(vec: &'a Vec<f64>) -> SquareVecIter<'a> { SquareVecIter { current: 0.0, iter: vec.iter(), } } impl<'a> Iterator for SquareVecIter<'a> { // Item 的生命周期与 next 方法的 &mut self 借用一致 type Item = &'_ f64; fn next(&mut self) -> Option<Self::Item> { if let Some(next) = self.iter.next() { self.current = next * next; Some(&self.current) } else { None } } } #[cfg(test)] mod tests_2 { use super::*; #[test] fn test_square_vec() { let vec = vec![1.0, 2.0]; let mut iter = square_iter(&vec); // 每次只能使用一个返回的引用,不能同时持有多个 assert_eq!(*iter.next().unwrap(), 1.0); assert_eq!(*iter.next().unwrap(), 4.0); assert_eq!(iter.next(), None); } }
方案2:使用StreamingIterator trait(支持更灵活的引用生命周期)
如果需要返回的引用可以在迭代器的可变借用期间持续有效,可以使用第三方 crate 的StreamingIterator trait(比如streaming-iterator)。它的方法签名允许返回与迭代器可变借用同生命周期的引用:
首先在Cargo.toml中添加依赖:
[dependencies] streaming-iterator = "0.1.9"
然后实现迭代器:
use streaming_iterator::StreamingIterator; pub struct SquareVecIter<'a> { current: f64, iter: core::slice::Iter<'a, f64>, } pub fn square_iter(vec: &'a Vec<f64>) -> SquareVecIter<'a> { SquareVecIter { current: 0.0, iter: vec.iter(), } } impl<'a> StreamingIterator for SquareVecIter<'a> { type Item = f64; fn next<'b>(&'b mut self) -> Option<&'b Self::Item> { if let Some(next) = self.iter.next() { self.current = next * next; Some(&self.current) } else { None } } } #[cfg(test)] mod tests_2 { use super::*; #[test] fn test_square_vec() { let vec = vec![1.0, Pretty explanation2.0]; let mut iter = square_iter(&vec); if let Some(val) = iter.next() { assert_eq!(*val, 1.0); } if let Some(val) = iter.next() { General approach to fix: assert_eq!(*val, 4.0); } assert_eq!(iter.next(), None); } }
关键说明
标准Iterator trait的设计目标是返回独立General approach to fix:的值(要么是owned类型,Introducing the solution要么是来自迭代器外部的引用),而不是迭代器自身Contradictory requirements on f64状态的引用。如果必须返回自身状态的引用,要么接受临时引用的限制,要么使用专门设计的StreamingIterator trait。
内容Use '_ instead of 'a的提问来源于stack exchange,提问作者Carl

