Comment by Thaxll

5 years ago

You can "ignore" error is Rust using _ like in Go.

Not really. In Go you can `val, _ := func()` and use the value even if there is an error. AFAIK there is no equivalent in Rust (for Option) outside of unsafe shenaniganry. You can choose to panic / return err / etc, but you can't choose to use the value regardless of the presence of an error.

  • Yep. I'm pretty sure that even with unsafe shenanigans, you can't access the value without being very explicit about it. You'd need something like:

        let value = unsafe {
            match result {
                Ok(value) => value,
                _ => hint::unreachable_unchecked()
            }
        };
    

    At this point, the fact that you've skipped an error check should be abundantly clear to anyone reading your code.

  • You can return tuples from Rust fns just like you would in Go, if that's your thing - no unsafe necessary:

      fn foo() -> (usize, Result<(),()>) { (0, Ok(())) }
      let (a, err) = foo(); err?; // propigate error
      let (b, _) = foo(); // discard error
    

    Or more typically, you might use one of the many fn s such as unwrap_or, unwrap_or_else, unwrap_or_default, etc. - to provide your own appropriate default value. I usually find that useful default values are often caller specific anyways (and doesn't require remembering which fns return which default values on error):

      fn foo() -> Result<usize> { Ok(1) }
      fn bar() -> Option<usize> { None }
      let val = foo().unwrap_or(3); // val == 1
      let val = bar().unwrap_or(4); // val == 4
    

    Alternatively you can use out parameters, which occasionally crops up in Rust's own stdlib:

      let mut line = String::new();
      let _ = buf_read.read_line(&mut line);
      // ...use line, even if there was an error...
    

    Also, the "error" type might contain values itself, although you're certainly not ignoring the error if you use it:

      // https://doc.rust-lang.org/std/ffi/struct.OsString.html#method.into_string
      match os_string.into_string() {
          Ok(string) => println!("Valid UTF8 string: {}", string),
          Err(os_string) => println!("Invalid UTF8: {:?}", os_string),
      }

    • Sure, it's always possible to design a system that avoids the type system of a language. At worst case you can just resort to creating an un-typed lambda calculus and re-implementing your logic there.

      Community habits and language frictions matter a lot. In Go, doing the equivalent of `Result` requires custom private types for every combination, with private fields and accessor methods that protect against misuse. And you still only gain runtime safety. And they can still make a zero-valued var and use it without "initialization" (unless you use private types, which are an even bigger pain for a variety of reasons). Any other approach makes it trivial to bypass - public fields can just be accessed, multiple returns can be ignored, etc. In Rust, the community and language make `Result` common, and then you gain compile-time safety in most cases, and warnings in all others (AFAIK, as Result is annotated as "must use", so you get a warning or a `_ =` as a visible marker that you're ignoring something).

      ---

      tl;dr multiple returns of course bypass this, but in practice you won't see that in Rust unless it's intended to allow ignoring the error. Underscores on out-param funcs are a good point, but they're also fairly rare / serve as a visual warning of shenaniganry.

  • If you're not going to use the success value, you can ignore errors in Rust easily:

        let _ = something_returning_Result();
    

    This does not even give a warning.

    • > This does not even give a warning.

      Just to clarify: the "let _ = ..." construct is the explicit way of suppressing the warning in Rust. You acknowledge that there is indeed a return value but you choose to ignore it. Just calling the function without explicitly discarding the Result will give you a warning.

That's still very explicit. If you don't bind the returned Result to something (`let _ = ...`), the compiler bitches at you.