Is there a better way to return from long sequences of recursive function calls?
I currently panic with a marker value like this:
type exitNow int
...
panic(exitnow(0))
to return multiple levels at once. At the root function a call to recover does general error handling (turning panics into errors) and handles exitNow as a special case.
This works fine I just want to know if there is a better way.
I already use a bool return value for a related purpose but using another one for this would be a pain. (every call to every function would need an if statment)
If it helps any this is part of the implimentation of a recusive decent parser.
I use this approach myself in my parsers. I don't panic with an integer value though. I use the actual current error as the panic message. The top level call doing the recover() simply appends some file/line/column information and then returns it as a regular error.
This method, and just returning errors from all functions are the only ways to do this in Go. The panic approach is a great deal more effective for the parser case, as it makes the lexer rules considerably simpler to implement (and read) as there are no if err != nil { return } parts littered everywhere.
Related
I've been reading some Golang code from github.com/lib/pq which provides drivers for interacting with a postgres database.
Among the code I came across this:
go func() {
select {
case <-done:
_ = cn.cancel()
finished <- struct{}{}
case <-finished:
}
}()
The cancel function looks like:
func (cn *conn) cancel() error
As far as I can tell, the underscore isn't being used as a static assertion about a type (and therefore the compiler doesn't evaluate any side effects as far as I can see (as in this example)) and it isn't a second parameter whereby the author may want to discard it.
In summary: Why assign the result of the cancel function (the error) to an underscore?
Code must be correct. To be sure that code is correct, code must be readable.
The First Rule of Go: Check for errors.
func (cn *conn) cancel() error
If I write
cn.cancel()
did I forget to check for errors or did I decide to discard the error value?
However, if I write
_ = cn.cancel()
I did not forget to check for errors and I did decide to discard the error value.
The Go Programming Language Specification
Blank identifier
The blank identifier is represented by the underscore character _. It
serves as an anonymous placeholder instead of a regular (non-blank)
identifier and has special meaning in declarations, as an operand, and
in assignments.
Assignments
The blank identifier provides a way to ignore right-hand side values
in an assignment:
The blank identifier “_” is a special anonymous identifier. When used in an assignment, like this case, it provides a way to explicitly ignore right-hand side values. So, the developer has decided to ignore/discard the error returned from this method call.
A few reasons why they may have done this (based on a quick glance at the method call and context, my guess is 3 or 4):
The method call is guaranteed to succeed in this context.
The error is already handled sufficiently within the method call; no reason to handle it again.
The error doesn’t matter (eg the relevant process is going to end anyway and the outcome will be the same as if the method call has succeeded without error).
The developer was in a hurry to get something working, ignored the error to save time, then failed to come back and handle the error.
For code append(slice1, 1), Go compile will give error "append(...) evaluated but not used". And we have to use like slice1 = append(slice1,1) because append doesn't modify slice1 and it will return a new slice.
I think it is a good hint since this will prevent lots of bug since we often didn't know function like append will change original array or not. In JavaScript array1.push('item') will change array1 in place and return new length of the array.
I want to utilize this kind of code checking:
func appendStr(str string, tail string) string {
b := str + tail
return b
}
a := "a"
appendStr(a, "b")
But Go compiler didn't give error. So compiler do some special checking on append method? Since Go pass parameter by value, Compiler should know appendStr has no change to modify pass-in parameter.
append() is special because it's a built-in function, and the compiler does extra check on it. It is very rarely useful to not use the return value of append(), so the Go authors decided to make it a compile-time error if it is not used.
On the other hand, calling "ordinary" functions which have return values often have "side effects", and it's common to just call a function for its "side effects" and not use its return values. A very common example is fmt.Println(): you often print something to the console, and you rarely (if ever) check if that succeeds or how many bytes were actually written.
The language spec allows you to not use the return values of functions, so it's valid to do so and you can't force the compiler to make an error or warning out of it, you can't make the compiler to "mark" valid code with error.
See related question: Return map like 'ok' in Golang on normal functions
The way this is typically done in Go is by using an extra tool, a linter if you will. go vet is commonly used to point out things in the code that "don't look right" and which are probably bugs. It seems that you cannot make it check your own functions out-of-the-box, only things like fmt.Sprintf will be checked.
Another tool is errcheck which reports ignored errors.
You could fork one of these tools and insert your own function(s) there, then make everyone check their code before committing it to source control or check it automatically.
I've been learning Go and one thing that stood out as particularly interesting to me is the way that the behavior of type assertions changes based on how many return values are being captured:
var i interface{} = "hello"
val, ok := i.(int) // All good
fmt.Println(val, ok)
val = i.(int) // Panics
fmt.Println(val)
This feels like a pattern that can be very useful for user defined functions. The user either has to explicitly get the "ok" second return value or use an underscore to ignore it. In either case, they're making it clear that they're aware that the function can fail. Whereas if they just get one return value, it could silently fail. Hence, it seems reasonable to panic or similar if the user isn't checking for an error (which would be reasonable if the error should "never" happen). I assume that's the logic behind the language developers in making type assertions work this way.
But when I tried to find out how that could be done, I found nothing. I'm aware that type assertions aren't an actual function. And many languages with multiple return values can't check how many return values are actually being used (MATLAB is the only one I'm aware of), but then again, most of those don't use behavior like the type assertions demonstrate.
So, is it possible and if so, how? If not, is there a particular reason that this behavior was excluded despite it being possible with the built in type assertions?
Sadly they cannot be used in normal functions. As far as i know only type assertions, map value access and range allow it.
Usually when you want to have a function with one and optional a second error argument you name them like
func DoSomething() (string, error) {...} // i will return an error
func MustDoSomething() string {...} // i will panic
An example would be https://golang.org/pkg/regexp/#MustCompile
This answer: https://stackoverflow.com/a/41816171/10278 by #christian provides the best practical advice for how to emulate the "overloaded-on-result-count" pattern.
My aim is to address another part of the question—this part: "But when I tried to find out how that could be done, I found nothing".
The following explains how it is done for Go type assertions.
Invocations of type assertions in Go behave as though they are overloaded based on number of results.
Yet, Go does not support overloading of methods and operators.
Looking at Go's implementation, here is the reason type assertions appear to be overloaded based on number of results:
The Go compiler provides special handling that is peculiar to these built-in operations.
This special dispatching occurs for the built-in concept of type assertions because the compiler is carving out special logic that is not available to non-built-in code.
The Go compiler and runtime are written in Go. That made it (somewhat) easy for me to discover that the compiler is the key to explaining this behavior.
Take a look at this part of the compiler:
https://github.com/golang/go/blob/8d86ef2/src/cmd/compile/internal/gc/ssa.go#L4782
The code comment already reveals a lot:
// dottype generates SSA for a type assertion node.
// commaok indicates whether to panic or return a bool.
// If commaok is false, resok will be nil.
We can go further by using a debugger to step through some type assertion code.
Take this playground snippet for example. Specifically, these lines:
object_as_closer_hardstop := thing.(io.Closer) // will panic!!
object_as_closer, ok := thing.(io.Closer)
(If you build Go from source, then) if you use a debugger to step into the first type assertion, you will end up at the following code in the Go runtime:
https://github.com/golang/go/blob/8d86ef2/src/runtime/iface.go#L438
If you step into the second one, you end up at:
https://github.com/golang/go/blob/8d86ef2/src/runtime/iface.go#L454
On line 438, you see func assertI2I (with a single return value). A bit lower, on line 454, you see assertI2I2. Note that these two functions have nearly identical names, but not quite!
The second function has a trailing 2 at the end of its name. That function also has two returned results.
As we expect:
assertI2I can panic, but
assertI2I2 cannot.
(Look at the function bodies in iface.go and note which contains panic.)
assertI2I and assertI2I2 abide by the overloading rules we expect. If they were to differ only by number of results, then those of us who compile Go from source would be unable to compile the Go runtime, due to a compiler error such as "assertI2I redeclared".
Users of the language are generally not aware of these builtin runtime functions, so on the surface, both lines of code seem to call the same function:
object_as_closer_hardstop := thing.(io.Closer) // will panic!!
object_as_closer, ok := thing.(io.Closer)
However, at compile time the compiler branches based on whether it found the case "commaok":
https://github.com/golang/go/blob/8d86ef2/src/cmd/compile/internal/gc/ssa.go#L4871
Our own end-user code does not get to modify Go's lexing/parsing/AST-walking in order to dispatch different flavors of our functions based on "commaok".
For better or for worse, that is why user-written code cannot leverage this pattern.
Many functions in go return errors to fit an interface, but these errors are always nil. Should these errors still be checked?
An example for this is the crypto/sha1 Write() function, which does not set the err value. So the code does not need to be:
_, err = sha1Hasher.Write(buffer)
if err != nil {
log.Printf("sha1 could not be calculated (%s)", err)
}
but only:
sha1Hasher.Write(buffer)
The second option is shorter and cleaner and go is a lot about simple code but it is suggested to handle all errors:
But remember: Whatever you do, always check your errors!
https://blog.golang.org/errors-are-values
Clearly, we must handle any errors; we can't just ignore them.
https://stackoverflow.com/a/16126516/4944254
Which is the best way to go?
In situation that you described, you will probably be fine with not checking error, same as not checking errors when using fmt.Println.
However, when you use fmt.Println you know which concrete implementations are being used. When you are using Writer interface (which os.Stdout implements) - you can not assume if implementation will return any errors. In that case, IMHO, you should always check errors.
Btw, writing to os.Stdout, which fmt.Print uses, can fail (one might be able to replace value of os.Stdout to something else).
This is general programming, but if it makes a difference, I'm using objective-c. Suppose there's a method that returns a value, and also performs some actions, but you don't care about the value it returns, only the stuff that it does. Would you just call the method as if it was void? Or place the result in a variable and then delete it or forget about it? State your opinion, what you would do if you had this situation.
A common example of this is printf, which returns an int... but you rarely see this:
int val = printf("Hello World");
Yeah just call the method as if it was void. You probably do it all the time without noticing it. The assignment operator '=' actually returns a value, but it's very rarely used.
It depends on the environment (the language, the tools, the coding standard, ...).
For example in C, it is perfectly possible to call a function without using its value. With some functions like printf, which returns an int, it is done all the time.
Sometimes not using a value will cause a warning, which is undesirable. Assigning the value to a variable and then not using it will just cause another warning about an unused variable. For this case the solution is to cast the result to void by prefixing the call with (void), e.g.
(void) my_function_returning_a_value_i_want_to_ignore().
There are two separate issues here, actually:
Should you care about returned value?
Should you assign it to a variable you're not going to use?
The answer to #2 is a resounding "NO" - unless, of course, you're working with a language where that would be illegal (early Turbo Pascal comes to mind). There's absolutely no point in defining a variable only to throw it away.
First part is not so easy. Generally, there is a reason value is returned - for idempotent functions the result is function's sole purpose; for non-idempotent it usually represents some sort of return code signifying whether operation was completed normally. There are exceptions, of course - like method chaining.
If this is common in .Net (for example), there's probably an issue with the code breaking CQS.
When I call a function that returns a value that I ignore, it's usually because I'm doing it in a test to verify behavior. Here's an example in C#:
[Fact]
public void StatService_should_call_StatValueRepository_for_GetPercentageValues()
{
var statValueRepository = new Mock<IStatValueRepository>();
new StatService(null, statValueRepository.Object).GetValuesOf<PercentageStatValue>();
statValueRepository.Verify(x => x.GetStatValues());
}
I don't really care about the return type, I just want to verify that a method was called on a fake object.
In C it is very common, but there are places where it is ok to do so and other places where it really isn't. Later versions of GCC have a function attribute so that you can get a warning when a function is used without checking the return value:
The warn_unused_result attribute causes a warning to be emitted if a caller of the function with this attribute does not use its return value. This is useful for functions where not checking the result is either a security problem or always a bug, such as realloc.
int fn () __attribute__ ((warn_unused_result));
int foo ()
{
if (fn () < 0) return -1;
fn ();
return 0;
}
results in warning on line 5.
Last time I used this there was no way of turning off the generated warning, which causes problems when you're compiling 3rd-party code you don't want to modify. Also, there is of course no way to check if the user actually does something sensible with the returned value.