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: make use of return values mandatory #27845

Closed
empijei opened this issue Sep 25, 2018 · 11 comments
Closed

Proposal: Go 2: make use of return values mandatory #27845

empijei opened this issue Sep 25, 2018 · 11 comments

Comments

@empijei
Copy link
Contributor

empijei commented Sep 25, 2018

This issue has been bugging me for several years and I currently use tools to enforce the expected behavior on my projects, but would be nice to have language support on this one.

In go the return values of functions can be ignored. Variables cannot be assigned a value if the value is not used later on, but this seems to be inconsistent with how return values are handled.

The following is invalid (a declared and not used)

func invalid() {
    a := 3
}

This, instead, is valid

func valid() {
    a := 3
    _ = a
}

In order to make sure error values are never implicitly ignored and to be consistent with the "do not ignore values" rule, I would also expect the following code to be invalid:

func givesErr() error {
    return myErr
}

func usesErr() {
    givesErr()    // ← invalid, ignores return value
}

And should be corrected in

func givesErr() error {
    return myErr
}

func usesErr() {
    _ = givesErr()    // ← Warns the reader that something is being ignored

This would allow a reader that does not know the API to perfection to easily spot issues due to errors or other values being ignored.

An example of where this is also important for non-error values is Request.WithContext which returns a shallow copy of r with the provided context but does not set context to the given request.

Even if this is a method that takes a pointer to the given struct it does not work on the given struct but on a copy, which might be unexpected or surprising.

In my opinion a good way to make the user of this kind of API not make the mistake to ignore the return value would be to make it mandatory to use them.

@gopherbot gopherbot added this to the Proposal milestone Sep 25, 2018
@empijei empijei changed the title Proposal: make use of return values mandatory in go2 Proposal: Go 2: make use of return values mandatory in go2 Sep 25, 2018
@empijei empijei changed the title Proposal: Go 2: make use of return values mandatory in go2 Proposal: Go 2: make use of return values mandatory Sep 25, 2018
@cznic
Copy link
Contributor

cznic commented Sep 25, 2018

The variable declared as the function's return value is always used - to store the return value of the function. The analogy with "declared but not used" does not hold.

return 42 in func foo() (int bar)is the same as bar = 42; return

@empijei
Copy link
Contributor Author

empijei commented Sep 25, 2018

Maybe I didn't state this properly, what I meant is that the caller can currently ignore the return value, I am not inspecting the callee.

@cznic
Copy link
Contributor

cznic commented Sep 25, 2018

You've stated it properly, I've misread the proposal (again).

I need more coffee in the morning, sorry.

@bradfitz
Copy link
Contributor

One cute thing I always liked about Perl was wantarray (http://perldoc.perl.org/functions/wantarray.html) that lets a function ask at runtime what context its caller was calling it in: scalar, list, or void context.

This let you do things like return an error if the caller was looking at it, or die (panic) if the caller was ignoring your error.

@empijei
Copy link
Contributor Author

empijei commented Sep 25, 2018

To add one more point to this:

When a deferred call ignores the returned value, it can get dangerous. This is the simplified version of the code that caused a bug:

defer doStuff()

While the correct code would have been

defer doStuff()()

or

cleanup := doStuff()
defer cleanup()

The proposed check would have prevented the bug.

One downside: making everyone change all the occurrences of

defer stuff.Close()

into

defer func(){ _ = stuff.Close() }()

is a pain and creates unneeded noise most of the time (or it would require some syntactic sugar).

It would be nice to have something on a language level to guard against ignored/mishandled return values (as @bradfitz pointed out) and I'm very open to suggestions to address the issue.

The simplest change would be to disallow deferred calls that return a func, but it feels like we would be missing the bigger picture. ( I was chatting about this with @mvdan several days ago )

@mvdan
Copy link
Member

mvdan commented Sep 25, 2018

Isn't this a duplicate of #21291?

In any case, I overall agree with the sentiment. Very few times are naked returns actually useful, and more often than not they lead to subtle bugs.

@blainsmith
Copy link
Contributor

Using with goroutines is another issue to.

From

go doSomething()

Into

go func() {
	_ = doSomething()
}()

Perhaps this enforces thoughtful error handling within goroutines if the former is not allowed.

@empijei
Copy link
Contributor Author

empijei commented Sep 25, 2018

Isn't this a duplicate of #21291?

In any case, I overall agree with the sentiment. Very few times are naked returns actually useful, and more often than not they lead to subtle bugs.

This is about ignored return values, I'm not discussing about naked returns. How do you think I can state this more clearly?

@ianlancetaylor
Copy link
Member

I believe this is a duplicate of #20803.

@mvdan
Copy link
Member

mvdan commented Sep 25, 2018

This is about ignored return values, I'm not discussing about naked returns.

My bad, I was reading too many threads on my notifications mailbox :)

@empijei
Copy link
Contributor Author

empijei commented Sep 25, 2018

I believe this is a duplicate of #20803.

Indeed it is, sorry about this, I looked for a potential dupe but didn't find it.

Thanks for pointing it out, closing this to merge the discussion.

@empijei empijei closed this as completed Sep 25, 2018
@golang golang locked and limited conversation to collaborators Sep 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants