Avatar ·

Как создать литерал *int64 в Go? Прямая инициализация указателя на число

Всем привет! Я только начал изучать Go и столкнулся с проблемой при работе с указателями. Мне нужно передать в функцию указатель на int64, но я не хочу создавать отдельную переменную. Например, у меня есть функция: ```go func SetValue(val *int64) { *val = 42 } ``` Я хочу вызвать её так: ```go SetValue(&123) // Ошибка: cannot take address of 123 ``` Но компилятор выдаёт ошибку, потому что нельзя взять адрес от литерала. Я попробовал: ```go SetValue(&int64(123)) // Тоже ошибка ``` И даже: ```go SetValue(&(int64(123))) // Не работает ``` Как правильно создать литерал *int64 на лету? Мне нужно передать указатель на число 123 без объявления отдельной переменной. В других языках, например в C, это делается просто: &(int){123}. Есть ли в Go эквивалент? Или только через вспомогательную переменную? Заранее спасибо за помощь! <<>>

Проблема: Почему нельзя взять адрес от литерала?

В Go вы не можете взять адрес от литерала (например, &123), потому что литералы не имеют адреса в памяти — они существуют только на этапе компиляции. Компилятор Go запрещает это для безопасности и предсказуемости.

Однако есть несколько простых и элегантных способов создать указатель на int64 без объявления отдельной переменной.

Способ 1: Вспомогательная функция (рекомендуется)

Самый популярный и читаемый способ — написать небольшую вспомогательную функцию:

func Int64Ptr(v int64) *int64 {
    return &v
}

// Использование:
SetValue(Int64Ptr(123))

Эта функция принимает значение int64 и возвращает указатель на него. Параметр v является копией, которая живёт в стеке, и мы можем безопасно взять её адрес. В Go 1.22+ компилятор оптимизирует такие вызовы, и аллокации в куче не будет.

Способ 2: Использование переменной в одной строке

Можно объявить переменную и сразу взять её адрес:

val := int64(123)
SetValue(&val)

Это два оператора, но выглядит компактно. Если вы используете такой паттерн часто, лучше вынести в функцию.

Способ 3: Анонимная переменная (Go 1.22+)

Начиная с Go 1.22, можно использовать короткое объявление с _ (пустой идентификатор) для создания временной переменной:

SetValue(func() *int64 { v := int64(123); return &v }())

Но это избыточно — проще использовать вспомогательную функцию.

Способ 4: Пакет github.com/samber/lo (для проектов с зависимостями)

Если вы используете популярный пакет lo (Lodash-style библиотека), там есть готовые функции:

import "github.com/samber/lo"

SetValue(lo.ToPtr(int64(123)))

lo.ToPtr — это та же самая вспомогательная функция, но уже реализованная для всех типов.

Что выбрать новичку?

  • Если это единичный случай — используйте Способ 2 (переменная + адрес).
  • Если таких вызовов много в коде — напишите свою функцию-хелпер (Способ 1).
  • Если проект уже использует lo — берите lo.ToPtr.

Важно: не пытайтесь городить сложные конструкции с new(int64) и присваиванием — это менее читаемо:

p := new(int64)
*p = 123
SetValue(p) // Работает, но много кода

В Go ценится простота и читаемость. Вспомогательная функция — это идиоматичный способ.

<<>>

Альтернативный подход: Использование new() и присваивания

Можно использовать встроенную функцию new(), которая выделяет память под тип и возвращает указатель. Но есть нюанс: new(int64) возвращает указатель на нулевое значение (0). Поэтому нужно отдельно присвоить нужное значение:

p := new(int64)  // *int64, указывает на 0
*p = 123         // Присваиваем нужное значение
SetValue(p)

Этот способ работает, но он многословен. Если вы используете его часто, лучше обернуть в функцию:

func Ptr[T any](v T) *T {
    return &v
}

// Дженерик-версия для любого типа:
SetValue(Ptr(int64(123)))

Дженерики (Go 1.18+) позволяют создать одну универсальную функцию для всех типов. Это самый гибкий вариант, если вы работаете с разными типами указателей.

Минус: new() в некоторых случаях может вызывать аллокацию в куче, в отличие от вспомогательной функции с локальной переменной, которая может быть оптимизирована компилятором.