Reputation: 2459
Suppose I have a lot of different structs, but they all share a common field, such as "name". For example:
type foo struct {
name string
someOtherString string
// Other fields
}
type bar struct {
name string
someNumber int
// Other fields
}
Further on in the program, I repeatedly encounter the situation where I get pointers to these structs (so *foo, *bar, etc.) and need to perform operations depending on whether the pointer is nil or not, basically like so:
func workOnName(f *foo) interface{} {
if (f == nil) {
// Do lots of stuff
} else {
// Do lots of other stuff
}
// Do even more stuff
return something
}
This function, which only uses name
, is the same across all structs. If these were not pointers, I know I could write a common interface for each struct that returns the name and use that as the type. But with pointers, none of this has worked. Go either complains while compiling, or the nil check doesn't work and Go panics. I haven't found anything smarter than to copy/paste the exact same code for every struct that I have, so basically to implement all the functions:
func (f *foo) workOnName() interface{}
func (b *bar) workOnName() interface{}
func (h *ham) workOnName() interface{}
// And so on...
Is there a way to do this better, i.e. to only implement a simple function (or even better, no function at all) for all my structs and simply write the complicated stuff once, for all the structs?
Edit: Thank you to the answers so far, but simply using an interface of the type:
func (f foo) Name() string {
return f.name
}
for some interface that provides Name()
does not work, because the pointer is not recognized as nil. See this playground: https://play.golang.org/p/_d1qiZwnMe_f
Upvotes: 0
Views: 1041
Reputation: 16534
You can declare an interface which declares a function returning a name:
type WithName interface {
Name() string
}
In order to implement that interface, you types (foo
, bar
, etc) need to have that method - not just the field, the method.
func (f foo) Name() string {
return f.name
}
Then, workOnName
needs to receive a reference of that interface:
func workOnName(n WithName) interface{} {
if (n == nil || reflect.ValueOf(n).isNil()) {
// Do lots of stuff
} else {
// Do lots of other stuff
}
// Do even more stuff
return something
}
Keep in mind that the parameter n WithName
is always treated as a pointer, not an object value.
Upvotes: 2
Reputation: 3547
I think that's the case for reflect
.
Something like:
package main
import (
"fmt"
"reflect"
)
func SetFieldX(obj, value interface{}) {
v := reflect.ValueOf(obj).Elem()
if !v.IsValid() {
fmt.Println("obj is nil")
return
}
f := v.FieldByName("X")
if f.IsValid() && f.CanSet() {
f.Set(reflect.ValueOf(value))
}
}
func main() {
st1 := &struct{ X int }{10}
var stNil *struct{}
SetFieldX(st1, 555)
SetFieldX(stNil, "SSS")
fmt.Printf("%v\n%v\n", st1, stNil)
}
https://play.golang.org/p/OddSWT4JkSG
Note that IsValid
checks more than just obj==nil
but if you really want to distinguish cases of nil pointers
and non-struct objects - you are free to implement it.
Upvotes: 0