Як очистити фрагмент у програмі Go?


125

Який відповідний спосіб очистити фрагмент в Go?

Ось що я знайшов на форумах go :

// 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))
}

Це правильно?

Для уточнення буфер очищається, щоб його можна було повторно використовувати.

Приклад - функція Buffer.Truncate в пакеті байтів.

Зауважте, що Скидання просто викликає скорочення (0). Отже, видається, що в цьому випадку рядок 70 буде оцінювати: 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) }

1
Швидкий тест на: play.golang.org/p/6Z-qDQtpbg, здається, підказує, що він спрацює (не змінить ємність, але скоротить довжину)
Джейсон Сперський

Відповіді:


120

Все залежить від того, яке ваше визначення поняття "чітке". Одним із дійсних є:

slice = slice[:0]

Але є улов. Якщо елементи зрізів типу T:

var slice []T

то примусовий len(slice)до нуля, зазначеним вище "трюком", не робить жодного елемента

slice[:cap(slice)]

придатний для вивезення сміття. Це може бути оптимальним підходом у деяких сценаріях. Але це також може бути причиною "протікання пам'яті" - пам'ять не використовується, але потенційно доступна (після повторного нарізання "фрагмента") і, таким чином, не є сміттям "збиральним".


1
Цікаво. Чи є якийсь інший спосіб видалити всі елементи з нижнього масиву фрагмента, залишаючи базову ємність незмінною?
Кріс Вебер

3
@ChrisWeber: просто перегляньте основний масив і встановіть всі елементи на нове значення
newacct

2
@jnml, я хочу повторно використовувати фрагмент (і основне сховище масиву), тому я не виділяю постійно новий фрагмент (з масивом). Я відредагував своє запитання, щоб уточнити та показати деякий приклад коду зі стандартної бібліотеки.
Кріс Вебер

1
Я новачок у Go. Скажіть, будь ласка, докладніше, чому це може бути оптимальним підходом? Заздалегідь спасибі.
сатору

Ви впевнені, що скидання розміру фрагмента спричинить витік пам'яті? Я не в змозі його відтворити
Томмазо Барбуглі

197

Встановлення зрізу на nil - це найкращий спосіб очищення фрагмента. nilскибочки в дорозі чудово поводяться, і встановлення шматочка nilдозволить звільнити основну пам'ять для смітника.

Дивіться майданчик

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)
}

Друкує

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

Зауважте, що фрагменти можна легко псевдонімати, так що два фрагменти вказують на одну і ту ж основну пам'ять. Установка наnil видалення цього псевдоніму.

Цей метод, проте, змінює ємність до нуля.


Нік подякував за відповідь. Перегляньте, будь ласка, моє оновлення. Я очищаю фрагмент для повторного використання. Тому я не обов'язково хочу, щоб основна пам'ять була випущена в GC, оскільки мені доведеться просто виділити її знову.
Кріс Вебер

це те, що я шукав!)
Тимур Файзрахманов

5
Виходячи з назви "Як очистити фрагмент у" Го "? це набагато безпечніша відповідь і має бути прийнятою. Ідеальною відповіддю було б поєднання спочатку прийнятої відповіді і цього, щоб люди могли вирішити самі.
Шадоніня

1
appendIнг до nilшматочка завжди працював у Go?
аледіаферія

@alediaferia з тих пір виходити 1,0 звичайно.
Нік Крейг-Вуд

4

Я трохи розглядав це питання для власних цілей; У мене був шматочок струків (включаючи деякі покажчики), і я хотів переконатися, що я його правильно; опинився на цій темі і хотів поділитися своїми результатами.

Щоб потренуватися, я трохи пішов на майданчик: https://play.golang.org/p/9i4gPx3lnY

що до цього дорівнює:

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)
}

Запустивши цей код як є, буде показана однакова адреса пам'яті як для змінних "meow", так і для "meow2":

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

що я думаю, підтверджує, що структура - це зібране сміття. Як не дивно, коментуючи коментований рядок друку, буде створено різні адреси пам'яті для meows:

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

Я думаю, це може бути пов’язано з тим, що друк відкладено якимось чином (?), Але цікавою ілюстрацією деякої поведінки mgmt пам’яті та ще одним голосом за:

[]MyStruct = nil

Приємні докладні приклади. Дякую!
Доланор

2
Це не показує, що адреси пам'яті meo1 та meow2 однакові: 0x1030e0c0не дорівнює 0x1030e0f0(перший закінчується c0, другий - f0).
карбокація

Погодьтеся з @carbocation тут, ці адреси пам'яті не однакові. Я не стверджую, що зможу краще пояснити, що тут відбувається, але це не слугує для мене свідченням. Я бачу однакову 8- meow2
байтну
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.