`?` operator but for `Ok` instead of `Err`?

The question mark operator exists so that you don't have to check if a result is an Err, then return it every time you want to propagate an error to the previous function.

I would imagine that the inverse would also be true. Some operator that checks if a result is an Ok, then returns it.

I realize that it would be used less than the ? but it would still be a nice tool to have.
Here is an example of some code that would do well with the Ok operator.

fn combined_function(input: u32) -> Result<bool, String> {
   let r = is_not_modN(input, 3);
   if r.is_ok() {
      return r;
   }
   let r = is_not_modN(input, 5);
   if r.is_ok() {
      return r;
   }
   let r = is_not_modN(input, 7);
   if r.is_ok() {
      return r;
   }
   Err(String::from("Invalid number. It is devisable by either 3, 5, or 7!!"))
}

fn is_not_modN(input: u32, n: u32) -> Result<bool, ()> {
   if input%n != 0 {
      Ok(true)
   } else {
      Err(())
   } 
}

Given on Ok operator (I will use # for brevity), the code is a LOT shorter and easier to read.

fn combined_function(input: u32) -> Result<bool, String> {
  is_not_modN(input, 3)#;
  is_not_modN(input, 5)#;
  is_not_modN(input, 7)#;
  Err(String::from("Invalid number. It is devisable by either 3, 5, or 7!!"))
}

fn is_not_modN(input: u32, n: u32) -> Result<bool, ()> {
  if input%n != 0 {
     Ok(true)
  } else {
     Err(())
  } 
}

Is there anything like this in Rust already? And if there isn't, what would be the reasons for not adding this? (other than, cases like this being more rare than cases where ? can be used)

The Result::or and Result::or_else methods may be useful. Example:

fn combined_function(input: u32) -> Result<bool, String> {
    is_not_modN(input, 3).or_else(|_|
    is_not_modN(input, 5)).or_else(|_|
    is_not_modN(input, 7)).or_else(|_|
    Err(String::from("Invalid number. It is devisable by either 3, 5, or 7!!")))
}
3 Likes

I think that would be the deal breaking reason. It wouldn't just be more rare, it is vanishingly rare compared to the use of ?. Such terse syntax needs strong justification, and I think frequency of usage is a key element of that. (There are other aspects of ? besides control flow too that aren't clearly warranted for your use case.)

5 Likes

? was originally a macro called try, and it's dead simple. If you need that short circuiting behavior, and don't want explicit match statements, you should write a similar macro for your own case.

5 Likes

So the idea is that if there is an error one should continue processing. To me that sounds totally counter to the whole concept of being an error.

Perhaps you should present a better motivating example to make your case. After all an integer is either odd or even so is_not_modN would be better returning a bool. It also has double negatives in it which make things harder to understand. ìs_mod_nwould be a better function.

Personally I would rather not see ugly warts like #cluttering up the code, overloaded with obscure meanings.

The not in the function name is itself suspicious. First, I was going to recommend that you simply exchange the two sides of the result, i.e. you write a function that converts Result<T, E> to Result<E, T>. However, by seeing the negation in the function name, I think you should just un-negate that and write it as

fn is_divisible_by(dividend: u32, divisor: u32) -> Result<(), ()> {
   if dividend % divisor == 0 {
      Ok(())
   } else {
      Err(())
   } 
}

And then you can rewrite your top-level function as:

fn combined_function(input: u32) -> Result<(), ()> {
    is_divisible_by(input, 3)?;
    is_divisible_by(input, 5)?;
    is_divisible_by(input, 7)?;
    Ok(())
}

Which correctly checks for the number not being divisible by either of 3, 5, or 7, unlike your current implementation, which seems to have the bug that if the number is not divisible by at least one of these numbers, it reports success (instead of requiring the lack of divisibility by all three simultaneously).

However, this is still a weird and overly elaborate way to write a logical AND operation, and it would certainly not pass a code review on my part.

All in all, there are problems with your code: the intent, the clarity, the style, the unnecessary complexity. These should first be resolved with the existing tools of the language, because it's unlikely that such a piece of code would benefit from yet another operator – it would make the code less clear, if anything. So please first think more about what problem you are actually trying to solve, before proposing changes to the language.

2 Likes

It's a terrible example of the use case. The fact that a number is or is not divisible by something is not an error by itself.

I assume the OP just intends it as a stand in for something that may actually fail with an error.

But to my mind if it was such a proper failure prone example it would counter the entire argument for negating the '?' operator.

If something like Try Trait V2 gets implemented, it would be possible to make a struct that works with the ? operator to return early in the successful case.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.