Всем привет!
Warning: статья не принесет ничего нового для профи, но будет полезна новичкам.
Если вы это читаете, значитя уже мертв вы, как минимум, интересуетесь языком Go. Следовательно, знаете, о такой вещи, как interface{}. А что будет, если я скажу, что любой интерфейс это просто структура? А также, что довольно легко реализовать самому свои интерфейсы? Прошу под кат.
Давайте начнем с предельно простого. Напишем простенькую программу на Go:
Всё просто — main, который вызывает метод ifacePrint и собственно ifacePrint, который явно принимает один аргумент типа interface{}.
Если мы её запустим, то получим вывод строки «Habrahabr». Но нам это не интересно,
поэтому мы запустим нашу программку под надзором gdb.
И так, go build main.go && gdb ./main. Ставим breakpoint на наш метод:
И запускаем программу:
gdb тут же останавливает выполнение, дойдя до бряка. Давайте посмотрим, какого типа у нас переменная а:
Что ж, логично, interface{}, ну, заглянем, тогда, что ли в её содержимое?
Интересный поворот, мы же должны были отправить строку… Однако это и есть наша строка, точнее её interface, в поле data лежит указатель, на область в памяти, в которой и содержится наша строка, а _type — указатель на внутренюю (рантайма) структуру TypeDescriptor.
Забегая вперёд и в сторону покажу, как я делал интерфейсы для своей ОС на Go (gccgo):
В «классическом» Go — всё примерно также:
Здесь мы видим примерно всю ту же информацию о типа данных «скрывающемся» за пустым интерфейсом.
Думаю, на сей ноте можно закончить повествование. Я вас немного просветил и показал направление куда копать, а копать или нет — пусть каждый решит сам ;)
P.S. Всем желающим разобраться с runtime Go рекомендую ковырять свои програмки под gdb и читать исходники gccgo и Go.
Warning: статья не принесет ничего нового для профи, но будет полезна новичкам.
Если вы это читаете, значит
Давайте начнем с предельно простого. Напишем простенькую программу на Go:
package main
import "fmt"
func ifacePrint(a interface{}) {
fmt.Println(a)
}
func main() {
ifacePrint("Habrahabr")
}
Всё просто — main, который вызывает метод ifacePrint и собственно ifacePrint, который явно принимает один аргумент типа interface{}.
Если мы её запустим, то получим вывод строки «Habrahabr». Но нам это не интересно,
поэтому мы запустим нашу программку под надзором gdb.
И так, go build main.go && gdb ./main. Ставим breakpoint на наш метод:
(gdb) b main.ifacePrint
Breakpoint 1 at 0x401000: file /tmp/main.go, line 5.
И запускаем программу:
(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 тут же останавливает выполнение, дойдя до бряка. Давайте посмотрим, какого типа у нас переменная а:
(gdb) whatis a
type = interface {}
Что ж, логично, interface{}, ну, заглянем, тогда, что ли в её содержимое?
(gdb) p a
$1 = {_type = 0x4b8e00, data = 0xc8200761b0}
Интересный поворот, мы же должны были отправить строку… Однако это и есть наша строка, точнее её interface, в поле data лежит указатель, на область в памяти, в которой и содержится наша строка, а _type — указатель на внутренюю (рантайма) структуру TypeDescriptor.
Забегая вперёд и в сторону покажу, как я делал интерфейсы для своей ОС на Go (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 Указатель на массив методов, в которых ресивером является данный тип данных
}
В «классическом» Go — всё примерно также:
(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}
Здесь мы видим примерно всю ту же информацию о типа данных «скрывающемся» за пустым интерфейсом.
(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>
Думаю, на сей ноте можно закончить повествование. Я вас немного просветил и показал направление куда копать, а копать или нет — пусть каждый решит сам ;)
P.S. Всем желающим разобраться с runtime Go рекомендую ковырять свои програмки под gdb и читать исходники gccgo и Go.
Поделиться с друзьями
kirill_danshin
спасибо за статью!
а о GC будет? :)
t0pep0
Когда-нибудь в будущем =)
kirill_danshin
оно уже настало ;)
t0pep0
Всё относительно, друг Горацио…