I'm trying to understand Go's interfaces and embedding.
What I'm trying to do here is create my own custom writer
and reader
where each one implements either io.Writer
or io.Reader
Now I want to take these custom structures that implement basic interfaces and embed them into another custom struct that implements Read/Write/Close
. The below code is what I have so far but when I run it I get the following error
cannot use test (type MyReadWriteCloser) as type io.Writer in argument to fmt.Fprintf: MyReadWriteCloser does not implement io.Writer (missing Write method)
I thought when you embed a structure within another structure you also get the methods of the embedded structures. Can someone tell me what I'm missing?
package main
import (
"fmt"
"io"
)
type MyWriter struct {
w io.Writer
}
func (m MyWriter) Write(b []byte) (n int, err error) {
// encrypt b and write to underlying writer
m.w.Write(b)
return
}
type MyReader struct {
r io.Reader
}
func (m MyReader) Read(b []byte) (n int, err error) {
// decrypt b
m.r.Read(b)
return
}
type MyReadWriteCloser struct {
MyWriter
MyReader
}
func (m MyReadWriteCloser) Close() error {
return nil
}
func main() {
fmt.Println("main start")
r, w := io.Pipe()
test := MyReadWriteCloser{
MyWriter{w},
MyReader{r},
}
fmt.Fprintf(test, "hello world
")
}
Your syntax is wrong so the interfaces/types are not actually being imbedded.
type MyWriter struct {
w io.Writer
}
should be
type MyWriter struct {
io.Writer
}
And based on my understanding a proper embed leaves you no reason to define a method like;
func (m MyWriter) Write(b []byte) (n int, err error) {
m.w.Write(b)
return
}
because the body would just be m.Write(b)
so that just doesn't make sense. If you embed a type the embedding type will be able to call methods from the embedded type directly (it's the nearest thing to inheritance, it looks the same to a casual reader). I don't remember the resolution rules if there are conflicts between method or property names in embedded types or the embedding type an embedded type but a good rule of thumb would be to avoid embedding types that will cause naming conflicts because the behavior will not be obvious.
EDIT: Example of how to override a method in the underlying type and invoke the 'base' (i quote cause some gophers might be offended by that word choice) method from inside it;
func (m MyWriter) Write(b []byte) (n int, err error) {
b = EncodeBFromMethodThatsInScopeHere(b)
return m.Writer.Write(b)
}
Basically, if you want to explicitly use the embedded types method you can refer to it as a property of the type using the embedded types type as if it were a property name.
You're not embedding the interfaces, you're making them fields in your struct.
Embedding looks like:
type MyReader struct {
io.Reader
}
If you want to manually delegate to the interface, you need to use the same method name
func (m MyReader) Read(b []byte) (n int, err error) {
return m.r.Read(b)
}