3

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.

4
  • How is model going to be used after assignment? The best solution will depend on that. Commented Apr 7, 2016 at 19:16
  • 5
    "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. Commented Apr 7, 2016 at 20:44
  • 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. Commented Apr 8, 2016 at 12:30
  • @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 :). Commented Apr 9, 2016 at 0:01

3 Answers 3

6

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.

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

5 Comments

To prevent the use of interface{} you may want to look into interfaces (sic)
@RickyA Agreed but based on the question he is asking about assigning a specific model rather than defining behaviors. Of course, the behavior could also be added to the models themselves to avoid these problems but it really depends on the asker's use case.
true, I'd just added that comment because an interface is the only way to generalise types without doing reflection with interface{}. If he is building a factory it might help him.
@WillC i think you could probably point out interface{} is a general type in golang, and distinguish it from a proper interface. The way you expressed sounds like interface is not a good practice to golang beginners.
@WillC can you look at "HERE IS NEW UPDATE" please.
2

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.

8 Comments

this is not working because when trying to do something like model.SomeFiled = "somevalue" it says type interface {} has no field or method SomeFiled
@pregmatch yes interface{} is the most general type in Go, it doesn't expose any fields or methods from the underlying type. You have to do a type assertion like typeA := interfaceInstance.(A) in order to access the fields on A
@pregmatch the way this is generally handled would be to have your db access method return an interface{} and then use a type switch in the calling scope to 'unbox' the value.
@pregmatch Not impossible but probably wrong abstraction and use case for the language.
@pregmatch if you provide a more complete example I might find the time to fix it up. Thing is, your question doesn't indicate much about the scope of various things, it's just a couple of excepts out of context. You may have to structure your program a bit differently.
|
1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.