5 releases (3 stable)
2.0.0 | Nov 11, 2020 |
---|---|
1.1.0 | Nov 11, 2020 |
1.0.0 | Sep 18, 2019 |
0.1.1 | Sep 8, 2019 |
0.1.0 | Sep 8, 2019 |
#1463 in Rust patterns
27KB
204 lines
These
These
represents a 3-way split of data. Think of it as a Result
except that we have an extra case that can contain both the result T
and
the error E
. This can be useful for when we can still compute the final result
but we have also encountered an error.
enum These<T, U> {
This(T),
That(U),
Both(T, U)
}
We have three constructors This
which holds a T
, That
which holds a U
,
and Both
which holds both.
Here and There
If we want to talk about all T
s we use the terminology Here
. So this
means we either have a This
or Both
. Or in code:
use these::These;
fn is_here<T: Copy, U: Copy>(these: &These<T, U>) -> bool {
these.is_this() || these.is_these()
}
If we want to talk about all U
s we use the terminology There
. So this
means we either have a That
or Both
. Or in code
use these::These;
fn is_here<T: Copy, U: Copy>(these: These<T, U>) -> bool {
these.is_that() || these.is_these()
}
Contrived Example
Let us say that we have a function that only allows numbers that are less than
10. We expose a new type LessThanTen
and expect our users to use is_less_than_ten
to validate i8
s into this type. We can use Result
and model this below:
#[derive(Debug, PartialEq)]
struct LessThanTen(i8);
#[derive(Debug, PartialEq)]
pub enum Error {
IsGreaterThanOrEqualToTen,
}
pub fn is_less_than_ten(i: i8) -> Result<LessThanTen, Error> {
if i < 10 {
Ok(LessThanTen(i))
} else {
Err(Error::IsGreaterThanOrEqualToTen)
}
}
assert_eq!(is_less_than_ten(8), Ok(LessThanTen(8)));
assert_eq!(is_less_than_ten(10), Err(Error::IsGreaterThanOrEqualToTen));
But after a while we realise we can start to support all numbers that are less than 20.
We can do a similar approach, but we would like to be backwards compatible, and also keep
track of when we encounter numbers that are greater than 10. Maybe we would like to keep
statistics on these errors, or convert successful results to LessThanTen
for backwards
compatibility. We can use These
to solve this and can modelled as below:
use these::These;
#[derive(Debug, PartialEq)]
struct LessThanTen(i8);
#[derive(Debug, PartialEq)]
struct LessThanTwenty(i8);
#[derive(Debug, PartialEq)]
pub enum Error {
IsGreaterThanOrEqualToTen,
IsGreaterThanOrEqualToTwenty,
}
pub fn is_less_than_ten(i: i8) -> Result<LessThanTen, Error> {
if i < 10 {
Ok(LessThanTen(i))
} else {
Err(Error::IsGreaterThanOrEqualToTen)
}
}
pub fn is_less_than_twenty(i: i8) -> These<Error, LessThanTwenty> {
if i < 10 {
These::That(LessThanTwenty(i))
} else if i < 20 {
These::Both(Error::IsGreaterThanOrEqualToTen, LessThanTwenty(i))
} else {
These::This(Error::IsGreaterThanOrEqualToTwenty)
}
}
// Convert to the backwards compatible scenario
pub fn backwards_compatible(r: These<Error, LessThanTwenty>) -> Result<LessThanTen, Error> {
r.collapse_these(
|e| Err(e),
|LessThanTwenty(i)| Ok(LessThanTen(i)),
|e, _| Err(e),
)
}
assert_eq!(is_less_than_ten(8), Ok(LessThanTen(8)));
assert_eq!(is_less_than_ten(10), Err(Error::IsGreaterThanOrEqualToTen));
assert_eq!(is_less_than_twenty(8), These::That(LessThanTwenty(8)));
assert_eq!(is_less_than_twenty(10), These::Both(Error::IsGreaterThanOrEqualToTen, LessThanTwenty(10)));
assert_eq!(is_less_than_twenty(20), These::This(Error::IsGreaterThanOrEqualToTwenty));
assert_eq!(backwards_compatible(is_less_than_twenty(8)), Ok(LessThanTen(8)));