1
votes

Here is Playground link https://play.golang.org/p/qMKxqrOcc2. Problem is similar to one that is on Playground.

Let's say I have a condition and need to do this:

if modelName == "a"{
    model = models.A
} 
else{
    model = models.B
}

where A and B are some models:

type A struct{
    filed1 string
    field2 string
    //etc

}

and model B is

type B struct{
    filed1 string
    field2 string
    //etc

}

Fields in A and B has some same fields but mostly they reflect database table (document) and they are of same type (type struct).

When I say in front of all that:

var model interface{}

I got error:

type models.A is not an expression 

I am doing this to avoid code redundancy in code if you are asking why.

Question is similar to this: How to return dynamic type struct in Golang?

Here is update for code:

b := c.mainHelper.GetModelBy("id", id, modelName).(map[string]interface{})
mapstructure.Decode(b, &model)

if modelName == "a"{
    model.Photos = []string{"ph1","ph2"}
}
if modelName == "b"{
    model.Docs = []string{"doc1","doc2"}
}

c.mainHelper.UpdateModel(product, id, modelName)

I know this is stupid and probably is impossible to do but is there and way to do this:

var model models.modelName --> somehow to concat modelName to this models?

HERE IS NEW UPDATE

I have two models Post and Product. Both of them has Photos field.

type Post struct{

    Photos []string
    //etc
}

type Product {

    Photos []string
    //
}

Now I need one function that will say this:

func () RemovePhotos(id string, modelName string){

//if modelName=="post"
    //get model post with id

//if modelName=="product"
    //get model product with id

//set model.Photos = []string
//update model in db
}

I can understand that I can not assign type but how to use this one function to remove data from differnt types? As far as I can see code redundancy will look like this:

func () RemovePhotos(id string, modelName string) return bool{

    if modelName == "post"{

      var model models.Post
      modelWithdata := getModelWithId.(*model)
      modelWithdata.Photos = []string
      //update model in db here
    } 
    if modelName == "product"{
      var model models.Product
      modelWithdata := getModelWithId.(*model)
      modelWithdata.Photos = []string
      //update model in db here
    }

    //it does not matter what I return this is just redundancy example
    return true

}

As you can only difference is var model models.Post/var model models.Product. This is redundancy in code and it looks ugly but if there is no way around this then ok, i will have this one completed with redundancy.

3
How is model going to be used after assignment? The best solution will depend on that. - Brian
"I am doing this to avoid code redundancy". Don't do that too early! Keep away from avoiding redundancy until you can see what really is redundant and can be factored into a (non-empty) interface. Then you avoid redundancy. - Volker
I can not accept any answer since they do not provide enough answers. I tried all of that things from answers before I asked question here. - pregmatch
@pregmatch I can take a look at your updated question but I would prefer that you ask it as a separate question considering that there has been a good amount of activity already eg. the votes and the comments may not reflect the original answer with the new content. If you can cut the new content out of the question and put a link here for the new question, I'll be sure to answer it :). - Will C

3 Answers

6
votes

You can't assign types. You have to assign instances. Your code will effectively have to be the following. I added comments in the two lines that you'll want to change.

package main

import "fmt"

type B struct {
    filed1 string
    field2 string
    //etc

}

type A struct {
    filed1 string
    field2 string
    //etc

}

func main() {
    var model interface{}
    modelName := "b"
    if modelName == "a" {
        model = A{} // note the {} here
    } else {
        model = B{} // same here
    }

    fmt.Println(model)
}

Just a word of advice though, you probably don't want to use a generic interface{} type, instead its better to use an actual interface that both A and B implements. The generic interface type will cause you more headaches and really defeats the purpose of using a statically typed language like Go.

2
votes

You're getting the error because you're trying to assign a type to the interface{} instance. You need to assign an instance.

If you instead had;

var model interafce{}

if modelName == "a"{
    model = models.A{}
} 
else{
    model = models.B{}
}

then it would work fine.

1
votes

This is your program from the edit with a interface type implementation:

package main

import (
    "log"
)

//// Interfaces ////
type PhotoManager interface {
    AddPhotos(id string) (bool, error)
}

//// Post ////
type Post struct {
    Photos []string
}

func (p *Post) AddPhotos(id string) (bool, error) {
    p.Photos = append(p.Photos, id)
    return true, nil
}

//// Product ////
type Product struct {
    Photos []string
    Docs   []string
}

func (p *Product) AddPhotos(id string) (bool, error) {
    p.Photos = append(p.Photos, id)
    return true, nil
}

// Useless function to demonstrate interface usage //
func AddPhotoToInterfaceImplementation(id string, pm PhotoManager) {
    pm.AddPhotos(id)
}

//// Main ////
func main() {
    post := Post{}
    product := Product{}
    post.AddPhotos("123")
    product.AddPhotos("321")
    AddPhotoToInterfaceImplementation("456", &post)
    AddPhotoToInterfaceImplementation("654", &product)
    log.Println(post)
    log.Println(product)
}

The moving parts here are:

  • the type PhotoManager interface that is used to define an interface with generic functions
  • the implementations of AddPhotos on Post and Product to provide the actual implementations of the interface functions
  • the usage of pm PhotoManager as parameter to AddPhotoToInterfaceImplementation to show the usage of the interface type.