Anonymous interface implementation in Golang
GoAnonymous TypesGo Problem Overview
In Go, is there a way to satisfy an interface anonymously? It doesn't seem like there is, but this was my best attempt.
(In the Playground)
package main
import "fmt"
type Thing interface {
Item() float64
SetItem(float64)
}
func newThing() Thing {
item := 0.0
return struct {
Item (func() float64)
SetItem (func(float64))
}{
Item: func() float64 { return item },
SetItem: func(x float64) { item = x },
}
}
func main() {
thing := newThing()
fmt.Println("Hello, playground")
fmt.Println(thing)
}
Go Solutions
Solution 1 - Go
Go uses method sets to declare which methods belong to a type. There is only one way to declare functions with receiver types (methods):
func (v T) methodName(...) ... { }
Since nested functions are forbidden, there is no way to define a method set on anonymous structs.
The second thing that will not allow this is that methods are read-only. Method values were introduced to allow to pass methods around and use them in goroutines but not to manipulate the method set.
What you can do instead is to provide a ProtoThing and refer to underlying implementations of your anonymous struct (on play):
type ProtoThing struct {
itemMethod func() float64
setItemMethod func(float64)
}
func (t ProtoThing) Item() float64 { return t.itemMethod() }
func (t ProtoThing) SetItem(x float64) { t.setItemMethod(x) }
// ...
t := struct { ProtoThing }{}
t.itemMethod = func() float64 { return 2.0 }
t.setItemMethod = func(x float64) { item = x }
This works because by embedding ProtoThing
the method set is inherited. Thus the anonymous struct also satisfies the Thing
interface.
Solution 2 - Go
Here's neat way to satisfy an interface with an anonymous function.
type Thinger interface {
DoThing()
}
type DoThingWith func()
// Satisfy Thinger interface.
// So we can now pass an anonymous function using DoThingWith,
// which implements Thinger.
func (thing DoThingWith) DoThing() {
// delegate to the anonymous function
thing()
}
type App struct {
}
func (a App) DoThing(f Thinger) {
f.DoThing()
}
//...Somewhere else in your code:
app := App{}
// Here we use an anonymous function which satisfies the interface
// The trick here is to convert the anonymous function to the DoThingWith type
// which delegates to the anonymous function
app.DoThing(DoThingWith(func() {
fmt.Println("Hey interface, are you satisfied?")
}))
Playground: https://play.golang.org/p/k8_X9g2NYc
nb, it looks like HandlerFunc in http package uses this pattern: https://golang.org/pkg/net/http/#HandlerFunc
edit: Changed type DoThing to DoThingWith for clarity. Updated playground
Solution 3 - Go
You can not instantiate a struct with methods, they need to be declared as functions, but in Go functions are "first class citizens", so they can be field values just like in JavaScript (but typed).
You can make a generic struct that accepts func fields to implement the interface:
package main
import "fmt"
type Thing interface {
Item() float64
SetItem(float64)
}
// Implements Thing interface
type thingImpl struct {
item func() float64
setItem func(float64)
}
func (i thingImpl) Item() float64 { return i.item() }
func (i thingImpl) SetItem(v float64) { i.setItem(v) }
func newThing() Thing {
item := 0.0
return thingImpl{
item: func() float64 { return item },
setItem: func(x float64) { item = x },
}
}
func main() {
thing := newThing()
fmt.Println("Hello, playground")
fmt.Println(thing)
}
Solution 4 - Go
I arrived here when looking into how the typical http middleware implementation returned a http.Handler
interface without defining a ServeHTTP
method for the anonymous function it returns:
func LoggingMiddleware(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
log.Println(r.Method, r.URL.String())
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
Here, the anonymous function is being converted to the HandlerFunc
type to fulfill the interface requirements: http.HandlerFunc(fn)
Even though the anonymous function doesn't directly fulfill the Handler
interface itself by implementing a ServeHTTP
method, the HandlerFunc type that it's converted to does implement a ServeHTTP
method.
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
Here's a stripped down playground example: https://play.golang.org/p/JX0hrcXyj6Q
package main
import (
"fmt"
)
type Walker interface {
Walk() // interface to be fulfilled by anonymous function
}
type WalkerFunc func()
// Walk method satisfies Walker interface
func (wf WalkerFunc) Walk() {
fmt.Println("start walking")
wf()
fmt.Printf("stop walking\n\n")
}
func main() {
// use type conversion to convert anonymous function to WalkerFunc type, satisfying Walker interface
WalkerFunc(func() { fmt.Println("chew gum") }).Walk()
WalkerFunc(func() { fmt.Println("smell roses") }).Walk()
}