rust - Iterator::collect 函数是如何工作的?
问题描述
我试图collect
通过阅读一些文档来了解函数的全部功能。我遇到了一些挑战,特别是在页面上引用的最后一个示例中(也在下面列出,我的评论内嵌)
let results = [Ok(1), Err("nope"), Ok(3), Err("bad")];
let result: Result<Vec<_>, &str> = results.iter().cloned().collect();
// gives us the first error <-- Point 1
assert_eq!(Err("nope"), result);
let results = [Ok(1), Ok(3)];
let result: Result<Vec<_>, &str> = results.iter().cloned().collect();
// gives us the list of answers
assert_eq!(Ok(vec![1, 3]), result);
我用我自己的一些代码跟进了这段代码(如下所示)
let results: [std::result::Result<i32, &str>; 2] = [Err("nope"), Err("bad")];
let result: Vec<Result<i32, &str>> = results.iter().cloned().collect();
// The following prints <-- Point 2
// "nope"
// "bad"
for x in result{
println!("{:?}", x.unwrap_err());
}
查看struct上FromIterator
traitResult
的实现,我们看到它提到“获取Iterator
: 中的每个元素,如果它是 an Err
,则不再获取其他元素,并Err
返回 。如果不Err
发生,容器的值为每个Result
都返回。
这种解释与在第 1 点看到的结果一致,但似乎不适用于第 2 点。在第 2 点中,我期望只打印“nope”而不是两个值。
因此,我试图了解这种(选择性)转换发生在哪里并遇到了挑战。
如果我们查看方法定义本身,我们会看到以下内容。
#[inline]
fn from_iter<I: IntoIterator<Item=Result<A, E>>>(iter: I) -> Result<V, E> {
// FIXME(#11084): This could be replaced with Iterator::scan when this
// performance bug is closed.
iter::process_results(iter.into_iter(), |i| i.collect())
}
它表明该into_iter()
方法正在迭代器上被调用。搜索into_iter
给出了两种实现
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, E> IntoIterator for Result<T, E> {
type Item = T;
type IntoIter = IntoIter<T>;
/// Returns a consuming iterator over the possibly contained value.
///
/// The iterator yields one value if the result is [`Result::Ok`], otherwise none.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let x: Result<u32, &str> = Ok(5);
/// let v: Vec<u32> = x.into_iter().collect();
/// assert_eq!(v, [5]);
///
/// let x: Result<u32, &str> = Err("nothing!");
/// let v: Vec<u32> = x.into_iter().collect();
/// assert_eq!(v, []);
/// ```
#[inline]
fn into_iter(self) -> IntoIter<T> {
IntoIter { inner: self.ok() }
}
}
#[stable(since = "1.4.0", feature = "result_iter")]
impl<'a, T, E> IntoIterator for &'a Result<T, E> {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Iter<'a, T> {
self.iter()
}
}
然而,在我对语言的有限理解中,似乎没有人能够解释文档所说的内容以及第 2 点中发生的事情。
有人可以解释这是如何工作的,或者指出我在源代码中实现这种选择逻辑的正确位置吗?
我想了解的不是为什么我们在一个向量中得到所有值而在结果中只有一个值,而是一个。Err
从值列表中选择第一个的代码/逻辑在哪里?为什么在将结果收集在列表中时选择多个Err
值(根据文档,它应该只是第一个Err
值)
解决方案
在这个例子中
let result: Vec<Result<i32, &str>> = results.iter().cloned().collect();
您不会收集到 aResult
中,而是收集到 a 中Vec
,因此所有值都被收集,不受影响。这是预期的Vec
。
这与
let result: Result<Vec<_>, &str> = results.iter().cloned().collect();
您在哪里收集到 a Result
,它会根据是否Err
找到 an 来过滤元素。这来自impl<A, E, V> FromIterator<Result<A, E>> for Result<V, E> where V: FromIterator<A>,
.
推荐阅读
- sql - 访问 SQL 查询以比较两个表并列出所有数据,而不仅仅是那些不匹配的数据
- json - 使用 json-server 时如何使用 typicode url 而不是 localhost
- node.js - 我正在将我的反应应用程序部署到 github 页面但是当我写这个命令 npm run deploy 我得到这个错误
- javascript - 返回到动态大小页面上的滚动条位置?
- django - 如何最好地将 Django 模型的转换值传递给上下文?
- node.js - 缓冲区是ArrayBuffer?
- r - 将百分位数估计扩展到多个预测变量
- amazon-web-services - 文件直接上传到 s3 不保存标签
- angular - Angular keycloak 试图在两个不同的 Angular 项目之间实现单点登录
- laravel - 防止在我的 laravel 项目中重复输入客户 ID 和借款人姓名的数据