Impl Not for Result

  1. For a method to do this, please provide some real world use cases for when one wants to invert a Result.
  2. For operator support via impl'ing the Not trait, please do (1) and show that this usage is common enough to warrant operator support for it.

Result is designed for error handling. While negation may exist strictly algebraic, in practice, I doubt negation is common or useful. Usually the Err variant has additional trait bounds that make it specifically useful for error handling that the Ok variant doesn't.

Speaking as a library team memeber, instead of arguing about abstractions and what "makes sense" from a vague consistency/theoretical perspective, please provide concrete usages that motivate the additions proposed.

26 Likes

when we asked for try on bools nobody wanted it, so now we want not on result to get the same effect.

parsing is a pain. it makes sense to want inversion of failure.

Yet this is the first clue you've given for what problem you're trying to solve.

I suspect this might be better served by a custom wrapper when Try is stabilized, so you can shortcut your desired case. The current redesign also proposes an enum ControlFlow that might work for you.

8 Likes

what churn is there in adding Not to Result?

I could ask that about any number of things; it's not a valid reason to do something on its own. You haven't shown where this would be objectively clearer in most situations. If I ran across someone negating a Result in a code review, I'd immediately reject it in favor of something that has obvious behavior.

4 Likes

Let's say you have a Result<(&str, &str), &str>, as coming from a lexer. It makes sense to fail if a token matches, so then you want to (!result.map(HelpfulError))? or (!result).map_err(HelpfulError)?. Alternatively you need to make your lexer take an &mut &str (not to be confused with &mut str) and output bools and use bools everywhere, which is, in fact, a lot easier than trying to work with the existing Result.

1 Like

The "ideal" lexer API is fn(&str) -> Option<usize> (for a single token kind) or fn(&str) -> Option<(Kind, usize)> (for any kind in a set). This just handles the needed information, and no more. Optionally, bake "unknown" into the token Kind and skip blocks of unrecognized input together.

Part of the API that makes a lexer a lexer is that it doesn't have rich errors. You just try all the cases and return the one that matches (or the longest match if you don't have a "perfect" lexical grammar).

If you take &str, there's no need to return the input back on failure, because it's already just borrowed.

For a new feature, you need to motivate it in some manner. What problem does it solve? Why don't current language features serve it well enough?

You consistently fail to provide motivation for your suggestions beyond some implication of "it would be nice", leaving the rest of us to guess at what you're trying to do. Please, while you don't have to follow the RFC template exactly on IRLO, provide some motivation for your suggestions. Nobody else knows what you're running into.

10 Likes

but having the input back is nice because you can just chain the things, either with and_then/or_else or with foo(bar(baz)?)? or whatnot.

these Try-related suggestions (beside the one about Try-on-bool, which we already use with a bry! macro everywhere) would take about as much effort as rewriting our parsers from scratch if we wanted to provide real examples.

Please explain how it's clearer, which is what I asked. Think of how you'd explain this to a newcomer to Rust, not an experienced person. While the parsers I've written don't follow the pattern you've shown, I understand that some do. I'm not asking about utility, I'm asking about clarity. Consider that the RFC-proposed Try trait would let you do this, as well.

7 Likes

It seems clear that implementing Not doesn't really fit. Is there any opposition to a flip method?

impl<T, E> Result<T, E> {
    fn flip(self) -> Result<E, T> {
        match self {
            Ok(t) => Err(t),
            Err(e) => Ok(e),
        }
    }
}
3 Likes

I think mostly from a philosophical point of view. Where having this sort of method seems like you are abusing result to be a generic ether type.

19 Likes

I think this equivalent to result.err().ok_or(HelpfulError)? This little detour through Option for such a niche case seems reasonable.

Well, there might be cases where you want an error to happen. That's the reason why unwrap_err exists.

unwrap_err panics if the Result is Ok, but there is no function that returns an error if the Result is Ok.

1 Like

If we never had Result in the language it'd be possible to experiment with API design for it. Too bad trait impls are insta-stable because we feel like this is something that would heavily benefit from experimentation.

At least parsers would be able to take benefit from it.

That isn't what is being asked for here though. That could very well be added behind a feature gate. Or even a new topic. Though I think Inline error recovery - #16 by steffahn is basically what is being asked for here anyway.

You could already experiment with this today, in multiple ways. The most obvious would be to add a method (e.g. .flip()) yourself, via an extension trait. You could then make an argument for why that method is not only useful but so incredibly useful it needs to have a one-character unary operator (as well as whether it fits the semantics of that operator). As a hint, such an argument would start with "a massive number of crates are using this, or contain a code pattern that would benefit from this"; that's the threshold such a proposal would need to meet, at a minimum.

You could also experiment with the syntax you propose using a macro, or a different Result type, and then use that to make a similarly compelling argument.

We almost never jump directly to "this needs short native syntax". Important steps that come before that: "does this need any native syntax", "could this be done in a library", "could this be done in a macro", "what's the most idiomatic way to write this today", "should this be done at all".

22 Likes

Yeah, Option::filter existed for almost two years as a third-party library before it was stabilized in std. If you think a Result::flip is useful, then you can do the same thing.

2 Likes

But names like "flip" and "reverse" are confusing whereas the operator would be less confusing due to not having a "name" exactly.

@Soni Two library team members (myself included) have already gone on record to say what would be minimally required to make this proposal actionable. I would suggest focusing your efforts on that. Otherwise, this discussion is just chasing our tails around the maypole.

11 Likes

Maybe it's because I came from a background of languages with confusing operators, but personally I would find an operator much more confusing than a named method.

I'd also probably agree that the use cases put forward so far would probably be better served by you defining your own Either type, and then you can do whatever you want.

13 Likes