There's a reason most go code is littered with "if err != nil" on nearly all function calls.
There's a reason most go code is littered with "if err != nil" on nearly all function calls.
There is no language that enforce error checking afaik.
This code will compile (see https://play.rust-lang.org/?version=stable&mode=debug&editio...):
pub fn foo() -> Result<(), i64> {
Err(1)
}
pub fn bar() -> Result<(), ()> {
foo();
Ok(())
}
It will provide a warning, but there's a ton of stuff in c++ that would throw a warning and you wouldn't say that it "will not compile".A trivial change that still doesn't handle the error would get rid of the warning.
pub fn foo() -> Result<(), i64> {
Err(1)
}
pub fn bar() -> Result<(), ()> {
println!("{:?}", foo());
Ok(())
}(EDIT: nevermind, just looked again and pcwalton was referring to the warning and specifically `Result<(), E>`; oh well)
Because the latter is impossible in Rust and probably more relevant to the usual cited issue with Go's pair approach (i.e. using the null/zeroed `Foo` without checking if there was an error).
I do agree though that the warning isn't to stop you from not handling the error at all, it's more of a hint that maybe you forgot something.
Printing a `Result` may be the legitimate way to handle it in that case, it's largely left to the user to decide what propagates and what doesn't.
fmt.Println("foo")
You're not forced to handle the error. Not to mention more obscure cases like a, err1 := foo()
if err1 != nil { return err1 }
b, err2 := bar()
if err2 != nil { return err1 } // bug a, err := Foo()
b, err := Bar()
c, err := Baz()
check(err)
doSomethingWith(a, b, c)
because "err" is ultimately used so doesn't trigger the "unused variable" compile error. The Go compiler doesn't care that it's written to thrice and only checked once.In fact thinking about it that's a perfect example of "solving 90% of the problem, badly" the article talks about (though it's probably closer to 70% here): the Go compiler doesn't really try to understand that errors are a thing and are relevant. To avoid developers writing
val, err := Foo()
then going on to use `val` without checking `err` the devs decided to… require using variable.This solves that specific issue but does nothing if, say, you miss that the function returns just an error, or you don't care about the result so you just ignore everything it returns. Or as above if you've got multiple calls binding to the generic (and conventional) `err` and think to check the last one (possibly because the calls above were only added later and the compiler never complained).
Meanwhile it makes Go throw a fit and literally refuse to compile your code because you wrote an innocent:
val := 5
and hadn't come around to use it yet, or removed the one print you didn't care for anymore.The only thing in Go source code that you see more often than the boiler plate "if err != nil" is "a, _ = foo()".
It's all too easy to ignore an error in Go.
Where did you see that ? because that's not been my experience at all, and I've looked at a lot of Go code.
Go has many community linters available, https://github.com/kisielk/errcheck is popular for checking unhandled errors.
If you'd like a combo-pack, check out https://github.com/golangci/golangci-lint which includes all of the popular linters in a configurable way.
The reason why you don't see that is because you have to be explicit about that, it's not something you forget it's done on purpose which obviously no one does.
For a language which refuses to compile if you have an unused import, it seems like an odd gap not to have the compiler force you to access the error before it’s reassigned.