golang struct not implementing interface?

Issue

I am a beginner with go so bear with me on this. I have an interface defined as the following:

type DynamoTable interface {
    Put(item interface{}) interface{ Run() error }
}

also I have a Repo struct like so:

type TenantConfigRepo struct {
    table DynamoTable
}

and i have a struct dynamo.Table which has a Put function defined like so:

func (table dynamo.Table) Put(item interface{}) *Put

and the Put struct has a Run function as follows:

func (p *Put) Run() error

What I am trying to do is have a generic DynamoTable interface which will then be used for mocking and unit tests. however this is causing an issue with creating a new Repo:

func newDynamoDBConfigRepo() *TenantConfigRepo {
    sess := session.Must(session.NewSession())
    db := dynamo.New(sess)
    table := db.Table(tableName) //=> this returns a type dynamo.Table
    return &TenantConfigRepo{
        table: table,
    }
}

This however is throwing an error like so

cannot use table (variable of type dynamo.Table) as DynamoTable value in struct literal: wrong type for method Put (have func(item interface{}) *github.com/guregu/dynamo.Put, want func(item interface{}) interface{Run() error})

this is very strange for me because from what i see is the interface that has a Run() error should be sufficient for the Put struct since it has the same signature. I am not sure what i am doing wrong here.

Thanks!.

Solution

wrong type for method Put (have func(item interface{}) *github.com/guregu/dynamo.Put, want func(item interface{}) interface{Run() error})

your function returns a *Put. The interface expects an interface{Run() error}. A *Put might satisfy this interface, but they’re still different types. A function signature returning a type that satisfies that interface is not interchangeable with a function signature returning that interface.

So, start by giving your interface a name. We refer to it in 2 places, and you should avoid anonymous interface (and struct) definitions because they have no inherent benefit and make your code more verbose and less DRY.

type Runner interface{
   Run() error
}

Now update DynamoTable to use that interface

type DynamoTable interface {
    Put(item interface{}) Runner
}

You say dynamo.Table is outside of your control. But you can create a new type equal to dynamo.Table and then override the put method.

In overridden method, we’l cast our dynamoTable back to dynamo.Table, call the original dynamo.Table.Put, and then return the result.

type dynamoTable dynamo.Table

func (table *dynamoTable) Put(item interface{}) Runner {
  return (*dynamo.Table)(table).Put(item)
}

dynamo.Table Can still return a *Put because *Put implements Runner. The return value will be Runner and the underlying type will be *Put. Then the interface will be satisfied, and that error will be fixed.

https://go.dev/play/p/y9DKgwWbXOO illustrates how this retype and override process works.

Answered By – Daniel Farrell

Answer Checked By – Katrina (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.