如何编写 returns 引用自身的迭代器?
How do I write an iterator that returns references to itself?
我无法表达 Iterator
实现的 return 值的生命周期。如何在不更改迭代器的 return 值的情况下编译此代码?我希望它成为 return 一个引用向量。
很明显我没有正确使用生命周期参数,但在尝试了各种方法后我放弃了,我不知道该怎么做。
use std::iter::Iterator;
struct PermutationIterator<T> {
vs: Vec<Vec<T>>,
is: Vec<usize>,
}
impl<T> PermutationIterator<T> {
fn new() -> PermutationIterator<T> {
PermutationIterator {
vs: vec![],
is: vec![],
}
}
fn add(&mut self, v: Vec<T>) {
self.vs.push(v);
self.is.push(0);
}
}
impl<T> Iterator for PermutationIterator<T> {
type Item = Vec<&'a T>;
fn next(&mut self) -> Option<Vec<&T>> {
'outer: loop {
for i in 0..self.vs.len() {
if self.is[i] >= self.vs[i].len() {
if i == 0 {
return None; // we are done
}
self.is[i] = 0;
self.is[i - 1] += 1;
continue 'outer;
}
}
let mut result = vec![];
for i in 0..self.vs.len() {
let index = self.is[i];
result.push(self.vs[i].get(index).unwrap());
}
*self.is.last_mut().unwrap() += 1;
return Some(result);
}
}
}
fn main() {
let v1: Vec<_> = (1..3).collect();
let v2: Vec<_> = (3..5).collect();
let v3: Vec<_> = (1..6).collect();
let mut i = PermutationIterator::new();
i.add(v1);
i.add(v2);
i.add(v3);
loop {
match i.next() {
Some(v) => {
println!("{:?}", v);
}
None => {
break;
}
}
}
}
error[E0261]: use of undeclared lifetime name `'a`
--> src/main.rs:23:22
|
23 | type Item = Vec<&'a T>;
| ^^ undeclared lifetime
据我了解,您希望迭代器 return 指向自身的引用向量,对吗?不幸的是,这在 Rust 中是不可能的。
这是修剪后的 Iterator
特征:
trait Iterator {
type Item;
fn next(&mut self) -> Option<Item>;
}
请注意 &mut self
和 Option<Item>
之间没有生命周期连接。这意味着 next()
方法不能 return 引用迭代器本身。您只是无法表达 returned 引用的生命周期。这基本上就是您找不到指定正确生命周期的方法的原因 - 它看起来像这样:
fn next<'a>(&'a mut self) -> Option<Vec<&'a T>>
除了这不是 Iterator
特征的有效 next()
方法。
此类迭代器(可以 return 引用自身的迭代器)称为 流式迭代器。如果需要,您可以找到更多 here, here and here。
Update. 但是,您可以 return 从您的迭代器中引用一些其他结构 - 这就是大多数集合迭代器的工作方式。它可能看起来像这样:
pub struct PermutationIterator<'a, T> {
vs: &'a [Vec<T>],
is: Vec<usize>
}
impl<'a, T> Iterator for PermutationIterator<'a, T> {
type Item = Vec<&'a T>;
fn next(&mut self) -> Option<Vec<&'a T>> {
...
}
}
请注意生命周期 'a
现在是如何在 impl
块上声明的。这样做是可以的(实际上是必需的),因为您需要在结构上指定生命周期参数。然后,您可以在 Item
和 next()
return 类型中使用相同的 'a
。同样,这就是大多数集合迭代器的工作方式。
解释 为什么 您的代码无法编译的方式是正确的。简而言之,它表示迭代器不能从自身内部产生借来的值。
但是,它可以从其他东西中产生借来的值。这就是 Vec
和 Iter
所实现的:Vec
拥有值,而 Iter
只是一个能够在 [=12= 中产生引用的包装器].
这是一个实现你想要的设计。迭代器与 Vec
和 Iter
一样,只是对实际拥有值的其他容器的包装。
use std::iter::Iterator;
struct PermutationIterator<'a, T: 'a> {
vs : Vec<&'a [T]>,
is : Vec<usize>
}
impl<'a, T> PermutationIterator<'a, T> {
fn new() -> PermutationIterator<'a, T> { ... }
fn add(&mut self, v : &'a [T]) { ... }
}
impl<'a, T> Iterator for PermutationIterator<'a, T> {
type Item = Vec<&'a T>;
fn next(&mut self) -> Option<Vec<&'a T>> { ... }
}
fn main() {
let v1 : Vec<i32> = (1..3).collect();
let v2 : Vec<i32> = (3..5).collect();
let v3 : Vec<i32> = (1..6).collect();
let mut i = PermutationIterator::new();
i.add(&v1);
i.add(&v2);
i.add(&v3);
loop {
match i.next() {
Some(v) => { println!("{:?}", v); }
None => {break;}
}
}
}
与您最初的问题无关。如果这只是我,我会确保所有借用的矢量都立即被使用。这个想法是删除对 add
的重复调用,并在构造时直接传递所有借用的向量:
use std::iter::{Iterator, repeat};
struct PermutationIterator<'a, T: 'a> {
...
}
impl<'a, T> PermutationIterator<'a, T> {
fn new(vs: Vec<&'a [T]>) -> PermutationIterator<'a, T> {
let n = vs.len();
PermutationIterator {
vs: vs,
is: repeat(0).take(n).collect(),
}
}
}
impl<'a, T> Iterator for PermutationIterator<'a, T> {
...
}
fn main() {
let v1 : Vec<i32> = (1..3).collect();
let v2 : Vec<i32> = (3..5).collect();
let v3 : Vec<i32> = (1..6).collect();
let vall: Vec<&[i32]> = vec![&v1, &v2, &v3];
let mut i = PermutationIterator::new(vall);
}
(编辑:将迭代器设计更改为采用 Vec<&'a [T]>
而不是 Vec<Vec<&'a T>>
。引用容器比构建容器更容易参考文献的容器。)
如其他答案中所述,这称为 流式迭代器 ,它需要与 Rust 的 Iterator
不同的保证。一个提供这种功能的板条箱被恰当地称为 streaming-iterator and it provides the StreamingIterator
trait.
这是一个实现特征的例子:
extern crate streaming_iterator;
use streaming_iterator::StreamingIterator;
struct Demonstration {
scores: Vec<i32>,
position: usize,
}
// Since `StreamingIterator` requires that we be able to call
// `advance` before `get`, we have to start "before" the first
// element. We assume that there will never be the maximum number of
// entries in the `Vec`, so we use `usize::MAX` as our sentinel value.
impl Demonstration {
fn new() -> Self {
Demonstration {
scores: vec![1, 2, 3],
position: std::usize::MAX,
}
}
fn reset(&mut self) {
self.position = std::usize::MAX;
}
}
impl StreamingIterator for Demonstration {
type Item = i32;
fn advance(&mut self) {
self.position = self.position.wrapping_add(1);
}
fn get(&self) -> Option<&Self::Item> {
self.scores.get(self.position)
}
}
fn main() {
let mut example = Demonstration::new();
loop {
example.advance();
match example.get() {
Some(v) => {
println!("v: {}", v);
}
None => break,
}
}
example.reset();
loop {
example.advance();
match example.get() {
Some(v) => {
println!("v: {}", v);
}
None => break,
}
}
}
不幸的是,在实现 RFC 1598 中的 generic associated types (GATs) 之前,流式迭代器将受到限制。
我不久前写了这段代码,不知何故在这里偶然发现了这个问题。它完全按照问题的要求进行操作:它展示了如何实现一个迭代器,该迭代器将其回调传递给自身的引用。
它向 IntoIterator
个实例添加了一个 .iter_map()
方法。最初我认为它应该为 Iterator
本身实现,但这是一个不太灵活的设计决定。
我为它创建了一个小箱子并将我的代码发布到 GitHub 如果你想试验它,你 can find it here.
WRT OP 在定义项目的生命周期方面遇到了麻烦,我没有 运行 在依赖默认的省略生命周期的同时实现这一点。
这是一个用法示例。请注意回调接收的参数是迭代器本身,回调应该从中提取数据并按原样传递或执行任何其他操作。
use iter_map::IntoIterMap;
let mut b = true;
let s = "hello world!".chars().peekable().iter_map(|iter| {
if let Some(&ch) = iter.peek() {
if ch == 'o' && b {
b = false;
Some('0')
} else {
b = true;
iter.next()
}
} else { None }
}).collect::<String>();
assert_eq!(&s, "hell0o w0orld!");
因为 IntoIterMap
通用特性是为 IntoIterator
实现的,所以您可以从任何支持该接口的东西中获得“iter 映射”。例如,可以直接从数组中创建一个,如下所示:
use iter_map::*;
fn main()
{
let mut i = 0;
let v = [1, 2, 3, 4, 5, 6].iter_map(move |iter| {
i += 1;
if i % 3 == 0 {
Some(0)
} else {
iter.next().copied()
}
}).collect::<Vec<_>>();
assert_eq!(v, vec![1, 2, 0, 3, 4, 0, 5, 6, 0]);
}
这是完整的代码 - 用这么少的代码来实现真是太神奇了,而且在将它们放在一起时一切似乎都运行得很顺利。它让我对 Rust 本身的灵活性及其设计决策有了新的认识。
/// Adds `.iter_map()` method to all IntoIterator classes.
///
impl<F, I, J, R, T> IntoIterMap<F, I, R, T> for J
//
where F: FnMut(&mut I) -> Option<R>,
I: Iterator<Item = T>,
J: IntoIterator<Item = T, IntoIter = I>,
{
/// Returns an iterator that invokes the callback in `.next()`, passing it
/// the original iterator as an argument. The callback can return any
/// arbitrary type within an `Option`.
///
fn iter_map(self, callback: F) -> ParamFromFnIter<F, I>
{
ParamFromFnIter::new(self.into_iter(), callback)
}
}
/// A trait to add the `.iter_map()` method to any existing class.
///
pub trait IntoIterMap<F, I, R, T>
//
where F: FnMut(&mut I) -> Option<R>,
I: Iterator<Item = T>,
{
/// Returns a `ParamFromFnIter` iterator which wraps the iterator it's
/// invoked on.
///
/// # Arguments
/// * `callback` - The callback that gets invoked by `.next()`.
/// This callback is passed the original iterator as its
/// parameter.
///
fn iter_map(self, callback: F) -> ParamFromFnIter<F, I>;
}
/// Implements an iterator that can be created from a callback.
/// does pretty much the same thing as `std::iter::from_fn()` except the
/// callback signature of this class takes a data argument.
pub struct ParamFromFnIter<F, D>
{
callback: F,
data: D,
}
impl<F, D, R> ParamFromFnIter<F, D>
//
where F: FnMut(&mut D) -> Option<R>,
{
/// Creates a new `ParamFromFnIter` iterator instance.
///
/// This provides a flexible and simple way to create new iterators by
/// defining a callback.
/// # Arguments
/// * `data` - Data that will be passed to the callback on each
/// invocation.
/// * `callback` - The callback that gets invoked when `.next()` is invoked
/// on the returned iterator.
///
pub fn new(data: D, callback: F) -> Self
{
ParamFromFnIter { callback, data }
}
}
/// Implements Iterator for ParamFromFnIter.
///
impl<F, D, R> Iterator for ParamFromFnIter<F, D>
//
where F: FnMut(&mut D) -> Option<R>,
{
type Item = R;
/// Iterator method that returns the next item.
/// Invokes the client code provided iterator, passing it `&mut self.data`.
///
fn next(&mut self) -> Option<Self::Item>
{
(self.callback)(&mut self.data)
}
}
我无法表达 Iterator
实现的 return 值的生命周期。如何在不更改迭代器的 return 值的情况下编译此代码?我希望它成为 return 一个引用向量。
很明显我没有正确使用生命周期参数,但在尝试了各种方法后我放弃了,我不知道该怎么做。
use std::iter::Iterator;
struct PermutationIterator<T> {
vs: Vec<Vec<T>>,
is: Vec<usize>,
}
impl<T> PermutationIterator<T> {
fn new() -> PermutationIterator<T> {
PermutationIterator {
vs: vec![],
is: vec![],
}
}
fn add(&mut self, v: Vec<T>) {
self.vs.push(v);
self.is.push(0);
}
}
impl<T> Iterator for PermutationIterator<T> {
type Item = Vec<&'a T>;
fn next(&mut self) -> Option<Vec<&T>> {
'outer: loop {
for i in 0..self.vs.len() {
if self.is[i] >= self.vs[i].len() {
if i == 0 {
return None; // we are done
}
self.is[i] = 0;
self.is[i - 1] += 1;
continue 'outer;
}
}
let mut result = vec![];
for i in 0..self.vs.len() {
let index = self.is[i];
result.push(self.vs[i].get(index).unwrap());
}
*self.is.last_mut().unwrap() += 1;
return Some(result);
}
}
}
fn main() {
let v1: Vec<_> = (1..3).collect();
let v2: Vec<_> = (3..5).collect();
let v3: Vec<_> = (1..6).collect();
let mut i = PermutationIterator::new();
i.add(v1);
i.add(v2);
i.add(v3);
loop {
match i.next() {
Some(v) => {
println!("{:?}", v);
}
None => {
break;
}
}
}
}
error[E0261]: use of undeclared lifetime name `'a`
--> src/main.rs:23:22
|
23 | type Item = Vec<&'a T>;
| ^^ undeclared lifetime
据我了解,您希望迭代器 return 指向自身的引用向量,对吗?不幸的是,这在 Rust 中是不可能的。
这是修剪后的 Iterator
特征:
trait Iterator {
type Item;
fn next(&mut self) -> Option<Item>;
}
请注意 &mut self
和 Option<Item>
之间没有生命周期连接。这意味着 next()
方法不能 return 引用迭代器本身。您只是无法表达 returned 引用的生命周期。这基本上就是您找不到指定正确生命周期的方法的原因 - 它看起来像这样:
fn next<'a>(&'a mut self) -> Option<Vec<&'a T>>
除了这不是 Iterator
特征的有效 next()
方法。
此类迭代器(可以 return 引用自身的迭代器)称为 流式迭代器。如果需要,您可以找到更多 here, here and here。
Update. 但是,您可以 return 从您的迭代器中引用一些其他结构 - 这就是大多数集合迭代器的工作方式。它可能看起来像这样:
pub struct PermutationIterator<'a, T> {
vs: &'a [Vec<T>],
is: Vec<usize>
}
impl<'a, T> Iterator for PermutationIterator<'a, T> {
type Item = Vec<&'a T>;
fn next(&mut self) -> Option<Vec<&'a T>> {
...
}
}
请注意生命周期 'a
现在是如何在 impl
块上声明的。这样做是可以的(实际上是必需的),因为您需要在结构上指定生命周期参数。然后,您可以在 Item
和 next()
return 类型中使用相同的 'a
。同样,这就是大多数集合迭代器的工作方式。
但是,它可以从其他东西中产生借来的值。这就是 Vec
和 Iter
所实现的:Vec
拥有值,而 Iter
只是一个能够在 [=12= 中产生引用的包装器].
这是一个实现你想要的设计。迭代器与 Vec
和 Iter
一样,只是对实际拥有值的其他容器的包装。
use std::iter::Iterator;
struct PermutationIterator<'a, T: 'a> {
vs : Vec<&'a [T]>,
is : Vec<usize>
}
impl<'a, T> PermutationIterator<'a, T> {
fn new() -> PermutationIterator<'a, T> { ... }
fn add(&mut self, v : &'a [T]) { ... }
}
impl<'a, T> Iterator for PermutationIterator<'a, T> {
type Item = Vec<&'a T>;
fn next(&mut self) -> Option<Vec<&'a T>> { ... }
}
fn main() {
let v1 : Vec<i32> = (1..3).collect();
let v2 : Vec<i32> = (3..5).collect();
let v3 : Vec<i32> = (1..6).collect();
let mut i = PermutationIterator::new();
i.add(&v1);
i.add(&v2);
i.add(&v3);
loop {
match i.next() {
Some(v) => { println!("{:?}", v); }
None => {break;}
}
}
}
与您最初的问题无关。如果这只是我,我会确保所有借用的矢量都立即被使用。这个想法是删除对 add
的重复调用,并在构造时直接传递所有借用的向量:
use std::iter::{Iterator, repeat};
struct PermutationIterator<'a, T: 'a> {
...
}
impl<'a, T> PermutationIterator<'a, T> {
fn new(vs: Vec<&'a [T]>) -> PermutationIterator<'a, T> {
let n = vs.len();
PermutationIterator {
vs: vs,
is: repeat(0).take(n).collect(),
}
}
}
impl<'a, T> Iterator for PermutationIterator<'a, T> {
...
}
fn main() {
let v1 : Vec<i32> = (1..3).collect();
let v2 : Vec<i32> = (3..5).collect();
let v3 : Vec<i32> = (1..6).collect();
let vall: Vec<&[i32]> = vec![&v1, &v2, &v3];
let mut i = PermutationIterator::new(vall);
}
(编辑:将迭代器设计更改为采用 Vec<&'a [T]>
而不是 Vec<Vec<&'a T>>
。引用容器比构建容器更容易参考文献的容器。)
如其他答案中所述,这称为 流式迭代器 ,它需要与 Rust 的 Iterator
不同的保证。一个提供这种功能的板条箱被恰当地称为 streaming-iterator and it provides the StreamingIterator
trait.
这是一个实现特征的例子:
extern crate streaming_iterator;
use streaming_iterator::StreamingIterator;
struct Demonstration {
scores: Vec<i32>,
position: usize,
}
// Since `StreamingIterator` requires that we be able to call
// `advance` before `get`, we have to start "before" the first
// element. We assume that there will never be the maximum number of
// entries in the `Vec`, so we use `usize::MAX` as our sentinel value.
impl Demonstration {
fn new() -> Self {
Demonstration {
scores: vec![1, 2, 3],
position: std::usize::MAX,
}
}
fn reset(&mut self) {
self.position = std::usize::MAX;
}
}
impl StreamingIterator for Demonstration {
type Item = i32;
fn advance(&mut self) {
self.position = self.position.wrapping_add(1);
}
fn get(&self) -> Option<&Self::Item> {
self.scores.get(self.position)
}
}
fn main() {
let mut example = Demonstration::new();
loop {
example.advance();
match example.get() {
Some(v) => {
println!("v: {}", v);
}
None => break,
}
}
example.reset();
loop {
example.advance();
match example.get() {
Some(v) => {
println!("v: {}", v);
}
None => break,
}
}
}
不幸的是,在实现 RFC 1598 中的 generic associated types (GATs) 之前,流式迭代器将受到限制。
我不久前写了这段代码,不知何故在这里偶然发现了这个问题。它完全按照问题的要求进行操作:它展示了如何实现一个迭代器,该迭代器将其回调传递给自身的引用。
它向 IntoIterator
个实例添加了一个 .iter_map()
方法。最初我认为它应该为 Iterator
本身实现,但这是一个不太灵活的设计决定。
我为它创建了一个小箱子并将我的代码发布到 GitHub 如果你想试验它,你 can find it here.
WRT OP 在定义项目的生命周期方面遇到了麻烦,我没有 运行 在依赖默认的省略生命周期的同时实现这一点。
这是一个用法示例。请注意回调接收的参数是迭代器本身,回调应该从中提取数据并按原样传递或执行任何其他操作。
use iter_map::IntoIterMap;
let mut b = true;
let s = "hello world!".chars().peekable().iter_map(|iter| {
if let Some(&ch) = iter.peek() {
if ch == 'o' && b {
b = false;
Some('0')
} else {
b = true;
iter.next()
}
} else { None }
}).collect::<String>();
assert_eq!(&s, "hell0o w0orld!");
因为 IntoIterMap
通用特性是为 IntoIterator
实现的,所以您可以从任何支持该接口的东西中获得“iter 映射”。例如,可以直接从数组中创建一个,如下所示:
use iter_map::*;
fn main()
{
let mut i = 0;
let v = [1, 2, 3, 4, 5, 6].iter_map(move |iter| {
i += 1;
if i % 3 == 0 {
Some(0)
} else {
iter.next().copied()
}
}).collect::<Vec<_>>();
assert_eq!(v, vec![1, 2, 0, 3, 4, 0, 5, 6, 0]);
}
这是完整的代码 - 用这么少的代码来实现真是太神奇了,而且在将它们放在一起时一切似乎都运行得很顺利。它让我对 Rust 本身的灵活性及其设计决策有了新的认识。
/// Adds `.iter_map()` method to all IntoIterator classes.
///
impl<F, I, J, R, T> IntoIterMap<F, I, R, T> for J
//
where F: FnMut(&mut I) -> Option<R>,
I: Iterator<Item = T>,
J: IntoIterator<Item = T, IntoIter = I>,
{
/// Returns an iterator that invokes the callback in `.next()`, passing it
/// the original iterator as an argument. The callback can return any
/// arbitrary type within an `Option`.
///
fn iter_map(self, callback: F) -> ParamFromFnIter<F, I>
{
ParamFromFnIter::new(self.into_iter(), callback)
}
}
/// A trait to add the `.iter_map()` method to any existing class.
///
pub trait IntoIterMap<F, I, R, T>
//
where F: FnMut(&mut I) -> Option<R>,
I: Iterator<Item = T>,
{
/// Returns a `ParamFromFnIter` iterator which wraps the iterator it's
/// invoked on.
///
/// # Arguments
/// * `callback` - The callback that gets invoked by `.next()`.
/// This callback is passed the original iterator as its
/// parameter.
///
fn iter_map(self, callback: F) -> ParamFromFnIter<F, I>;
}
/// Implements an iterator that can be created from a callback.
/// does pretty much the same thing as `std::iter::from_fn()` except the
/// callback signature of this class takes a data argument.
pub struct ParamFromFnIter<F, D>
{
callback: F,
data: D,
}
impl<F, D, R> ParamFromFnIter<F, D>
//
where F: FnMut(&mut D) -> Option<R>,
{
/// Creates a new `ParamFromFnIter` iterator instance.
///
/// This provides a flexible and simple way to create new iterators by
/// defining a callback.
/// # Arguments
/// * `data` - Data that will be passed to the callback on each
/// invocation.
/// * `callback` - The callback that gets invoked when `.next()` is invoked
/// on the returned iterator.
///
pub fn new(data: D, callback: F) -> Self
{
ParamFromFnIter { callback, data }
}
}
/// Implements Iterator for ParamFromFnIter.
///
impl<F, D, R> Iterator for ParamFromFnIter<F, D>
//
where F: FnMut(&mut D) -> Option<R>,
{
type Item = R;
/// Iterator method that returns the next item.
/// Invokes the client code provided iterator, passing it `&mut self.data`.
///
fn next(&mut self) -> Option<Self::Item>
{
(self.callback)(&mut self.data)
}
}