为什么在 Iterator trait 对象上调用 next 在运行时给我一个 "out of memory"?
Why does calling next on an Iterator trait object give me an "out of memory" at runtime?
我在 Rust 中实现了一个 BoxedIterator
,它只是将另一个 Iterator
作为特征对象。完整的实现在 Github。为什么 Rust 毫无怨言地编译这段代码,但当它第一次尝试在 Box
中的 Iterator
trait 对象上调用 next
时失败并显示 "out of memory" 消息(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());
}
我不认为我会使用这段代码,因为我认为我的用例将需要完全遍历 Result
的 Iterator
以提取任何错误所以我不妨在那个时候将它们聚集在 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
,只需使用 collect
创建一个 BoxedIterator
,代码按预期运行:
use iterator::BoxedIterator;
fn main() {
let mut iter: BoxedIterator<&str> = vec!["test1", "test2"].iter().cloned().collect();
println!("{:?}", iter.next());
// prints: Some("test1")
}
您对 FromIterator
的实现不正确;具体来说,您不能将 I::IntoIter: 'static
绑定到该位置。您的实施范围必须与特征本身的范围相匹配。编译器应该对此进行诊断,but currently doesn't.
在更高层次上,我不确定您要做什么。您希望 File
句柄存储在哪里?你通常会这样写:
let files: Result<Vec<File>, io::Error> =
["/bin/bash"].iter().cloned().map(File::open).collect();
我在 Rust 中实现了一个 BoxedIterator
,它只是将另一个 Iterator
作为特征对象。完整的实现在 Github。为什么 Rust 毫无怨言地编译这段代码,但当它第一次尝试在 Box
中的 Iterator
trait 对象上调用 next
时失败并显示 "out of memory" 消息(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());
}
我不认为我会使用这段代码,因为我认为我的用例将需要完全遍历 Result
的 Iterator
以提取任何错误所以我不妨在那个时候将它们聚集在 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
,只需使用 collect
创建一个 BoxedIterator
,代码按预期运行:
use iterator::BoxedIterator;
fn main() {
let mut iter: BoxedIterator<&str> = vec!["test1", "test2"].iter().cloned().collect();
println!("{:?}", iter.next());
// prints: Some("test1")
}
您对 FromIterator
的实现不正确;具体来说,您不能将 I::IntoIter: 'static
绑定到该位置。您的实施范围必须与特征本身的范围相匹配。编译器应该对此进行诊断,but currently doesn't.
在更高层次上,我不确定您要做什么。您希望 File
句柄存储在哪里?你通常会这样写:
let files: Result<Vec<File>, io::Error> =
["/bin/bash"].iter().cloned().map(File::open).collect();