Synonymous struct field and method name for interface - go

I'm building a type structure that represents some devices on my network. There are lots of types of these devices, such as:
type Foo struct { ... }
type Bar struct { ... }
type Spam struct { ... }
but they all share a few common fields, one of which is IP. I'd like to define Device as an interface so I can group these together logically
type Device interface {
IP() string
...
}
type LogicalGrouping struct {
Devices []Device
...
}
but I'm running into an error with overlapping names.
func (f *Foo) IP() string { return f.IP } // same method name as field name
I can rename either the field or the method or both, but surely this must be a common use case -- grouping structs by the fields they have in common? Is there an idiomatic solution here?

The general rule of thumb is that interfaces define behaviours and fields define data.
The problem here is that you are using an interface to access data in fields. On it's own that's not necessarily a bad thing. But you might want to reconsider how to structure your code so that you don't get this collision.
The first question I would as is:
"Why do you need to export the field IP if the struct defines an IP() method that has access to it?"
You could make the struct field unexported i.e. ip not IP, and then in the place you are using struct.IP, use struct.IP() instead.
The second question is:
"If you have structs such as foo, bar, spam and they all share common fields, why not use composition to deduplicate the definition of those shared fields."
This would probably only work if you are really only after the data that's inside the struct, I would generally advocate for using interfaces in all cases.
If you have the following structs:
type Foo struct {
IP string
A string
B string
}
type Bar struct {
IP string
A string
B string
}
type Spam struct {
IP string
A string
B string
}
You could use a new struct to represent the fields they have in common, and anonymously compose that inside:
type Inner struct {
IP string
A string
B string
}
type Foo struct {
Inner
}
type Bar struct {
Inner
}
type Spam struct {
Inner
}
In the function call where you were using struct.IP(), accept an Inner and call it with struct.Inner. You will still have access to struct.IP even though IP is defined inside Inner.

You also may consider option to have ip field and GetIP & SetIP methods (or IP & SetIP accordingly to this doc) - it's clear, concise and simple.

Related

Defining a generics constraint on type fields [duplicate]

I am trying to define a generic function in Go that accepts values that have certain fields, for example, ID int. I have tried several approaches but none seems to work. Here is an example of what I have tried.
package main
import (
"fmt"
)
func Print[T IDer](s T) {
fmt.Print(s.ID)
}
func main() {
Print(Person{3, "Test"})
}
type IDer interface {
~struct{ ID int }
}
type Person struct {
ID int
Name string
}
type Store struct {
ID int
Domain string
}
And here is the playground link: https://gotipplay.golang.org/p/2I4RsUCwagF
In the example above, I want to guarantee every value passed to the Print function has a property ID int, which is also accessible in the function. Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
No, you have to define the method in an interface.
The generics implementation in Go 1.18 doesn't have support for structural types, even though the original type parameters proposal suggests it would. For accessing common fields in a union instead see also this explanation.
Although, I think it's worth it to point out a misconception that can easily arise from your example: the meaning of the approximation ~T (tilde-type) means "the set of types whose underlying type is T.
Now, when you write:
~struct{ ID int }
this means types whose underlying type is exactly struct{ ID int }. No matter what, this does not include structs that have the field ID int and something else. E.g. the underlying type of type Foo struct { ID int; Name string } is struct { ID int; Name string }, and not struct{ ID int }, so that wouldn't satisfy the constraint anyway.
The current time param implementation doesn't have syntax to specify partial struct types. I recall a proposal to add field terms in interface constraints (along with type terms and methods), something on the line:
type IDer interface {
ID int
}
which would enable what you are trying to do without breaking the meaning of the tilde ~. But this won't be included in Go 1.18.

Is there a way we can ensure passed values have certain fields using generics?

I am trying to define a generic function in Go that accepts values that have certain fields, for example, ID int. I have tried several approaches but none seems to work. Here is an example of what I have tried.
package main
import (
"fmt"
)
func Print[T IDer](s T) {
fmt.Print(s.ID)
}
func main() {
Print(Person{3, "Test"})
}
type IDer interface {
~struct{ ID int }
}
type Person struct {
ID int
Name string
}
type Store struct {
ID int
Domain string
}
And here is the playground link: https://gotipplay.golang.org/p/2I4RsUCwagF
In the example above, I want to guarantee every value passed to the Print function has a property ID int, which is also accessible in the function. Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
Is there any way I can achieve this is Go without defining a method in an interface (e.g., GetID() int)?
No, you have to define the method in an interface.
The generics implementation in Go 1.18 doesn't have support for structural types, even though the original type parameters proposal suggests it would. For accessing common fields in a union instead see also this explanation.
Although, I think it's worth it to point out a misconception that can easily arise from your example: the meaning of the approximation ~T (tilde-type) means "the set of types whose underlying type is T.
Now, when you write:
~struct{ ID int }
this means types whose underlying type is exactly struct{ ID int }. No matter what, this does not include structs that have the field ID int and something else. E.g. the underlying type of type Foo struct { ID int; Name string } is struct { ID int; Name string }, and not struct{ ID int }, so that wouldn't satisfy the constraint anyway.
The current time param implementation doesn't have syntax to specify partial struct types. I recall a proposal to add field terms in interface constraints (along with type terms and methods), something on the line:
type IDer interface {
ID int
}
which would enable what you are trying to do without breaking the meaning of the tilde ~. But this won't be included in Go 1.18.

Does whether or not a field is exported inside an anonymous struct make any difference?

Does exporting (or not exporting) a field in an anonymous struct in golang make any difference? In other words are the following two blocks of code different from each other in any way? Considering it's an anonymous struct, you can't use the struct outside the variable itself so I would assume the capital F doesn't make a difference when compiling (no compilation speed or run time memory differences).
Block 1:
var test = struct {
FieldOne string
FieldTwo string
} {
FieldOne: "some string",
FieldTwo: "some other string",
}
Block 2:
var test = struct {
fieldOne string
fieldTwo string
} {
fieldOne: "some string",
fieldTwo: "some other string",
}
Sure does, due to reflection. Try calling json.Marshal(test) after each of your declarations. In the first case, you will get {"FieldOne":"some string","FieldTwo":"some other string"}, but in the second case you will get {}. That's because the JSON marshaler only "sees" exported struct fields. More generally, even though the type of test doesn't have a name, doesn't mean that the type itself is inaccessible; it is accessible to anything with reflection.
Does exporting (or not exporting) a field in an anonymous struct in golang make any difference?
Of course, all the usual rules apply (you'll see the difference if you try to feed your struct to json.Marshal, for example).

How do I make sure that the fileds of the root struct get parsed first and then the fields of embedded struct gets parsed

I have the struct as follows:-
type Inner struct {
FooInner string `json:"fooInner"`
BarInner string `json:"barInner,omitempty"`
}
type Root struct {
Inner
Foo string `json:"foo"`
Bar string `json:"bar"`
}
I want the fields of "Root" struct to be parsed first and then the fields of the "Inner" struct. But here the fields of Inner struct is getting parsed first.
If you are asking about JSON marshaling (which is not parsing) and want fields marshaled in a certain order, a marshaler will typically marshal fields in their index order & recurse any embedded structs along the way. Struct field indices - as seen by the reflect package that json.Marhsal uses - are defined by their order of appearance in your code.
So put the fields you want first - and any embedded structs later:
type Root struct {
Foo string `json:"foo"`
Bar string `json:"bar"`
Inner // <- move this last
}
Playground Example
b, _ := json.Marshal(Root{})
{"foo":"","bar":"","fooInner":""}

Golang - add "inheritance" to structs

I would like to optimize my code, I have then the following situation:
I have a general struct where only one field gives the specification, let say a cache struct example:
# the main cache struct
type Cache struct {
name string
memory_cache map[string]interface{}
mutex *sync.Mutex
... ...
# common fields
}
# an element stored in the Cache.memory_cache map
type ElementA {
name string
count int64
}
# an element stored in the Cache.memory_cache map
type ElementB {
name string
tags []string
}
My current solution follow the previously definition and I create a cache for each element (it must be so: one cache per element):
var cache_for_element_A Cache{}
var cache_for_element_B Cache{}
But in this way I must always cast the memory_cache when reading, even if I know already what is the content (then no cast-case should be needed).
The following code do what I would like to have, but it defines twice a lot of redundants/commons fields, for this reason I would like to find another solution.
type CacheForA struct {
name string
memory_cache map[string]ElementA{}
mutex *sync.Mutex
... ...
# common fields
}
type CacheForB struct {
name string
memory_cache map[string]ElementB{}
mutex *sync.Mutex
... ...
# common fields
}
Then, is it possible to define a field in the struct (more precisely Cache.memory_cache) that can be further defined when the declaration occurs and without using interface?
Go doesn't have generics, so there's no simple way of doing this, like you would in Java for instance (class Cache<T>()....).
One thing you can do is wrap your cache with a small typed function that just fetches objects from a generic cache and converts the interface to the right type. This just saves you from writing the interface conversion over and over in your code.
type ElemACache struct {
Cache
}
func (c *ElemeACache)Get(key string) ElemeA {
return c.Cache.Get(key).(ElemeA) //of course add checks here
}
Struct embedding is the main thing you are looking for I think:
type Cache struct {
name string
mutex *sync.Mutex
}
type CacheA struct {
Cache
memory_cache map[string]ElementA{}
}
Then you make a type of interface, say "Cacher" that has a set of methods for the things you need to do with your various caches (cacheA, CacheB). Create those methods for CacheA, CacheB, and assertions are only needed only for the return type:
type Cacher interface {
GetItem(string) (interface{}, error)
}
If all your CacheFor types have that GetItem method, the interface will be fulfilled.
Still a fair amount of boilerplate, but this reduces the problem with redundancy in struct definitions. There are code generation tools if you don't want to type the boiler plate.

Resources