How about separating check and handle as follows:
-
handlecan just works likedeferas the draft suggested(called after return in reversed order of definition), and the magic identifiererrcould be changed to any error varible decalared, not the result ofcheck. -
The error result as the last return value of a function should not be omitted in the presence of
check. -
The return statements in
handlejust return thehandlescope, not the function scope. -
Restrict functions to use named result parameters when using
check.
func CopyFile(src, dst string) (err error) {
var err1, err2 error
handle err1, err2 { //at least one is non-nil
...
}
handle err1 {
err = fmt.Errorf("prepare copy %s %s: %v", src, dst, err1)
}
handle err2 {
err = fmt.Errorf("copy %s %s: %v", src, dst, err2)
}
r, err1 := check os.Open(src)
defer r.Close()
w, err1 := check os.Create(dst)
handle err2 {
w.Close()
os.Remove(dst)
}
err2 = check io.Copy(w, r)
// or need additional handling before returning to handles above
if err2 = io.Copy(w, r); err2 != nil {
... special handling ...
return
}
err2 = check w.Close()
}Or in the simplest case:
func CopyFile(src, dst string) (err error) {
handle err {
err = fmt.Errorf("copy %s %s: %v", src, dst, err)
}
r, err := check os.Open(src)
defer r.Close()
w, err := check os.Create(dst)
handle err {
w.Close()
os.Remove(dst)
}
err = check io.Copy(w, r)
err = check w.Close()
}In this way, check works just equivalently to:
if err != nil {
return
}handle works just equivalently to:
defer func() {
if err != nil {
}
}()