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}