Skip to content

Instantly share code, notes, and snippets.

@forstmeier
Last active October 18, 2018 23:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save forstmeier/b6c6a6d2f6f2f72a81a076322959c959 to your computer and use it in GitHub Desktop.
Save forstmeier/b6c6a6d2f6f2f72a81a076322959c959 to your computer and use it in GitHub Desktop.
A slight variation on the Go 2 design draft

Personally, I've never really minded the bloat created by the use of the if err != nil { ... }. I've always liked the ability to see right there on the next line exactly what happens in the event of an error.

However, I do understand the eye strain created by many of these popping up in quick succession, particuarlly when calling many helper functions (e.g. anything to do with handling and processing files). In the event that this moves to a full proposal, I would absolutely support the need to continue to allow for the more verbose if err != nil { ... } option, but I wouldn't mind seeing the check/handle desgin be implemented.

One suggestion I would have is to create a bit more clarity at the cost of introducing a few additional characters. I don't really like the idea of when check is triggered it rolls back up through the handle statements - it seems like something that could get confusing when scrolling through longer files.

Maybe something like this could be an option:

func CopyFile(src, dst string) error {
	handle printErr err {
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}

	r := check printErr os.Open(src)
	defer r.Close()

	w := check printErr os.Create(dst)
	handle removeDst err {
		w.Close()
		os.Remove(dst) // (only if a check fails)
	}

	check removeDst io.Copy(w, r)
	check removeDst w.Close()
	return nil
}

Or, alternatively:

func CopyFile(src, dst string) error {
	handle printErr {
		return fmt.Errorf("copy %s %s: %v", src, dst, printErr)
	}

	r, printErr := check os.Open(src)
	defer r.Close()

	w, printErr := check os.Create(dst)
	handle removeDst {
		w.Close()
		os.Remove(dst)
	}

	_, removeDst := check io.Copy(w, r)
	_, removeDst := check w.Close()
	return nil
}

Including a "labeled" handle with correspondingly assigned checks would make it clear which handle the check is being sent to.

This is just a thought as I'm generally okay with the current draft design. I've seen some other suggestions for clarity of the check/handle parings including putting the check inside the handle scope but if anyone else has posted something like what I'm suggesting I'd love to see that too.

@kardianos
Copy link

FYI:

I don't really like the idea of when check is triggered it rolls back up to the nearest handle

This isn't the proposal. If "check" fails, all the "handle" statements declared in the current function thus far are called in reverse order until the function returns from a "handle" statement. It isn't the nearest handle, it is a handle chain (lexically, not runtime).

@forstmeier
Copy link
Author

Ah, good point, @kardianos, thank you for clarifying - I'll adjust my gist accordingly. Even with that amendment, I'm still not entirely in favor of rolling back through every one in reverse order and would prefer a bit more clarity with regards to handle statements.

@dblokhin
Copy link

dblokhin commented Sep 3, 2018

check becomes soo often word.
May be introduce new lexical term? For exmaple with @ (or something else):

func CopyFile(src, dst string) error {
	handle printErr err {
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}

	@r := printErr os.Open(src)
	defer r.Close()

	@w := printErr os.Create(dst)
	handle removeDst err {
		w.Close()
		os.Remove(dst) // (only if a check fails)
	}

	@removeDst io.Copy(w, r)
	@removeDst w.Close()
	return nil
}

@forstmeier
Copy link
Author

@dblokhin, that's not a bad alternative. Personally, I prefer using words rather than symbols wherever practical/possible, but this doesn't seem unreasonable.

@elimisteve
Copy link

@dblokhin That's pretty good!...

@bserdar
Copy link

bserdar commented Oct 18, 2018

A slight variation to your idea: inline functions that are called when passed a non-nil arg:

func f() error {
   handle printErr(err) {
      return fmt.Errorf(...)
   }

   handle doSomething(err) {
       // do stuff
   }

   x, printErr:=os.Open("...")
   doSomething=io.Copy(...)

Above, printErr(err) will be called only if err is non-nil. Similarly, doSomething(err) will be called if io.Copy returns a non-nil error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment