163 votes

Comment effacer une tranche en Go ?

Quelle est la manière appropriée d'effacer une tranche en Go ?

Voici ce que j'ai trouvé dans la section les forums de discussion :

// test.go
package main

import (
    "fmt"
)

func main() {
    letters := []string{"a", "b", "c", "d"}
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
    // clear the slice
    letters = letters[:0]
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
}

Est-ce correct ?

Pour clarifier, le tampon est effacé pour pouvoir être réutilisé.

Un exemple est Buffer.Truncate dans le paquet d'octets.

Remarquez que Reset appelle simplement Truncate(0). Il semble donc que dans ce cas, la ligne 70 serait évaluée : b.buf = b.buf[0 : 0]

http://golang.org/src/pkg/bytes/buffer.go

// Truncate discards all but the first n unread bytes from the buffer.
60  // It panics if n is negative or greater than the length of the buffer.
61  func (b *Buffer) Truncate(n int) {
62      b.lastRead = opInvalid
63      switch {
64      case n < 0 || n > b.Len():
65          panic("bytes.Buffer: truncation out of range")
66      case n == 0:
67          // Reuse buffer space.
68          b.off = 0
69      }
70      b.buf = b.buf[0 : b.off+n]
71  }
72  
73  // Reset resets the buffer so it has no content.
74  // b.Reset() is the same as b.Truncate(0).
75  func (b *Buffer) Reset() { b.Truncate(0) }

251voto

Nick Craig-Wood Points 18742

En réglant la tranche sur nil est la meilleure façon d'effacer une tranche. nil Les tranches dans go se comportent parfaitement bien et le fait de définir la tranche à nil libère la mémoire sous-jacente pour le ramasseur d'ordures.

Voir l'aire de jeux

package main

import (
    "fmt"
)

func dump(letters []string) {
    fmt.Println("letters = ", letters)
    fmt.Println(cap(letters))
    fmt.Println(len(letters))
    for i := range letters {
        fmt.Println(i, letters[i])
    }
}

func main() {
    letters := []string{"a", "b", "c", "d"}
    dump(letters)
    // clear the slice
    letters = nil
    dump(letters)
    // add stuff back to it
    letters = append(letters, "e")
    dump(letters)
}

Imprimés

letters =  [a b c d]
4
4
0 a
1 b
2 c
3 d
letters =  []
0
0
letters =  [e]
1
1
0 e

Notez que les tranches peuvent facilement être aliasées de sorte que deux tranches pointent vers la même mémoire sous-jacente. Le paramètre nil supprimera cet aliasing.

Cependant, cette méthode fait passer la capacité à zéro.

152voto

zzzz Points 23017

Tout dépend de votre définition de "clair". L'une des plus valables l'est certainement :

slice = slice[:0]

Mais il y a un hic. Si les éléments de la tranche sont de type T :

var slice []T

puis en appliquant len(slice) pour être zéro, par le "truc" ci-dessus, n'a pas faire en sorte que tout élément de

slice[:cap(slice)]

éligibles pour la collecte des ordures. Cela peut être l'approche optimale dans certains scénarios. Mais elle peut également être à l'origine de "fuites de mémoire", c'est-à-dire de mémoire non utilisée, mais potentiellement accessible (après un nouveau découpage de la "tranche") et donc non "collectable" par les ordures.

6voto

max garvey Points 85

Je me suis penché sur cette question pour mes propres besoins ; j'avais une tranche de structs (y compris quelques pointeurs) et je voulais être sûr de bien faire les choses ; j'ai atterri sur ce fil, et je voulais partager mes résultats.

Pour m'entraîner, j'ai fait un petit terrain de jeu de go : https://play.golang.org/p/9i4gPx3lnY

qui témoigne de cela :

package main

import "fmt"

type Blah struct {
    babyKitten int
    kittenSays *string
}

func main() {
    meow := "meow"
    Blahs := []Blah{}
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{1, &meow})
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{2, &meow})
    fmt.Printf("Blahs: %v\n", Blahs)
    //fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays)
    Blahs = nil
    meow2 := "nyan"
    fmt.Printf("Blahs: %v\n", Blahs)
    Blahs = append(Blahs, Blah{1, &meow2})
    fmt.Printf("Blahs: %v\n", Blahs)
    fmt.Printf("kittenSays: %v\n", *Blahs[0].kittenSays)
}

L'exécution de ce code tel quel montrera la même adresse mémoire pour les deux variables "meow" et "meow2" comme étant la même :

Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
Blahs: []
Blahs: [{1 0x1030e0f0}]
kittenSays: nyan

ce qui, je pense, confirme que la structure est collectée par les ordures. Curieusement, en décommentant la ligne print commentée, on obtient des adresses mémoire différentes pour les meows :

Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
kittenSays: meow
Blahs: []
Blahs: [{1 0x1030e0f8}]
kittenSays: nyan

Je pense que cela peut être dû au fait que l'impression est différée d'une certaine manière ( ?), mais c'est une illustration intéressante du comportement de la gestion de la mémoire, et un vote de plus pour :

[]MyStruct = nil

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X