Reputation: 272
Preference: I'm new to Golang and keen to improve.
So I'm trying to learn from Dave Cheney's talk here: https://youtu.be/NwEuRO_w8HE?t=812 where we pass interfaces to methods to make code clearer and more generic.
I've implemented an example below, where I have a struct that can be outputted either to the std.out or to a file. However, I feel like it's a bit redundant just making empty structs (called "print" and "save" in ,my example), just so I can attach methods. How can I improve this?
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
)
type file struct {
data string
}
func (f *file) output(w io.Writer) error {
len, err := w.Write([]byte(f.data))
if err != nil {
return err
} else {
log.Println("Bytes Out: ", len)
return nil
}
}
type print struct{}
func (print *print) Write(p []byte) (n int, err error) {
return fmt.Printf("%s\n", p)
}
type save struct{}
func (s *save) Write(p []byte) (n int, err error) {
err = ioutil.WriteFile("./dat1", []byte(p), 0644)
if err != nil {
return -1, err
}
return len(p), nil
}
func main() {
f := file{"test"}
s := save{}
p := print{}
f.output(&s)
f.output(&p)
}
Upvotes: 1
Views: 567
Reputation: 46442
You don't need print
or save
types here. You have a method, output
, that takes an io.Writer
; the whole point of that is that you can pass it any writer. Both stdout and files are writers, so everything else here is unnecessary. You could just:
func main() {
f := file{"test"}
fp,err := os.Create("./dat1")
if err != nil {
panic(err)
}
f.output(fp) // This writes to a file
f.output(os.Stdout) // This writes to stdout
}
Upvotes: 1
Reputation: 417622
Your intentions aren't clear, but to answer your original question: how to pass functions as interface values?
You could create a single adapter-like type that implements io.Writer
, and when its Write()
method is called, it forwards to a function of your choice. A typical example of this is the http.HandlerFunc
: it's a function type that implements http.Handler
, so a function can be passed when an http.Handler
implementation is required.
For an io.Writer
adapter, it could look like this:
type WriteFunc func(p []byte) (n int, err error)
func (w WriteFunc) Write(p []byte) (n int, err error) {
return w(p)
}
And if you have functions like these:
func PrintWrite(p []byte) (n int, err error) {
return fmt.Printf("%s\n", p)
}
func SaveWrite(p []byte) (n int, err error) {
err = ioutil.WriteFile("./dat1", []byte(p), 0644)
if err != nil {
return -1, err
}
return len(p), nil
}
You can use them as io.Writer
like this:
f.output(WriteFunc(PrintWrite))
f.output(WriteFunc(SaveWrite))
Try it on the Go Playground.
Upvotes: 2