Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Go 2: simplify error handling with ? and ... #33152

Closed
earthboundkid opened this issue Jul 17, 2019 · 20 comments
Closed

proposal: Go 2: simplify error handling with ? and ... #33152

earthboundkid opened this issue Jul 17, 2019 · 20 comments
Labels
error-handling Language & library change proposals that are about error handling. FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Milestone

Comments

@earthboundkid
Copy link
Contributor

The try proposal #32437 was recently declined. This is an alternate proposal to achieve the goal of simplifying error handling. It has two parts:

  1. Make EXPRESSION? return a bool, true if the expression is a non-blank value, false is the expression equals the blank value. So, for interface types, it would be equivalent to EXPRESSION != nil.

Usage:

func CopyFile(src, dst string) error {
	r, err := os.Open(src)
	if err? {
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}
	defer r.Close()

	w, err := os.Create(dst)
	if err? {
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}

	if _, err := io.Copy(w, r); err? {
		w.Close()
		os.Remove(dst)
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}

	if err := w.Close(); err? {
		os.Remove(dst)
		return fmt.Errorf("copy %s %s: %v", src, dst, err)
	}
}
  1. Make return ..., EXPRESSION fill in as many blank values as needed to fulfill the signature of the function.

Usage:

return ..., err

return ..., fmt.Errorf("something went wrong: %v")

Rationale:

Given the design goals for improving error handling, it is difficult to imagine any proposal that doesn't end up being non-orthogonal with if and defer. This makes it simpler for text editors to insert the correct boilerplate for an early return automatically, and it saves a few characters for programmers who type the whole thing out manually.

@gopherbot gopherbot added this to the Proposal milestone Jul 17, 2019
@3timeslazy
Copy link

Second point looks good

@ianlancetaylor ianlancetaylor changed the title Proposal: simplify error handling with ? and ... proposal: Go 2: simplify error handling with ? and ... Jul 17, 2019
@ianlancetaylor ianlancetaylor added v2 A language change or incompatible library change LanguageChange labels Jul 17, 2019
@ianlancetaylor
Copy link
Contributor

The ? part of this proposal looks essentially the same as #32845.

@earthboundkid
Copy link
Contributor Author

Didn't see that issue. I've seen proposals to allow return _, err (although I can't find an issue ATM), but not return .... The difference between return _ and return ... is that ... would return as many values as necessary, not just one, so it could be used in any function with an error return.

@hchargois
Copy link

I was going to open an issue to propose the same thing as the 2nd point.

I see two main benefits of this:

  1. It is a pain to have to write the zero values explicitly while they don't carry any meaning anyway because it is an error return
  2. If you change the signature of the function, you often have to change multiple lines because you have multiple error returns

This proposal solves both issues beautifully.

Compare

func before() (bool, string, MyStruct, error) {
    return false, "", MyStruct{}, errors.New("error")
}

func after() (bool, string, MyStruct, error) {
    return ..., errors.New("error")
}

Let's throw the "ellipsis" keyword here so that this issue can be found more easily, because I think that's what "..." is commonly called.

Also, if #21291 passes, this could be used to return explicitly "all-zeros" with return ...

@ziflex
Copy link

ziflex commented Jul 17, 2019

Hey, thanks for the proposal.
But I'm having issues with understanding what value it brings.
Both options, conceptually do not bring any solutions to the problem. They just save few characters that you have to type. It feels more like a workaround than a solid solution.

@earthboundkid
Copy link
Contributor Author

They just save few characters that you have to type.

Yes, I have come to the conclusion that apart from try(), saving a few characters is all that is possible, because all the other proposals end up reinventing if and defer with non-orthogonally different semantics.

@ziflex
Copy link

ziflex commented Jul 17, 2019

My gut feeling says that maybe it’s too early to solve this “problem” with the current state of the language.
Probably, we need new language constructions for a better solution. (Pattern matching, for example)
I would encourage the community to put it on hold and focus on other parts of the language.

@OneOfOne
Copy link
Contributor

IMO those are 2 different proposals, for example I really like the ..., err part but absolutely hate the first.

@ilango100
Copy link

ilango100 commented Jul 18, 2019

Isn't named return values an alternate solution for ellipsis?

func aFunction() (a int, b string, c bool, err error) {
  if er := someCall(); er != nil {
    // Should be aware not to shadow return params
    err = fmt.Errorf("Error: %v", er)
    return
  }

  ...
}

We don't have to explicitly return the zero values each time this way. They are auto populated at function start.
If the naked return affects the readability, we can even use

return a, b, c, err

@earthboundkid
Copy link
Contributor Author

Named values may have been set and not zero. Depending on the function, that matters. Also they can't be used in all (or even most?) functions, which makes it inappropriate for an editor macro.

@ilango100
Copy link

A lot of times, it is needed to return whatever values are set when some error occurs. Just because an error occurs doesn't mean you should always return zero values for other return parameters.

The calling code may inspect this error and take necessary action using the "intermediate" result.
The best example would be io.Reader, when an EOF occurs. Underlying implementation may return read bytes and also an error.
So using named values solves both these problems (zero values or intermediate values).

I wonder why it cannot be used in most functions.

@earthboundkid
Copy link
Contributor Author

io.Reader/Writer is the exception that proves the rule. Almost nothing else can return both an error and a result. It's pretty much one or other. IO is different because it is possible to do a partial read/write and still get meaningful results, but there's no such thing as half an HTTP response header or half a decompressed file.

@qaisjp
Copy link
Contributor

qaisjp commented Jul 19, 2019

Let's throw the "ellipsis" keyword here so that this issue can be found more easily, because I think that's what "..." is commonly called.

Also "variadic return"

@mvndaai
Copy link

mvndaai commented Jul 19, 2019

@carlmjohnson I love the return ..., err it might be worth making a separate proposal just for it.

@donaldnevermore
Copy link

The if statement is like

if <bool> {
        ...
}

Looks like err? returns a bool. I think using ? to replace !=nil doesn't solve the error handling problem at all.

@dongweigogo
Copy link

dongweigogo commented Aug 19, 2019

why not just
r := os.Open(src)?

@bradfitz
Copy link
Contributor

bradfitz commented Oct 8, 2019

@nigeltao in 2012: https://groups.google.com/forum/#!msg/golang-dev/iAysKGpniLw/qSbtBUx4-sMJ

An alternative proposal is to make _ more like /dev/zero than /dev/null:

var x struct{} = _
var y int = _
func f() (bool, string, io.Reader) {


  c := make(chan struct{}, 99)
  c <- _
  return _, _, _
}

@ianlancetaylor
Copy link
Contributor

The first part of this proposal, using ? to mean != <zero value>, is a duplicate of #32845, which is closed.

I suggest that someone open a new issue to focus only on the return idea, and that when that is done we can close this issue in favor of those two.

@jimmyfrasche
Copy link
Member

@ianlancetaylor There's #21182 for ... in return. I retracted it, but don't mind if it's reopened. I closed it in favor of #19642 which is the issue for _ as a universal zero value

@ianlancetaylor
Copy link
Contributor

@jimmyfrasche Thanks. I reopened #21182.

Closing this issue as a dup of #21182 and #32845.

@bradfitz bradfitz added the error-handling Language & library change proposals that are about error handling. label Oct 29, 2019
@golang golang locked and limited conversation to collaborators Oct 28, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
error-handling Language & library change proposals that are about error handling. FrozenDueToAge LanguageChange Proposal v2 A language change or incompatible library change
Projects
None yet
Development

No branches or pull requests