简体   繁体   中英

Add struct or pointer to struct to slice

Say I have structs like so:

type Foo struct {
  F string `zoom:"1"`
}

type Bar struct {
  F string `zoom:"2"`
}

type Baz struct {
  F string `zoom:"3"`
}

Say I wanted to create a func that can extract the f field from each struct, it might look like:

func extractField(s []struct{}){
  for _, v := range s {
    t := reflect.TypeOf(v{})
    f, _ := t.FieldByName("F")
    v, ok := f.Tag.Lookup("zoom")
  }
}

is there a way to pass the structs to extractField? If I do this:

extractField([]struct{}{Foo, Bar, Baz})

I get this error:

Type Foo is not an expression
Type Bar is not an expression
Type Baz is not an expression

I just want to pass the 3 structs to the extractField func.

The only way I could figure out how to do this, is like so:

type Foo struct {
    F string `zoom:"1"`
}

type Bar struct {
    F string `zoom:"2"`
}

type Baz struct {
    F string `zoom:"3"`
}


func extractField(s []interface{}){
    for _, v := range s {
        t := reflect.TypeOf(v)
        f, _ := t.FieldByName("F")
        v, ok := f.Tag.Lookup("zoom")
        fmt.Println(v,ok)
    }
}

func main(){

    extractField([]interface{}{Foo{},Bar{},Baz{}})  // <<<< here

}

not sure if there is a way to pass a struct without "instantiating it" first.

The original code looks like it follows a JavaScript approach, where a function would mutate an object. Go is a little bit different, where it's more common to self-mutate.

For example:

type Generic struct {
  field string
}

func (generic *Generic) Value () string {
  return generic.field
}

someObject := &Generic{
  field: "some value",
}

log.Print(someObject.Value()) // Outputs "some value"

If you're coming from the JavaScript world, think of structs a little bit like an object/class that can contain attributes and functions. Structs are merely a definition until an instance is instantiated. This differs from JavaScript where the definition of the object and the data are both defined at the same time.

As the other answer points out, an interface is another similar approach to dealing with this.

Clarification

In JavaScript terms, the OP is attempting to do something akin to:

class Foo {...}
class Bar {...}
class Baz {...}

extractField(Foo, Bar, Baz)

In JS-speak, this would pass a class definition to the extractField method. You would still have to instantiate an instance of a class if you want to manipulate/read from it, like:

extractField(new Foo(), new Bar(), new Baz())

This is basically what is being accomplished with

extractField([]interface{}{Foo{},Bar{},Baz{}})

I think the problem you're running into is the Foo/Bar/Baz structs are instantiated, but the nested F struct is not.

The overall problem seems like a much better match for an interface type.

type HasF interface {
    GetF() string
}

It's easy enough to define these methods, eg

func (foo Foo) GetF() string { return foo.F }

Your method to iterate over them becomes almost trivial

func extractField(s []HasF) {
    for _, v := range s {
        fmt.Printf(v.GetF())
    }
}

func main() {
    extractField([]HasF{Foo{},Bar{},Baz{}})
}

https://play.golang.org/p/uw0T7TGVC0n has a complete version of this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM