I have several structures, in each of them there is a common property Id. I need to pass these structures to a function and perform operations on them.

func (diff *difference) searchDiff(arr1, arr2 []interface{}) { for i := range arr1 { for j := range arr2 { if arr1[i].Id == arr2[j].Id { // Как ΠΌΠ½Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΈΡ… свойства? .... } } } } 

The problem is that I cannot specify a specific structure as the type of parameters, because they are different. But they have each Id int. The first thing that came to mind was to use the interface as the type. But I cannot get the properties. I also thought about reflect (check types every time), but this is somehow non-kosher. For if there are 10 similar structures, the code will simply be duplicated. Maybe there is some more simple way?

I pass structures like this:

 type Object struct { Id int `xml:"id,attr"` Name string `xml:"name,attr"` Address string `xml:"address,attr"` Code string `xml:"code,attr"` } 

    2 answers 2

    There are no properties in GO. GO has fields and functions. The interface can contain only functions.

    If you want the structure to implement some kind of interface, then you must implement all the functions of the interface for the structure. Moreover, for the structure it is only necessary to create functions with the same signature as in the interface (it is not necessary to declare that such and such an interface is implemented).

    Example:

     package main import "fmt" type Identifier interface { GetValue() int32 } type Struct1 struct { id int32 } // Π­Ρ‚Π° функция Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ интСрфСйса Identifier func (s Struct1) GetValue() int32 { return s.id } type Struct2 struct { id int32 } // Π­Ρ‚Π° функция Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ интСрфСйса Identifier func (s Struct2) GetValue() int32 { return s.id } func main() { var ids = []Identifier{Struct1{id: 1}, Struct2{id: 2}} for _, id := range ids { value := id.GetValue() fmt.Println(value) } } 
    • But what should I do if I need to pass an array of structures to a function? ie for example searchDiff ([] Struct1 {Struct1 {1, "lol"}, Struct1 {1, "lol"}}) like this? I have the data in this form - Alex
    • @ Aleksey is a good question that can be issued separately. Open a new question and you will write it down. - mals

    There is no simple way, alas.

    This is due to the fact that [] interface {} (slice of interfaces)! = [] Object (slice of Objects)! = [] IDer (slice of interface IDer). These structures are displayed differently in memory - so the compiler refuses to do these implicit conversions automatically.

    You can, of course, every time obviously force it to do

     arr := make([]IDer, len(arr1)) for i := range arr1 { arr[i] = IDer(arr1[i]) } 

    but ugly and eats memory.

    Reading your question, I thought about what you want to do inside searchDiff. Already from the question itself suggests the conclusion that you want to determine the behavior , but not the behavior of the elements, namely the behavior of the slice elements. And idiomatic behavior is set via interfaces.

    Option 1

    We define types:

     type IDer interface { ID() int } // Object implements IDer func (obj *Object) ID() int { return obj.Id } 

    And Slice Interfaces IDer

     type IDerSlice []IDer 

    Then your searchDiff function is defined as

     func (diff *difference) searchDiff(arr1, arr2 IDerSlice) { for i := range arr1 { for j := range arr2 { if arr1[i].ID() < arr2[j].ID() { //... } } } } 

    Everything, you can work. https://play.golang.org/p/gnTnf23973

     package main import () type difference struct{} type Object struct { Id int `xml:"id,attr"` Name string `xml:"name,attr"` Address string `xml:"address,attr"` Code string `xml:"code,attr"` } type IDer interface { ID() int } // Object implements IDer func (obj *Object) ID() int { return obj.Id } type IDerSlice []IDer func (diff *difference) searchDiff(arr1, arr2 IDerSlice) { for i := range arr1 { for j := range arr2 { if arr1[i].ID() < arr2[j].ID() { //... } } } } func main() { // ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ Ρ‡Ρ‚ΠΎ создаСм ΠΌΡ‹ ΠΈΠΌΠ΅Π½Π½ΠΎ IDerSlice arr1 := IDerSlice{ &Object{ Id: 1, Name: "object1", Address: "earth", Code: "er", }, &Object{ Id: 2, Name: "object2", Address: "moon", Code: "mn", }, } arr2 := IDerSlice{ &Object{ Id: 10, Name: "object10", Address: "mars", Code: "mr", }, &Object{ Id: 11, Name: "object11", Address: "phobos", Code: "ph", }, &Object{ Id: 12, Name: "object12", Address: "deimos", Code: "de", }, } diff := &difference{} diff.searchDiff(arr1, arr2) } 

    Notice that we create slices of objects exactly as IDerSlice, those as a collection of elements with which we want to work uniformly through the interface. Therefore, all data from the underlying structures will have to be proxied by getters and setters to this interface, which is inconvenient, but this is exactly what you wanted - uniform work with different data structures through behavior.

    Option 2

    If you only have slices of objects of the type you need, you will have to do it as done in the sort package. https://play.golang.org/p/e5Dt7-Ocnx

     package main import ( //"fmt" ) type difference struct{} type Object struct { Id int `xml:"id,attr"` Name string `xml:"name,attr"` Address string `xml:"address,attr"` Code string `xml:"code,attr"` } type Object2 struct { Id int `xml:"id,attr"` Name2 string `xml:"name,attr"` Address2 string `xml:"address,attr"` Code2 string `xml:"code,attr"` } type searchDiffInterface interface { Len() int GetID(i int) int } type ObjectSlice []*Object type Object2Slice []*Object2 func (obj ObjectSlice) GetID(i int) int { return obj[i].Id } func (obj ObjectSlice) Len() int { return len(obj) } func (obj Object2Slice) GetID(i int) int { return obj[i].Id } func (obj Object2Slice) Len() int { return len(obj) } func (diff *difference) searchDiff(arr1, arr2 searchDiffInterface) { for i := 0; i < arr1.Len(); i++ { for j := 0; j < arr2.Len(); j++ { if arr1.GetID(i) == arr2.GetID(j) { //... } } } } func main() { arr1 := []*Object{ &Object{ Id: 1, Name: "object1", Address: "earth", Code: "er", }, &Object{ Id: 2, Name: "object2", Address: "moon", Code: "mn", }, } arr2 := []*Object2{ &Object2{ Id: 10, Name2: "object10", Address2: "mars", Code2: "mr", }, &Object2{ Id: 11, Name2: "object11", Address2: "phobos", Code2: "ph", }, &Object2{ Id: 12, Name2: "object12", Address2: "deimos", Code2: "de", }, } diff := &difference{} // ΠžΠ±Ρ€Π°Ρ‚ΠΈ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ явноС ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½ΠΈΠ΅ Ρ‚ΠΈΠΏΠΎΠ² Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ Ρ‚ΡƒΡ‚ diff.searchDiff(ObjectSlice(arr1), Object2Slice(arr2)) } 

    Whichever approach you choose, you have to explicitly type somewhere. Predefine searchDiffInterface with all the methods you need in searchDiff.

    If they are all the same for you, or are very similar, then you can use code generation using go generate or a trick similar to sort.Reverse.

    And in the end, the variant is in reflect (with automatic type conversion), but it is terribly slow.

     func (diff *difference) searchDiff(arr1, arr2 interface{}) { arr1slice := reflect.ValueOf(arr1) arr2slice := reflect.ValueOf(arr2) for i := 0; i < arr1slice.Len(); i++ { for j := 0; j < arr2slice.Len(); j++ { val_arr1i := arr1slice.Index(i) val_arr2j := arr2slice.Index(j) arr1id := val_arr1i.Elem().FieldByName("Id") arr2id := val_arr2j.Elem().FieldByName("Id") fmt.Println(val_arr1i.Elem(), val_arr2j.Elem()) if arr1id == arr2id { //... } } } }