Expand description
The cumulative sibling of Result and Either.
The Validated type has special FromIterator instances that enable
all errors in a sequence to be reported, not just the first one.
§Motivation
We might think of Iterator::collect as being for consolidating the
result of some chained iteration into a concrete container, like Vec.
let v = vec![1,2,3];
assert_eq!(vec![2,4,6], v.into_iter().map(|n| n * 2).collect::<Vec<u32>>());But collect isn’t limited to this; it can be used to “fold” down into any
type you like, provided that it implements FromIterator. Consider the
effects of such an impl for Result:
let v: Vec<u32> = vec![1, 2, 3];
let r: Result<Vec<u32>, &str> = v
.into_iter()
.map(|n| if n % 2 == 0 { Err("Oh no!") } else { Ok(n * 2) })
.collect();
assert_eq!(Err("Oh no!"), r);The Result has been “interweaved” and pulled back out. Critically, this
collect call short-circuits; n * 2 is never called for 3, since the
map “fails” at 2. This is useful when we require a sequence of IO
actions to all succeed and we wish to cancel remaining operations as soon
as any error occurs.
But what if we don’t want to short circuit? What if we want a report of all the errors that occurred?
§Cumulative Errors and Validated
Consider three cases where we’d want a report of all errors, not just the first one:
- Form input validation.
- Type checking.
- Concurrent IO.
In the first case, if a user makes several input mistakes, it’s the best experience for them if all errors are reported at once so that they can make their corrections in a single pass.
In the second case, knowing only the first detected type error might not actually be the site of the real issue. We need everything that’s broken to be reported so we can make the best decision of what to fix.
In the third case, it may be that halting your entire concurrent job upon detection of a single failure isn’t appropriate. You might instead want everything to finish as it can, and then collect a bundle of errors at the end.
The Validated type accomodates these use cases; it is a “cumulative Result”.
use validated::Validated::{self, Good, Fail};
use nonempty_collections::*;
let v = vec![Good(1), Validated::fail("No!"), Good(3), Validated::fail("Ack!")];
let r: Validated<Vec<u32>, &str> = Fail(nev!["No!", "Ack!"]);
assert_eq!(r, v.into_iter().collect());§Use of non-empty Vectors (NEVec)
In the spirit of “make illegal states unrepresentable”, the Fail variant
of Validated contains a NEVec, a non-empty Vec. NEVec can do
everything that Vec can do, plus some additional benefits. In the case of
this crate, this representation forbids the otherwise meaningless Fail(vec![]).
In other words, if you have a Validated<T, E>, you either have a concrete
T, or at least one E.
§Features
rayon: EnableFromParallelIteratorinstances forValidated.
§Resources
Structs§
- Into
Iter - An iterator over the value in a
Goodvariant of aValidated. - Iter
- An iterator over a reference to the
Goodvariant of aValidated. - IterMut
- An iterator over a mutable reference to the
Goodvariant of aValidated.