Having structures with common fields...
type Definition struct {
Id string
...
}
type Requirement struct {
Id string
...
}
type Campaign struct {
Id string
...
}
...I have multiple functions like this:
func fillDefinitionIds(values *map[string]Definition) {
for key, value:=range *values { // Repeated code
value.Id=key // Repeated code
(*values)[key]=value // Repeated code
} // Repeated code
}
func fillRequirementIds(values *map[string]Requirement) {
for key, value:=range *values { // Repeated code
value.Id=key // Repeated code
(*values)[key]=value // Repeated code
} // Repeated code
}
func fillCampaignIds(values *map[string]Campaign) {
for key, value:=range *values { // Repeated code
value.Id=key // Repeated code
(*values)[key]=value // Repeated code
} // Repeated code
}
I would like to have a single function, generalizing the access with generics (or interfaces, whatever), kind of...
func fillIds[T Definition|Requirement|Campaign](values *map[string]T) {
for key, value:=range *values {
value.Id=key
(*values)[key]=value
}
}
Of course, this gives value.Id undefined (type T has no field or method Id)
. I've been able many times to overcome similar issues, but this time I can't find a solution for this.
How can be this set of functions be abstracted as a single one?
type Definition struct {
Id string
}
type Requirement struct {
Id string
}
type Campaign struct {
Id string
}
func (v Definition) WithId(id string) Definition { v.Id = id; return v }
func (v Requirement) WithId(id string) Requirement { v.Id = id; return v }
func (v Campaign) WithId(id string) Campaign { v.Id = id; return v }
type WithId[T any] interface {
WithId(id string) T
}
func fillIds[T WithId[T]](values map[string]T) {
for key, value := range values {
values[key] = value.WithId(key)
}
}
func main() {
m1 := map[string]Definition{"foo": {}, "bar": {}}
fillIds(m1)
fmt.Println(m1)
m2 := map[string]Campaign{"abc": {}, "def": {}}
fillIds(m2)
fmt.Println(m2)
}
https://go.dev/play/p/F3Qk0gcyKEa
An alternative to @blackgreen's answer if using a map of values is a requirement.
type Common struct {
Id string
}
func (v *Common) SetId(id string) { v.Id = id }
type Definition struct {
Common
}
type Requirement struct {
Common
}
type Campaign struct {
Common
}
type IdSetter[T any] interface {
*T
SetId(id string)
}
func fillIds[T any, U IdSetter[T]](values map[string]T) {
for key, value := range values {
U(&value).SetId(key)
values[key] = value
}
}
func main() {
m1 := map[string]Definition{"foo": {}, "bar": {}}
fillIds(m1)
fmt.Println(m1)
m2 := map[string]Campaign{"abc": {}, "def": {}}
fillIds(m2)
fmt.Println(m2)
}