-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Add inspect
and inspect_err
methods to Result
, and inspect
method to Option
#3190
Comments
so it's map_err with a readable reference? is moving the err so expensive such that it's worth the effort and extra complexity (small admittedly) of adding an extra API to Result? |
It's not as much about the performance cost of moving the inner value than it is about code readability and ergonomics. But you are right - it is a tiny change. If it's an obscure API that maybe 5% of rust users would ever use then I surely wouldn't have bothered. However, considering |
I'll pitch in that I've had multiple times where I wanted these methods for messing with results |
I think this is an improvements for ergonomics As far as I understand, |
Let me rewrite my deleted comment... I got confused by myself on what I wanted to say. Yes I took inspiration from And just to get my bases covered, we cannot simply call |
Yes, just trying to point out that the semantic already exists, so IMO it doesn't feel out of place |
I believe that this is quite comprehensively covered by the |
Small API additions like this don't usually need an RFC, so I went ahead and opened a PR: rust-lang/rust#91346 |
I've stumbled with a compatibility warning on We've been successfully using the following extension methods: impl<T> Option<T> {
pub fn inspect_some(self, f: impl FnOnce(&T)) -> Self;
pub fn inspect_none(self, f: impl FnOnce()) -> Self;
}
impl<Ok, Err> Result<Ok, Err> {
pub fn inspect_ok(self, f: impl FnOnce(&Ok)) -> Self;
pub fn inspect_err(self, f: impl FnOnce(&Err)) -> Self;
} Here is how they are defined and documented in our code (writing it separately under the spoiler to decrease verbosity): Detailspub trait OptionExt<T> {
/// Similar to [`std::iter::Iterator::inspect()`], but the closure is
/// invoked if the [`Option`] contains [`Some`] value.
///
/// ```
/// use stdx::prelude::*;
///
/// let mut invoked = false;
///
/// None::<i32>.inspect_some(|_| {
/// invoked = true;
/// });
///
/// assert!(!invoked);
///
/// Some(99).inspect_some(|&val| {
/// assert_eq!(val, 99);
/// invoked = true;
/// });
///
/// assert!(invoked);
/// ```
fn inspect_some(self, f: impl FnOnce(&T)) -> Self;
/// Similar to [`std::iter::Iterator::inspect()`], but the closure is
/// invoked if the [`Option`] contains [`None`] value.
///
/// ```
/// use stdx::prelude::*;
///
/// let mut invoked = false;
///
/// Some(99).inspect_none(|| {
/// invoked = true;
/// });
///
/// assert!(!invoked);
///
/// None::<i32>.inspect_none(|| {
/// invoked = true;
/// });
///
/// assert!(invoked);
/// ```
fn inspect_none(self, f: impl FnOnce()) -> Self;
}
impl<T> OptionExt<T> for Option<T> {
fn inspect_some(self, f: impl FnOnce(&T)) -> Self {
if let Some(ref it) = self {
f(it);
}
self
}
fn inspect_none(self, f: impl FnOnce()) -> Self {
if self.is_none() {
f();
}
self
}
}
pub trait ResultExt<Ok, Err> {
/// Similar to [`std::iter::Iterator::inspect()`], but the closure is
/// invoked if [`Result`] contains [`Ok`] value.
///
/// ```
/// use stdx::prelude::*;
/// use std::convert::Infallible as Never;
///
/// let mut invoked = false;
///
/// Err::<Never, i32>(99).inspect_ok(|val| {
/// // empty match means that the code is statically deduced unreachable
/// match *val {}
/// });
///
/// Ok::<i32, Never>(99).inspect_ok(|&val| {
/// invoked = true;
/// assert_eq!(val, 99);
/// });
///
/// assert!(invoked);
/// ```
fn inspect_ok(self, f: impl FnOnce(&Ok)) -> Self;
/// Similar to [`std::iter::Iterator::inspect()`], but the closure is
/// invoked if [`Result`] contains [`Err`] value.
///
/// ```
/// use stdx::prelude::*;
/// use std::convert::Infallible as Never;
///
/// let mut invoked = false;
///
/// Ok::<i32, Never>(99).inspect_err(|err| {
/// // empty match means that the code is statically deduced unreachable
/// match *err {}
/// });
///
/// Err::<Never, i32>(99).inspect_err(|&err| {
/// invoked = true;
/// assert_eq!(err, 99);
/// });
///
/// assert!(invoked);
/// ```
fn inspect_err(self, f: impl FnOnce(&Err)) -> Self;
}
impl<Ok, Err> ResultExt<Ok, Err> for Result<Ok, Err> {
fn inspect_ok(self, f: impl FnOnce(&Ok)) -> Self {
if let Ok(ref it) = self {
f(it);
}
self
}
fn inspect_err(self, f: impl FnOnce(&Err)) -> Self {
if let Err(ref err) = self {
f(err);
}
self
}
} I propose Rust's I propose block stabilization of this API until we take the proposed 4 methods into account. cc @dtolnay Btw. do you have any suggestions on how we could work around the |
Stabilizing any of these methods doesn't require stabilizing all of them, more methods shouldn't block each other |
I agree with @Veetaha. In my opinion |
Closing this issue. For any further comments please use tracking issue. |
When you have a function call that returns a
Result
type, it's a very common need to perform some immutable action using one of its contained values (such as printing an additional message), and then pass on theResult
for other uses. I guess this is best described as theinspect
operation of a wrapper type, similar toIterator::inspect
in its purpose.Consider this pseudocode snippet:
Ideally, I would want to write this:
Currently, we could write this code:
Or this:
Or this:
The first two options are verbose; the third is overkill because we don't actually need to map in this case, so there's no need to take ownership of
err
. If we introduce aninspect_err
method, it would make the code more concise, but more importantly it delivers the intent much more clearly. Also bear in mind that this is a very simple example - in many use cases method calls onResult
andOption
are often chained. In those situations the small ergonomics improvements would be more pronounced.The proposed
Result::inspect
would have the signature:Result::inspect_err
would have the signature:In the same spirit,
Option::inspect
would have the signature:The text was updated successfully, but these errors were encountered: