Как мне сделать литерал *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",
    )
    // ...
}
Другие вопросы по тегам