Golang Cast interface to struct

GoRedis

Go Problem Overview


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
}

Go Solutions


Solution 1 - Go

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.

Solution 2 - Go

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.

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
QuestionMadzQuestioningView Question on Stackoverflow
Solution 1 - Goleaf bebopView Answer on Stackoverflow
Solution 2 - Gomu is too shortView Answer on Stackoverflow