Is function parameter validation using errors a good pattern in Go? - validation

Is parameter validation using error return codes considered good practice ? I mean where should somebody use errors vs panics (are there any guidelines?).
For instance:
Is checking for non-nil + returning an error if it is nil a good
practice ?
Or checking for correct integer ranges etc.
I imagine that using errors that often would make Go feel very C-ish and would look pretty bad. Are panics a good alternative in those situations ?
Or should a Gopher use the Python/Ruby/JS-approach "just let it fail" ?
I'm a bit confused because panics are for real "errors" in my understanding. But using errors all the time is just bad.
And even if I would return error code: What could I do if somebody passes wrong parameter to my function but ignores the errors codes ? -> Nothing! So honestly I would say panics are nice for those situations but in a language where error codes are used over panics this is not very clear.

"Escaping" panics1 in Go (I mean, those which might be produced by the functions comprising the public API of your package) are to deal with errors programmers do. So, if your function gets a pointer to an object, and that can't be nil (say, to indicate that the value is missing) just go on and dereference the pointer to make the runtime panic itself if it happens to be nil. If a function expects an integer that must be in a certain range, panic if it's not in that range — because in a correct program all values which might be passed to your function are in that range, and if they don't then either the programmer failed to obey the API or they did not sanitize the value acquired from the outside which, again, is not your fault.
On the other hand, problems like failure to open a file or pefrorm some other action your function is supposed to perform when called correctly should not cause panics and the function should return an appropriate error instead.
Note that the recommendation for explicit checking for null parameters in the functions of public APIs in .NET and Java code has different goal of making such kinds of errors sort-of more readable. But since 99% of .NET and Java code just lets all the exceptions propagate to the top level (and then be displayed or may be logged) it's just replacing one (generated by runtime) exception with another. It might make errors more obvious—the execution fails in the API function, not somewhere deeper down the call stack—but adds unnecessary cruft to these API functions. So yes, this is opinionated but my subjective opinion is: to let it just crash is OK in Go—you'll get a descriptive stack trace.
TL;DR
With regard to processing of run-time problems,
panics are for programming errors;
returning errors is for problems with carrying out the intended tasks of functions.
1 Another legitimate use for panics is quick "cold-path" returning from deep recursive processing/computation; in this case panic should be caught and processed by your package, and the corresponding public API functions should return errors. See this and this for more info.

The answer to this is subjective. Here are my thoughts:
Regarding panic, I like this quote from Go By Example (ref)
A panic typically means something went unexpectedly wrong. Mostly we use it to fail fast on errors that shouldn’t occur during normal operation, or that we aren’t prepared to handle gracefully.
In the description of your use case, I would argue that you should raise an errors and handle the errors. I would further argue that it is good practice to check the error status when one is provided by the function you are using and that the user should check if one is provided in the documentation.
Panics I would use to stop the execution if I run across an error that is returned from the function you are writing that I check and don't have a way to recover from.

Related

How can Go code plan for exceptions (panic)?

I do not know Go. I was going through the docs to get an idea of the language and came up against the Defer Panic Recover functionality.
Panic seems to work like exceptions. However I could not find how my code would guard against these exceptions, which can be thrown by layers deep below the ones I call. Java has checked exceptions. Does Go have something similar?
How does this work?
Edit: There seem to be 2 ways to think about this
Panic is very rare and it should be allowed to kill the program as described here
Panic can be used in the regular flow of code - as an example of defer panic as explained here, which describes how to use it for malformed input.
My question pertains to usage of panic in situations like 2, which seems to be easily doable.
Sure, you can use panic to control the regular flow of the program, but I think the more idiomatic way is to just return an error to the caller in such cases (like the case of malformed user input).
Panic is normally used in exceptional cases that imply some kind of a bug in the program. You can call the panic yourself if something goes wrong, but it's also called automatically in the case of nil pointer dereference, out of bounds array access and other similiar cases. Often things like this should crash the program, which it does by default, but if your program is some kind of service, you probably don't want the whole program to crash, you just want it to stop what it was doing when it encountered the crash and return to it's normal state.
Take a web server for example, if some strange request causes an out of bounds array access due to some bug. You don't want the whole server to crash, instead you want it to exit the handler function for that request, but keep listening for new requests. The way to do that is described pretty well in your example two. So you defer a recover() function in the place where you want the panic to stop. If the recover returns an error you know your program was paniccing. You can then do some logging, maybe send an email to yourself saying that something is wrong, etc., and then continue normal program flow. But if you had some validation function that noticed there's something wrong with the request, you probably wouldn't want it to panic. Instead you'd want it to return an error the the caller (request handler) which could then answer the request with 400 bad request, and then exit the handler normally.

Replacing golang error codes with panic for all error handling

We have medium sized application written in go. From all the code lines about 60 percent goes to code error handling. Something like this:
if err != nil {
return err
}
After some time, writing this lines over and over again becomes tiresome and we are now thinking to replace all error codes with panics.
I know panics aren't meant to be used like that.
What can be potential pitfall and does anyone have experience with something similar ?
The main pitfall would be a widespread use of hammers to drive screws. Panic is for unrecoverable/unexpected errors, error return values are for recoverable/expected errors.
Replace the word "panic" with "crash", because that is conceptually what a panic is. Do you honestly want to write an application that by design crashes whenever anything goes in any way remotely wrong? That would be the most fragile application on Earth, the very antithesis of fault tolerance.
As all the comments suggest you could be signing up for a disaster and let me add, whosoever is going to maintain this code will go insane trying to figure out what exactly went wrong when things do go awry if you don't do proper error handling (even if that person is you, it's going to haunt you).
Just repeating what others have said - the problem is being unable to differentiate between these two
a predictable & possibly recoverable condition
perhaps what is unrecoverable (need to exit the application or be handled at a higher level - this may be coming from your own application's conditions or some third party library, but is forcing you out of your normal execution path)
Panics are reserved for the latter types.

Why did Go add panic and recover in addition to error handling? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Why did Go end up adopting exception handling with panic/recover, when the language is so idiomatic and a strong advocate of error codes? What scenarios did the designers of Go envision not handled by error codes and necessitate panic/recover?
I understand convention says limit panic/recover, but does the runtime also limit them in ways that they can't be used as general throw/catch in C++?
Some history:
In the early days of Go (before version 1.0) there was no recover(). A call to panic() would terminate an application without any way to stop that.
I've found the original discussion that led to adding recover(), you can read it on the golang-nuts discussion forum:
Proposal for an exception-like mechanism
Beware: the discussion dates back to March, 25, 2010, and it is quite exhausting and long (150 posts through 6 pages).
Eventually it was added on 2010-03-30:
This release contains three language changes:
The functions panic and recover, intended for reporting and recovering from
failure, have been added to the spec:
http://golang.org/doc/go_spec.html#Handling_panics
In a related change, panicln is gone, and panic is now a single-argument
function. Panic and recover are recognized by the gc compilers but the new
behavior is not yet implemented.
Multi-return values and conventions provide a cleaner way to handle errors in Go.
That does not mean however that in some (rare) cases the panic-recover is not useful.
Quoting from the official FAQ: Why does Go not have exceptions?
Go also has a couple of built-in functions to signal and recover from truly exceptional conditions. The recovery mechanism is executed only as part of a function's state being torn down after an error, which is sufficient to handle catastrophe but requires no extra control structures and, when used well, can result in clean error-handling code.
Here is a "real-life" example for when/how it can be useful: quoting from blog post Defer, Panic and Recover:
For a real-world example of panic and recover, see the json package from the Go standard library. It decodes JSON-encoded data with a set of recursive functions. When malformed JSON is encountered, the parser calls panic to unwind the stack to the top-level function call, which recovers from the panic and returns an appropriate error value (see the 'error' and 'unmarshal' methods of the decodeState type in decode.go).
Another example is when you write code (e.g. package) which calls a user-supplied function. You can't trust the provided function that it won't panic. One way is not to deal with it (let the panic wind up), or you may choose to "protect" your code by recovering from those panics. A good example of this is the http server provided in the standard library: you are the one providing functions that the server will call (the handlers or handler functions), and if your handlers panic, the server will recover from those panics and not let your complete application die.
How you should use them:
The convention in the Go libraries is that even when a package uses panic internally, its external API still presents explicit error return values.
Related and useful readings:
http://blog.golang.org/defer-panic-and-recover
http://dave.cheney.net/2012/01/18/why-go-gets-exceptions-right
https://golang.org/doc/faq#exceptions
http://evanfarrer.blogspot.co.uk/2012/05/go-programming-language-has-exceptions.html
I think that your question is the result of a mental model you maintain which is instilled by popular mainstream languages such as Java, C++, C#, PHP and zillions of other which simply got exceptions wrong.
The thing is, exceptions per se are not a wrong concept but abusing them to handle cases which are, in reality, not exceptional is. My personal pet peeve is the filesystem handling API of Java (and .NET, which copied that of Java almost verbatim): why on Earth failure to open a file results in an exception if that file does not exist? The filesystem is an inherently racy medium, and is specified to be racy, so the only correct way to ensure a file exists before opening it for reading is to just open it and then check for "file does not exist" error: the case of the file not existing is not exceptional at all.
Hence Go clearly separates exceptional cases from plain normal errors.
The motto of the stance Go maintains on handling errors is "Errors are values" and thus normal expected errors are handled as values, and panic() serves to handle exceptional cases. A good simple example:
An attempt to dereference a nil pointer results in a panic.
The rationale: your code went on and tried to dereference a pointer which does not point to any value. The immediately following code clearly expects that value to be available—as the result of the dereferencing operation. Hence the control flow clearly can't proceed normally in any sensible way, and that's why this is an exceptional situation: in a correct program, dereferencing of nil pointers cannot occur.
The remote end of a TCP stream abruptly closed its side of the stream and the next attempt to read from it resulted in an error.
That's pretty normal situation: one cannot sensbily expect a TCP session to be rock-solid: network outages, packet drops, unexpected power blackouts do occur, and we have to be prepared for unexpected stream closures by our remote peers.
A minor twist to panic() is that Go does not force you to blindly follow certain dogmas, and you can freely "abuse" panic/recover in tightly-controlled specific cases such as breaking out from a deeply-nested processing loop by panicking with an error value of a specific type known and checked for at the site performing recover.
Further reading:
"Why should I have written ZeroMQ in C, not C++".
"Errors are values".
"Is function parameter validation using errors a good pattern in Go?".
I think the reason is concurrency model. Go is highly concurrent language in nature and core syntax. Scenario when some computation localized to concurrent process failure, but hole system continue to work can be viewed as normal. To my mind panic and recover are about failure handling, not exceptions.
Because this is more powerful as it allows to create more resilient programs.
In C you can't recover from a NULL pointer dereference.
In Go you can catch it with recover.
This allows for example to create in Go an HTTP server that can catch a NULL pointer dereference when handling a request and return an error 500 instead of crashing the whole server.

If as assert fails, is there a bug?

I've always followed the logic: if assert fails, then there is a bug. Root cause could either be:
Assert itself is invalid (bug)
There is a programming error (bug)
(no other options)
I.E. Are there any other conclusions one could come to? Are there cases where an assert would fail and there is no bug?
If assert fails there is a bug in either the caller or callee. Why else would there be an assertion?
Yes, there is a bug in the code.
Code Complete
Assertions check for conditions that
should never occur. [...]
If an
assertion is fired for an anomalous
condition, the corrective action is
not merely to handle an error
gracefully- the corrective action is
to change the program's source code,
recompile, and release a new version
of the software.
A good way to
think of assertions is as executable
documentation - you can't rely on them
to make the code work, but they can
document assumptions more actively
than program-language comments can.
That's a good question.
My feeling is, if the assert fails due to your code, then it is a bug. The assertion is an expected behaviour/result of your code, so an assertion failure will be a failure of your code.
Only if the assert was meant to show a warning condition - in which case a special class of assert should have been used.
So, any assert should show a bug as you suggest.
If you are using assertions you're following Bertrand Meyer's Design by Contract philosophy. It's a programming error - the contract (assertion) you have specified is not being followed by the client (caller).
If you are trying to be logically inclusive about all the possibilities, remember that electronic circuitry is known to be affected by radiation from space. If the right photon/particle hits in just the right place at just the right time, it can cause an otherwise logically impossible state transition.
The probability is vanishingly small but still non-zero.
I can think of one case that wouldn't really class as a bug:
An assert placed to check for something external that normally should be there. You're hunting something nutty that occurs on one machine and you want to know if a certain factor is responsible.
A real world example (although from before the era of asserts): If a certain directory was hidden on a certain machine the program would barf. I never found any piece of code that should have cared if the directory was hidden. I had only very limited access to the offending machine (it had a bunch of accounting stuff on it) so I couldn't hunt it properly on the machine and I couldn't reproduce it elsewhere. Something that was done with that machine (the culprit was never identified) occasionally turned that directory hidden.
I finally resorted to putting a test in the startup to see if the directory was hidden and stopping with an error if it was.
No. An assertion failure means something happened that the original programmer did not intend or expect to occur.
This can indicate:
A bug in your code (you are simply calling the method incorrectly)
A bug in the Assertion (the original programmer has been too zealous and is complaining about you doing something that is quite reasonable and the method will actually handle perfectly well.
A bug in the called code (a design flaw). That is, the called code provides a contract that does not allow you to do what you need to do. The assertion warns you that you can't do things that way, but the solution is to extend the called method to handle your input.
A known but unimplemented feature. Imagine I implement a method that could process positive and negative integers, but I only need it (for now) to handle positive ones. I know that the "perfect" implementation would handle both, but until I actually need it to handle negatives, it is a waste of effort to implement support (and it would add code bloat and possibly slow down my application). So I have considered the case but I decide not to implement it until the need is proven. I therefore add an assert to mark this unimplemented code. When I later trigger the assert by passing a negative value in, I know that the additional functionality is now needed, so I must augment the implementation. Deferring writing the code until it is actually required thus saves me a lot of time (in most cases I never imeplement the additiona feature), but the assert makes sure that I don't get any bugs when I try to use the unimplemented feature.

Test Cases AND assertion statements

The code in this question made me think
assert(value>0); //Precondition
if (value>0)
{
//Doit
}
I never write the if-statement. Asserting is enough/all you can do.
"Crash early, crash often"
CodeComplete states:
The assert-statement makes the application Correct
The if-test makes the application Robust
I don't think you've made an application more robust by correcting invalid input values, or skipping code:
assert(value >= 0 ); //Precondition
assert(value <= 90); //Precondition
if(value < 0) //Just in case
value = 0;
if (value > 90) //Just in case
value = 90;
//Doit
These corrections are based on assumptions you made about the outside world.
Only the caller knows what "a valid input value" is for your function, and he must check its validity before he calls your function.
To paraphrase CodeComplete:
"Real-world programs become too messy when we don't rely solely on assertions."
Question: Am I wrong, stuborn, stupid, too non-defensive...
The problem with trusting just Asserts, is that they may be turned off in a production environment. To quote the wikipedia article:
Most languages allow assertions to be
enabled or disabled globally, and
sometimes independently. Assertions
are often enabled during development
and disabled during final testing and
on release to the customer. Not
checking assertions avoiding the cost
of evaluating the assertions while,
assuming the assertions are free of
side effects, still producing the same
result under normal conditions. Under
abnormal conditions, disabling
assertion checking can mean that a
program that would have aborted will
continue to run. This is sometimes
preferable.
Wikipedia
So if the correctness of your code relies on the Asserts to be there you may run into serious problems. Sure, if the code worked during testing it should work during production... Now enter the second guy that works on the code and is just going to fix a small problem...
Use assertions for validating input you control: private methods and such.
Use if statements for validating input you don't control: public interfaces designed for consumption by the user, user input testing etc.
Test you application with assertions built in. Then deploy without the assertions.
I some cases, asserts are disabled when building for release. You may not have control over this (otherwise, you could build with asserts on), so it might be a good idea to do it like this.
The problem with "correcting" the input values is that the caller will not get what they expect, and this can lead to problems or even crashes in wholly different parts of the program, making debugging a nightmare.
I usually throw an exception in the if-statement to take over the role of the assert in case they are disabled
assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff
I would disagree with this statement:
Only the caller knows what "a valid
input value" is for your function, and
he must check its validity before he
calls your function.
Caller might think that he know that input value is correct. Only method author knows how it suppose to work. Programmer's best goal is to make client to fall into "pit of success". You should decide what behavior is more appropriate in given case. In some cases incorrect input values can be forgivable, in other you should throw exception\return error.
As for Asserts, I'd repeat other commenters, assert is a debug time check for code author, not code clients.
Don't forget that most languages allow you to turn off assertions... Personally, if I was prepared to write if tests to protect against all ranges of invalid input, I wouldn't bother with the assertion in the first place.
If, on the other hand you don't write logic to handle all cases (possibly because it's not sensible to try and continue with invalid input) then I would be using the assertion statement and going for the "fail early" approach.
If I remember correctly from CS-class
Preconditions define on what conditions the output of your function is defined. If you make your function handle errorconditions your function is defined for those condition and you don't need the assert statement.
So I agree. Usually you don't need both.
As Rik commented this can cause problems if you remove asserts in released code. Usually I don't do that except in performance-critical places.
I should have stated I was aware of the fact that asserts (here) dissappear in production code.
If the if-statement actually corrects invalid input data in production code, this means the assert never went off during testing on debug code, this means you wrote code that you never executed.
For me it's an OR situation:
(quote Andrew) "protect against all ranges of invalid input, I wouldn't bother with the assertion in the first place." -> write an if-test.
(quote aku) "incorrect input values can be forgivable" -> write an assert.
I can't stand both...
For internal functions, ones that only you will use, use asserts only. The asserts will help catch bugs during your testing, but won't hamper performance in production.
Check inputs that originate externally with if-conditions. By externally, that's anywhere outside the code that you/your team control and test.
Optionally, you can have both. This would be for external facing functions where integration testing is going to be done before production.
A problem with assertions is that they can (and usually will) be compiled out of the code, so you need to add both walls in case one gets thrown away by the compiler.

Resources