Como faço um literal * int64 no Go?

103

Eu tenho um tipo de estrutura com um *int64campo.

type SomeType struct {
    SomeField *int64
}

Em algum ponto do meu código, quero declarar um literal disso (digamos, quando eu sei que o valor mencionado deve ser 0, ou apontando para 0, você sabe o que quero dizer)

instance := SomeType{
    SomeField: &0,
}

... exceto que isso não funciona

./main.go:xx: cannot use &0 (type *int) as type *int64 in field value

Então eu tento isso

instance := SomeType{
    SomeField: &int64(0),
}

... mas isso também não funciona

./main.go:xx: cannot take the address of int64(0)

Como eu faço isso? A única solução que posso encontrar é usar uma variável de espaço reservado

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Nota: a &0sintaxe funciona bem quando é um * int em vez de um *int64. Edit: não, não. Desculpe por isto.

Editar:

Aparentemente, minha pergunta era ambígua demais. Estou procurando uma maneira de afirmar literalmente a *int64. Isso pode ser usado dentro de um construtor ou para declarar valores de estrutura literais ou mesmo como argumentos para outras funções. Mas funções auxiliares ou usar um tipo diferente não são soluções que procuro.

Esse cara
fonte
1
Os ponteiros para int são lamentáveis, pois o int ocupa o mesmo espaço que um ponteiro, portanto, você não está economizando espaço. Ele apenas adiciona um valor NULL que geralmente é apenas mais complexidade do que vale. Na maioria dos casos, um 0 seria suficiente. Se você precisar de um valor extra, um bool "IsValidSomeField" também funciona e se você der a esse bool um nome melhor, ele poderá dizer mais sobre por que você precisa desse valor extra, o que é bom para legibilidade.
voutasaurus
2
Você pode usar o ponteiro do pacote , por exemplo:var _ *int64 = pointer.Int64(64)
Xou

Respostas:

212

The Go Specification Language ( operadores de endereço faz) não permite tomar o endereço de uma constante numérica (não de um sem tipo nem de um digitado constante).

O operando deve ser endereçável , ou seja, uma variável, indireção de ponteiro ou operação de indexação de fatia; ou um seletor de campo de um operando de estrutura endereçável; ou uma operação de indexação de matriz de uma matriz endereçável. Como uma exceção ao requisito de endereçabilidade, x[na expressão de &x] também pode ser um literal composto (possivelmente entre parênteses) .

Para saber por que isso não é permitido, consulte a pergunta relacionada: Encontre o endereço da constante em go . Uma pergunta semelhante (da mesma forma não tem permissão para levar seu endereço): Como posso armazenar a referência ao resultado de uma operação em Go?

Suas opções (experimente tudo no Go Playground ):

1) Com new()

Você pode simplesmente usar a new()função integrada para alocar um novo valor zero int64e obter seu endereço:

instance := SomeType{
    SomeField: new(int64),
}

Mas observe que isso só pode ser usado para alocar e obter um ponteiro para o valor zero de qualquer tipo.

2) Com variável auxiliar

O mais simples e recomendado para elementos diferentes de zero é usar uma variável auxiliar cujo endereço pode ser obtido:

helper := int64(2)
instance2 := SomeType{
    SomeField: &helper,
}

3) Com função auxiliar

Nota: As funções auxiliares para adquirir um ponteiro para um valor diferente de zero estão disponíveis em minha github.com/icza/goxbiblioteca, no goxpacote, portanto, você não precisa adicioná-las a todos os seus projetos onde precisar.

Ou, se você precisar disso muitas vezes, pode criar uma função auxiliar que aloca e retorna um *int64:

func create(x int64) *int64 {
    return &x
}

E usando:

instance3 := SomeType{
    SomeField: create(3),
}

Observe que, na verdade, não alocamos nada, o compilador Go fez isso quando retornamos o endereço do argumento da função. O compilador Go executa a análise de escape e aloca variáveis ​​locais no heap (em vez da pilha) se elas podem escapar da função. Para obter detalhes, consulte O retorno de uma fatia de um array local em uma função Go é seguro?

4) Com uma função anônima de uma linha

instance4 := SomeType{
    SomeField: func() *int64 { i := int64(4); return &i }(),
}

Ou como uma alternativa (mais curta):

instance4 := SomeType{
    SomeField: func(i int64) *int64 { return &i }(4),
}

5) Com literal de fatia, indexação e obtenção de endereço

Se você quiser *SomeFieldser diferente de 0, então precisa de algo endereçável.

Você ainda pode fazer isso, mas é feio:

instance5 := SomeType{
    SomeField: &[]int64{5}[0],
}
fmt.Println(*instance2.SomeField) // Prints 5

O que acontece aqui é que uma []int64fatia é criada com um literal, tendo um elemento ( 5). E é indexado (0º elemento) e o endereço do 0º elemento é obtido. No fundo, uma matriz de [1]int64também será alocada e usada como matriz de apoio para a fatia. Portanto, há muito clichê aqui.

6) Com um literal de estrutura auxiliar

Vamos examinar a exceção aos requisitos de endereçamento:

Como uma exceção ao requisito de endereçamento, x[na expressão de &x] também pode ser um literal composto (possivelmente entre parênteses) .

Isso significa que pegar o endereço de um literal composto, por exemplo, um literal de estrutura está ok. Se fizermos isso, teremos o valor da estrutura alocado e um ponteiro obtido para ele. Mas se for assim, outro requisito se tornará disponível para nós: "seletor de campo de um operando de estrutura endereçável" . Portanto, se o literal de estrutura contém um campo do tipo int64, também podemos obter o endereço desse campo!

Vamos ver essa opção em ação. Usaremos este tipo de estrutura de wrapper:

type intwrapper struct {
    x int64
}

E agora podemos fazer:

instance6 := SomeType{
    SomeField: &(&intwrapper{6}).x,
}

Observe que este

&(&intwrapper{6}).x

significa o seguinte:

& ( (&intwrapper{6}).x )

Mas podemos omitir o parêntese "externo", pois o operador de endereço &é aplicado ao resultado da expressão do seletor .

Observe também que em segundo plano acontecerá o seguinte (esta também é uma sintaxe válida):

&(*(&intwrapper{6})).x

7) Com literal de estrutura anônima auxiliar

O princípio é o mesmo do caso nº 6, mas também podemos usar um literal de estrutura anônima, portanto, nenhuma definição de tipo de estrutura auxiliar / wrapper é necessária:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}
icza
fonte
1
isso é funcionalmente diferente do último exemplo na pergunta com o espaço reservado, uma vez que dois instanceobjetos diferentes apontariam para dois int64s diferentes . Mas parece atender às intenções dos OPs de forma adequada
Ryan Haining
2
isso definitivamente responde à minha pergunta. Mas isso me deixa bastante chateado :,&[]int64{2}[0] acho que, com base na descrição das constantes em blog.golang.org/constants, isso deve funcionar como &0.
ThisGuy
@RyanHaining E o que aconteceria se funcionasse como o mesmo endereço seria atribuído? Se dois instanceobjetos diferentes int64apontassem para o mesmo e um modificasse o objeto apontado, ambos mudariam. E se agora você usar o mesmo literal para criar um terceiro instance? O mesmo endereço agora apontaria para um int64valor diferente ... então um endereço diferente deve ser usado, mas então por que usar o mesmo no caso dos primeiros 2?
icza
@icza, você normalmente não gostaria que eles apontassem para o mesmo objeto, não estou dizendo que faria. Estou apenas apontando a diferença.
Ryan Haining
4
@Conslo Constantes são avaliadas em tempo de compilação. Um valor de ponteiro válido, um endereço de memória válido só existe em tempo de execução, portanto, não é o mesmo que constantes.
icza de
6

Use uma função que retorna um endereço de uma variável int64 para resolver o problema.

No código a seguir, usamos uma função fque aceita um inteiro e retorna um valor de ponteiro que contém o endereço do inteiro. Usando este método, podemos resolver facilmente o problema acima.

type myStr struct {
    url *int64
}

func main() {
    f := func(s int64) *int64 {
        return &s
    }
    myStr{
        url: f(12345),
    }
}
ASHWIN RAJEEV
fonte