¿Cómo hago un literal * int64 en Go?

103

Tengo un tipo de estructura con un *int64campo.

type SomeType struct {
    SomeField *int64
}

En algún momento de mi código, quiero declarar un literal de esto (digamos, cuando sé que dicho valor debe ser 0, o apuntando a un 0, ya sabes a lo que me refiero)

instance := SomeType{
    SomeField: &0,
}

... excepto que esto no funciona

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

Así que intento esto

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

... pero esto tampoco funciona

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

¿Cómo hago esto? La única solución que se me ocurre es usar una variable de marcador de posición

var placeholder int64
placeholder = 0

instance := SomeType{
    SomeField: &placeholder,
}

Nota: la &0sintaxis funciona bien cuando es un * int en lugar de un *int64. Editar: no, no lo hace. Perdón por esto.

Editar:

Aparentemente, había demasiada ambigüedad en mi pregunta. Estoy buscando una manera de decir literalmente a *int64. Esto podría usarse dentro de un constructor, o para indicar valores de estructuras literales, o incluso como argumentos para otras funciones. Pero las funciones auxiliares o el uso de un tipo diferente no son las soluciones que estoy buscando.

Este chico
fuente
1
Los punteros a int son desafortunados ya que int ocupa la misma cantidad de espacio que un puntero, por lo que no está ahorrando espacio. Simplemente agrega un valor NULL que generalmente es más complejo de lo que vale. En la mayoría de los casos, un 0 estaría bien. Si necesita un valor adicional, un bool "IsValidSomeField" también funciona y si le da a ese bool un nombre mejor, puede decir más acerca de por qué necesita ese valor adicional, lo cual es bueno para la legibilidad.
voutasaurus
2
Puede usar el puntero de paquete , por ejemplo:var _ *int64 = pointer.Int64(64)
Xor

Respuestas:

212

La especificación del lenguaje Go ( operadores de direcciones ) no permite tomar la dirección de una constante numérica (no de un sin tipo ni de un escrito constante).

El operando debe ser direccionable , es decir, una variable, una dirección indirecta del puntero o una operación de indexación de sectores; o un selector de campo de un operando de estructura direccionable; o una operación de indexación de matriz de una matriz direccionable. Como excepción al requisito de direccionabilidad, x[en la expresión de &x] también puede ser un literal compuesto (posiblemente entre paréntesis) .

Para razonar por qué esto no está permitido, consulte la pregunta relacionada: Buscar la dirección de la constante en marcha . Una pregunta similar (tampoco se permite tomar su dirección): ¿Cómo puedo almacenar una referencia al resultado de una operación en Go?

Tus opciones (prueba todas en Go Playground ):

1) Con new()

Simplemente puede usar la new()función incorporada para asignar un nuevo valor cero int64y obtener su dirección:

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

Pero tenga en cuenta que esto solo se puede usar para asignar y obtener un puntero al valor cero de cualquier tipo.

2) Con variable auxiliar

Lo más simple y recomendado para elementos distintos de cero es utilizar una variable auxiliar cuya dirección se pueda tomar:

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

3) Con función auxiliar

Nota: Las funciones auxiliares para adquirir un puntero a un valor distinto de cero están disponibles en mi github.com/icza/goxbiblioteca, en el goxpaquete, por lo que no tiene que agregarlas a todos sus proyectos donde las necesite.

O si lo necesita muchas veces, puede crear una función auxiliar que asigne y devuelva un *int64:

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

Y usándolo:

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

Tenga en cuenta que en realidad no asignamos nada, el compilador Go lo hizo cuando devolvimos la dirección del argumento de la función. El compilador Go realiza análisis de escape y asigna variables locales en el montón (en lugar de en la pila) si pueden escapar de la función. Para obtener más información, consulte ¿Es seguro devolver una porción de una matriz local en una función Go?

4) Con una función anónima de una sola línea

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

O como alternativa (más corta):

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

5) Con literal de corte, indexación y toma de dirección

Si quieres *SomeFieldser diferente a 0, entonces necesitas algo direccionable.

Todavía puedes hacer eso, pero eso es feo:

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

Lo que sucede aquí es que []int64se crea un segmento con un literal, que tiene un elemento ( 5). Y se indexa (elemento 0) y se toma la dirección del elemento 0. En segundo plano [1]int64, también se asignará una matriz de y se utilizará como matriz de respaldo para el segmento. Así que hay mucho texto estándar aquí.

6) Con una estructura de ayuda literal

Examinemos la excepción a los requisitos de direccionabilidad:

Como excepción al requisito de direccionabilidad, x[en la expresión de &x] también puede ser un literal compuesto (posiblemente entre paréntesis) .

Esto significa que tomar la dirección de un literal compuesto, por ejemplo, un literal de estructura está bien. Si lo hacemos, tendremos el valor de la estructura asignado y un puntero obtenido. Pero si es así, otro requisito estará disponible para nosotros: "selector de campo de un operando de estructura direccionable" . Entonces, si la estructura literal contiene un campo de tipo int64, ¡también podemos tomar la dirección de ese campo!

Veamos esta opción en acción. Usaremos este tipo de estructura de envoltura:

type intwrapper struct {
    x int64
}

Y ahora podemos hacer:

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

Tenga en cuenta que esto

&(&intwrapper{6}).x

significa lo siguiente:

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

Pero podemos omitir el paréntesis "externo" ya que el operador de dirección &se aplica al resultado de la expresión del selector .

También tenga en cuenta que en segundo plano sucederá lo siguiente (esta también es una sintaxis válida):

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

7) Con ayuda de estructura anónima literal

El principio es el mismo que con el caso n. ° 6, pero también podemos usar un literal de estructura anónimo, por lo que no se necesita una definición de tipo de estructura auxiliar / contenedor:

instance7 := SomeType{
    SomeField: &(&struct{ x int64 }{7}).x,
}
icza
fuente
1
esto es funcionalmente diferente del último ejemplo en la pregunta con el marcador de posición, ya que dos instanceobjetos diferentes apuntarían a dos int64s diferentes . Pero parece cumplir adecuadamente con la intención de los OP
Ryan Haining
2
esto definitivamente responde a mi pregunta. Pero me molesta bastante: &[]int64{2}[0]creo que, según la descripción de constantes en blog.golang.org/constants, esto debería funcionar como &0.
ThisGuy
@RyanHaining ¿Y qué pasaría si funcionara como si se asignara la misma dirección? Si dos instanceobjetos diferentes int64apuntaran al mismo y uno modificara el objeto puntiagudo, ambos cambiarían. ¿Y si ahora usa el mismo literal para crear un tercero instance? La misma dirección ahora apuntaría a un int64valor diferente ... por lo que debería usarse una dirección diferente, pero entonces ¿por qué usar la misma en el caso de las 2 primeras?
icza
@icza, por lo general, no querrás que apunten al mismo objeto, no estoy diciendo que lo harías. Solo estoy señalando la diferencia.
Ryan Haining
4
Las constantes de @Conslo se evalúan en tiempo de compilación. Un valor de puntero válido, una dirección de memoria válida solo existe en tiempo de ejecución, por lo que no es lo mismo que las constantes.
icza
6

Utilice una función que devuelva una dirección de una variable int64 para resolver el problema.

En el siguiente código usamos la función fque acepta un número entero y devuelve un valor de puntero que contiene la dirección del número entero. Al usar este método, podemos resolver fácilmente el problema anterior.

type myStr struct {
    url *int64
}

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