Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedded Interface

Tags:

go

I'm still quite new to Go and I was surprised to not be able to use the subtype of an embedded interface. Here is a small example to explain what I mean:

func test(sl bufio.ReadWriter){
    // cannot use sl(type bufio.ReadWriter) as type bufio.Reader in function argument
    readStuff(sl) 
    [...]
    writeStuff(sl) // same kind of error
}

func readStuff(sl bufio.Reader){
    [...]
}

As every interface have the same memory layout and ReadWriter is a Reader and a Writer, I was expecting this code to work. I did try to convert the interface type with:

readStuff(sl.(buffio.Reader))

But it doesn't work either. So I've got two questions:

  • Why doesn't it work?
  • What's the go philosophy about that problem?
like image 424
user1612346 Avatar asked Dec 16 '12 20:12

user1612346


2 Answers

They're different types. However, a bufio.ReadWriter contains a pointer to both a bufio.Reader type and a bufio.Writer type as elements of its struct. So passing the correct one should be easy enough. Try this:

func test(sl bufio.ReadWriter){
    readStuff(sl.Reader)
    [...]
    writeStuff(sl.Writer)
}

// Changed this bufio.Reader to a pointer receiver
func readStuff(sl *bufio.Reader) {
    [...]
}
like image 94
Daniel Avatar answered Oct 28 '22 10:10

Daniel


bufio.ReadWriter is a concrete type, not an interface. However, it does satisfy an interface (io.ReadWriter) so it can be assigned to a variable/function argument of an appropriate interface type. Then it works the way you may have anticipated (your code actually doesn't use any interfaces):

package main

import (
        "bufio"
        "bytes"
        "fmt"
        "io"
        "log"
)

func readStuff(r io.Reader) {
        b := make([]byte, 10)
        n, err := r.Read(b)
        if err != nil && err != io.EOF {
                log.Fatal(err)
        }
        fmt.Printf("readStuff: %q\n", b[:n])
}

func writeStuff(w io.Writer) {
        b := []byte("written")
        n, err := w.Write(b)
        if n != len(b) {
                log.Fatal("Short write")
        }

        if err != nil {
                log.Fatal(err)
        }
}

func test(rw io.ReadWriter) {
    readStuff(rw)
    writeStuff(rw)
}

func main() {
        r := io.Reader(bytes.NewBufferString("source"))
        var uw bytes.Buffer
        w := io.Writer(&uw)
        rw := bufio.NewReadWriter(bufio.NewReader(r), bufio.NewWriter(w))
        test(rw)
        rw.Flush()
        fmt.Printf("The underlying bytes.Buffer writer contains %q\n", uw.Bytes())
}

(Also here)


Output:

readStuff: "source"
The underlying bytes.Buffer writer contains "written"

This way test can consume any io.ReadWriter, not only a specific one. Which is a hint towards your question about go "philosophy".

like image 27
zzzz Avatar answered Oct 28 '22 10:10

zzzz