我应该为可迭代表达式使用什么返回类型?

时间:2017-10-01 05:43:32

标签: rust

我有一个返回我认为应该可迭代的东西的函数,但到目前为止我一直在收集它并将其作为向量返回。

pub fn grid_coords() -> Vec<(i32, i32)> {
    (0..SIZE).flat_map(|y| (0..SIZE).map(move |x| (x, y))).collect()
}

我假设这将在返回之前迭代,然后调用它的代码将不得不再次迭代。

我尝试删除.collect()并让编译器引导我并最终得到类似FlatMap<Range<i32>, Map<Range<i32>, Fn>, Fn>的内容,是否有一种不那么难看的类型我可以使用?

2 个答案:

答案 0 :(得分:2)

在当前(稳定)Rust中,返回&#34;概念性&#34;的最简单方法迭代器是返回一个实现Iterator的盒装特征对象:

pub fn grid_coords() -> Box<Iterator<Item=(i32, i32)>> {
    let real_iter = (0..SIZE).flat_map(|y| (0..SIZE).map(move |x| (x, y)));
    Box::new(real_iter)
}

有效地使用具体迭代器对象的框erases the type,并且调用者不需要知道它使用flat_map实现的事实。如果稍后切换grid_coords以使用不同的迭代原语,例如yield一旦稳定,则该功能不会改变签名,并且保证不会破坏其调用者。你甚至可以更进一步,完全隐藏来自调用者的盒子:

pub struct GridCoordIter {
    inner: Box<Iterator<Item=(i32, i32)>>
}

impl Iterator for GridCoordIter {
    type Item = (i32, i32);
    fn next(&mut self) -> Option<(i32, i32)> {
        self.inner.next()
    }
}

pub fn grid_coords() -> GridCoordIter {
    let real_iter = (0..SIZE).flat_map(|y| (0..SIZE).map(move |x| (x, y)));
    GridCoordIter { inner: Box::new(real_iter) }
}

盒装返回的缺点是每次调用grid_coords需要一个小的堆分配,并且每次调用next()都会通过vtable样式的间接调用,而这两个调用都没有优化远离当前的锈病。这在实践中是否存在问题取决于您的使用情况,但如果grid_coords()是一个可能被称为数百万次的非常基本的功能,那么它就不会被解雇。

可以通过使用GridCoordIter的不同实现来消除分配和间接。很遗憾,无法将inner字段设为FlatMap值并保留grid_coords的当前实现。内部FlatMap需要引用函数的类型,grid_coords使用的闭包类型是匿名的。这可以通过将闭包重写为可调用的结构来解决,但此时首先使用flat_mapmap的便利性将丢失,并且更容易实现GridCoordIter::next具有所需的逻辑:

pub struct GridCoordIter {
    i: i32,
    j: i32,
}

impl Iterator for GridCoordIter {
    type Item = (i32, i32);

    fn next(&mut self) -> Option<(i32, i32)> {
        if self.j == SIZE {
            return None;
        }
        let coord = (self.i, self.j);
        self.i += 1;
        if self.i == SIZE {
            self.i = 0;
            self.j += 1;
        }
        Some(coord)
    }
}

pub fn grid_coords() -> GridCoordIter {
    GridCoordIter { i: 0, j: 0 }
}

要实现效率和简洁,需要来自不稳定Rust的impl Trait功能,如Francis's answer所示。它允许函数直接返回一个匿名类型的值,调用者不需要知道任何关于它的信息,除非它实现了特定的特征。

答案 1 :(得分:1)

如果你使用夜间编译器,那么你可以使用不稳定的impl Trait syntax

#![feature(conservative_impl_trait)]

pub fn grid_coords() -> impl Iterator<Item = (i32, i32)> {
    (0..SIZE).flat_map(|y| (0..SIZE).map(move |x| (x, y)))
}