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

Method called with NIL pointer receiver when using interfaces #25496

Closed
mandelsoft opened this issue May 22, 2018 · 4 comments
Closed

Method called with NIL pointer receiver when using interfaces #25496

mandelsoft opened this issue May 22, 2018 · 4 comments

Comments

@mandelsoft
Copy link

mandelsoft commented May 22, 2018

What version of Go are you using (go version)?

1.10.1 + 1.10.2

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

linux amd64

What did you do?

Please have a look at the programm below.

I'm just building a simple list using an interface type as parent attribute.
When traversing the list the recursion base test checking this parent attribute against NIL does not work.
The recursion continues, with calling the method on a NIL pointer receiver.

Further investigations shows that all of the following changes would solve the problem:
a) don't use the interface type (Node) for the parent attribute, but the pointer to struct type (*_Node)
b) change the argument type of new from *_Nodeto Node
c) Not using the direct assigment this.parent = p, but the strange construct

      if p == nil {
          this.parent = nil
      } else {
          this.parent = p
      }

This is really a strange behaviour. It seems not to be possible to assign a pointer variable containing nil to an interface variable reflecting nil after the assignment. Instead it points to a pointer pointing to nil which is evaluated with the == nil expression to false.

This is really contra-intuitive.


package main

import (
	"fmt"
)

type Node interface {
	Map(s string) Node
	Process(string) string
}

type _Node struct {
	parent Node
	data   string
}

var _ Node = &_Node{}

func NewNode() Node {
	return (&_Node{}).new(nil, "")
}

func (this *_Node) new(p *_Node, s string) *_Node {
	this.parent = p
	this.data = s
	return this
}

func (this *_Node) Map(s string) Node {
	return (&_Node{}).new(this, s)
}

func (this *_Node) Process(data string) string {
	fmt.Printf("THIS: %+v\n", this)
	if this.parent == nil {
		fmt.Printf("parent :NIL\n")
		return data
	}
	fmt.Printf("recursion %p\n", this.parent)
	return this.parent.Process(data) + "->" + this.data
}

func main() {
	n := NewNode().Map("a").Map("b")
	fmt.Printf("%s\n", n.Process("start"))
}

What did you expect to see?

THIS: &{parent:0xc42000a7c0 data:b}
recursion 0xc42000a7c0
THIS: &{parent:0xc42000a7a0 data:a}
recursion 0xc42000a7a0
THIS: &{parent:<nil> data:}
parent :NIL
start->a->b

What did you see instead?

THIS: &{parent:0xc4200a22a0 data:b}
recursion 0xc4200a22a0
THIS: &{parent:0xc4200a2280 data:a}
recursion 0xc4200a2280
THIS: &{parent:<nil> data:}
recursion 0x0
THIS: <nil>
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x48bdf9]

goroutine 1 [running]:
main.(*_Node).Process(0x0, 0x4c111b, 0x5, 0x1, 0x1)
	/main.go:40 +0x79
main.(*_Node).Process(0xc4200a2280, 0x4c111b, 0x5, 0x1, 0x1)
	/main.go:45 +0x106
main.(*_Node).Process(0xc4200a22a0, 0x4c111b, 0x5, 0x1, 0x1)
	/main.go:45 +0x106
main.(*_Node).Process(0xc4200a22c0, 0x4c111b, 0x5, 0x4d34a0, 0xc4200a22c0)
	/main.go:45 +0x106
main.main()
	/main.go:50 +0xee
@AlekSi
Copy link
Contributor

AlekSi commented May 22, 2018

It is basically https://golang.org/doc/faq#nil_error, just replace error with another interface type Node.

@bcmills
Copy link
Contributor

bcmills commented May 22, 2018

See also #22729 and related proposals.

Unfortunately, there is nothing we can do to make this less confusing in Go 1.

@bcmills bcmills closed this as completed May 22, 2018
@mandelsoft
Copy link
Author

Ok, that's what I expected, but is there at least a possibility to check for a nil pointer (==nil does not work)
to be able to avoid the nil pointer dereference.

@bcmills
Copy link
Contributor

bcmills commented May 23, 2018

Checking for a nil pointer is usually not the right way to resolve this sort of panic. If a nil pointer is not a valid, usable instance of the interface, you should avoid passing it as that interface in the first place.

In your particular example, it's not obvious why the Parent field needs to be an interface at all, since you are only ever setting it to a *_Node. If you can, make the field a *_Node instead. (See also https://golang.org/wiki/CodeReviewComments#interfaces.)

@golang golang locked and limited conversation to collaborators May 23, 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

4 participants