Is it possible to assert types dynamically in golang?

huangapple go评论96阅读模式
英文:

Is it possible to assert types dynamically in golang?

问题

我有一个名为Deduplicate的方法,它将传入的切片作为interface{}返回去重后的副本。有没有办法将此方法返回的interface{}值转换为与我在此方法中传入的相同类型,而无需显式编写它?例如,如果我将myStruct.RelatedIDs的类型从[]int更改为[]uint,它将阻止代码编译。

以下是代码示例:

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) // << 在这里我能动态断言类型吗?
	// s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // 不起作用
	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
}

请注意,我只会返回翻译好的部分,不会回答关于翻译的问题。

英文:

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 (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type myStruct struct {
	ID         int
	RelatedIDs []int
}

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

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

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

	seen := make(map[interface{}]struct{})
	for i := 0; i &lt; 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
}

答案1

得分: 3

请稍等,我会为您翻译这段代码。

英文:

Try this

package main

import (
	&quot;fmt&quot;
	&quot;reflect&quot;
)

type myStruct struct {
	ID         int
	RelatedIDs []int
}

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

func Deduplicate(slice interface{}) error {
	rts := reflect.TypeOf(slice)
	rtse := rts.Elem()
	if rts.Kind() != reflect.Ptr &amp;&amp; rtse.Kind() != reflect.Slice {
		return fmt.Errorf(&quot;slice has wrong type: %T&quot;, slice)
	}

	rvs := reflect.ValueOf(slice)
	rvse := rvs.Elem()

	seen := make(map[interface{}]struct{})
	var e int
	for i := 0; i &lt; 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

答案2

得分: 2

以下是翻译好的内容:

为了完整起见,这里是一个通用版本的代码(Playground),它不需要使用反射。只能在2022年2月之后打开!然而,关于NaN常规注意事项仍然适用。

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
}

输出结果:

[]int{1, 2, 3}
英文:

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 (
&quot;fmt&quot;
)
func main() {
s := []int{1, 1, 2, 3}
res := Deduplicate(s)
fmt.Printf(&quot;%#v\n&quot;, 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}

huangapple
  • 本文由 发表于 2021年7月24日 03:00:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/68503982.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定