This is a creation in Article, where the information may have evolved or changed.
Interface Values
Languages with methods typically fall to one of both camps:prepare tables for all the method calls statically (as in C + + and Java), or do a method of lookup at each call (as in Smalltalk and its many imitators, JavaScript and Python included) an D Add fancy caching to make the call efficient. Go sits halfway between the Two:it have method tables but computes them at run time. I don ' t know whether Go is the first language to use this technique, but it's certainly not a common one. (I ' d be interested to hear about earlier examples; Leave a comment below.)
As a warmup, a value of type Binary is just a 64-bit an integer made up of and 32-bit words (like in the Last post, we'll be in the Ume a 32-bit machine; This time memory grows down instead by the right):
Interface values is represented as a Two-word pair giving a pointer to information on the type stored in the Interface And a pointer to the associated data. Assigning B to an interface value of type Stringer sets both words of the interface value.
(The pointers contained in the interface value is gray to emphasize that they is implicit, not directly exposed to Go PR Ograms.)
The first word in the interface value points at what I call an interface table or itable (pronounced i-table; Ntime sources, the C implementation name is Itab). The itable begins with some metadata on the types involved and then becomes a list of function pointers. Note the itable corresponds to the interface type, not the dynamic type. In terms of We example, the itable for Stringer holding type Binary lists the methods used to satisfy Stringer, which are Just String:binary ' s other methods (Get) make no appearance in the itable.
The second word in the interface value is points at the actual data, and 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 is supposed to has the original value, not the new one. Values stored in interfaces might was arbitrarily large, but only one word was dedicated to holding the value in the Interfa CE structure, so the assignment allocates a chunk of memory on the heap and records the pointer in the One-word slot. (There's obvious optimization when the value does fit in the slot; we'll get to that later.)
To check whether an interface value holds a particular type, as in the type switch above, the Go compiler generates cod e equivalent to the C expression s.tab->type
to obtain the type pointer and check it against the desired t Ype. If the types match, the value can be copied by by Dereferencing S.data.
To call s.string ()
, the Go compiler generates code, does the equivalent of the C expression s.ta B->fun[0] (s.data)
: It calls the appropriate function pointer from the itable, passing the interface value ' s data Word as the function ' s first (in this example, only) argument. You can see this code if you run 8g-s x.go
(details at the bottom of this post). Note that the function in the itable are being passed the 32-bit pointer from the second word of the interface value, not t He 64-bit value it points at. In general, the interface call site doesn ' t know the meaning of this word nor how much data it points at. Instead, the interface code arranges, the function pointers in the itable expect the 32-bit representation stored in t He interface values. Thus The function pointer in this example is (*binary). String
not binary.string.
The example we ' re considering is a interface with just one method. An interface with more methods would has more entries in the fun list at the bottom of the itable.
Computing the Itable
Now we know what is the itables look like, but where does they come from? Go ' s dynamic type conversions mean that it's isn ' t reasonable for the compiler or linker to precompute all possible itables: There is too many (interface type, concrete type) pairs, and most won ' t is needed. Instead, the compiler generates a type description structure for each concrete type like Binary or int or func (map[i nt]string)
. Among other metadata, the type description structure contains a list of the methods implemented by that type. Similarly, the compiler generates a (different) type description structure for each interface type like Stringer; It too contains a method list. The interface runtime computes the itable by looking for each method listed in the interface type ' s method table in the CO Ncrete type ' s method table. The runtime caches the itable after generating it, so that this correspondence need is only being computed once.
In We simple example, the method table for Stringer have one method, while the table for Binary have both methods. In general there might is NI methods for the interface type and NT methods for the concrete type. The obvious search to find the mapping from interface methods to concrete methods would take O (NIXNT) time, but we can D o better. By sorting the method tables and walking them simultaneously, we can build the mapping in O (NI + nt) time instead.