Every time when a function returns we should test whether it resulted in an error: this can lead to repetitive and tedious code. Combining the defer/panic/recover mechanism with closures can result in a far more elegant scheme that we will now discuss. However it is only applicable when all functions have the same signature, which is rather restrictive. A good example of its use is in web applications, where all handler functions are of the following type:
func handler1(w http.ResponseWriter, r *http.Request) { … }
Suppose all functions have the signature: func f(a type1, b type2)
The number of parameters and their types is irrelevant.
We give this type a name: fType1 = func f(a type1, b type2)
Our scheme uses 2 helper functions:
i) check: a function which tests whether an error occurred, and panics if so:
func check(err os.Error) { if err != nil { panic(err) } }
ii) errorhandler: this is a wrapper function. It takes a function fn of our type fType1 and returns such a function by calling fn. However it contains the defer/recover mechanism, outlined in § 13.3
func errorHandler(fn fType1) fType1 {
return func(a type1, b type2) {
defer func() {
if e, ok := recover().(os.Error); ok {
log.Printf("run time panic: %v", err)
}
}()
fn(a, b)
}
}
When an error occurs it is recovered and printed on the log; apart from simply printing the application could also produce a customized output for the user by using the template package (§ 15.6).
The check() function is used in every called function, like this:
func f1(a type1, b type2) {
…
f, _, err := // call function/method
check(err)
t, err := // call function/method
check(err)
_, err2 := // call function/method
check(err2)
…
}
The main() or other caller-function should then call the necessary functions wrapped in errorhandler, like this:
func main() {
errorHandler(f1)
errorHandler(f2)
…
}
Using this mechanism all errors are recovered and the error-checking code after a function call is reduced to check(err).In this scheme different error-handlers have to be used for different function types; they could be hidden inside an error-handling package. Alternatively a more general approach could be using a slice of empty interface as parameter and return type.
No comments:
Post a Comment