¿Cuál es el punto de tener punteros en Go?

100

Sé que los punteros en Go permiten la mutación de los argumentos de una función, pero ¿no habría sido más sencillo si adoptaran solo referencias (con calificadores adecuados const o mutables)? Ahora tenemos punteros y para algunos tipos integrados, como mapas y canales, pase implícito por referencia.

¿Me estoy perdiendo algo o los punteros en Go son solo una complicación innecesaria?

luego
fuente
1
Aquí hay una pregunta que puede ayudar a aclarar: stackoverflow.com/questions/795160/… Hay una diferencia entre pasar referencias por valor y pasar realmente por referencia.
R. Martinho Fernandes
1
Nota: la pregunta es sobre Java, pero también se aplica aquí.
R. Martinho Fernandes
1
"y para algunos tipos integrados como mapas y canales, paso implícito por referencia". No, todo se transfiere por valor en Go. Algunos tipos son (informalmente descritos como) tipos de referencia, ya que tienen un estado mutable interno.
newacct
El problema con esta pregunta es que las "referencias" no son una sola cosa con propiedades bien definidas. El término "referencias" es muy vago. Podemos ver en las respuestas cuántas personas leen diferentes cosas en la palabra "referencias". Por lo tanto, esta pregunta debería detallar exactamente qué diferencias hay entre los punteros Go y las referencias que la pregunta tiene en mente.
mtraceur

Respuestas:

36

Realmente me gusta el ejemplo tomado de http://www.golang-book.com/8

func zero(x int) {
    x = 0
}
func main() {
    x := 5
    zero(x)
    fmt.Println(x) // x is still 5
}

en contraste con

func zero(xPtr *int) {
    *xPtr = 0
}
func main() {
    x := 5
    zero(&x)
    fmt.Println(x) // x is 0
}
Piotr Kochański
fuente
42
La pregunta era "por qué tenemos punteros en lugar de referencias " y no entiendo por qué este ejemplo no funcionaría con referencias.
AndreKR
@AndreKR Porque podemos elegir si pasar por referencia o pasar por valor. Hay algunos casos en los que ambos pueden ser deseables.
JDSweetBeat el
9
@DJMethaneMan ¡Son "punteros vs. referencias", no "punteros vs. paso por valor"!
AndreKR
Como comentario lateral, se agregó paso por referencia en C # 2.0 a través de la palabra clave "ref". Por supuesto, los punteros son aún más convenientes en ciertos casos, porque podemos tener de puntero a puntero a puntero ...
robbie fan
No entiendo cómo se supone que Go es uno de los lenguajes populares más fáciles y, sin embargo, contienen esa "característica" ... Es confuso y parece innecesario, al menos para las personas que señalan esto aquí.
Akito
33

Los punteros son útiles por varias razones. Los punteros permiten controlar el diseño de la memoria (afecta la eficiencia de la memoria caché de la CPU). En Go podemos definir una estructura donde todos los miembros están en memoria contigua:

type Point struct {
  x, y int
}

type LineSegment struct {
  source, destination Point
}

En este caso, las Pointestructuras están incrustadas dentro de la LineSegmentestructura. Pero no siempre puede incrustar datos directamente. Si desea admitir estructuras como árboles binarios o listas vinculadas, debe admitir algún tipo de puntero.

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode
}

Java, Python, etc.no tienen este problema porque no le permite incrustar tipos compuestos, por lo que no es necesario diferenciar sintácticamente entre incrustación y apuntar.

Problemas con estructuras Swift / C # resueltos con punteros Go

Una posible alternativa para lograr lo mismo es diferenciar entre structy classcomo lo hace C # y Swift. Pero esto tiene limitaciones. Aunque normalmente puede especificar que una función toma una estructura como inoutparámetro para evitar copiar la estructura, no le permite almacenar referencias (punteros) a estructuras. Esto significa que nunca puede tratar una estructura como un tipo de referencia cuando lo encuentra útil, por ejemplo, para crear un asignador de grupo (ver más abajo).

Asignador de memoria personalizado

Usando punteros también puede crear su propio asignador de grupo (esto está muy simplificado con muchas verificaciones eliminadas para mostrar el principio):

type TreeNode {
  value int
  left  *TreeNode
  right *TreeNode

  nextFreeNode *TreeNode; // For memory allocation
}

var pool [1024]TreeNode
var firstFreeNode *TreeNode = &pool[0] 

func poolAlloc() *TreeNode {
    node := firstFreeNode
    firstFreeNode  = firstFreeNode.nextFreeNode
    return node
}

func freeNode(node *TreeNode) {
    node.nextFreeNode = firstFreeNode
    firstFreeNode = node
}

Intercambia dos valores

Los punteros también le permiten implementar swap. Eso es intercambiar los valores de dos variables:

func swap(a *int, b *int) {
   temp := *a
   *a = *b
   *b = temp
}

Conclusión

Java nunca ha podido reemplazar por completo a C ++ para la programación de sistemas en lugares como Google, en parte porque el rendimiento no se puede ajustar en la misma medida debido a la falta de capacidad para controlar el diseño y el uso de la memoria (las fallas de caché afectan el rendimiento de manera significativa). Go ha tenido como objetivo reemplazar C ++ en muchas áreas y, por lo tanto, necesita admitir punteros.

Erik Engheim
fuente
7
C # permite pasar estructuras por referencia. Consulte las palabras clave "ref" y "out".
olegz
1
De acuerdo, es como Swift. Pensaré en una forma de actualizar mi ejemplo.
Erik Engheim
29

Las referencias no se pueden reasignar, mientras que los punteros sí. Esto por sí solo hace que los punteros sean útiles en muchas situaciones en las que no se pueden utilizar referencias.

zildjohn01
fuente
17
Si las referencias son reasignables es un problema de implementación específico del idioma.
crantok
28

Go está diseñado para ser un lenguaje escueto y minimalista. Por lo tanto, comenzó solo con valores y punteros. Posteriormente, por necesidad, se agregaron algunos tipos de referencia (cortes, mapas y canales).


The Go Programming Language: Preguntas frecuentes sobre el diseño del lenguaje: ¿Por qué los mapas, los cortes y los canales son referencias, mientras que las matrices son valores?

"Hay mucha historia sobre ese tema. Al principio, los mapas y los canales eran punteros sintácticos y era imposible declarar o usar una instancia sin puntero. Además, tuvimos problemas con cómo deberían funcionar las matrices. Finalmente, decidimos que la separación estricta de punteros y valores hizo que el lenguaje fuera más difícil de usar. La introducción de tipos de referencia, incluidos los segmentos para manejar la forma de referencia de las matrices, resolvió estos problemas. Los tipos de referencia añaden una complejidad lamentable al lenguaje, pero tienen un gran efecto en la usabilidad: Go se convirtió en un lenguaje más productivo y cómodo cuando se introdujeron ".


La compilación rápida es uno de los principales objetivos de diseño del lenguaje de programación Go; eso tiene sus costos. Una de las víctimas parece ser la capacidad de marcar variables (excepto las constantes de tiempo de compilación básicas) y parámetros como inmutables. Se solicitó, pero se rechazó.


golang-nuts: ir al idioma. Algunos comentarios y dudas.

"Agregar const al sistema de tipos lo obliga a aparecer en todas partes y obliga a uno a eliminarlo en todas partes si algo cambia. Si bien puede haber algún beneficio al marcar objetos como inmutables de alguna manera, no creemos que un calificador de tipo const sea demasiado ir."

peterSO
fuente
FWIW, los "tipos de referencia" en Go también se pueden reasignar. ¿Son más como punteros implícitos?
Matt Joiner
1
Son solo una sintaxis especial para estructuras que contienen un puntero (y longitud, capacidad, ...).
mk12