Home > database >  How to check if slice interface elements have the same dynamic type?
How to check if slice interface elements have the same dynamic type?

Time:03-01

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
}
  • Related