The explanation of how it solves these problems has not been forthcoming.
It'd be interesting to see what you'd do with the code above in C++, and where you'd put the error handling code for diverse errors that might occur reading this particular file.
The Go approach is to handle errors locally, often in the calling method, which makes it clear where they are handled and what the outcome is, and easier to recover gracefully, without unexpected exceptions from code in libraries or other code in the program. Some large users of C++ (like Google) refuse to use C++ exceptions in their own code - so they are not entirely without controversy.
In the code above, if you used exceptions, and relied on the libraries to throw exceptions for errors, you'd have to throw your own exception at:
error("Level %d is corrupt", num);
So you'd have a mix places where exceptions were generated (in unknown lib code, in your code) and an unknown (for the reader) mix of places where they are handled. I'm sure this could be done gracefully, but it does mean errors missed might be handled at a much higher level in the code, far away from where they were generated, which can lead to errors being missed until it is too late to do anything but output a stack trace and exit, which to the user seems equally stupid as crashing or panicking at some later point.
If you exit the program on simple errors like being unable to load a single game file, it's not very pleasant for the user - I'd expect it instead to recover gracefully and show the user an error before continuing, which is easy enough when using Go's pattern of error returns, and harder with exceptions where you have unrolled the stack possibly past the loading code, unless you start handling exceptions in calling code one level up, which looks very much like the error handling of Go. So there are trade-offs to using either method aren't there?
Not using C++ exceptions in Google's code bases is partly due to historic reasons (since the original code base did not have it), and partly due to them being "harder" to implement in C++ correctly.
They even acknowledge in their C++ style guide[1] that: " Things would probably be different if we had to do it all over again from scratch."
It'd be interesting to see what you'd do with the code above in C++, and where you'd put the error handling code for diverse errors that might occur reading this particular file.
The Go approach is to handle errors locally, often in the calling method, which makes it clear where they are handled and what the outcome is, and easier to recover gracefully, without unexpected exceptions from code in libraries or other code in the program. Some large users of C++ (like Google) refuse to use C++ exceptions in their own code - so they are not entirely without controversy.
In the code above, if you used exceptions, and relied on the libraries to throw exceptions for errors, you'd have to throw your own exception at:
error("Level %d is corrupt", num);
So you'd have a mix places where exceptions were generated (in unknown lib code, in your code) and an unknown (for the reader) mix of places where they are handled. I'm sure this could be done gracefully, but it does mean errors missed might be handled at a much higher level in the code, far away from where they were generated, which can lead to errors being missed until it is too late to do anything but output a stack trace and exit, which to the user seems equally stupid as crashing or panicking at some later point.
If you exit the program on simple errors like being unable to load a single game file, it's not very pleasant for the user - I'd expect it instead to recover gracefully and show the user an error before continuing, which is easy enough when using Go's pattern of error returns, and harder with exceptions where you have unrolled the stack possibly past the loading code, unless you start handling exceptions in calling code one level up, which looks very much like the error handling of Go. So there are trade-offs to using either method aren't there?