Why can you ignore a returned error without an underscore?

Perhaps there is a simple reason for this… but I don’t understand why the compiler allows this:

package main

import (
	"errors"
	"fmt"
)

func hello() error {
	fmt.Println("hello")
	return errors.New("bad things")
}

func main() {
	hello()
}

It’s not that hard to forget that the hello function returns an error (in a more complicated scenario) and if you were just code reviewing main (assume the hello func is in some lib) a reviewer would definitely miss the fact that an error might have occurred.

Running example: The Go Playground

If I saw a call to a function that took no params and returned nothing, I would probably examine it. I don’t think the Go compiler is trying to prevent you from doing certain things like ignoring errors. It’s just trying to make you do so intentionally. This, to me, seems intentional.

I’ve been bitten by plenty of things while writing software. Can’t say that calling a function named hello while ignoring that it might return something has been one of them. Especially in go, given that it’s idiomatic to return errors if errors are possible. And thus it’s idiomatic to also check for errors. What’s the real-world scenario you are trying to protect against?

I guess my example was quite contrived. I was just trying to illustrate the function signature that would cause the issue I was asking about. The context for the question is that I have been working to bring Go into my team at work - so I’ve been trying to learn a lot about the smaller edge cases in the language and to understand where a new Go programmer might make mistakes.

I would also counter that Go does attempt to ensure you don’t make simple mistakes (maybe not errors specifically since they are just another value). To me, my hello example would be the same thing as attempting to do this (again just a contrived example for illustrative purposes):

package main

import "fmt"

func executeUpdate(count int) (int, error) {
	return count, nil
}

func main() {
	count := executeUpdate(10)
	fmt.Println(count)
}

Which of course does not compile. Instead, the compiler says: ./prog.go:12:11: assignment mismatch: 1 variable but executeUpdate returns 2 values.

Why doesn’t my hello example fail to compile with the same type of error (e.g. assignment mismatch: 0 variables but hello returns 1 value)?

I would expect that you would need to write _ = hello() for the compiler to accept it (which is OK as well… but not required).

I should note that I don’t think anyone disagrees that this is bad code and not idiomatic Go. I’m just trying to understand why the compiler doesn’t enforce it because it seems like a simple case that would ensure cleaner and easier to read code. I also know (don’t have an exact specific example) that I’ve copy/pasted function signatures and forgot to handle the error and when the compiler doesn’t complain, I forget. So it’s not about trying to take a short cut so much as trying to catch simple mistakes.

Here’s a GitHub issue with some discussion:

Some creative solutions were put forward but it didn’t get much traction and at this point it would break the compatibility promise.

Ah! Thank you for sharing that. I missed that in my search. I think that pretty much answers my question then - and makes sense that at this point it would break backwards compatibility.

They make a reference to this package: GitHub - kisielk/errcheck: errcheck checks that you checked errors.

Which I think would probably satisfy my concern in terms of catching mistakes. Perhaps a bit noisy, but it looks like you can supply an allow-list for functions that you don’t care about.

I’ll give that a try and keep track of those proposals to see where they land. Thanks again!

Thanks

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.