Estoy un poco confundido acerca de pasar por referencia y valor en Go.

He visto esto explicado del * delante de un tipo.

  • delante de un nombre de tipo, significa que la variable declarada almacenará una dirección de otra variable de ese tipo (no un valor de ese tipo).

Esto simplemente no tiene sentido para mí.

En Java, si estuviera pasando una instancia de base de datos a una función, haría

 databaseFunction(DatabaseType db) {
      // do something
}

Sin embargo, en el ejemplo de go que tengo, ha pasado así.

func PutTasks(db *sql.DB) echo.HandlerFunc {

}

¿Por qué necesitamos tener el asterisco delante del tipo?

De acuerdo con esta hoja de trucos, encontré.

func PrintPerson (p * Person) SOLO recibe la dirección del puntero (referencia)

No entiendo por qué solo querría enviar una dirección de puntero como parámetro.

2
Nick Pocock 15 nov. 2017 a las 01:30

2 respuestas

La mejor respuesta

Primero, técnicamente, Go solo tiene valor de transferencia. Cuando se pasa un puntero a un objeto, se pasa un puntero por valor, no un objeto por referencia. La diferencia es sutil pero ocasionalmente relevante. Por ejemplo, puede sobrescribir el valor del puntero que no tiene ningún impacto en la persona que llama, en lugar de desreferenciarlo y sobrescribir la memoria a la que apunta.

// *int means you *must* pass a *int (pointer to int), NOT just an int!
func someFunc(x *int) {
    *x = 2 // Whatever variable caller passed in will now be 2
    y := 7
    x = &y // has no impact on the caller because we overwrote the pointer value!
}

En cuanto a su pregunta "¿Por qué necesitamos tener el asterisco delante del tipo?": El asterisco indica que el valor es de tipo puntero a sql.DB, en lugar de un valor de tipo sql.DB. ¡Estos no son intercambiables!

¿Por qué querría enviar una dirección de puntero? Para que pueda compartir el valor entre la persona que llama a una función y el cuerpo de la función, con los cambios realizados dentro de la función reflejados en la persona que llama (por ejemplo, un puntero es la única forma en la que un "configurador" El método puede funcionar en un objeto). Mientras que Java pasa los objetos por referencia siempre, Go pasa siempre por valor (es decir, crea una copia del valor en la función); si pasa algo a una función y esa función modifica ese valor, la persona que llama no verá esos cambios. Si desea que los cambios se propaguen fuera de la función, debe pasar un puntero.

Consulte también: la sección Go tour en Pointers, la Ir a la sección de especificaciones sobre punteros, la Ir a la sección de especificaciones sobre los operadores de direcciones

10
Adrian 14 nov. 2017 a las 22:46

El propósito de la semántica de referencia es permitir que una función manipule datos fuera de su propio ámbito. Comparar:

func BrokenSwap(a int, b int) {
  a, b = b, a
}

func RealSwap(a *int, b *int) {
  *a, *b = *b, *a
}

Cuando llama a BrokenSwap(x, y), no hay ningún efecto, porque la función recibe y manipula una copia privada de los datos. Por el contrario, cuando llama a RealSwap(&x, &y), en realidad intercambia los valores de la persona que llama x y y. Tomar la dirección de las variables de forma explícita en el sitio de la llamada informa al lector que esas variables pueden estar mutadas.

3
Kerrek SB 14 nov. 2017 a las 22:33