These are my thoughts on the Go 2 draft for error handling.
The example go program they give is:
func CopyFile(src, dst string) error {
r, err := os.Open(src)
if err != nil {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
if _, err := io.Copy(w, r); err != nil {
w.Close()
os.Remove(dst)
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
if err := w.Close(); err != nil {
os.Remove(dst)
return fmt.Errorf("copy %s %s: %v", src, dst, err)
}
}
But I imediately saw two issues, 1) The error formatting can be extracted out to a defer function and 2) We can extract out the close logic
func CopyFile(src, dst string) (err error) {
defer func() {
if err != nil {
err = fmt.Errorf("copy %s %s: %v", src, dst, err)
}
}()
r, err := os.Open(src)
if err != nil {
return err
}
defer r.Close()
w, err := os.Create(dst)
if err != nil {
return err
}
_, err = io.Copy(w, r)
ec := w.Close()
if err == nil {
err = ec
}
if err != nil {
os.Remove(dst)
}
return err
}
Now, what I see as the remaining problem is people wanting to address the number of lines. I've always longed for oneline conditionals (coming from ruby) The code shortens to
func CopyFile(src, dst string) (err error) {
defer func() {
err = fmt.Errorf("copy %s %s: %v", src, dst, err) if err != nil
}()
r, err := os.Open(src)
return err if err != nil
w, err := os.Create(dst)
return err if err != nil
_, err = io.Copy(w, r)
ec := w.Close()
err = ec if err == nil
os.Remove(dst) if err != nil
return err
}
This is now 1 line shorter than the proposed solution and all we did was remove multi-line conditionals. No new keywords.
We could further simplify by allowing implicit nil checks (I have no idea how hard this would be technically)
func CopyFile(src, dst string) (err error) {
defer func() {
err = fmt.Errorf("copy %s %s: %v", src, dst, err) if err
}()
r, err := os.Open(src)
return err if err
w, err := os.Create(dst)
return err if err
_, err = io.Copy(w, r)
err = w.Close() || err
os.Remove(dst) if err
return err
}
(This last point goes a little beyond the draft, but it's also something I've always wanted!)
I also would like to bear in mind something that Rob Pike talked about a while ago (sorry can't find the link) about how programming languages are converging and I want to be careful about making go look more like ruby (or whatever)
I look forward to everyone's feedback.
Thanks @barisere for your detailed response! I'm working on my response, but busy, so hopefully I can respond soon.