如何在不指定类型的情况下修改函数输入参数?
How to modify function input parameters without specifying type?
我正在尝试制作一个接受 2 个参数的函数,并将第二个参数的值设置为第一个参数,它可能看起来像(我不确定):
func set(a interface{}, b interface{}) {
// do something to make a = b
}
它是这样工作的:
a := 0
b := 10
set(&a, b)
// here a should be 10
s1 := ""
s2 := "what"
set(&s1, s2)
// here s1 should be "what"
它预计适用于所有基本类型,例如整数、浮点数、字符串、布尔值……
我怎样才能做到这一点?
这可以通过反射实现:
func set(a interface{}, b interface{}) {
reflect.ValueOf(a).Elem().Set(reflect.ValueOf(b))
}
请注意,此代码不检查 a
是否为指针及其元素类型是否与 b
中的值类型匹配。另请注意,使用反射速度较慢,并且会失去编译时类型安全性。上面的代码比将单个 int
值分配给 int
变量或将 string
值分配给 string
变量要慢得多。
正在测试:
a := 0
b := 10
set(&a, b)
fmt.Println(a)
s1 := ""
s2 := "what"
set(&s1, s2)
fmt.Println(s1)
输出(在 Go Playground 上尝试):
10
what
这是一个添加检查以避免运行时恐慌的版本:
func set(a interface{}, b interface{}) error {
v1 := reflect.ValueOf(a)
if v1.Kind() != reflect.Ptr {
return errors.New("a must be pointer")
}
if v1.IsZero() {
return errors.New("a must not be a nil pointer")
}
v1 = v1.Elem()
v2 := reflect.ValueOf(b)
if v1.Type() != v2.Type() {
return errors.New("a's element type must match b's type")
}
if !v1.CanSet() {
return errors.New("unsettable value in a")
}
v1.Set(v2)
return nil
}
正在测试:
a := 0
b := 10
err := set(a, b)
fmt.Printf("set(a, b): a=%v, err: %v\n", a, err)
err = set(&a, b)
fmt.Printf("set(&a, b): a=%v, err: %v\n", a, err)
err = set((*int)(nil), b)
fmt.Printf("set((*int)(nil), b): a=%v, err: %v\n", a, err)
s1 := ""
s2 := "what"
err = set(&s1, s2)
fmt.Printf("set(&s1, s2): s1=%v, err: %v\n", s1, err)
err = set(&s1, b)
fmt.Printf("set(&s1, s2): s1=%v, err: %v\n", s1, err)
输出(在 Go Playground 上尝试):
set(a, b): a=0, err: a must be pointer
set(&a, b): a=10, err: <nil>
set((*int)(nil), b): a=10, err: a must not be a nil pointer
set(&s1, s2): s1=what, err: <nil>
set(&s1, s2): s1=what, err: a's element type must match b's type
我正在尝试制作一个接受 2 个参数的函数,并将第二个参数的值设置为第一个参数,它可能看起来像(我不确定):
func set(a interface{}, b interface{}) {
// do something to make a = b
}
它是这样工作的:
a := 0
b := 10
set(&a, b)
// here a should be 10
s1 := ""
s2 := "what"
set(&s1, s2)
// here s1 should be "what"
它预计适用于所有基本类型,例如整数、浮点数、字符串、布尔值……
我怎样才能做到这一点?
这可以通过反射实现:
func set(a interface{}, b interface{}) {
reflect.ValueOf(a).Elem().Set(reflect.ValueOf(b))
}
请注意,此代码不检查 a
是否为指针及其元素类型是否与 b
中的值类型匹配。另请注意,使用反射速度较慢,并且会失去编译时类型安全性。上面的代码比将单个 int
值分配给 int
变量或将 string
值分配给 string
变量要慢得多。
正在测试:
a := 0
b := 10
set(&a, b)
fmt.Println(a)
s1 := ""
s2 := "what"
set(&s1, s2)
fmt.Println(s1)
输出(在 Go Playground 上尝试):
10
what
这是一个添加检查以避免运行时恐慌的版本:
func set(a interface{}, b interface{}) error {
v1 := reflect.ValueOf(a)
if v1.Kind() != reflect.Ptr {
return errors.New("a must be pointer")
}
if v1.IsZero() {
return errors.New("a must not be a nil pointer")
}
v1 = v1.Elem()
v2 := reflect.ValueOf(b)
if v1.Type() != v2.Type() {
return errors.New("a's element type must match b's type")
}
if !v1.CanSet() {
return errors.New("unsettable value in a")
}
v1.Set(v2)
return nil
}
正在测试:
a := 0
b := 10
err := set(a, b)
fmt.Printf("set(a, b): a=%v, err: %v\n", a, err)
err = set(&a, b)
fmt.Printf("set(&a, b): a=%v, err: %v\n", a, err)
err = set((*int)(nil), b)
fmt.Printf("set((*int)(nil), b): a=%v, err: %v\n", a, err)
s1 := ""
s2 := "what"
err = set(&s1, s2)
fmt.Printf("set(&s1, s2): s1=%v, err: %v\n", s1, err)
err = set(&s1, b)
fmt.Printf("set(&s1, s2): s1=%v, err: %v\n", s1, err)
输出(在 Go Playground 上尝试):
set(a, b): a=0, err: a must be pointer
set(&a, b): a=10, err: <nil>
set((*int)(nil), b): a=10, err: a must not be a nil pointer
set(&s1, s2): s1=what, err: <nil>
set(&s1, s2): s1=what, err: a's element type must match b's type