英文:
Set an interface to nil in Golang
问题
我正在尝试将接口的内部值设置为nil
,类似于以下代码:
typ := &TYP{InternalState: "filled"}
setNil(typ)
fmt.Printf("Expecting that %v to be nil", typ)
我需要知道如何实现setNil(typ interface{})
方法。
更多细节请参考play.golang.org上的代码。
英文:
I'm trying to set an internal value of an interface to nil
something like the following :
typ := &TYP{InternalState: "filled"}
setNil(typ)
fmt.Printf("Expecting that %v to be nil", typ)
And I need to know how to implement the setNil(typ interface{})
method.
For more details see this code in play.golang.org.
答案1
得分: 8
事实上,你没有一个接口值。你有一个指针值,指向一个具体类型的指针。这与接口值不同。
如果你想要改变任意类型变量的值,你必须传递一个指向它的指针。这也包括接口类型的变量,以及指针类型的变量。这是一个非常罕见的情况,其中一个指向interface{}
的指针是有意义的(*interface{}
),事实上是不可避免的。
但是,如果你的函数期望一个接口,而你传递一个非接口值,一个接口值将会被隐式地创建,并且你只能将这个隐式创建的值设置为nil
。
所以我们有两种不同的情况:
将interface{}
置为nil
的函数
func setNilIf(v *interface{}) {
*v = nil
}
使用它:
var i interface{} = "Bob"
fmt.Printf("Before: %v\n", i)
setNilIf(&i)
fmt.Printf("After: %v\n", i)
输出:
Before: Bob
After: <nil>
所以它起作用了。
将指针置为nil
的函数;使用unsafe
实际上这是你的情况。我们想要改变指针类型变量的值。为了接受任意类型的指针,我们可以使用unsafe.Pointer
。unsafe.Pointer
是一种语言支持,它是一种特殊的指针类型,可以从任意指针类型转换。
我们想要接受指针变量的地址(指针),这类似于**SomeType
。为了能够给指针变量赋予新值(nil
),我们必须对它进行解引用(*
操作符)。但是unsafe.Pointer
不能被解引用,所以我们首先必须将其转换为指向指针的指针,但是由于我们只想要赋值nil
(不管指向的值的类型如何,nil
都是相同的),所以这个“something”并不重要,我只会使用int
,然后将unsafe.Pointer
指针值转换为**int
。
func setNilPtr(p unsafe.Pointer) {
*(**int)(p) = nil
}
使用它:
typ := &TYP{InternalState: "filled"}
fmt.Printf("Before: %v\n", typ)
setNilPtr(unsafe.Pointer(&typ))
fmt.Printf("After: %v\n", typ)
输出:
Before: &{filled}
After: <nil>
所以这个也起作用了。还有另一种使用反射的方法:
将指针置为nil
的函数;使用reflect
你也可以只使用反射(reflect
包)将指针置为nil
。我们仍然必须传递指针类型变量的地址。注意,在这种情况下,参数的类型将简单地是interface{}
。它将包含一个动态类型,比如**SomeType
。由于指针的零值是nil
,我们可以使用reflect.Zero()
获得这样一个值,并使用Value.Set()
进行设置:
func setNilPtr2(i interface{}) {
v := reflect.ValueOf(i)
v.Elem().Set(reflect.Zero(v.Elem().Type()))
}
使用它:
typ2 := &TYP{InternalState: "filled"}
fmt.Printf("Before: %v\n", typ2)
setNilPtr2(&typ2)
fmt.Printf("After: %v\n", typ2)
输出:
Before: &{filled}
After: <nil>
所以这个也起作用了。在Go Playground上尝试一下这些。
但是说真的:如果你想要将某个东西置为nil
,直接赋值nil
给它就可以了。不要不必要地复杂化事情。
i = nil
typ = nil
英文:
The thing is you don't have an interface value. You have a pointer value, a pointer to a concrete type. That is not the same as an interface value.
If you want to change the value of a variable of any type, you have to pass a pointer to it. This also includes variables of interface type, and also variables of pointer type. This is one of those very rare cases when a pointer to interface{}
makes sense (*interface{}
), in fact it's inevitable.
But if your function expects an interface and you pass a non-interface value, an interface value will be created implicitly and you could only nil
this implicitly created value.
So we have 2 different / distinct cases:
Function to nil
an interface{}
func setNilIf(v *interface{}) {
*v = nil
}
Using it:
var i interface{} = "Bob"
fmt.Printf("Before: %v\n", i)
setNilIf(&i)
fmt.Printf("After: %v\n", i)
Output:
Before: Bob
After: <nil>
So it works.
Function to nil
a pointer; using unsafe
Actually this is your case. We want to change the value of a variable of pointer type. To accept a pointer to any type, we can use unsafe.Pointer
. unsafe.Pointer
is a language support, it's a special pointer type which can be converted from and to any pointer type.
We want to accept the address (pointer) of the pointer variable, which is something like **SomeType
. To actually be able to assign a new value (nil
) to the pointer variable, we have to dereference it (*
operator). But unsafe.Pointer
cannot be dereferenced, so first we have to convert it to a pointer to pointer to "something", but since we only want to assign nil
(which is the same to all pointer types regardless of the type of the pointed value), the "something" doesn't matter, I will just use int
, and so I will convert the unsafe.Pointer
pointer value to **int
.
func setNilPtr(p unsafe.Pointer) {
*(**int)(p) = nil
}
Using it:
typ := &TYP{InternalState: "filled"}
fmt.Printf("Before: %v\n", typ)
setNilPtr(unsafe.Pointer(&typ))
fmt.Printf("After: %v\n", typ)
Output:
Before: &{filled}
After: <nil>
So this one also works. There is still another way using reflection:
Function to nil
a pointer; using reflect
You can also nil
a pointer using reflection only (reflect
package). We still have to pass the address of the variable of pointer type. Note that in this case the type of the parameter will simply be interface{}
. And it will contain a dynamic type like **SomeType
. Since pointers have zero value nil
, we can obtain such a value with reflect.Zero()
which we will set using Value.Set()
:
func setNilPtr2(i interface{}) {
v := reflect.ValueOf(i)
v.Elem().Set(reflect.Zero(v.Elem().Type()))
}
Using it:
typ2 := &TYP{InternalState: "filled"}
fmt.Printf("Before: %v\n", typ2)
setNilPtr2(&typ2)
fmt.Printf("After: %v\n", typ2)
Output:
Before: &{filled}
After: <nil>
So this one also works. Try these on the Go Playground.
But seriously: if you want to nil
something, assign nil
to it. Do not complicate things unnecessarily.
i = nil
typ = nil
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论