Hey folks, we're proud to announce the initial release of the validated (GIthub) crate. This provides the Validated
type, the "cumulative sibling" of Result
or Either
.
Error Weaving
You may be familiar with using collect
to short-circuit a sequence of operations that return 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);
Note that here, the potentially expensive operation (usually IO, but here is just n * 2
) is skipped for 3
, since the closure failed on 2
. But what if you didn't want to short-circuit, but collect all errors that occurred?
Enter Validated
:
use validated::Validated::{self, Good, Fail};
use nonempty::NonEmpty;
let v = vec![Good(1), Validated::fail("No!"), Good(3), Validated::fail("Ack!")];
let r: Validated<Vec<u32>, &str> = Fail(NonEmpty::from(("No!", vec!["Ack!"])));
assert_eq!(r, v.into_iter().collect());
This is very useful for cases where you want to know all failed cases as once, say when validating user input on a form, or when running concurrent operations with rayon
. In fact, validated
supports rayon
, allowing you to collect all potential errors across spawned threads.
We're also not limited to collecting into an inner Vec
: you can fold that inner value into anything that implements FromIterator
!
Mapping Composite Results
This crate also provides various mapN
methods (currently up to 4
), which will only succeed if all subcases succeeded. And here there is still no short-circuiting; all errors are concatinated in the final result.
use validated::Validated::{self, Good, Fail};
let v: Validated<u32, &str> = Good(1).map3(Good(2), Good(3), |a, b, c| a + b + c);
assert_eq!(v, Good(6));
Such methods would be useful for Option
and Result
too, but they are strangely missing. FP folks will recognize such methods.
Thanks everyone, and let us know if you have any issues! Also it's my birthday. Cheers!