Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How exactly are interface variables implemented in Go?

In the below code snippet, I'd like to understand what exactly gets stored in iPerson when its contents are still uninitialized: just a value of 0-bytes? Or is it actually a pointer under the hood (and also initialized to 0-bytes of course)? In any case, what exactly happens at iPerson = person?

If iPerson = person makes a copy of person, what happens then when an object implementing IPerson but with a different size/memory footprint gets assigned to iPerson? I understand iPerson is a variable stored on the stack, so its size must be fixed. Does that mean that the heap is actually used under the hood, so iPerson is actually implemented as a pointer, but assignments still copy the object, as demonstrated by the above code? Here's the code:

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
}

(This question is the result of me having second thoughts on the precise factual correctness of my answer to why runtime error on io.WriterString?. So I decided to try to do some investigation to understand how is it exactly that interface variables and assignments to them work in Go.)

EDIT: after having received a few useful answers, I'm still puzzled with this:

iPerson = person
iPerson = &person

—both are legal. However, to me, this raises the question of why the compiler allows such weak typing to occur? One implication of the above is this:

iPerson = &person
var person2 = iPerson.(Person)  # panic: interface conversion: interface is *main.Person, not main.Person

whereas changing the first line fixes it:

iPerson = person
var person2 = iPerson.(Person)  # OK

...so it's not possible to determine statically whether iPerson holds a pointer or a value; and it seems that anything can assign either one to it at runtime with no errors raised. Why was such design decision made? What purpose does it serve? It definitely does not to fit within the "type safety" mindset.

like image 524
Erik Kaplun Avatar asked Oct 07 '13 10:10

Erik Kaplun


People also ask

How are interfaces implemented in Go?

To implement an interface in Go, you need to implement all the methods declared in the interface. Go Interfaces are implemented implicitly. Unlike Java, you don't need to explicitly specify using the implements keyword.

What is [] interface {} Golang?

interface{} means you can put value of any type, including your own custom type. All types in Go satisfy an empty interface ( interface{} is an empty interface). In your example, Msg field can have value of any type.

How are variables used in interfaces?

The value of the variable must be assigned in a static context in which no instance exists. The final modifier ensures the value assigned to the interface variable is a true constant that cannot be re-assigned. In other words, interfaces can declare only constants, not instance variables.

Can interface implement another interface Golang?

An interface extends another interface because the interface that extends another interface just adds its own abstract method(s) and doesn't provide method definitions for abstract methods of the other interface. An interface will never extend a class as interfaces don't provide method definitions.


2 Answers

You ask why both of

iPerson = person
iPerson = &person

are permitted. They are both permitted because both person and &person implement the IPerson interface. This is obvious, because IPerson is the empty interface--every value implements it.

It's true that you can't determine statically whether a value of IPerson holds a pointer or a value. So what? All you know about IPerson is that any object stored in a value of that type implements the list of methods in the interface. The assumption is that those methods are implemented correctly. Whether IPerson holds a value or a pointer is irrelevant to that.

For example, if the method is supposed to change something stored in the object, then that the method pretty much has to be a pointer method, in which case only a pointer value can be stored in the variable of interface type. But if none of the methods change something stored in the object, then they can all be value methods, and a non-pointer value can be stored in the variable.

like image 180
iant Avatar answered Dec 09 '22 18:12

iant


So, looks like internally, the interface variable does hold a pointer to what was assigned to it. An excerpt from http://research.swtch.com/interfaces:

The second word in the interface value points at the actual data, in this case a copy of b. The assignment var s Stringer = b makes a copy of b rather than point at b for the same reason that var c uint64 = b makes a copy: if b later changes, s and c are supposed to have the original value, not the new one.

My question

[...] what happens then when an object implementing IPerson but with a different size/memory footprint gets assigned to iPerson?

...also gets answered in the article:

Values stored in interfaces might be arbitrarily large, but only one word is dedicated to holding the value in the interface structure, so the assignment allocates a chunk of memory on the heap and records the pointer in the one-word slot.

So yeah, a copy on the heap is made and a pointer to it assigned to the interface variable. But, apparently, to the programmer, the interface variable has the semantics of a value variable not a pointer variable.

(Thanks to Volker for providing the link; but also, the first part of his answer is factually plain wrong... So I don't know if I should downvote for the misleading information or upvote for the non-misleading and rather useful link (which also happens to contradict his own answer).)

like image 39
Erik Kaplun Avatar answered Dec 09 '22 17:12

Erik Kaplun