Почему вызов next для трейт-объекта Iterator приводит к нехватке памяти во время выполнения?

Я реализовал BoxedIterator в Rust, который просто упаковывает другой Iterator в качестве трейт-объекта. Полная реализация находится на Github. Почему Rust компилирует этот код без жалоб, но выдает сообщение «недостаточно памяти» (OOM) при первой попытке вызвать next для трейт-объекта Iterator в Box?

Насколько я могу судить, он не выделяет много памяти перед сбоем, поэтому я склонен думать, что сообщение 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.

При создании минимального примера я обнаружил, что без выполнения файлового ввода-вывода я получаю 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());
}

Если я не использую 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")
}

person Apanatshka    schedule 07.07.2016    source источник
comment
OOM происходит только в режиме отладки. В деблокированном режиме программа работает нормально play.rust-lang.org/ . Я думаю, вы должны сообщить об этом как об ошибке.   -  person malbarbo    schedule 08.07.2016
comment
Можете ли вы отредактировать свой вопрос, включив в него текст, указывающий на то, что это ошибка нехватки памяти? Когда я запускаю его, я просто получаю segfault.   -  person Shepmaster    schedule 08.07.2016


Ответы (1)


Ваша реализация FromIterator неверна; в частности, вам не разрешено помещать I::IntoIter: 'static в эту позицию. Границы вашей реализации должны совпадать с границами самого трейта. Компилятор должен это диагностировать, но сейчас этого не делает.

На более высоком уровне я не уверен, что вы пытаетесь сделать. Где вы ожидаете хранить дескрипторы File? Обычно вы пишете что-то вроде этого:

let files: Result<Vec<File>, io::Error> =
    ["/bin/bash"].iter().cloned().map(File::open).collect();
person Eli Friedman    schedule 08.07.2016
comment
Для вопроса более высокого уровня: см. примечание под первым фрагментом кода (первоначально с префиксом NB), который в основном комментирует бесполезность кода ^^ Я действительно переключился на сбор дескрипторов File в Vec. - person Apanatshka; 08.07.2016