Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to define an anonymous interface implementation in Go?

Tags:

interface

go

Consider some given interface and a function of an imaginary library that uses it like

// Binary and Ternary operation on ints
type NumOp interface {
    Binary(int, int) int
    Ternary(int, int, int) int
}

func RandomNumOp(op NumOp) {
    var (
        a = rand.Intn(100) - 50
        b = rand.Intn(100) - 50
        c = rand.Intn(100) - 50
    )
    fmt.Printf("%d <op> %d = %d\n", a, b, op.Binary(a,b))
    fmt.Printf("%d <op> %d <op> %d = %d\n", a, b, c, op.Ternary(a,b,c))
}

A possible type implementing that interface could be

// MyAdd defines additions on 2 or 3 int variables
type MyAdd struct {}
func (MyAdd) Binary(a, b int) int {return a + b }
func (MyAdd) Ternary(a, b, c int) int {return a + b + c }

I am dealing with many different interfaces defining a few functions that in some cases need to be implemented using functions mostly working as NOP-like operations, do not rely on any struct member and are only used in a single position in the project (no reusability needed).

Is there a simpler (less verbose) way in Go to define a (preferably) anonymous implementation of an interface using anonymous functions, just like (pseudo code, I know it's not working that way):

RandomNumOp({
   Binary: func(a,b int) int { return a+b},
   Ternary: func(a,b,c int) int {return a+b+c},
})
like image 793
muffel Avatar asked Jan 10 '19 11:01

muffel


1 Answers

If implementation must work

If the value implementing the interface must work (e.g. its methods must be callable without panic), then you can't do it.

Method declarations must be at the top level (file level). And to implement an interface that has more than 0 methods, that requires to have the method declarations somewhere.

Sure, you can use a struct and embed an existing implementation, but then again, it requires to already have an existing implementation, whose methods must already be defined "somewhere": at the file level.

If you need a "dummy" but workable implementation, them use / pass any implementation, e.g. a value of your MyAdd type. If you want to stress that the implementation doesn't matter, then create a dummy implementation whose name indicates that:

type DummyOp struct{}

func (DummyOp) Binary(_, _ int) int     { return 0 }
func (DummyOp) Ternary(_, _, _ int) int { return 0 }

If you need to supply implementation for some of the methods dynamically, you may create a delegator struct type which holds functions for the methods, and the actual methods check if the respective function is set, in which case it is called, else nothing will be done.

This is how it could look like:

type CustomOp struct {
    binary  func(int, int) int
    ternary func(int, int, int) int
}

func (cop CustomOp) Binary(a, b int) int {
    if cop.binary != nil {
        return cop.binary(a, b)
    }
    return 0
}

func (cop CustomOp) Ternary(a, b, c int) int {
    if cop.ternary != nil {
        return cop.ternary(a, b, c)
    }
    return 0
}

When using it, you have the freedom to only supply a subset of functions, the rest will be a no-op:

RandomNumOp(CustomOp{
    binary: func(a, b int) int { return a + b },
})

If implementation is not required to work

If you only need a value that implements an interface but you don't require its methods to be "callable" (to not panic if called), you may simply use an anonymous struct literal, embedding the interface type:

var op NumOp = struct{ NumOp }{}
like image 175
icza Avatar answered Nov 15 '22 03:11

icza