I am trying to understand the full capabilities of collect
function by going through some documentation. I am running into some challenges, specifically in the last example quoted on the page (also listed below, with my comments inline)
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);
I followed up this code with some of my own (shown below)
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());
}
Looking at the implementation of the FromIterator
trait on the Result
struct, we see that it mentions that "Takes each element in the Iterator
: if it is an Err
, no further elements are taken, and the Err
is returned. Should no Err
occur, a container with the values of each Result
is returned.
This explanation is in line with the result that is seen at Point 1 but doesn't seem to work with Point 2. In point 2 I was expecting only "nope" to be printed instead of both values.
Hence, I am trying to understand where this (selective) conversion is happening and running into a challenge.
If we look at the method definition itself we see the following.
#[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())
}
It shows that the into_iter()
method is being called on the iterator. Searching for into_iter
gives two implementations
#[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()
}
}
However, in my limited understanding of the language none seem to be able to explain what the documentation says as well as what is happening in Point 2.
Could someone please explain how this is working or point me to the right place in source where such selection logic is implemented?
What I want to understand is not why we get all values in a vector and only one in the result, but a. where is the code/logic to select the first Err
from a list of values and b. why are multiple Err
values selected when the result is collected in a list (when according to the documentation it should be only the first Err
value)