103

Hi I'm trying to retrieve the function/method of one struct but I'm using an interface as parameter and using this interface I'm trying to access the function of the struct. To demonstrate what I want below is my code

// Here I'm trying to use "GetValue" a function of RedisConnection but since "c" is an interface it doesn't know that I'm trying to access the RedisConnection function. How Do I fix this?
func GetRedisValue(c Connection, key string) (string, error) {
    value, err := c.GetValue(key)

    return value, err
}

// Connection ...
type Connection interface {
    GetClient() (*redis.Client, error)
}

// RedisConnection ...
type RedisConnection struct {}

// NewRedisConnection ...
func NewRedisConnection() Connection {
    return RedisConnection{}
}

// GetClient ...
func (r RedisConnection) GetClient() (*redis.Client, error) {
    redisHost := "localhost"
    redisPort := "6379"

    if os.Getenv("REDIS_HOST") != "" {
        redisHost = os.Getenv("REDIS_HOST")
    }

    if os.Getenv("REDIS_PORT") != "" {
        redisPort = os.Getenv("REDIS_PORT")
    }

    client := redis.NewClient(&redis.Options{
        Addr:     redisHost + ":" + redisPort,
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    return client, nil
}

// GetValue ...
func (r RedisConnection) GetValue(key string) (string, error) {
    client, e := r.GetClient()
    result, err := client.Ping().Result()
    return result, nil
}
1
  • GetValue returns an interface. Use redis.String() to cast it to string Commented Jun 20, 2018 at 16:20

2 Answers 2

261

To answer the question directly, i.e., to cast an interface into a concrete type, you do:

v = i.(T)

where i is the interface and T is the concrete type. It will panic if the underlying type is not T. To have a safe cast, you use:

v, ok = i.(T)

and if the underlying type is not T, ok is set to false, otherwise true. Note that T can also be an interface type and if it is, the code cast i into a new interface instead of a concrete type.

And please be noted, casting an interface is likely a symbol of bad design. As in your code, you should ask yourself, does your custom interface Connection solely requires GetClient or does it always requires a GetValue? Does your GetRedisValue function requires a Connection or does it always wants a concrete struct?

Change your code accordingly.

Sign up to request clarification or add additional context in comments.

1 Comment

I was so happy to find that this type of casting was possible in golang that I was about to start using it when I read you last paragraph... and then :( go back to the design diagrams and avoid using this type of casting. You are absolutely right on your note, thanks!
13

Your Connection interface:

type Connection interface {
    GetClient() (*redis.Client, error)
}

only says that there is a GetClient method, it says nothing about supporting GetValue.

If you want to call GetValue on a Connection like this:

func GetRedisValue(c Connection, key string) (string, error) {
    value, err := c.GetValue(key)
    return value, err
}

then you should include GetValue in the interface:

type Connection interface {
    GetClient() (*redis.Client, error)
    GetValue(string) (string, error) // <-------------------
}

Now you're saying that all Connections will support the GetValue method that you want to use.

2 Comments

Yeah I figured that will be the case. But it doesn't make sense that the Connection interface will have a GetValue method. So I was thinking of having a separate interface with a GetValue function in it. How will I go about that?
That depends on how far down the rabbit hole you want to go. Do you really need your Connection interface? Maybe you only need some sort of type Valuer interface { GetValue(string) (string, error) } then just wrap your redis connection in a struct that implements that interface.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.