iex/
outcome.rs

1use crate::{iex, imp::Marker};
2
3pub trait Sealed {}
4
5/// Properties of a generalized result type.
6///
7/// This unifies [`Result`] and `#[iex] Result`.
8///
9/// # Ownership
10///
11/// The semantics of ownership and capturing for `#[iex] Result` complicates the use of `map_err`
12/// and `inspect_err` in some cases. Notably, using `f(...).map_err(|e| ...)` requires that `f(...)`
13/// and `|e| ...` don't capture variables in incompatible ways:
14///
15/// ```compile_fail
16/// use iex::{iex, Outcome};
17///
18/// struct Struct;
19///
20/// impl Struct {
21///     #[iex]
22///     fn errors(&mut self) -> Result<(), i32> {
23///         Err(123)
24///     }
25///     fn error_mapper(&mut self, err: i32) -> i32 {
26///         err + 1
27///     }
28///     #[iex]
29///     fn calls(&mut self) -> Result<(), i32> {
30///         // closure requires unique access to `*self` but it is already borrowed
31///         self.errors().map_err(|err| self.error_mapper(err))
32///     }
33/// }
34/// ```
35///
36/// `#[iex]` provides a workaround for this particular usecase. The patterns
37/// `(..).map_err(#[iex(shares = ..)] ..)?` and similarly for `inspect_err` (only these patterns,
38/// the `?` is required) allows you to share variables between the fallible function and the error
39/// handler. A *mutable reference* to the variable will be visible to the fallible function, and the
40/// *value* of the variable will be visible to the error handler. This applies to `self` too:
41///
42/// ```
43/// use iex::{iex, Outcome};
44///
45/// struct Struct;
46///
47/// impl Struct {
48///     #[iex]
49///     fn errors(&mut self) -> Result<(), i32> {
50///         Err(123)
51///     }
52///     fn error_mapper(&mut self, err: i32) -> i32 {
53///         err + 1
54///     }
55///     #[iex]
56///     fn calls(&mut self) -> Result<(), i32> {
57///         Ok(self.errors().map_err(#[iex(shares = self)] |err| self.error_mapper(err))?)
58///     }
59/// }
60/// ```
61///
62/// In a more complicated case, you would have to resort to the less efficient
63/// [`into_result`](Self::into_result):
64///
65/// ```
66/// use iex::{iex, Outcome};
67///
68/// struct Struct;
69///
70/// impl Struct {
71///     #[iex]
72///     fn errors(&mut self) -> Result<(), i32> {
73///         Err(123)
74///     }
75///     fn error_mapper(&mut self, err: i32) -> i32 {
76///         err + 1
77///     }
78///     #[iex]
79///     fn calls(&mut self) -> Result<(), i32> {
80///         self.errors().into_result().map_err(|err| self.error_mapper(err))
81///     }
82/// }
83/// ```
84#[must_use]
85pub trait Outcome: Sealed + crate::Context<Self::Output, Self::Error> {
86    /// The type of the success value.
87    type Output;
88
89    /// The type of the error value.
90    type Error;
91
92    #[doc(hidden)]
93    fn get_value_or_panic(self, marker: Marker<Self::Error>) -> Self::Output;
94
95    /// Calls a function with a reference to the contained value if `Err`.
96    ///
97    /// Returns the original result.
98    ///
99    /// This is a generalized and more efficient version of [`Result::inspect_err`].
100    #[iex]
101    fn inspect_err<F>(self, f: F) -> Result<Self::Output, Self::Error>
102    where
103        F: FnOnce(&Self::Error);
104
105    /// Apply a function to the `Err` value, leaving `Ok` untouched.
106    ///
107    /// This is a generalized and more efficient version of [`Result::map_err`].
108    ///
109    /// # Example
110    ///
111    /// ```
112    /// use iex::{iex, Outcome};
113    ///
114    /// enum MyError {
115    ///     IO(std::io::Error),
116    ///     Custom(String),
117    /// }
118    ///
119    /// #[iex]
120    /// fn producing_io_error() -> Result<(), std::io::Error> {
121    ///     Ok(())
122    /// }
123    ///
124    /// #[iex]
125    /// fn producing_string<T: std::fmt::Debug>(arg: T) -> Result<(), String> {
126    ///     Err(format!("Could not handle {:?}", arg))
127    /// }
128    ///
129    /// #[iex]
130    /// fn producing_my_error() -> Result<(), MyError> {
131    ///     producing_io_error().map_err(MyError::IO)?;
132    ///     producing_string(123).map_err(MyError::Custom)?;
133    ///     Ok(())
134    /// }
135    ///
136    /// assert!(matches!(
137    ///     producing_my_error().into_result(),
138    ///     Err(MyError::Custom(s)) if s == "Could not handle 123",
139    /// ));
140    /// ```
141    #[iex]
142    fn map_err<F, O>(self, op: O) -> Result<Self::Output, F>
143    where
144        O: FnOnce(Self::Error) -> F;
145
146    /// Cast a generic result to a [`Result`].
147    ///
148    /// The [`Result`] can then be matched on, returned from a function that doesn't use
149    /// [`#[iex]`](macro@crate::iex), etc.
150    ///
151    /// This method is typically slow on complex code. Avoid it in the hot path if you can. For
152    /// example,
153    ///
154    /// ```rust
155    /// # use iex::{iex, Outcome};
156    /// # #[iex] fn f() -> Result<(), ()> { Ok(()) }
157    /// # #[iex] fn g() -> Result<(), ()> { Ok(()) }
158    /// # #[iex] fn fg() -> Result<(), ()> {
159    /// let result = f().into_result();
160    /// g()?;
161    /// result
162    /// # }
163    /// ```
164    ///
165    /// is perhaps better written as
166    ///
167    /// ```rust
168    /// # use iex::{iex, Outcome};
169    /// # #[iex] fn f() -> Result<(), ()> { Ok(()) }
170    /// # #[iex] fn g() -> Result<(), ()> { Ok(()) }
171    /// # #[iex] fn fg() -> Result<(), ()> {
172    /// let value = f().inspect_err(|_| drop(g().into_result()))?;
173    /// g()?;
174    /// Ok(value)
175    /// # }
176    /// ```
177    ///
178    /// despite repetitions.
179    fn into_result(self) -> Result<Self::Output, Self::Error>;
180}