Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
proposal: Go 2: Error handling with try-with-resources

Currently:

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)
	}
}

Proposal inspired by Java 7's try-with-resources:

func CopyFile(src, dst string) error {
	check 
		r, err1 := os.Open(src); // Related to the first catch block
		w, err2 := os.Create(dst); // Related to the second catch block, if the second catch block does not exists then the last existing catch block
		_, err3 := io.Copy(w, r); // Related to the third catch block, if the third catch block does not exists then the last existing catch block
	{
		os.Remove(dst)
	} catch _ {
		return fmt.Errorf("copy %s %s: %v", src, dst, err1)
	} catch _ {
		// This catch block can be optional 
		return fmt.Errorf("copy %s %s: %v", src, dst, err2)
	} catch _ {
		// This catch block can be optional 
		return fmt.Errorf("copy %s %s: %v", src, dst, err3)
	}
}

Error messages are the same format, simplified version:

func CopyFile(src, dst string) error {
	check 
		r, err1 := os.Open(src);
		w, err2 := os.Create(dst);
		_, err3 := io.Copy(w, r);
	{
		os.Remove(dst)
	} catch err { // Error pointer on err1, or err2, or err3
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}
}

Notes:

  • The Open(), Create(), and Copy() functions signature must be function() T, error where T is a generic type which implements a Close() function.
  • check executes instructions one by one and implicitely does the err != nil operation, if err isn't nil, the related catch block is executed. Syntactic sugar: If the related catch block does not exists, try to execute the last catch block.
  • You can add a "finally" block to this proposal.
  • Drawback: This technique does not handle the last if err := w.Close(); err != nil { ... } block, but should a close method return an error? Even Java's or C#'s close function doesn't return anything :)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment