Як зробити буквальний * int64 в Go?


103

У мене є тип struct із *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. Редагувати: ні, це не так. Вибачте за це.

Редагувати:

Очевидно, у моєму питанні було занадто багато неясностей. Я шукаю спосіб буквально сказати a *int64. Це може бути використано всередині конструктора, або для викладу літеральних значень структури, або навіть як аргументи для інших функцій. Але допоміжні функції або використання іншого типу - це не рішення, які я шукаю.


1
Вказівники на int невдалі, оскільки int займає стільки ж місця, що і вказівник, тож ви не економите місце. Він просто додає значення NULL, яке, як правило, просто складніше, ніж варто. У більшості випадків 0 було б добре. Якщо вам потрібне додаткове значення, також працює "IsValidSomeField" bool, і якщо ви дасте цьому bool кращу назву, він може сказати більше про те, навіщо вам це додаткове значення, що добре для читабельності.
voutasaurus

2
Ви можете використовувати вказівник на пакет , наприклад:var _ *int64 = pointer.Int64(64)
Xor

Відповіді:


212

Специфікація мови Go ( оператори адрес ) не дозволяє приймати адресу числової константи (а не нетипізованої та не введеної константи).

Операнд повинен бути адресним , тобто або змінною, непрямим вказівником або операцією індексування зрізу; або селектор поля адресного структурного операнда; або операція індексації масиву адресного масиву. Як виняток із вимоги адресації, x[у виразі &x] також може бути (можливо, у дужках) складений літерал .

Для аргументації, чому це не дозволено, див. Відповідне запитання: Знайдіть адресу константи в go . Подібне запитання (так само заборонено приймати його адресу): Як я можу зберегти посилання на результат операції в Go?

Ваші варіанти (спробуйте все на майданчику Go ):

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 виконує аналіз екранування та виділяє локальні змінні в купі (замість стека), якщо вони можуть уникнути функції. Детальніше див. У розділі Чи повернення фрагмента локального масиву у функції Go безпечно?

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] також може бути (можливо, у дужках) складений літерал .

Це означає, що прийняття адреси складеного літералу, наприклад, структурного літералу - це нормально. Якщо ми зробимо це, нам буде призначено значення struct і отриманий на нього покажчик. Але якщо це так, нам стане доступна ще одна вимога: "селектор поля адресного структурного операнда" . Отже, якщо літерал struct містить поле типу 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,
}

1
це функціонально відрізняється від останнього прикладу у питанні із заповнювачем, оскільки два різні instanceоб’єкти вказували б на два різні int64s. Але це, схоже, відповідає намірам ОП належним чином
Райан Хейнінг,

2
це однозначно відповідає на моє запитання. Але це мене дуже засмучує:, &[]int64{2}[0]я відчуваю, що, виходячи з опису констант на blog.golang.org/constants, це просто повинно працювати як &0.
ThisGuy

@RyanHaining А що було б, якби це працювало так, як буде призначена та сама адреса? Якби два різних instanceоб'єкти вказували на одне int64і те ж, а один модифікував загострений об'єкт, обидва змінювались. А що, якщо тепер ви використовуєте той самий літерал для створення 3-го instance? Тепер одна і та ж адреса вказуватиме на інше int64значення ... тому слід використовувати іншу адресу, але навіщо тоді використовувати ту саму у випадку перших 2?
icza

@icza ти зазвичай не хотів би, щоб вони вказували на один і той же об'єкт, я не кажу, що ти хотів би. Я просто вказую на різницю.
Райан Хейнінг,

4
Константи @Conslo обчислюються під час компіляції. Дійсне значення вказівника, дійсна адреса пам'яті існує лише під час виконання, тому це не те саме, що константи.
icza

6

Для вирішення проблеми використовуйте функцію, яка повертає адресу змінної int64.

У наведеному нижче коді ми використовуємо функцію, fяка приймає ціле число і повертає значення покажчика, яке містить адресу цілого числа. За допомогою цього методу ми можемо легко вирішити вищезазначену проблему.

type myStr struct {
    url *int64
}

func main() {
    f := func(s int64) *int64 {
        return &s
    }
    myStr{
        url: f(12345),
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.