#Result
Status: standard library specification draft.
This chapter defines the part of std.result that lib/std writes in
ordinary hd over the language tier. That is the inherent methods
and_then, map_err, unwrap_or, map, ok, err, is_ok, is_err,
and expect of Result[T, E].
The language tier keeps what the compiler knows by name (Result Types):
| Item | Why it stays in the language tier |
|---|---|
Result[T, E], .Ok, and .Err |
the prelude enum and its construction rules |
postfix ? on a Result |
Result Propagation, which may convert the error through From |
invariance in T and E |
types.option.invariant.result |
| a variant constructor as a function value | data.enum.fn-value.use, as in map_err(ConfigError.Number) |
Note.
std.errordeclares one more inherent method ofResult[T, E], forE < Error:context, which wraps an error with a message. See Error Context.
#Result Methods
std.result gives every Result[T, E] these methods:
use std.num.{ParseNumberError}
enum ConfigError:
Missing
Number(error: ParseNumberError)
fn setting(settings: Map[string, string], key: string) -> Result[string, ConfigError]:
match settings.get(key):
.Some(text) => .Ok(text)
.None => .Err(ConfigError.Missing)
fn number(text: string) -> Result[i32, ConfigError]:
text.parse::[i32]().map_err(ConfigError.Number)
fn port(settings: Map[string, string]) -> i32:
setting(settings, "port").and_then(number).unwrap_or(8080)| Rule | Method | Result |
|---|---|---|
std-result.and-thenand_then |
fn and_then[U](self, transform: fn(T) -> Result[U, E]) -> Result[U, E] |
transform(value) for .Ok(value), and .Err(error) for .Err(error) |
std-result.map-errmap_err |
fn map_err[F](self, transform: fn(E) -> F) -> Result[T, F] |
.Ok(value) for .Ok(value), and .Err(transform(error)) for .Err(error) |
std-result.unwrap-orunwrap_or |
fn unwrap_or(self, fallback: T) -> T |
value for .Ok(value), and fallback for .Err(_) |
- std-result.methods.inherentThe three are inherent methods that
stddeclares onResult[T, E], so every result has them without ause, bytrait.own.inherent.std.no-use. - std-result.and-then.once
and_thencallstransformonce for.Ok, and never for.Err. - std-result.map-err.once
map_errcallstransformonce for.Err, and never for.Ok. - std-result.and-then.same-error
and_thenkeeps the error typeE, and converts no error. Atransformwhose result has another error type is an error. Error:type-mismatch. - std-result.methods.non-suspendingNo method suspends. Each
transformcallback is a plain function type with the empty row.
use std.num.{ParseNumberError}
enum ConfigError:
Missing
Number(error: ParseNumberError)
fn setting(settings: Map[string, string], key: string) -> Result[string, ConfigError]:
match settings.get(key):
.Some(text) => .Ok(text)
.None => .Err(ConfigError.Missing)
fn parsed(text: string) -> Result[i32, ParseNumberError]:
text.parse::[i32]()
fn port(settings: Map[string, string]) -> Result[i32, ConfigError]:
setting(settings, "port").and_then(parsed) # error: type-mismatchNote. A step with another error type is mapped first, as
and_then(number)does above withmap_err. Postfix?is the one place an error converts throughFromby itself.
Note.
fallbackis an ordinary argument, so it is evaluated before the call, even when the result is.Ok.
Why. Postfix
?works only where the enclosing function or closure returns aResult.and_thenchains two fallible steps in any expression, as Rust'sResult::and_thendoes.
#Mapping The Value
map transforms the value of .Ok, as map on T? transforms the
value of .Some:
use std.num.{ParseNumberError}
fn doubled(text: string) -> Result[i32, ParseNumberError]:
text.parse::[i32]().map(fn(value: i32) -> i32: value * 2)| Rule | Method | Result |
|---|---|---|
std-result.mapmap |
fn map[U](self, transform: fn(T) -> U) -> Result[U, E] |
.Ok(transform(value)) for .Ok(value), and .Err(error) for .Err(error) |
- std-result.map.inherent
mapis an inherent method thatstddeclares onResult[T, E]as well, so it needs nouseeither. - std-result.map.once
mapcallstransformonce for.Ok, and never for.Err. - std-result.map.non-suspending
mapdoes not suspend. Itstransformcallback is a plain function type with the empty row.
Why. One name,
map, transforms the success value of every container: a list, an optional, and a result.map_errnames the one transform of the other side.
#Variant Tests And Conversions
std.result also gives every Result[T, E] five methods that test the
variant, or turn it into a value:
fn port(text: string) -> i32?:
text.parse::[i32]().ok()
fn invalid(text: string) -> bool:
text.parse::[i32]().is_err()
fn configured_port(text: string) -> i32:
text.parse::[i32]().expect("the configured port is a number")| Rule | Method | Result |
|---|---|---|
std-result.okok |
fn ok(self) -> T? |
.Some(value) for .Ok(value), and .None for .Err(_) |
std-result.errerr |
fn err(self) -> E? |
.None for .Ok(_), and .Some(error) for .Err(error) |
std-result.is-okis_ok |
fn is_ok(self) -> bool |
true for .Ok(_), and false for .Err(_) |
std-result.is-erris_err |
fn is_err(self) -> bool |
false for .Ok(_), and true for .Err(_) |
std-result.expectexpect |
fn expect(self, message: string) -> T for E < Display |
value for .Ok(value), and a panic for .Err(error) |
- std-result.tests.inherentThe five are inherent methods that
stddeclares onResult[T, E]as well, so they need nouseeither. - std-result.expect.bound
expectis available whenE < Display, asstddeclares it in an impl with that bound. EveryErrorisDisplay, so everyResult[T, E]withE < Errorhas it. - std-result.expect.panic
expecton.Err(error)panics with the textmessage: error:message, a colon and a space, then theDisplaytext oferror. Its category isexplicit-panic. Panic:explicit-panic. - std-result.tests.non-suspendingNone of the five suspends.
Note.
messageis an ordinary argument, so it is evaluated before the call, even when the result is.Ok. The text oferroris built on the panic path only.
Why.
expectshows the error beside the message, as Rust'sResult::expectdoes, so a panic report names both what the caller assumed and what failed.
See also: Option, Error Context, List And Optional Map, Runtime Panics, Variance.