|
|
|
|
|
by apta
3234 days ago
|
|
Golang (may contain syntax errors): func readNumberFromFileAndDoubleIt(filename string) (int, err) {
file, err = os.Open(filename)
if err != nil {
return 0, err
}
defer file.Close() // BUG!! this returns an error, but since we defer it, it is not going to be handled
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return 0, err
}
contents := string(bytes)
i, err := strconv.Atoi(contents)
if err != nil {
return 0, err
}
return 2*i, nil
}
Rust (may contain syntax errors): fn read_number_from_file_and_double_it(filename: &str) -> Result<i32> {
let mut file = File::open(filename)?; // file automatically closed at end of scope
let mut contents = String::new();
file.read_to_string(&mut contents)?;
contents.parse().map(|i| 2*i)
}
4 vs 15 lines, I think it's obvious which one is easier to read |
|
Any practical code using Results soon ends up wanting to mix the errors from multiple sources. This requires a lot of boilerplate effort to make everything interop, and the machinery to reduce this is both complex and not standardised. If you don't go the upfront boilerplate-and-machinery route, things look awful.
And of course, if you use something else, like an Option, you're back to
Go is much more consistent, and less pathological.Your example is especially disingenuous, though. For example, you chastise Go with
but ignore the fact that this "bug" is nonoptionally hardcoded[1] into the Rust program. Which is it then?Rust's error handling looks nice on fake examples, and manageable inside self-contained libraries. My experience of actually using multiple libraries is that Rusts error handling is a choice between tangly handling of nested errors or verbose attempts to early-exit.
[1]: https://github.com/rust-lang/rust/blob/master/src/libstd/sys...