2011-11-05 20 views
23

En Google Go, leo que las cadenas son inmutables, ¿pero están int? ¿Qué pasa con otros tipos? Como programador un poco mayor, prefiero la mutabilidad, aunque conozco los beneficios de la inmutabilidad, prefiero vivir peligrosamente.¿Qué tipos son mutables e inmutables en Google Go Language?

Saber qué tipos son mutables o inmutables sería muy útil.


Actualización, lo que más me preocupa es que los problemas prácticos dependen de si el tipo es mutable o inmutable. Como en el ejemplo típico de Java, si crea un String en un bucle y un bucle por 10.000 veces, obtendrá 10,000 cadenas creadas que luego serán recolectadas. Esto realmente ha sido un problema serio en un proyecto en una compañía en la que trabajé.

La pregunta es, ¿la inmutabilidad de Go en algunos casos causa el mismo problema?

Afecta la forma de tratar la var. (o supongo que sí)


Actualizar de nuevo, también me preocupan otras cuestiones prácticas. Saber que algo es inmutable significa que puedo escribir código que es paralelo y las actualizaciones de una referencia del objeto no deberían actualizar las otras referencias. Sin embargo, a veces deseo hacer cosas peligrosas, quiero mutabilidad.

Estas son las consecuencias de la mutabilidad frente a la inmutabilidad y afectan la forma en que puedo escribir el código.

+0

La razón por la que pregunto es que este es un problema de rendimiento, si estoy escribiendo un ciclo que va alrededor de un millón de veces, no quiero que se creen muchos objetos. Por ejemplo, en un bucle de Java, no queremos crear una cadena cada vez que recorremos el ciclo, entonces usamos StringBuffer. – Phil

+0

Mi suposición es, por supuesto, que los tipos inmutables van a crear muchos objetos en general. (puede que no sea cierto, dependiendo del asignador) – Phil

+0

correcto, de alguna manera, porque el recolector de basura extremadamente inteligente podría reutilizar * fragmentos * de cadena * que dejas atrás con alguna implementación inteligente (cualquier cosa aparte de una matriz contigua de palabras). use '[] byte' luego, teniendo en cuenta que en su totalidad utf-8 * character * (aka runa) tiene longitud variable. – ypb

Respuesta

25

No se preocupe: ir le permitirá dispararse en el pie si realmente quiere :-)

Ir no es como Erlang, que podría ser a lo que se refiere con la pregunta.

x := 1 
x = 2 

asigna una variable, x, con un valor de 1, a continuación, vuelve a asignar a 2 - sin memoria adicional se asigna aquí.

Como observa, las cadenas son inmutables, por lo que realizar una manipulación de cadenas puede dar como resultado la realización de copias. Si encuentra que desea hacer modificaciones in situ a los datos de caracteres, probablemente desee operar en las variables de []byte a través del paquete bytes.

después de Russ Cox sobre este debe responder a la mayoría de sus preguntas sobre estructuras de datos básicos: http://research.swtch.com/2009/11/go-data-structures.html

Como otros comentaristas, tendrá que mirar a la semántica de valor de las funciones Go - que podría ser un poco sorprendente al principio.

Si usted tiene la siguiente función:

func (t MyType) myFunc() { 
    // do something to set a field in t 
} 

y llama en su código

myVar.myFunc() 

es posible que se sorprenda al ver que este no hace lo que quiere porque el t que se ve en myFunc() es realmente una copia de myVar.

Pero, lo siguiente será trabajo:

func (t *myType) myFunc() { 
    // do something to set a field in t 
} 

debido a que la función tiene una copia del puntero y se puede acceder a la estructura subyacente a través de ese puntero.

+0

¡Gracias! Esta era exactamente la información que necesitaba para solucionar un problema con un asterisco faltante en una firma de método. Me sorprende que Go te permita dispararte en el pie tan fácilmente en este aspecto, cuando es tan obscenamente vocal para "ayudarte" de otras maneras. –

2

Sí, la palabra inmutable aparece exactamente una vez en las especificaciones Go. Y eso es cuando se discute type string. Creo que deberías verlo más desde los puntos de vista dual de Assignability y Addressability. Por ejemplo, Go te prohibirá volver a vincular variable a un valor diferente de un tipo con propiedades no expuestas, obviamente. Un poco como en C++ para las clases que no proporcionan el constructor de copias, pero en Go Pimpl se siente mucho menos incómodo, lo que corresponde a la parte de los goroutines al comunicar la filosofía.

+0

Sí, pero si tengo un int, y establezco su valor, y estoy creando una nueva instancia de int (inmutable) o estableciendo su valor en la memoria (overwrite, mutable). Alternativamente, ¿de alguna manera estoy protegido incluso de saber si ese es el caso y depende de la implementación de Go? hmm tal vez puedo pensar en cómo probar esto, no estoy seguro. – Phil

+0

paquete principal \t importación "FMT" \t principal func() { \t var I int \t i = 5 \t fmt.Println (+ i) \t i = 6 \t fmt.Println (+ i) \t var k = 7 \t i = k \t fmt.Println (& i) \t} – Phil

+0

@Phil no estoy seguro de qué decir. quizás solo que Go pase por valor, por lo que debe tomar explícitamente la dirección de un objeto y el único tipo de "referencia" es la interfaz sin tipo {}./hmmm ... no mucho profesor, moi;} – ypb

-3

Esto me da la misma dirección cada vez, así que quizás son mutables.

package main 

import "fmt" 

func main() { 
var i int 
i = 5 
fmt.Println(&i) 
i = 6 
fmt.Println(&i) 
var k = 7 
i = k 
fmt.Println(&i) 
} 
+3

con la misma lógica, cadenas, de hecho, cualquier tipo, son mutables. 'func main() { var i cadena; i = "foo"; fmt.Println (&i); i = "bar"; fmt.Println (&i); var k = "Baz"; i = k; fmt.Println (+ i) } ' – newacct

+0

¿Cómo se explica que ¿Por qué? no cambia la dirección de i? – Phil

+1

la dirección de una variable nunca debe cambiar. Este es el equivalente C: 'int main() {char * i; i =" foo "; printf ("% p \ n ", &i); i = "barra"; printf ("% p \ n", &i); char * k = "baz"; i = k; printf ("% p \ n", &i); return 0;} '(' i' es un string (puntero char), pero si toma la dirección de 'i' (puntero a puntero), es lo mismo) Supongo que no busca la dirección de' i', sino la dirección del buffer de caracteres interno que 'i' se refiere a. Pero en G o la secuencia es un tipo opaco y no hay una forma directa de obtener este puntero interno, excepto tal vez a través de la reflexión – newacct

2

"mutabilidad" sólo tiene sentido cuando se habla de algún tipo compuesto, algo que no tiene partes "internos", que tal vez se puede cambiar independientemente de lo que lo contiene. Las cadenas están compuestas naturalmente por caracteres, y no hay ningún mecanismo en el lenguaje que nos permita cambiar un carácter en una cadena existente, excepto la asignación de una cadena completamente nueva, por lo que decimos que es inmutable.

Para una int, realmente no tiene sentido hablar de mutabilidad, porque, ¿cuáles son los "componentes" de una int? Cambia un int asignando un entero nuevo, pero la asignación no cuenta como "mutación".

Existe cierta conexión entre los problemas de mutabilidad y los tipos de referencia vs. valor. Semánticamente, no hay diferencia entre un tipo de referencia inmutable y un tipo de valor. ¿Por qué? Supongamos que int fuera en realidad un puntero a un objeto inmutable (es decir, *InternalIntObject sin funciones para cambiar InternalIntObject). Una vez que asigne dicho puntero a una variable, representará para siempre el mismo valor entero (no puede ser modificado por otros que comparten el mismo objeto) ya que el objeto es inmutable.Este es el mismo comportamiento que un tipo de valor entero. Puede asignar ints por operador de asignación; asimismo, puede asignar estos punteros por asignación; el resultado sería el mismo: la variable asignada representa el mismo número entero al que se le asignó. La única diferencia sería la comparación y los operadores aritméticos tendrían que redefinirse para desviar el puntero para calcular el resultado.

Por lo tanto, la mutabilidad solo es significativa para los tipos de referencia.

En cuanto a lo que ha preguntado, los tipos "mutables" generalmente se consideran los tipos de referencia excepto cadena: mapas, canales, sectores (con respecto a los datos apuntados por el segmento) y también apunta a cualquier cosa (ya que puede mutar el valor en la ubicación señalada por el puntero).

+0

Acerca de la oración "La prueba de si algo es mutable es si le das esa cosa a alguien, ¿puedes cambiar algún aspecto? ... ": creo que lo que escribiste es un punto de vista válido. Pero me parece que es mejor (desde mi punto de vista) pensar en la mutabilidad en términos de nombres y composición/superposición. Es decir, cómo obtener el "nombre" de un objeto, cambiar el objeto y qué otros objetos nombrados se cambian como consecuencia de eso. Un "nombre" es una cosa conceptual aquí. –

+0

bien borré esa sección ya que me doy cuenta de que en realidad se trata de los tipos de referencia – newacct

9

En mi opinión, uno debe primero separar los dos conceptos siguientes:

  • enteros como objetos matemáticos (es decir: los valores)

  • variables de tipo int

Entonces la respuesta es: Las variables enteras son mutables, los valores enteros son inmutables.

Esta vista es coherente con la especificación Go que establece que las cadenas son inmutables. Obviamente, una cadena variable es mutable.

variables (como concepto) en Go son al menos:

  • variables con nombre (por ejemplo: var i int)
  • las variables accesibles a través de punteros
  • objetos

mutable Go:

  • arrays y rodajas
  • mapas
  • canales
  • cierres que están capturando al menos 1 variable desde el ámbito exterior
  • objetos

Immutable Go:

  • interfaces de
  • booleanos, valores numéricos (incluidos los valores del tipo int)
  • cuerdas
  • punteros
  • punteros de función, y los cierres que se pueden reducir a los punteros de función
  • estructuras que tienen un solo campo

objetos Go, que algunas personas pueden considerar mutable, mientras que otras personas pueden considerarlos inmutables:

  • estructuras que tienen múltiples campos
+1

'struct' definitivamente es un tipo mutable, pero no lo sentimos porque siempre se está copiando al pasar. Entonces la mutación no se propaga. – Eonil

1

Su preocupación parece ser más sobre la asignación que la inmutabilidad. La inmutabilidad sin duda afecta la asignación al imposibilitar la reutilización de la memoria. Un compilador inteligente podría reutilizar cualquier memoria "inmutable" cuya dirección sepa que no escapa.

Además de las cadenas, tenga cuidado con las interfaces. Cualquier cosa más grande que el tamaño de la palabra tendrá que asignarse cuando se asigne a la interfaz (las optimizaciones a un lado). Además, las variables declaradas en un cuerpo de bucle cuyas direcciones escapan, incluso a través de un cierre, tendrán que asignarse cada vez a través del bucle. De lo contrario, sin embargo, una tarea es solo una tarea. El valor simplemente se copia en la memoria representada por la variable.

Si utiliza make o new en un bucle, o cualquier literal que produzca una referencia, la asignación tendrá que suceder (nuevamente, sujeto a optimización).

Básicamente, todo se reduce a tratar de reutilizar la memoria donde se pueda, y esperando que el compilador lo haga por ti cuando no puedas, si tiene sentido hacerlo.

Cuestiones relacionadas