英文:
Confused with implicit pointer dereference when assigning a pointer to interface in Go
问题
我是你的中文翻译助手,以下是翻译好的内容:
我对Go语言还不熟悉,正在学习它的接口特性。
这是代码:
package main
import (
"fmt"
"reflect"
)
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
func (a *Integer) Add(b Integer) {
*a += b
}
type LessAdder interface {
Less(b Integer) bool
Add(b Integer)
}
var a Integer = 1
var b LessAdder = &a
func main() {
fmt.Println(reflect.TypeOf(b))
fmt.Println(b.Less(2))
b.Add(a)
fmt.Println(a)
}
它将输出以下内容:
*main.Integer
true
2
嗯,这个代码运行得很好。
关键是:var b LessAdder = &a 是如何工作的。指针自动解引用是在这里发生,还是在b调用成员方法时发生?
输出 *main.Integer 告诉我们,b 是指向 Integer 类型的指针,因此是第二种情况。
然后有个棘手的问题:
当我在代码中添加 fmt.Println(*b) 时,编译器报错:
demo/demo1
./demo1.go:31: invalid indirect of b (type LessAdder)
这让我感到困惑。由于 b 是指向 Integer 类型的指针,那么解引用它应该是有效的。但为什么不行呢?
英文:
I am new to Go, and I am studying its interface feature.
Here is the code:
package main
import (
"fmt"
"reflect"
)
type Integer int
func (a Integer) Less(b Integer) bool {
return a < b
}
func (a *Integer) Add(b Integer) {
*a += b
}
type LessAdder interface {
Less(b Integer) bool
Add(b Integer)
}
var a Integer = 1
var b LessAdder = &a
func main() {
fmt.Println(reflect.TypeOf(b))
fmt.Println(b.Less(2))
b.Add(a)
fmt.Println(a)
}
And it will output the the following:
*main.Integer
true
2
Well, this works pretty well.
The Point is:
How var b LessAdder = &a works. Does the pointer auto-dereference happens right here, or when b invokes member method?
The output *main.Integer tells us that b is a pointer to type Integer, hence it is the second case.
Then the tricky thing comes:
when I add fmt.Pringln(*b) to the code, the compiler comes with an error:
demo/demo1
./demo1.go:31: invalid indirect of b (type LessAdder)
And it confuses me. Since b is a pointer type to Integer, then dereferencing it should work. But why not?
答案1
得分: 11
你的最后一句话是:
> “由于b是指向Integer的指针类型,所以对它进行解引用应该是有效的。”
停下来。b不是指针类型的变量,因此你不能对它进行解引用操作。
它是一个接口类型的变量,它在概念上是一个值和类型的对(value,type),其中值是&a,类型是*Integer(参考博文反射定律的接口的表示一节)。
这是一个指针类型的变量声明,*Integer:
var ip *Integer
而这是一个接口类型的变量声明:
var intf LessAdder
当你执行以下操作时:
var b LessAdder = &a
发生的是自动/隐式地创建了一个接口值(类型为LessAdder),它将保存值&a(和类型*Integer)。这是一个有效的操作,因为&a的类型(即*Integer)实现了接口LessAdder:*Integer的方法集是接口LessAdder的超集(在这种情况下,它们是相等的,接口类型的方法集就是接口本身)。
现在当你调用b.Less(2)时,由于Less()方法有一个值接收器,指针将被解引用,并且会创建指向的值的副本,并将其作为Less()方法的值接收器使用/传递。
fmt.Println(reflect.TypeOf(b))不会撒谎,但它将打印出b的_动态_类型。b的动态类型确实是*Integer,但是_静态_类型是LessAdder,而静态类型决定了你可以对一个值做什么以及允许在其上使用哪些运算符或方法。
英文:
Your last sentence:
> "Since b is a pointer type to Integer, then dereferencing it should work."
Stop right there. b is not a variable of pointer type and therefore you can't dereference it.
It is a variable of interface type which is schematically a pair of a value and a type (value,type), holding &a as the value and *Integer as the type (blog article The Laws of Reflection, section The representation of an interface).
This is a declaration of a variable of pointer type, *Integer:
var ip *Integer
And this is one of an interface type:
var intf LessAdder
When you do this:
var b LessAdder = &a
What happens is that an interface value (of type LessAdder) is created automatically/implicitly which will hold the value &a (and the type *Integer). This is a valid operation because the type of &a (which is *Integer) implements the interface LessAdder: the method set of *Integer is a superset of the interface LessAdder (in this case they are equal, the method set of an interface type is its interface).
Now when you call b.Less(2), since Less() has a value receiver, the pointer will be dereferenced and a copy of the pointed value will be made and used/passed as the value receiver of the method Less().
fmt.Println(reflect.TypeOf(b)) doesn't lie, but it will print the dynamic type of b. The dynamic type of b is indeed *Integer, but the static type of b is LessAdder and the static type is what determines what you can do with a value and what operators or methods are allowed on it.
答案2
得分: 1
LessAdder被声明为一个接口,具有Less和Add方法。由于Add方法的接收者声明为*Integer,所以*Integer可以是LessAdder的实现;而Integer则不行。当你执行var b LessAdder = &a时,存储在接口b中的是指向a的指针。
在调用b.Less(2)时,自动进行了间接引用,因为*Integer上的方法和Integer上的方法都贡献到了*Integer的方法集中。
你不能使用*b,因为尽管b包含一个*Integer,但从静态上看,它的类型是LessAdder,而不是*Integer。暂且不论接口的表示方式,LessAdder不是一个指针类型,如果允许使用*b,那么它将没有可表达的类型。
你可以使用类型断言将b再次作为*Integer访问;b.(*Integer)是一个*Integer类型的表达式,而*b.(*Integer)则是一个Integer类型。如果b中的值实际上不是*Integer,这两者都会在运行时引发恐慌。
英文:
LessAdder is declared as an interface with the methods Less and Add. Since Add is declared with a receiver of *Integer, a *Integer can be a LessAdder; an Integer can't. When you do var b LessAdder = &a, it's the pointer to a that's stored in the interface b.
The automatic indirection occurs at the call to b.Less(2), because both methods on *Integer and methods on Integer contribute to the method set of *Integer.
You can't use *b because although b contains a *Integer, statically its type is LessAdder, not *Integer. Leaving aside the representation of interfaces, LessAdder isn't a pointer type, and *b, if it was allowed, would have no expressible type at all.
You can use a type assertion to access b as an Integer * again; b.(*Integer) is an expression of type *Integer, and *b.(*Integer) is an Integer. Both of these will run-time panic if the value in b is not a *Integer after all.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论