'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 1:[1]

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

Solution 2:[2]

For completeness, here is a generic version (Playground), which doesn't require reflection. Only open in February 2022! The usual caveats about NaN apply, though.

package main

import (
    "fmt"
)

func main() {
    s := []int{1, 1, 2, 3}
    res := Deduplicate(s)
    fmt.Printf("%#v\n", res)
}

func Deduplicate[T comparable](s []T) []T {
    seen := make(map[T]struct{})
    res := make([]T, 0, len(s))
    for _, elem := range s {
        if _, exists := seen[elem]; exists {
            continue
        }
        seen[elem] = struct{}{}
        res = append(res, elem)
    }
    return res
}

Output:

[]int{1, 2, 3}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2