2012-05-10 16 views
26

Cuál es la manera más limpia para manejar un caso como este:Dirección de un temporal en Go?

func a() string { 
    /* doesn't matter */ 
} 

b *string = &a() 

Esto genera el error:

cannot take the address of a()

Mi entendimiento es que Go promueve automáticamente una variable local a la pila si su dirección es tomado. Aquí está claro que se debe tomar la dirección del valor de retorno. ¿Cuál es una forma idiomática de manejar esto?

+0

¿Qué quieres lograr con la construcción en cuestión? – Wolf

Respuesta

23

El operador de dirección devuelve un puntero a algo que tiene un "inicio", p. una variable. El valor de la expresión en tu código es "sin hogar". si realmente necesita una cadena *, tendrá que hacerlo en 2 pasos:

tmp := a(); b := &tmp 

Tenga en cuenta que si bien hay casos de uso completamente válidas para * cadena, muchas veces es un error de usarlos. En Go string es un tipo de valor, pero barato para pasar (un puntero y un int). El valor de String es inmutable, cambiando un *string cambia a donde apunta la "casa", no el valor de cadena, por lo que en la mayoría de los casos *string no es necesario en absoluto.

+1

o devuelve un '* string' de' a() ' – thwd

+2

también puede tomar la dirección de un literal compuesto, que no tiene un" hogar " – newacct

+0

Literal compuesto no necesariamente tiene un hogar, pero las especificaciones garantizan que un literal compuesto, cuya dirección es tomada por el operador '&' tiene una "casa" (no hay otra opción posible de todos modos, de lo contrario no habría una dirección que tomar). – zzzz

0

a() no apunta a una variable, ya que está en la pila. No puedes apuntar a la pila (¿por qué lo harías?).

Puede hacerlo si quieres

va := a() 
b := &va 

Pero lo que tu realmente queremos lograr es poco clara.

+0

Bueno, no, ahora creo que 'va' se almacenará en el montón. –

+0

¿Alguien puede confirmar que en el código de ejemplo proporcionado, 'va' se almacenará en el montón debido al operador de dirección en la siguiente línea? –

+0

La pregunta real parece ser "¿El GC va a limpiar el valor de va si el bloque declarante está guardado pero todavía existe?". ¿No es así? Creo que el valor no se guardará debido al puntero en b. –

12

Consulte la sección pertinente de Go language spec. & sólo se puede utilizar en:

  1. Algo que es direccionable: variable puntero indirecto, operación rebanada de indexación, selector de campo de una estructura direccionable, operación indexación de matrices de una matriz direccionable; O
  2. Un compuesto literal

Lo que tenemos es que ninguno de ellos, por lo que no funciona.

Ni siquiera estoy seguro de lo que significaría incluso si pudiera hacerlo. Tomando la dirección del resultado de una llamada de función? Por lo general, pasas un puntero de algo a alguien porque quieres que se pueda asignar a lo apuntado, y ves los cambios en la variable original. Pero el resultado de una llamada a función es temporal; nadie más lo "ve", a menos que primero se lo asigne a algo.

Si el propósito de crear el puntero es crear algo con una vida dinámica, similar a new() o tomar la dirección de un literal compuesto, entonces puede asignar el resultado de la llamada de función a una variable y tomar la dirección de ese.

+0

Si una función devuelve un valor no puntero, me parece claro que se supone que el valor devuelto se almacena en la pila.Pero si toma inmediatamente la dirección del valor de retorno, ¿no veo ninguna ambigüedad de promover el almacenamiento para el retorno al montón, como es normal? Demonios, dado que el marco de la persona que llama no ha tenido oportunidad de utilizar el valor de retorno todavía, la persona que llama podría copiar fácilmente el resultado directamente en el montón a su regreso. –

+0

+1 para la referencia de especificación –

5

Al final usted propone que Go debe permitir que tome la dirección de cualquier expresión, por ejemplo:

i,j := 1,2 
var p *int = &(i+j) 
println(*p) 

Los actuales impresiones compilador Va el error: cannot take the address of i + j

En mi opinión, permitiendo al programador tomar la dirección de cualquier expresión:

  • No parece ser muy útil (es decir: parece tener una probabilidad muy pequeña de ocurrencia real Ir a los programas).
  • Sería complicado el compilador y la especificación de idioma.

Parece contraproducente complicar el compilador y las especificaciones con poca ganancia.

+0

Esto realmente tiene mucho sentido. Pasé por alto el hecho de que la llamada a la función es una expresión. –

+0

El mensaje de error parece ser bastante claro acerca de cómo Go interpreta & (i + j), pero tal vez no. Sería razonable (quizás no en Go) que un programador interprete esto como que significa "la dirección del valor resultante de la ejecución de la expresión (i + j)", y algunos realmente lo están interpretando así. (No tengo ninguna opinión sobre si Go está bien o mal ... pero ¿es 'una' expresión? & A funciona, '& (a)' también funciona :-) Entonces, ¿Go está objetando una expresión o un valor asignado a la pila (y simplemente llamarlo por el nombre "i + j")? ¿Importa? – hutch

+1

@hutch Go (al menos en la implementación actual) se opone a la expresión. Desde el punto de vista del programador, no importa cómo lo esté haciendo el compilador. –

1

Hace poco estaba atado en nudos sobre algo similar.

Primera hablando de cadenas en el ejemplo es una distracción, utilizar una estructura en su lugar, vuelva a escribir a algo así como:

func a() MyStruct { 
    /* doesn't matter */ 
} 

var b *MyStruct = &a() 

Esto no compilará porque no se puede tomar la dirección de una(). Así que haga lo siguiente:

func a() MyStruct { 
    /* doesn't matter */ 
} 

tmpA := a() 
var b *MyStruct = &tmpA 

Esto compilará, pero que ha devuelto un MyStruct en la pila, asignado espacio suficiente en el montón para almacenar un MyStruct, luego se copia el contenido de la pila a la pila. Si se quiere evitar esto, a continuación, escribir así:

func a2() *MyStruct { 
    /* doesn't matter as long as MyStruct is created on the heap (e.g. use 'new') */ 
} 

var a *MyStruct = a2() 

copia normalmente es barato, pero esas estructuras podrían ser grandes. Peor aún cuando quiere modificar la estructura y hacer que 'pegue' no puede copiar y luego modificar las copias.

De todos modos, se vuelve aún más divertido cuando está utilizando un tipo de interfaz de retorno {}. La interfaz {} puede ser la estructura o un puntero a una estructura. El mismo problema de copiado surge.