У меня есть тип структуры с *int64полем.
type SomeType struct {
SomeField *int64
}
В какой-то момент в моем коде я хочу объявить литерал этого (скажем, когда я знаю, что указанное значение должно быть 0 или указывать на 0, вы понимаете, что я имею в виду)
instance:= SomeType{
SomeField: &0,
}
... кроме того, что это не работает
./main.go:xx: cannot use &0 (type *int) as type *int64 in field value
Так что я пробую это
instance:= SomeType{
SomeField: &int64(0),
}
... но это тоже не работает
./main.go:xx: cannot take the address of int64(0)
Как мне это сделать? Единственное решение, которое я могу придумать, это использовать переменную-заполнитель
var placeholder int64
placeholder = 0
instance:= SomeType{
SomeField: &placeholder,
}
Примечание: &0синтаксис работает нормально, если это *int вместо *int64. Редактировать: нет, это не так. Извини за это.
Редактировать:
Очевидно, в моем вопросе было слишком много двусмысленности. Я ищу способ буквально заявить. *int64Это может быть использовано внутри конструктора или для указания литеральных значений структуры или даже в качестве аргументов для других функций. Но вспомогательные функции или использование другого типа не являются решениями, которые я ищу.
Решение проблемы
Спецификация языка Go ( адресные операторы ) не позволяет брать адрес числовой константы (ни нетипизированной, ни типизированной константы).
Операнд должен быть адресуемым, то есть либо переменной, либо косвенным указателем, либо операцией индексации среза; или селектор поля операнда адресуемой структуры; или операция индексации массива адресуемого массива. В качестве исключения из требования адресации x[в выражении &x] также может быть составным литералом (возможно, заключенным в скобки).
Чтобы понять, почему это не разрешено, см. связанный вопрос: Найти адрес константы в go. Аналогичный вопрос (аналогично не разрешено брать его адрес): Как мне хранить ссылку на результат операции в Go?
Ваши варианты (попробуйте все на Go Playground ):
1) Сnew()
Вы можете просто использовать встроенную new()функцию для выделения нового нулевого значения int64и получения его адреса:
instance:= SomeType{
SomeField: new(int64),
}
Но обратите внимание, что это можно использовать только для выделения и получения указателя на нулевое значение любого типа.
2) С помощью вспомогательной переменной
Простейшим и рекомендуемым для ненулевых элементов является использование вспомогательной переменной, адрес которой можно взять:
helper:= int64(2)
instance2:= SomeType{
SomeField: &helper,
}
3) С вспомогательной функцией
Примечание. Вспомогательные функции для получения указателя на ненулевое значение доступны в моей github.com/icza/goxбиблиотеке в goxпакете, поэтому вам не нужно добавлять их во все свои проекты, где они вам нужны.
Или, если вам это нужно много раз, вы можете создать вспомогательную функцию, которая выделяет и возвращает *int64:
func create(x int64) *int64 {
return &x
}
И используя его:
instance3:= SomeType{
SomeField: create(3),
}
Обратите внимание, что мы на самом деле ничего не выделяли, это сделал компилятор Go, когда мы вернули адрес аргумента функции. Компилятор Go выполняет escape-анализ и размещает локальные переменные в куче (вместо стека), если они могут выйти из функции. Подробнее см. в разделе Безопасно ли возвращать фрагмент локального массива в функцию Go?
Обновление дженериков Go 1.18: дженерики добавлены в Go 1.18. Это означает, что мы можем создать единую общую create()функцию, которую можно использовать для всех типов. Надеюсь, он будет добавлен в стандартную библиотеку.
Вот как это может выглядеть:
func Ptr[T any](t T) *T {
return &t
}
Тестирование:
i:= Ptr(2)
log.Printf("%T %v", i, *i)
s:= Ptr("abc")
log.Printf("%T %v", s, *s)
x:= Ptr[any](nil)
log.Printf("%T %v", x, *x)
Что выведет (попробуйте на Go Playground ):
2009/11/10 23:00:00 *int 2
2009/11/10 23:00:00 *string abc
2009/11/10 23:00:00 *interface {} <nil>
4) С однострочной анонимной функцией
instance4:= SomeType{
SomeField: func() *int64 { i:= int64(4); return &i }(),
}
Или как (более короткая) альтернатива:
instance4:= SomeType{
SomeField: func(i int64) *int64 { return &i }(4),
}
5) С литералом среза, индексированием и получением адреса
Если вы хотите *SomeFieldбыть другим, чем 0, вам нужно что-то адресуемое.
Вы все еще можете сделать это, но это некрасиво:
instance5:= SomeType{
SomeField: &[]int64{5}[0],
}
fmt.Println(*instance2.SomeField) // Prints 5
Здесь происходит создание []int64среза с литералом, имеющим один элемент ( 5). И он индексируется (0-й элемент) и берется адрес 0-го элемента. В фоновом режиме массив [1]int64также будет выделен и использован в качестве резервного массива для среза. Так что здесь много шаблонов.
6) С литералом вспомогательной структуры
Давайте рассмотрим исключение из требований адресации:
В качестве исключения из требования адресации x[в выражении &x] также может быть составным литералом (возможно, заключенным в скобки).
Это означает, что прием адреса составного литерала, например, литерала структуры, допустим. Если мы это сделаем, у нас будет выделено значение структуры и получен указатель на него. Но если это так, нам станет доступно еще одно требование: «селектор поля адресуемого структурного операнда». Таким образом, если структурный литерал содержит поле типа int64, мы также можем взять адрес этого поля!
Давайте посмотрим этот вариант в действии. Мы будем использовать этот тип структуры оболочки:
type intwrapper struct {
x int64
}
И теперь мы можем сделать:
instance6:= SomeType{
SomeField: &(&intwrapper{6}).x,
}
Обратите внимание, что это
&(&intwrapper{6}).x
означает следующее:
& ( (&intwrapper{6}).x )
Но мы можем опустить «внешнюю» скобку, поскольку оператор адреса &применяется к результату выражения селектора.
Также обратите внимание, что в фоновом режиме будет происходить следующее (это также допустимый синтаксис):
&(*(&intwrapper{6})).x
7) С литералом вспомогательной анонимной структуры
Принцип тот же, что и в случае № 6, но мы также можем использовать литерал анонимной структуры, поэтому определение типа вспомогательной/оболочки не требуется:
instance7:= SomeType{
SomeField: &(&struct{ x int64 }{7}).x,
}
Комментариев нет:
Отправить комментарий