gotypesassertionreflect

Is it possible to assert types dynamically in golang?


I have a method Deduplicate that returns deduplicated copy of passed in slice as an interface{}. Is there a way to cast returned by this method interface{} value to the same type as I passed in this method without writing it explicitly? For example, if I change myStruct.RelatedIDs type from []int to []uint it will prevent code from compiling.

https://play.golang.org/p/8OT4xYZuwEn

package main

import (
    "fmt"
    "reflect"
)

type myStruct struct {
    ID         int
    RelatedIDs []int
}

func main() {
    s := &myStruct{
        ID:         42,
        RelatedIDs: []int{1, 1, 2, 3},
    }
    v, _ := Deduplicate(s.RelatedIDs)
    s.RelatedIDs = v.([]int) // << can I assert type dynamically here?
    // s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // does not work
    fmt.Printf("%#v\n", s.RelatedIDs)
}

func Deduplicate(slice interface{}) (interface{}, error) {
    if reflect.TypeOf(slice).Kind() != reflect.Slice {
        return nil, fmt.Errorf("slice has wrong type: %T", slice)
    }

    s := reflect.ValueOf(slice)
    res := reflect.MakeSlice(s.Type(), 0, s.Len())

    seen := make(map[interface{}]struct{})
    for i := 0; i < s.Len(); i++ {
        v := s.Index(i)
        if _, ok := seen[v.Interface()]; ok {
            continue
        }
        seen[v.Interface()] = struct{}{}
        res = reflect.Append(res, v)
    }

    return res.Interface(), nil
}


Solution

  • Try this

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type myStruct struct {
        ID         int
        RelatedIDs []int
    }
    
    func main() {
        s := &myStruct{
            ID:         42,
            RelatedIDs: []int{1, 1, 2, 3},
        }
        err := Deduplicate(&s.RelatedIDs)
        fmt.Println(err)
        // s.RelatedIDs = v.([]int) // << can I assert type dynamically here?
        // s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // does not work
        fmt.Printf("%#v\n", s.RelatedIDs)
    }
    
    func Deduplicate(slice interface{}) error {
        rts := reflect.TypeOf(slice)
        rtse := rts.Elem()
        if rts.Kind() != reflect.Ptr && rtse.Kind() != reflect.Slice {
            return fmt.Errorf("slice has wrong type: %T", slice)
        }
    
        rvs := reflect.ValueOf(slice)
        rvse := rvs.Elem()
    
        seen := make(map[interface{}]struct{})
        var e int
        for i := 0; i < rvse.Len(); i++ {
            v := rvse.Index(i)
            if _, ok := seen[v.Interface()]; ok {
                continue
            }
            seen[v.Interface()] = struct{}{}
            rvse.Index(e).Set(v)
            e++
        }
    
        rvse.SetLen(e)
        rvs.Elem().Set(rvse)
    
        return nil
    }
    
    

    https://play.golang.org/p/hkEW4u1aGUi

    with future generics, it might look like this https://go2goplay.golang.org/p/jobI5wKR8fU