Struct embedding of interfaces, panic: runtime error

-2

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 code

https://play.golang.org/p/SYiZ7M1OEhU

go
struct
interface
go-interface
asked on Stack Overflow Jan 28, 2019 by JuanPablo • edited Jan 28, 2019 by JuanPablo

2 Answers

0

What 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{}

answered on Stack Overflow Jan 28, 2019 by ııı • edited Jan 28, 2019 by ııı
0

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

answered on Stack Overflow Jan 28, 2019 by Elias Van Ootegem • edited Jan 28, 2019 by Elias Van Ootegem

User contributions licensed under CC BY-SA 3.0