Eri*_*lun 6 implementation pointers interface go
在下面的代码片段中,我想了解iPerson当其内容仍然未初始化时究竟存储了什么:只是一个0字节的值?或者它实际上是引擎盖下的指针(当然也初始化为0字节)?无论如何,究竟发生了iPerson = person什么?
如果iPerson = person制作副本person,当实现IPerson但具有不同大小/内存占用的对象被分配给时,会发生什么iPerson?我理解的iPerson是存储在堆栈中的变量,因此它的大小必须是固定的.这是否意味着堆实际上是在引擎盖下使用,所以iPerson实际上是作为指针实现的,但是赋值仍然复制对象,如上面的代码所示?这是代码:
type Person struct{ name string }
type IPerson interface{}
func main() {
var person Person = Person{"John"}
var iPerson IPerson
fmt.Println(person) // => John
fmt.Println(iPerson) // => <nil> ...so looks like a pointer
iPerson = person // ...this seems to be making a copy
fmt.Println(iPerson) // => John
person.name = "Mike"
fmt.Println(person) // => Mike
fmt.Println(iPerson) // => John ...so looks like it wasn't a pointer,
// or at least something was definitely copied
}
Run Code Online (Sandbox Code Playgroud)
(这个问题是我对iO.WriterString运行时错误原因的确切事实正确性的第二个想法的结果?所以我决定尝试做一些调查,以了解它是如何确切的界面变量和赋值他们在Go工作.)
编辑:收到一些有用的答案后,我仍然对此感到困惑:
iPerson = person
iPerson = &person
Run Code Online (Sandbox Code Playgroud)
- 这两个都是合法的.但是,对我来说,这就提出了为什么编译器允许这种弱类型发生的问题?上述的一个含义是:
iPerson = &person
var person2 = iPerson.(Person) # panic: interface conversion: interface is *main.Person, not main.Person
Run Code Online (Sandbox Code Playgroud)
而更改第一行修复它:
iPerson = person
var person2 = iPerson.(Person) # OK
Run Code Online (Sandbox Code Playgroud)
...所以不可能静态地确定是否iPerson持有指针或值; 并且看起来任何事情都可以在运行时为其分配任何一个而不会引发错误.为什么做出这样的设计决定?它有什么用途?它绝对不符合"类型安全"的心态.
因此,在内部看起来,接口变量确实保存了一个指向分配给它的内容的指针。摘自http://research.swtch.com/interfaces:
接口值中的第二个字指向实际数据,在本例中为
b. 赋值var s Stringer = b创建一个副本b而不是指向副本的b原因与var c uint64 = b创建副本相同:如果b以后发生变化,s并且c应该具有原始值,而不是新值。
我的问题
[...] 那么当一个实现 IPerson 但具有不同大小/内存占用的对象被分配给 iPerson 时会发生什么?
...也在文章中得到了回答:
存储在接口中的值可能是任意大的,但只有一个字专门用于保存接口结构中的值,因此赋值会在堆上分配一块内存并将指针记录在一个字槽中。
所以是的,在堆上创建了一个副本,并将指向它的指针分配给接口变量。但是,显然,对于程序员来说,接口变量具有值变量而不是指针变量的语义。
(感谢 Volker 提供链接;而且,他的回答的第一部分实际上是完全错误的......所以我不知道我是否应该对误导性信息投反对票,还是应该对非误导性且相当有用的链接投赞成票(这也恰好与他自己的答案相矛盾)。)
当您执行以下行时:
iPerson = person
Run Code Online (Sandbox Code Playgroud)
您正在Person接口变量中存储一个值。由于对结构的赋值执行复制,是的,您的代码正在复制。要从接口内部检索结构,您需要获取另一个副本:
p := iPerson.(Person)
Run Code Online (Sandbox Code Playgroud)
所以你很少想用可变类型来做这件事。如果您想在接口变量中存储指向结构的指针,则需要显式执行此操作:
iPerson = &person
Run Code Online (Sandbox Code Playgroud)
就引擎盖下发生的事情而言,接口变量分配堆空间来存储大于指针的值是正确的,但这通常对用户不可见。
你问为什么两个
iPerson = person
iPerson = &person
Run Code Online (Sandbox Code Playgroud)
是允许的.它们都被允许,因为人和人都实现了IPerson接口.这很明显,因为IPerson是空接口 - 每个值都实现它.
确实,您无法静态确定IPerson的值是保存指针还是值.所以呢?您对IPerson的所有了解都是存储在该类型值中的任何对象都实现了接口中的方法列表.假设这些方法是正确实现的.IPerson是持有值还是指针与此无关.
例如,如果该方法应该更改存储在对象中的内容,那么该方法几乎必须是指针方法,在这种情况下,只有指针值可以存储在接口类型的变量中.但是如果没有任何方法改变存储在对象中的东西,那么它们都可以是值方法,并且非指针值可以存储在变量中.