我应该使用哪种返回类型作为一个疑问表达式



我的功能可以返回我认为应该是值得一提的东西,但是到目前为止,我一直在收集它并将其返回为矢量。

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>的东西,我可以使用不太丑陋的类型吗?

在当前(稳定)生锈中,返回"概念"迭代器的最简单方法是返回实现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)
}

使用盒子有效地擦除了混凝土迭代器对象的类型,即呼叫者不需要知道它是使用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式的间接间接,这两个间接都不是通过当前Rust优化的。实践中的问题是否取决于您的用法,但是如果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 }
}

实现效率和简洁都需要不稳定生锈的impl Trait功能,如弗朗西斯的回答所示。它允许该函数直接返回呼叫者不需要了解的匿名类型的值,除了它实现了特定特征。

如果您可以使用夜间编译器,则可以使用不稳定的impl Trait语法:

#![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)))
}

相关内容

最新更新