Хм ... Вот как я понимаю ваш второй пример кода в настоящее время, на случай, если я допустил ошибку (если вы заметили что-то не так в том, что я описываю, мой ответ, вероятно, неверно неверен, и вы должны игнорировать остальныеиз того, что я должен сказать).
- Выделите память для интерфейса
i
в main
. - Установите значение
i
целочисленного типа со значением 2. - Выделить память для интерфейса
i
в data
. - Скопировать значение основного
i
в данные i
;то есть установите значение нового интерфейса в целочисленный тип со значением 2. - Преобразуйте адрес новой переменной в указатель на массив длины 2 из
uintptr
(с обслуживанием unsafe.Pointer
как посредник, который заставляет компилятор принять это приведение). - Приведение второго элемента массива (значение которого является адресом части значения
i
в data
) обратно в unsafe.Pointer
и верните его.
Я сделал попытку сделать то же самое в несколько шагов, но, к сожалению, я столкнулся с теми же проблемами: программа распознает, что яимеет не ноль указатель, и он может разыменовать указатель для чтения, но использование того же указателя для записи приводит к ошибке времени выполнения.
Это шаг 6, на который go vet
жалуется, и я думаю, что это потому, чтосогласно пакет документов,
uintptr является целым числом, а не ссылкой.Преобразование указателя в uintptr создает целочисленное значение без семантики указателя.Даже если uintptr содержит адрес какого-либо объекта, сборщик мусора не будет обновлять значение этого uintptr, если объект перемещается, и этот uintptr не будет препятствовать восстановлению объекта.
Более конкретно,из того, что я могу сказать (хотя я признаю, что у меня возникают проблемы с поиском явного подтверждения без сканирования компилятора и источника времени выполнения), среда выполнения, кажется, не отслеживает часть значения типа interface{}
как дискретнуюуказатель с собственным счетчиком ссылок;Вы, конечно, можете растоптать оба слова interface{}
, записав другое значение интерфейса в целом, но это совсем не то, что вы хотели сделать (напишите вадрес памяти указателя, который находится внутри типа интерфейса, все без перемещения указателя).
Интересно то, что мы, похоже, можем приблизить это поведение , просто определив наше собственноеструктурированный тип, который не с учетом особой обработки компилятором (интерфейсы явно несколько особенные, с синтаксисом утверждения типа и всем).То есть мы можем использовать unsafe.Pointer
для сохранения ссылки, указывающей на конкретную точку в памяти, и независимо от того, к чему мы ее приводим, адрес памяти никогда не перемещается, даже если значение изменяется (и значение может быть переосмыслено при помощи приведенияэто к чему-то еще).Часть, которая удивляет меня немного, состоит в том, что, по крайней мере, в моем собственном примере, и, по крайней мере, в среде Playground, указанное значение не имеет фиксированного размера;мы можем установить адрес для записи в один раз , и повторные записи в этот адрес будут успешными даже при огромных (или крошечных) объемах данных.
Конечно, по крайней мере с этой реализацией мыпотерять кучу других полезных вещей, которые мы ассоциируем с типами интерфейса, особенно с непустыми типами интерфейса (то есть с методами).Таким образом, нет никакого способа использовать это, чтобы (например) сделать супер-хитрый «универсальный» тип.Кажется, что интерфейс является его собственным значением, и часть определения этого значения является адресом в памяти, но это не совсем то же самое, что указатель.