You code calls l.fixup(obj) no matter what. If foo(bar, obj) returns an error, some handling is done and l.fixup(obj) is called - otherwise only l.fixup(obj) is called. Hence, your code can be rearranged like this:
// err will only be valid within the if-then-else-construct
if err := foo(bar, obj); err != nil {
// handle error from foo(bar,obj)
// you can even return it, if you wanted to
// For the sake of this example, we simply log it
log.Println("Executing foo: %s", err)
}
return l.fixup(obj)
Furthermore, you can use the fact that error is an interface to your advantage if you want to distinguish between the error potentially returned by foo or l.fixup. You can do that by creating a typed error for one (or both of them) and evaluate the type of the error by using what is called a type switch.
package main
import (
"errors"
"fmt"
)
// FooError is the error to be returned by foo
type FooError struct {
Bar string
}
// Error implements the interface
func (f FooError) Error() string {
return fmt.Sprintf("%s: interface is nil", f.Bar)
}
// dummy foo func
func foo(bar string, in interface{}) error {
if in == nil {
return FooError{Bar: bar}
}
return nil
}
// dummy fixup func
func fixup(in interface{}) error {
if in == nil {
return errors.New("Interface is nil")
}
return nil
}
// a wrapper, containing a variation of above code
func wrap(bar string) error {
if err := foo(bar, nil); err != nil {
// handle error from foo(bar,obj)
// you can even return it, if you wanted to
return err
}
return fixup(nil)
}
func main() {
err := wrap("test")
// The type switch itself
switch err.(type) {
case FooError:
// We have a FooError, so we can deal with it accordingly
fmt.Println("Foo Error:",err)
default:
// Every other error is handled by the default block
fmt.Println("Std Error:",err)
}
}
However, this does not feel quite right. If foo is called and it returning an error prevents something else in your logic not being executed, it might be valid to panic instead.