为什么在Iterator特征对象上调用next会在运行时给出“内存不足”?

时间:2016-07-07 20:26:01

标签: iterator out-of-memory rust

我在Rust中实现了一个BoxedIterator,只是将另一个Iterator作为特征对象。完整的实施工作在Github。为什么Rust在没有投诉的情况下编译此代码,但在第一次尝试在next中的Iterator特征对象上调用Box时失败并显示“内存不足”消息(OOM)?

据我所知,它在失败前没有分配太多内存,所以我倾向于认为OOM消息不正确。

//! BoxedIterator just wraps around a box of an iterator, it is an owned trait object.
//! This allows it to be used inside other data-structures, such as a `Result`.
//! That means that you can `.collect()` on an `I where I: Iterator<Result<V, E>>` and get out a
//! `Result<BoxedIterator<V>, E>`. And then you can `try!` it. At least, that was my use-case.

use std::iter::FromIterator;
use std::iter::IntoIterator;

pub struct BoxedIterator<T> {
    iter: Box<Iterator<Item = T>>,
}

impl<T> Iterator for BoxedIterator<T> {
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next() // The OOM comes from this call of `next`
    }
}

impl<T> FromIterator<T> for BoxedIterator<T> {
    fn from_iter<I>(iter: I) -> Self
        where I: IntoIterator<Item = T>,
              I::IntoIter: 'static
    {
        BoxedIterator { iter: Box::new(iter.into_iter()) }
    }
}

use std::fs::File;
use std::io;

fn main() {
    let iter: Result<BoxedIterator<File>, io::Error> =
        vec!["/usr/bin/vi"].iter().cloned().map(File::open).collect();
    let mut iter = iter.unwrap();

    println!("{:?}", iter.next());
}

我认为我不打算使用此代码,因为我认为我的用例需要完全遍历Iterator Result以提取任何错误,所以我也许那时候他们会聚集在Vec。但我仍然对这个OOM感到好奇。

在创建一个最小的例子时,我发现如果不进行文件IO,我会得到一个段错误:

use iterator::BoxedIterator;

fn main() {
    let iter: Result<BoxedIterator<&str>, ()> = 
        vec![Ok("test1"), Ok("test2")].iter().cloned().collect();
    let mut iter = iter.unwrap();

    println!("{:?}", iter.next());
}

如果我不使用任何Result,只需使用BoxedIterator创建collect,代码就会按预期运行:

use iterator::BoxedIterator;

fn main() {
    let mut iter: BoxedIterator<&str> = vec!["test1", "test2"].iter().cloned().collect();

    println!("{:?}", iter.next());
    // prints: Some("test1")
}

1 个答案:

答案 0 :(得分:4)

FromIterator的实施并不正确;具体而言,您不能将I::IntoIter: 'static绑定在该位置。您的实现的边界必须匹配特征本身的边界。编译器应该诊断为but currently doesn't

在更高级别,我不确定你要做什么。您希望将File句柄存储在哪里?你通常会写这样的东西:

let files: Result<Vec<File>, io::Error> =
    ["/bin/bash"].iter().cloned().map(File::open).collect();