¿Cómo puede contener una rebanada?

Intento aprender Golang usando “The Go Programming Language” y he llegado a la sección sobre rebanadas. Hacen la comparación entre matrices y sectores en que dos matrices se pueden comparar con == donde dos sectores no pueden. El texto dice lo siguiente:

 "== operator for arrays of strings, it may be puzzling that slice comparisons do not also work this way. There are two reasons why deep equivalence is problematic. First, unlike array elements, the elements of a slice are indirect, making it possible for a slice to contain itself. Although there are ways to deal with such cases, none is simple, efficient, and most importantly, obvious." 

¿Qué se entiende por que es posible que una porción se contenga debido a que los elementos son indirectos?

Rebanada que contiene

Además de un tipo recursivo (como el type Foo []Foo , ver la respuesta de ANisus) que no sirve para nada más que la demostración, un corte puede contenerse si, por ejemplo, el tipo de elemento del sector es la interface{} :

 s := []interface{}{"one", nil} s[1] = s 

En este ejemplo, el segmento tendrá 2 valores de interfaz, el primero “ajuste” una cadena simple "one" y otro valor de interfaz que envuelve el valor de corte mismo. Cuando se crea un valor de interfaz, se envuelve una copia del valor que en el caso de las divisiones significa una copia del encabezado / descriptor de la división, que contiene el puntero a la matriz subyacente, por lo que la copia tendrá el mismo valor de puntero apuntando a la misma matriz subyacente. (Para obtener más detalles sobre la representación de interfaces, consulte Las leyes de la reflexión: la representación de una interfaz ).

Si fue rápido para imprimirlo:

 fmt.Println(s) 

Obtendría un error fatal, algo así como:

 runtime: goroutine stack exceeds 250000000-byte limit fatal error: stack overflow 

Porque fmt.Println() intenta imprimir el contenido recursivamente, y como el segundo elemento es un sector que apunta a la misma matriz del sector que se está imprimiendo, se ejecuta en un bucle infinito.

Otra forma de ver si realmente es la rebanada misma:

 s := []interface{}{"one", nil} s[1] = s fmt.Println(s[0]) s2 := s[1].([]interface{}) fmt.Println(s2[0]) s3 := s2[1].([]interface{}) fmt.Println(s3[0]) 

Salida (pruébalo en el área de juegos Go ):

 one one one 

No importa qué tan profundo vayamos, el segundo elemento siempre será el valor de corte que apunte a la misma matriz como s , envuelto en un valor de interface{} .

El direccionamiento indirecto juega un papel importante ya que una copia estará envuelta en la interface{} pero la copia contendrá el mismo puntero.

Array no puede contenerse

Cambiar el tipo para que sea una matriz:

 s := [2]interface{}{"one", nil} s[1] = s fmt.Println(s[0]) s2 := s[1].([2]interface{}) fmt.Println(s2[0]) s3 := s2[1].([2]interface{}) fmt.Println(s3[0]) 

Salida (pruébalo en el área de juegos Go ):

 one one panic: interface conversion: interface is nil, not [2]interface {} 

Esto se debe a que cuando la matriz se envuelve en una interface{} , se envuelve una copia y una copia no es la matriz original. Entonces s tendrá un segundo valor, una interface{} envolviendo una matriz, pero esa es una matriz diferente cuyo 2do valor no está configurado y por lo tanto será nil (el valor cero de la interface{} tipo interface{} ), por lo que intenta “entrar en “Esta matriz entrará en pánico porque es nil (la aserción de tipo falla porque no se usó la forma especial” coma, ok “).

Como este conjunto de s no se contiene solo, un simple fmt.Println() revelará su contenido completo:

 fmt.Println(s) 

Salida:

 [one [one ]] 

interface{} adicional interface{} envolviendo el análisis

Si ajusta una matriz en una interface{} y modifica el contenido de la matriz original, el valor incluido en la interface{} no se verá afectado:

 arr := [2]int{1, 2} var f interface{} = arr arr[0] = 11 fmt.Println("Original array: ", arr) fmt.Println("Array in interface:", f) 

Salida:

 Original array: [11 2] Array in interface: [1 2] 

Si haces lo mismo con una porción, la porción envuelta (ya que apunta a la misma matriz subyacente) también se ve afectada:

 s := []int{1, 2} f = s s[0] = 11 fmt.Println("Original slice: ", s) fmt.Println("Slice in interface:", f) 

Salida:

 Original slice: [11 2] Slice in interface: [11 2] 

Pruébalos en el Go Playground .

El siguiente ejemplo crea un sector que se contiene a sí mismo:

 type Foo []Foo bar := make(Foo, 1) bar[0] = bar 

Esto se puede hacer porque el valor del corte contiene internamente un puntero a una matriz, una longitud y una capacidad.

Una matriz por otro lado es un valor. En el mejor de los casos, puede contener punteros a sí mismo.

una rebanada contiene un puntero a la memoria que contiene los elementos, una longitud para contar los elementos disponibles y una capacidad para qué tan grande es la memoria. por lo que me gusta:

 typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; 

Creo que es indirect , porque los elementos están referenciados por puntero. y, por supuesto, podemos tener el corte en sí mismo en void *data .