Skip to content

Strange interaction between defer and function taking an interface resulting in nil dereference #13200

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

Closed
krnowak opened this issue Nov 10, 2015 · 4 comments

Comments

@krnowak
Copy link

krnowak commented Nov 10, 2015

go version go1.5.1 linux/amd64

There is an example code in http://play.golang.org/p/5ZpPG0hhqJ
And here is a truncated code to the essence: http://play.golang.org/p/W1lmCPoc7D

I expected the nil check in maybeClose to prevent executing Close, but apparently it is executed on a nil variable.

@bradfitz
Copy link
Contributor

Working as designed. It's okay to call methods on nil non-interface values.

Let's move this to a forum since it's not a bug to track here on the bug tracker. See https://golang.org/wiki/Questions

@krnowak
Copy link
Author

krnowak commented Nov 10, 2015

Excuse me, but I have an impression that you haven't seen the full example at all. I'm not talking about calling methods on nil non-interface values here at all. I'll try to explain in here then.

Let's assume that type *A implements io.Closer. I have a function maybeClose implemented as follows:

func maybeClose(c io.Closer) {
    if c != nil {
        c.Close()
    }
}

I'm using it as follows:

a := &A{}
defer func() { maybeClose(a) }()
if err := someValidation(a); err != nil { return nil, err }
if err := someMoreChecks(a); err != nil { return nil, err }
...
if err := lastCheck(a); err != nil { return nil, err }
// alright, a is valid, let's not close it, but return it instead
b := a
a = nil
return b, nil

If all validations passed, I assign the object to other variable and nil the a variable. The deferred action is run, takes an a variable, which is nil at this point. Somehow c != nil in maybeCheck function does not catch it and happily calls Close on it, which might end in nil dereference if Close accesses some fields in *A.

Now, either change c io.Closer in maybeClose to c *A or change a := &A{} to a := func() io.Closer { return &A{} } () and it works - Close is not invoked anymore in deferred function.

@bradfitz
Copy link
Contributor

@krnowak
Copy link
Author

krnowak commented Nov 10, 2015

Thanks. That's very confusing.

@golang golang locked and limited conversation to collaborators Nov 10, 2016
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

3 participants