This is also how errors-as-values work in Rust. Functions that may fail return Result<T, E> - putting an ? at the end of a failable function call returns T on success, otherwise it propagates E. It reduces
I know about ? in Rust - the one thing that isn't very clear to me is how often it is enough. That is, with Go, it's quite typical to do something like:
result, err := call()
if err != nil {
return fmt.Errorf("Error while trying to call: %v", err)
}
Essentially manually building a stack trace. If just doing "return err", you end up with calls to a REST service failing with messages like `couldn't parse "" as int` even in internal logs, which isn't helpful. With exceptions (in any language except C++), the stack trace is often decent enough.
Does ? add any kind of context implicitly, or do you have to actually pattern match manually to add context?
There is not (normally) context added automatically. One can use `map_err(|e| ...)` to add context like this:
let result = call().map_err(|e| SomeError::MoreData(e))?;
Or if using one of the common error libraries to have non-specific error kinds:
let result = call().context("some data")?;
or
let result = call().with_context(format!("something happened: {}", other_thing))?;
For something that more directly matches the behavior of the go code, this (using the `anyhow` crate) is a possible match:
let result = call().map_err(|e| anyhow::Error::msg(format!("Error while trying to call: {:?}", e)?;
Though one would normally avoid this when using anyhow (or in rust in general) as it means we're flattening the error instead of generating a list of causes.
if you only limit yourself to the standard library, you would need to unwrap and rewrap the error - although admittedly since Result<T, E> is a type like any other, you could add an extension function (through a trait) so that you can add context more easily. If you use use one of the error handling libraries though, you're in luck - adding context is usually a single function call away:
//without context
some_function()?;
//with context, using anyhow
some_function().context("function returned error")?;
//with wrap_err, using eyre
some_function().wrap_err("function returned error eyre")?;