Override function couldnt be called in base struct?

package main              
                      
import (                  
    "fmt"                 
)                         
type T interface{         
    ToChild() T           
    Test()                
    Test2()               
}                         
                          
type A struct {                     
}                         
func (a *A)Test(){        
    fmt.Println("A-Test") 
}                         
func (a *A)Test2(){       
    a.ToChild().Test()    
}                         
               
type B struct {           
    A                     
}                         
                          
func (b *B)Test(){        
    fmt.Println("B-Test") 
}                         
       
func (b *B)ToChild()T{    
    return b              
}                         
                          
                          
func main(){              
    var a T               
    a = &B{}              
    a.Test2()             
}            

go run test.go:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0x47b739]

goroutine 1 [running]:
main.(*A).Test2(0xc42000e230)
	/root/e/source/device_map/src/device_map/test2.go:19 +0x29
main.main()
	/root/e/source/device_map/src/device_map/test2.go:42 +0x4e
exit status 2

this should cause the error as you said i think:

does not implement T (missing ToChild method)

but it’s not.

is this right?

---------------- sorry , struct A is defined like this before.

type A struct {                     
    T
}

When I run your code in the Go playground, I get a compile-time error message:

main.go:20: a.ToChild undefined (type *A has no field or method ToChild)

This makes sense, as A has indeed no ToChild method.
I wonder how you manage to execute the code despite this compile-time error. What is your go version? Are you using cgo rather than go?

(Side node: The terms “base struct” and “ToChild” indicate that you think in terms of object orientation and inheritance. Struct embedding is different from inheritance, and using OO terms can cause confusion about what actually happens when embedding a struct versus what you expect to happen in an OO language.)

thank you for your reply. this is the shell output. and your result is correct

root@75d8bb32036d:~/e/source# go run test2.go 
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x30 pc=0x47b739]

goroutine 1 [running]:
main.(*A).Test2(0xc42000e230)
	/root/e/source/test2.go:19 +0x29
main.main()
	/root/e/source/test2.go:42 +0x4e
exit status 2
root@75d8bb32036d:~/e/source# go version
go version go1.8 linux/amd64
root@75d8bb32036d:~/e/source# go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/root/go_path:/root/e/source/gopath"
GORACE=""
GOROOT="/usr/lib/go"
GOTOOLDIR="/usr/lib/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build445931478=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

This is strange. I ran your code locally and still get the same compile-time error as in the Go Playground.

I use Go 1.8.3 and my go env output has no substantial difference to yours (besides different paths and different GOOS (darwin)).

Maybe you’ll want to try Go 1.8.3 as there have been some fixes to the compiler since v1.8.

struct A defined is incorrect before.

type A struct {
    T
}

Ok, now I am getting the same runtime panic.

The point is:

  • Type A “implements” interface T by delegating all of T’s methods to the embedded anonymous field of type T. This is why the compiler does not complain.

  • At runtime, the embedded T field is nil. Test this with Printf:

func main() {
	var a T
	a = &B{}
	fmt.Printf("%+v\n", a)
	a.Test2()
}

which prints

&{A:{T:<nil>}}

And this the reason for the nil pointer panic.

To avoid this, instantiate the embedded A field with something that implements T.

func main() {
	b := &B{}   // b has Test() and ToChild()
	fmt.Printf("%+v\n", b)
	a := A{b}  // a has Test2() and both methods of b
	fmt.Printf("%+v\n", a)
	ba := &B{a} // b has all methods of a
	fmt.Printf("%+v\n", ba)
	ba.Test2()
}

Output:

&{A:{T:<nil>}}
{T:0xc42000e270}
&{A:{T:0xc42000e270}}
B-Test

(Printf's +v verb is really handy for inspecting values. For extra verbosity, also try #v.)

(Caveat - I changed the variable naming - a is of type A, b is of type B, and ba is of type B instantiated with a inside.)


Edited to add the output of the modified function main().

BTW, You can call Test2() on a already. ba is just another level of indirection.

2 Likes

you are so brilliant.
thank your.
that means instance of A should init T cause T is interface type in struct A.

Yes, an embedded interface on its own cannot do anything.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.