logoalt Hacker News

oncallthrowyesterday at 11:10 PM4 repliesview on HN

I don't agree. There isn't a standard convention for wrapping errors in Rust, like there is in Go with fmt.Errorf -- largely because ? is so widely-used (precisely because it is so easy to reach for).

The proof is in the pudding, though. In my experience, working across Go codebases in open source and in multiple closed-source organizations, errors are nearly universally wrapped and handled appropriately. The same is not true of Rust, where in my experience ? (and indeed even unwrap) reign supreme.


Replies

codystoday at 2:12 AM

One would still use `?` in rust regardless of adding context, so it would be strange for someone with rust experience to mention it.

As for the example you gave:

    File::create("foo.txt")?;
If one added context, it would be

    File::create("foo.txt").context("failed to create file")?;
This is using eyre or anyhow (common choices for adding free-form context).

If rolling your own error type, then

    File::create("foo.txt").map_err(|e| format!("failed to create file: {e}"))?;
would match the Go code behavior. This would not be preferred though, as using eyre or anyhow or other error context libraries build convenient error context backtraces without needing to format things oneself. Here's what the example I gave above prints if the file is a directory:

    Error: 
       0: failed to create file
       1: Is a directory (os error 21)

    Location:
       src/main.rs:7
kstrausertoday at 12:02 AM

> There isn't a standard convention for wrapping errors in Rust

I have to say that's the first time I've heard someone say Rust doesn't have enough return types. Idiomatically, possible error conditions would be wrapped in a Result. `foo()?` is fantastic for the cases where you can't do anything about it, like you're trying to deserialize the user's passed-in config file and it's not valid JSON. What are you going to do there that's better than panicking? Or if you're starting up and can't connect to the configured database URL, there's probably not anything you can do beyond bombing out with a traceback... like `?` or `.unwrap()` does.

For everything else, there're the standard `if foo.is_ok()` or matching on `Ok(value)` idioms, when you want to catch the error and retry, or alert the user, or whatever.

But ? and .unwrap() are wonderful when you know that the thing could possibly fail, and it's out of your hands, so why wrap it in a bunch of boilerplate error handling code that doesn't tell the user much more than a traceback would?

show 1 reply
bargainbinyesterday at 11:20 PM

My experience aligns with this, although I often find the error being used for non-errors which is somewhat of an overcorrection, i.e. db drivers returning “NoRows” errors when no rows is a perfectly acceptable result of a query.

It’s odd that the .unwrap() hack caused a huge outage at Cloudflare, and my first reaction was “that couldn’t happen in Go haha” but… it definitely could, because you can just ignore returned values.

But for some reason most people don’t. It’s like the syntax conveys its intent clearly: Handle your damn errors.

mh2266yesterday at 11:26 PM

I think the standard convention if you just want a stringly-typed error like Go is anyhow?

And maybe not quite as standard, but thiserror if you don’t want a stringly-typed error?