Rust, meanwhile, is way stricter than that, and has gone way further on the "not relying on developer discipline" path: a failing Rust function will return a `Result<Value, Error>`. You can't even access the value without explicitly checking whether it's a value or an error one way or the other, which you can very much do so in e.g. Go. Rust also has an attribute called `must_use`, which can be set on types and functions. That attribute causes the compiler to emit a warning if the marked object is not used at all (either the type or the function's result), so while go will not say anything if you write
Foo()
and that returns an error (or an error and a result you happened not to care for), Rust will absolutely complain by default in the same case, you will need to write at the very least let _ = Foo();
Go has a second issue, which is that in result, error := Foo()
that you "have to" handle the error is a consequence of the unused variable check (can't define a variable and never read from it). However because it's that instead of something dedicated, this: id, error := Foo()
result, error := Bar(id)
if error != nil {}
will work fine, with no complaint. Despite possibly passing complete nonsense to Bar if Foo is in error. Also works with id, error := Foo()
if error != nil {}
result, error := Bar(id)
Funnily enough Rust will also warn in both those cases, because it tries to track individual writes.Of course that leaves function that can fail but don't return any value, but since Result is tagged "must_use" you get a compiler warning if you don't explicitly discard the result with something like `let _ = foo()`.
If the function returns a (non-error) value that is directly accessible from the function call, the programmer is not forced to do anything with the error.
This is exactly the same problem with null: forget a null check, and you're hosed. Forget an error check, and you're hosed.
If you make errors a part of the actual single return type (as Rust does), then you have to explicitly deal with the possibility of an error before you are even allowed to touch the successful case.
Haven't there been some languages that force functions to return some kind of tuple like:
result,error
And forces the programmer to at least do:
if(error) { }
It does not force any kind of correct handling, but simply oversights should be caught. I might be imagining things though.