I'm trying an example related to struct embedding of interfaces
// https://talks.golang.org/2014/go4java.slide#52
// Struct embedding of interfaces
// https://play.golang.org/p/SYiZ7M1OEhU
package main
import (
"bytes"
"fmt"
"net"
)
// net.Conn has Read and Write
type loopBack struct {
net.Conn
buf bytes.Buffer
}
func (c *loopBack) Read(b []byte) (int, error) {
fmt.Println("loopBack Read")
return 0, nil
}
func main() {
loop := loopBack{}
loop.Read(nil)
loop.Write(nil)
}
and the Write
method is undefined, so I get this runtime error
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0xffffffff addr=0x0 pc=0xe28ca]
goroutine 1 [running]:
main.main()
/tmp/sandbox812386031/main.go:28 +0x6a
Exist some way to validate it at compile time?
link to codeWhat you are doing is not the same as saying "loopBack implements net.Conn
".
To get compile-time error(s) about the missing method – and the mismatched Read()
, too – declare loop
's type:
(And don't embed net.Conn
in loopBack
)
func main() {
var loop net.Conn = loopBack{}
Golang doesn't require exlicit interface implementations. What you do here:
type loopBack struct {
net.Conn
buf bytes.Buffer
}
Is the similar to:
type loopBack struct{
Conn net.Conn
buf bytes.Buffer
}
net.Conn
being an interface type, the first field of your loopBack
type can be anything that implements the net.Conn
interface, which is of course more than Read
and Write
alone (see here).
The advantage of embedding types is that the fields and receiver functions (name conflicts aside) can be accessed directly on the types that embeds them.
With an embedded net.Conn
field, you can indeed write:
loop.Write(nil)
If the Conn
field is initialised (otherwise, its value is nil
). Changing the declaration to the second version, loop.Write
won't work, you'll have to write:
loop.Conn.Write(nil)
Type embedding is very powerful, but there's a number of gotcha's when you first get started. Thankfully, there's an entire paragraph explaining embedding on the effective go doc
Anyway, as stated, you are able to call functions, and access fields (if you're embedding a struct type instead of an interface). There is one thing, though: the field must be initialised correctly!
And that's where you went wrong: You still have to initialise your Conn
part of the loopBack
variable, otherwise what you're doing is the same as:
net.Conn(nil).Write(nil)
Which, naturally, results in a panic (nil pointer dereference)...
For example:
conn, err := net.Dial("tcp", "localhost:80")
if err != nil {
log.Fatalf("failed to dial localhost: %+v", err)
}
loop := loopBack{
Conn: conn,
}
loop.Write(nil) // same as conn.Write(nil)
Failing to set the net.Conn
embedded field is akin to doing something like this:
s := make([]*int, 10) // make slice of 10 pointers to int
fmt.Println(len(s)) // outputs 10
*s[0]++ // add 1 to first element PANICS
The 10 elements in the slice exist, but they've all been initialised to nil
User contributions licensed under CC BY-SA 3.0