📜 ⬆️ ⬇️

A little bit about empty interfaces. Quick look inside

Hello!

Warning: the article will not bring anything new for the pros, but it will be useful for beginners.

If you're reading this, then I'm already dead. You are at least interested in Go. Therefore, you know about such a thing as interface {}. And what will happen if I say that any interface is just a structure? And also, what is pretty easy to implement your own interfaces? I ask under the cat.

Let's start with the simplest. Let's write a simple program on Go:
')
package main import "fmt" func ifacePrint(a interface{}) { fmt.Println(a) } func main() { ifacePrint("Habrahabr") } 

Everything is simple - main, which calls the ifacePrint method and ifacePrint itself, which explicitly takes one argument like interface {}.

If we run it, we get the output of the string “Habrahabr”. But we are not interested
so we will run our program under gdb supervision.

And so, go build main.go && gdb ./main. Put a breakpoint on our method:

 (gdb) b main.ifacePrint Breakpoint 1 at 0x401000: file /tmp/main.go, line 5. 

And run the program:

 (gdb) run Starting program: /tmp/main [New LWP 4892] [New LWP 4893] [New LWP 4894] [New LWP 4895] Breakpoint 1, main.ifacePrint (a=...) at /tmp/main.go:5 5 func ifacePrint(a interface{}) { 

gdb immediately stops execution, reaching the breakpoint. Let's see what type of variable we have:

 (gdb) whatis a type = interface {} 

Well, it is logical, interface {}, well, look, then, whether in its contents?

 (gdb) pa $1 = {_type = 0x4b8e00, data = 0xc8200761b0} 

An interesting twist, we had to send a string ... However, this is our string, or rather its interface, in the data field there is a pointer, to the area in memory that contains our string, and _type is a pointer to the internal (runtime) TypeDescriptor structure .

Looking ahead and to the side, I'll show you how I did the interfaces for my Go OS (gccgo):

 //   type EmptyInterface struct { __type_descriptor *TypeDescriptor //    -   __object uintptr //    } //  -   type TypeDescriptor struct { kind uint8 //  ,    ID align uint8 //   fieldAlign uint8 //  ( ) size uintptr // hash uint32 hashfn uint32 //TODO equalfn uint32 //TODO gc uintptr //TODO string *string //   ("string", "int") uncommonType *Uncommon //      ptrToThis *TypeDescriptor //      } //     type Uncommon struct { name *string //   pkgPath *string // ,    methods uintptr //TODO    ,        } 

In the "classic" Go - everything is approximately the same:

 (gdb) p *a._type $7 = {size = 16, ptrdata = 8, hash = 3774831796, _unused = 0 '\000', align = 8 '\b', fieldalign = 8 '\b', kind = 24 '\030', alg = 0x582860 <runtime.algarray+224>, gcdata = 0x52a3ac "\001\002\003\004\005\006\a\b\t\n\r\016\017\020\022\025\026\031\032\033\037,568<?AUr~\236\237\325\365\370\377", _string = 0x50e5e0, x = 0x4b8e40, ptrto = 0x4b1ba0} 

Here we see about all the same information about the data type "hidden" behind the empty interface.

 (gdb) p a._type._string $11 = (struct string *) 0x50e5e0 (gdb) p *a._type._string $12 = 0x4fdfb0 "string" (gdb) p *a._type.x $13 = {name = 0x50e5e0, pkgpath = 0x0, mhdr = {array = 0x4b8e68, len = 0, cap = 0}} (gdb) p *a._type.x.name $14 = 0x4fdfb0 "string" (gdb) p *a._type.ptrto $15 = {size = 8, ptrdata = 8, hash = 1511480045, _unused = 0 '\000', align = 8 '\b', fieldalign = 8 '\b', kind = 54 '6', alg = 0x5827d0 <runtime.algarray+80>, gcdata = 0x52a3ac "\001\002\003\004\005\006\a\b\t\n\r\016\017\020\022\025\026\031\032\033\037,568<?AUr~\236\237\325\365\370\377", _string = 0x5045d0, x = 0x0, ptrto = 0x0} (gdb) p *a._type.ptrto._string $16 = 0x4fc2b8 "*string" (gdb) p *a._type.alg $17 = {hash = {void (void *, uintptr, uintptr *)} 0x582860 <runtime.algarray+224>, equal = {void (void *, void *, bool *)} 0x582860 <runtime.algarray+224>} (gdb) p *a._type.alg.hash $18 = {void (void *, uintptr, uintptr *)} 0x582860 <runtime.algarray+224> (gdb) p *a._type.alg.equal $19 = {void (void *, void *, bool *)} 0x582868 <runtime.algarray+232> 

I think on this note, you can finish the story. I enlightened you a little and showed the direction where to dig, and dig or not - let everyone decide for himself;)

PS Anyone wishing to deal with runtime Go, I recommend picking your programs under gdb and reading the source code of gccgo and Go.

Source: https://habr.com/ru/post/315260/


All Articles