Anonymous interface implementation in Golang

GoAnonymous Types

Go 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()
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjocullView Question on Stackoverflow
Solution 1 - GonemoView Answer on Stackoverflow
Solution 2 - GoAllen HamiltonView Answer on Stackoverflow
Solution 3 - GotothemarioView Answer on Stackoverflow
Solution 4 - GoFeckmoreView Answer on Stackoverflow