I have the following structs and they follow this structure:
A
is the interface, and B
, C
, D
are all types with interface A
.
I have a slice of variables args
all with type with interface A
, each of which can be B
, C
, D
type specifically.
I'd like to write a for loop to judge if all variables in the slice fall into the same dynamic type.
I wrote the following code:
var existingTyp A
for i, d := range args {
switch typ := d.(type) {
case *B, *C, *D:
if existingTyp == nil {
existingTyp = typ
} else {
if typ != existingTyp {
panic("error!")
}
}
}
How to revise the code to achieve what I want?
CodePudding user response:
You can't use the equality operator ==
on the interface values. Even if the dynamic types are the same, the comparison may return false if they have fields with different values. Or it panics if B
, C
and D
aren't comparable to begin with.
Instead you can use reflection and use ==
on reflect.Type
. This solution doesn't require you to update the code if you add more types that implement A
.
func dynamicTypesEq(args []A) bool {
var a reflect.Type
for _, d := range args {
t := reflect.TypeOf(d)
if a == nil {
a = t
continue
}
if a != t {
return false
}
}
return true
}
Calling the function with some example slices:
func main() {
a := []A{&B{}, &B{}, &B{}}
fmt.Println(dynamicTypesEq(a)) // true
b := []A{&C{}, &B{}, &B{}}
fmt.Println(dynamicTypesEq(b)) // false
c := []A{&D{}, &D{}, &B{}}
fmt.Println(dynamicTypesEq(c)) // false
}
Note that this function reports false in case the input has *B
and B
. Arguably, a pointer type is not the same as the base type.
Playground: https://go.dev/play/p/QOCvSyxGPRU
CodePudding user response:
Here's how to do it using the reflect package. If type of some element is different from the type of the first element, then the slice contains mixed value types.
func check(args []interface{}) bool {
if len(args) == 0 {
return true
}
t := reflect.TypeOf(args[0])
for _, v := range args[1:] {
if reflect.TypeOf(v) != t {
return false
}
}
return true
}
Here's how to do it without reflect. Keep a state variable that records the last type seen. If the current type is not the last type, then the slice contains mixed value types.
func check(args []interface{}) bool {
const (
initState = iota
typeB
typeC
typeD
)
state := initState
for _, d := range args {
switch d.(type) {
case *B:
if state != initState && state != typeB {
return false
}
state = typeB
case *C:
if state != initState && state != typeC {
return false
}
state = typeC
case *D:
if state != initState && state != typeD {
return false
}
state = typeD
default:
panic("unsupported type")
}
}
return true
}