Go: Access a struct's properties through an interface{}-ThrowExceptions

Exception or error:

I am having trouble in accessing the one struct’s properties (named Params) in different file.

please consider x.go where i invoke a function(CreateTodo)

type Params struct {
    Title  string `json:"title"`
    IsCompleted int `json:is_completed`
    Status  string `json:status`
}

var data = &Params{Title:"booking hotel", IsCompleted :0,Status:"not started"}

isCreated := todoModel.CreateTodo(data) // assume todoModel is imported

now CreateTodo is a method on a struct (named Todo) in different file lets say y.go

type Todo struct {
    Id  int `json:todo_id`
    Title  string `json:"title"`
    IsCompleted int `json:is_completed`
    Status  string `json:status`
}

func (mytodo Todo)CreateTodo(data interface{}) bool{
    // want to access the properties of data here
    fmt.Println(data.Title) 
    return true
}

Now I just want to use properties of data in CreateTodo function in y.go.
But i am not able to do so and getting following error

data.Title undefined (type interface {} is interface with no methods)

I am sure issue is around accepting struct as an empty interface but i am not able to figure out.

Please help here.Thanks

How to solve:

So you have one of two options, depending on your model:

#1

Switch to data *Params instead of data interface{} as suggested in another answer but it looks like you are expecting different types in this function, if so; check option #2 below.

#2

Use Type switches as follows:

func (t Todo) CreateTodo(data interface{}) bool {
    switch x := data.(type) {
    case Params:
        fmt.Println(x.Title)
        return true

    // Other expected types

    default:
        // Unexpected type
        return false
    }
}

P.S. Be careful with your json tags: it should be json:"tagName". Notice the ""! Check go vet.

Answer:

You could just type the function parameter:

func (mytodo Todo)CreateTodo(data *Params) bool{
    // want to access the properties of data here
    fmt.Println(data.Title) 
    return true
}

See: https://play.golang.org/p/9N8ixBaSHdP

Answer:

If you want to operate on a Params (or *Params), you must do that.

If you want to operate on an opaque type hidden behind an interface{}, you must do that.

In short, you cannot peek behind the curtain without peeking behind the curtain. Either expose the actual type Params, so that you can look at it, or keep all the code that does look at it elsewhere. The “keep the code elsewhere” is where interface really shines, because it allows you to declare that something otherwise-opaque has behaviors and ask for those behaviors to happen:

type Titler interface {
    GetTitle() string
}

If Params has a GetTitle function, it becomes a Titler.

You can now define your CreateTodo as a function that takes a Titler, and then you can pass &data to this function.

This structure is overall quite klunky and it seems much more likely that Todo itself should be an embeddable struct instead, but see a more complete example starting from a stripped-down version of your sample code here, in the Go Playground.

Leave a Reply

Your email address will not be published. Required fields are marked *