Вы можете обратиться к статье « Как использовать интерфейсы в Go » (на основе « описания интерфейсов Рассом Коксом »):
Что такое интерфейс?
Интерфейс - это две вещи:
- это набор методов,
- но это тоже тип
interface{}
Типа, пустой интерфейс является интерфейсом , который не имеет методов.
Поскольку нет ключевого слова Implements, все типы реализуют по крайней мере нулевые методы, а удовлетворение интерфейса выполняется автоматически, все типы удовлетворяют пустому интерфейсу .
Это означает, что если вы напишете функцию, которая принимает interface{}
значение в качестве параметра, вы можете предоставить этой функции любое значение .
(Это то, что Msg
представляет в вашем вопросе: любое значение)
func DoSomething(v interface{}) {
// ...
}
Вот где это сбивает с толку:
что это за тип внутри DoSomething
функции ?v
Начинающих сусликов убеждают в том, что они «относятся к v
любому типу», но это неверно.
v
не любого типа; это interface{}
типа .
При передаче значения в DoSomething
функцию среда выполнения Go выполнит преобразование типа (при необходимости) и преобразует значение в interface{}
значение .
Все значения имеют ровно один тип во время выполнения и v
один статический тип interface{}
.
Значение интерфейса состоит из двух слов данных :
- одно слово используется для указания таблицы методов для базового типа значения,
- а другое слово используется для указания фактических данных, содержащихся в этом значении.
Приложение: Это статья Расса, довольно полная о структуре интерфейса:
type Stringer interface {
String() string
}
Значения интерфейса представлены в виде пары из двух слов, дающей указатель на информацию о типе, хранящемся в интерфейсе, и указатель на связанные данные.
Назначение b значению интерфейса типа Stringer устанавливает оба слова значения интерфейса.
Первое слово в значении интерфейса указывает на то, что я называю таблицей интерфейса или itable (произносится как i-table; в источниках времени выполнения имя реализации C - Itab).
Таблица начинается с некоторых метаданных о задействованных типах, а затем становится списком указателей на функции.
Обратите внимание, что itable соответствует типу интерфейса, а не динамическому типу .
В терминах нашего примера таблица для Stringer
хранения типа Binary перечисляет методы, используемые для удовлетворения Stringer, что справедливо String
: другие методы Binary ( Get
) не появляются в itable
.
Второе слово в значении интерфейса указывает на фактические данные , в данном случае на копию b
.
Присваивание var s Stringer = b
создает копию, b
а не указывает на нее b
по той же причине, что var c uint64 = b
и копия: если b
позже изменяется s
и c
предполагается, что оно будет иметь исходное значение, а не новое.
Значения, хранящиеся в интерфейсах, могут быть произвольно большими, но только одно слово предназначено для хранения значения в структуре интерфейса, поэтому назначение выделяет кусок памяти в куче и записывает указатель в слот для одного слова.
interface{}
более или менее эквивалентенvoid *
в C. Он может указывать на что угодно, и для его использования вам потребуется утверждение приведения / типа.