3

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

ifnotak
  • 4,147
  • 3
  • 22
  • 36
Siyaram Malav
  • 4,414
  • 2
  • 31
  • 31

3 Answers3

2

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.

ifnotak
  • 4,147
  • 3
  • 22
  • 36
  • but here i would need to import x.go for "Params" and that would not be allowed due to import cycle issue. – Siyaram Malav Oct 24 '19 at 10:47
  • @SiyaramMalav if `Params` is inherently needed for `Todo` then `Params` should be part of the package including `Todo`. Otherwise, you would have to move `Params` to a different package and import it in both. Check you model and choose one. – ifnotak Oct 24 '19 at 10:53
  • Todo and Params is already in different packages.Let me give more details about directory structure. [1]. x.go nothing but -> controllers/todo.go which contains Params struct(from where model function is called) [2]. y.go nothing but -> models/todo/todo.go which contains Todo struct few points a) i cannot put Params struct in model because if obvious purpose of connecting to db and fetching data. are you suggesting Params struct to be put in different package and should be imported in both controller and model ? – Siyaram Malav Oct 24 '19 at 11:15
  • Yes I do. More here: https://stackoverflow.com/a/50989710/5198756 – ifnotak Oct 24 '19 at 11:21
0

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

Roger Kreft
  • 200
  • 10
  • Accessing Params struct is not allowed in y.go due to import cycle issue because i have already imported y.go in x.go file , now can not do vice versa. – Siyaram Malav Oct 24 '19 at 10:51
  • I guess you mean they are in different packages, because different files is not a problem (given they are in the same package). If so: Can you move the CreateTodo method to the other file (meaning package)? – Roger Kreft Oct 24 '19 at 11:03
  • they are in different packages. for more information you can read above my replies to @ifnotak – Siyaram Malav Oct 24 '19 at 11:22
0

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.

torek
  • 448,244
  • 59
  • 642
  • 775