Как мне сделать литерал *int64 в Go?
У меня есть тип структуры с *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
, Это может быть использовано внутри конструктора или для определения литеральных значений структуры, или даже в качестве аргументов для других функций. Но вспомогательные функции или использование другого типа не являются решениями, которые я ищу.
4 ответа
Спецификация языка 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) с функцией помощника
Или, если вам нужно это много раз, вы можете создать вспомогательную функцию, которая выделяет и возвращает *int64
:
func create(x int64) *int64 {
return &x
}
И используя это:
instance3 := SomeType{
SomeField: create(3),
}
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,
}
Если вы не возражаете против использования сторонних библиотек, есть пакет lo , который использует дженерики (go 1.18+), который имеет
.ToPtr()
функция
ptr := lo.ToPtr("hello world")
// *string{"hello world"}
Использование функции, возвращающей адрес переменной int64, решит проблему.
В приведенном ниже коде мы используем функцию f, которая принимает целое число и возвращает значение указателя, которое содержит адрес целого числа. Используя этот метод, мы можем легко решить вышеуказанную проблему.
type myStr struct {
url *int64
}
func main() {
f := func(s int64) *int64 {
return &s
}
myStr{
url: f(12345),
}
}
Есть еще один элегантный способ добиться этого, который не создает много шаблонного кода и, на мой взгляд, не выглядит уродливо. В случае, если мне нужна структура с указателями на примитивы вместо значений, чтобы убедиться, что элементы структуры с нулевым значением не используются в проекте, я создам функцию с этими примитивами в качестве аргументов.
Вы можете определить функцию, которая создает вашу структуру, а затем передавать примитивы этой функции, а затем использовать указатели на аргументы функции.
type Config struct {
Code *uint8
Name *string
}
func NewConfig(code uint8, name string) *Config {
return &Config{
Code: &code,
Name: &name,
}
}
func UseConfig() {
config := NewConfig(1, "test")
// ...
}
// in case there are many values, modern IDE will highlight argument names for you, so you don't have to remember
func UseConfig2() {
config := NewConfig(
1,
"test",
)
// ...
}