Why does calling next on an Iterator trait object give me an “out of memory” at runtime?

只愿长相守 提交于 2019-12-12 20:30:21

问题


I implemented a BoxedIterator in Rust that just boxes another Iterator as a trait object. The full implementation is on Github. Why does Rust compile this code without complaint but fail with an "out of memory" message (OOM) when it first tries to call next on the Iterator trait object in the Box?

As far as I can tell it doesn't allocate much memory before failing, so I'm inclined to think the OOM message is not correct.

//! 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());
}

I don't think I'm going to use this code, as I've figured that my use case will need to traverse the Iterator of Results completely to extract any errors so I might as well gather them in a Vec at that point. But I'm still curious about this OOM.

While creating a minimal example, I found that without doing the File IO, I get a segfault:

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());
}

If I don't use any Result, just create a BoxedIterator with collect, the code works as expected:

use iterator::BoxedIterator;

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

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

回答1:


Your implementation of FromIterator isn't correct; specifically, you aren't allowed to put an I::IntoIter: 'static bound in that position. The bounds on your implementation have to match the bounds on the trait itself. The compiler should diagnose this, but currently doesn't.

At a higher level, I'm not sure what you're trying to do. Where do you expect the File handles to be stored? You would normally write something like this:

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


来源:https://stackoverflow.com/questions/38254647/why-does-calling-next-on-an-iterator-trait-object-give-me-an-out-of-memory-at

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!