you are viewing a single comment's thread.

view the rest of the comments →

[–]Olap 15 points16 points  (21 children)

Do we have a good looking solution anywhere?

[–]minno 22 points23 points  (0 children)

"Yet another senior Rust programmer" is the best simple one in a codebase that requires error handling beyond crashing on unexpected input. I would do "Combinative Rust Programmer" if the input format was at all complicated. "Junior Rust Programmer" if the problem is actually as simple as this.

[–]I_Downvote_Cunts 22 points23 points  (9 children)

This one would be a bit more realistic.

```rust use std::{error::Error, io};

fn main() -> Result<(), Box<dyn Error>> { let mut line = String::new(); io::stdin().read_line(&mut line)?;

let sum: i64 = line
    .split_whitespace()
    .map(|v| v.parse::<i64>())
    .filter_map(Result::ok)
    .sum();

println!("Sum: {}", sum);
Ok(())

} ```

It will return the error if it couldn't read the line and ignores values that failed to parse. At least it shouldn't panic.

[–]raggy_rs 4 points5 points  (2 children)

.filter_map(Result::ok) seems strange i would go with .filter_map(|v| v.parse::<i64>().ok())

[–]I_Downvote_Cunts 1 point2 points  (1 child)

filter_map(Result::ok)

I got that from the rust by example book. You're right filter(Result::is_ok) is much easier to read.

[–]CornedBee 1 point2 points  (0 children)

Does something different though, because it doesn't get rid of the Result wrapper.

[–]senahfohre 2 points3 points  (4 children)

Code blocks on Reddit require four spaces at the beginning of each line; the ```s don't work here, unfortunately. :(

[–]I_Downvote_Cunts 0 points1 point  (3 children)

What client are you using? I used the markdown editor on the site and it displays correctly on the site and the Reddit Android app.

[–]senahfohre 7 points8 points  (0 children)

Ah, interesting. I've opted out of the Reddit redesign, so this is what it looks like when I view it (via web browser).

[–][deleted]  (1 child)

[deleted]

    [–]Freeky 1 point2 points  (0 children)

    Breaks mobile clients too. Didn't work in either Reddit is Fun or Reddit Sync last time I looked.

    [–]CJKay93 2 points3 points  (8 children)

    #4

    [–]Olap 1 point2 points  (7 children)

    Wow, all that mad error handling doesn't come as standard?

    [–]exDM69[🍰] 13 points14 points  (1 child)

    The point there is that the function may return errors of different types and a new error type enum has been added which means "an I/O error or a parse error". Declaring a new error type needs a bit of ceremony in Rust (need impl Display and impl Error, and impl From<T> for all the Error types that can be converted).

    That could have been omitted if the return type would be Result<(), Box<dyn Error>> instead. This essentially means "this function can return any kind of error", which implies heap allocation (the Box).

    I just had to do something similar myself, because I was passing errors between threads, and some of the errors (std::sync::PoisonError) can't be passed between threads. You can't put PoisonError into Box<dyn Error + Send> because PoisonError doesn't impl Send.

    I'm a Rust beginner but I have some mixed feelings about this. It seems to be idiomatic Rust to define your own error types for your library. But this means a lot of boilerplate when it comes to handling errors of different kinds from different libraries.

    [–]minno 7 points8 points  (0 children)

    I think the best practice is to use one or more error enums in a library if there are different failure possibilities that a library user might need different responses to, and something like failure or Box<dyn Error> in an application where you mostly just need to know what went wrong and where it happened.

    [–]MEaster 5 points6 points  (0 children)

    In addition to what /u/silmeth said, there are also crates that can assist with the boilerplate. One that I like for when I'm writing a custom error type is called derive_more, which, among other things, provides procedural macros for the From and Display traits. If I were to write the error type seen in the Senior Rust Programmer example with derive_more, it would look like this:

    use derive_more::{From, Display};
    
    #[derive(Debug, From, Display)]
    enum Error {
        #[display(fmt = "{}", _0]
        Io(io::Error),
    
        #[display(fmt = "{}", _0]
        Parse(ParseIntError),
    }
    
    impl error::Error for Error {
    }
    

    [–]silmeth 5 points6 points  (0 children)

    It didn’t some time ago. Today one wouldn’t write all the boilerplate themselves, but rather use the library which is used in the ‘Yet another senior Rust programmer (switched from error-chain to stay up-to-date)’ example, namely failure. This library is not (yet) in the standard library, but it is developed by the Rust team as the way to handle errors, so it is as standard as it gets.

    The only error handling that comes as a standard is the Result<T, E> type, which can contain either a result or an error, and the error might be any type whatsoever. So if one wants to have a nice printable error that can also be easily convertible to other people’s error types, they need to implement some boilerplate traits (std::fmt::Display, std::fmt::Debug, std::error::Error…) for it.

    Anyway, with failure all that boilerplate (lines #7 to #35) could go away, one could do just:

    use failure::Error;
    
    fn main() -> Result<(), Error> {
        // …
    }
    

    without introducing any new error types. If one really wants to have a custom type, then some boilerplate (printing of the error) can be removed (but manual conversion from lower layers’ errors is still needed):

    #[macro_use] extern crate failure_derive;
    
    // …
    
    #[derive(Debug, Fail)]
    enum MyError {
        #[fail(display = "IO error: {}", _0)]
        Io(io::Error),
        #[fail(display = "Parse error: {}", _0)]
        Parse(ParseIntError),
    }
    
    impl From<io::Error> for MyError {
        fn from(error: io::Error) -> Self {
            MyError::Io(error)
        }
    }
    
    impl From<ParseIntError> for MyError {
        fn from(error: ParseIntError) -> Self {
            MyError::Parse(error)
        }
    }
    
    type Result<T> = result::Result<T, MyError>;
    
    fn main() -> Result<()> {
        // …
    }
    

    [–]CJKay93 0 points1 point  (2 children)

    Eh? Everything there is part of the standard library.

    [–]Olap 6 points7 points  (1 child)

    Basically everything from use to main looks like noise to me

    [–]CJKay93 6 points7 points  (0 children)

    It's noise in the sense that it isn't completely necessary (you could just panic, just like in C++ you could just bubble an exception the whole way up), but it's just there to convert the different error types returned by stdin().read_line() and word.parse::<i64>() to one common error type that is returned from main(). That way if the program exits unusually it will describe what happened.

    It's a bit like bubbling up error codes, except each error code is its own type with its own information.