英文:
How do you clear a slice in Go?
问题
在Go语言中,清空一个切片的适当方法是什么?
在Go论坛中,我找到了以下内容:
// test.go
package main
import (
"fmt"
)
func main() {
letters := []string{"a", "b", "c", "d"}
fmt.Println(cap(letters))
fmt.Println(len(letters))
// 清空切片
letters = letters[:0]
fmt.Println(cap(letters))
fmt.Println(len(letters))
}
这样做正确吗?
为了澄清,缓冲区被清空以便可以重用。
一个例子是bytes包中的Buffer.Truncate函数。
请注意,Reset只是调用了Truncate(0)。所以在这种情况下,第70行将被评估为:
b.buf = b.buf[0 : 0]
http://golang.org/src/pkg/bytes/buffer.go
// Truncate从缓冲区中丢弃除了前n个未读字节之外的所有内容。
60 // 如果n为负数或大于缓冲区的长度,则会引发panic。
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 // 重用缓冲区空间。
68 b.off = 0
69 }
70 b.buf = b.buf[0 : b.off+n]
71 }
72
73 // Reset重置缓冲区,使其不包含任何内容。
74 // b.Reset()与b.Truncate(0)相同。
75 func (b *Buffer) Reset() { b.Truncate(0) }
英文:
What is the appropriate way to clear a slice in Go?
Here's what I've found in the go forums:
// 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))
}
Is this correct?
To clarify, the buffer is cleared so it can be reused.
An example is Buffer.Truncate function in the bytes package.
Notice that Reset just calls Truncate(0). So it appears that in this case line 70 would evaluate:
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
得分: 251
将切片设置为nil
是清除切片的最佳方法。在Go中,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)
// 清除切片
letters = nil
dump(letters)
// 添加内容
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
将消除这种别名。
但是,这种方法会将容量更改为零。
英文:
Setting the slice to nil
is the best way to clear a slice. nil
slices in go are perfectly well behaved and setting the slice to nil
will release the underlying memory to the garbage collector.
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)
}
Prints
letters = [a b c d]
4
4
0 a
1 b
2 c
3 d
letters = []
0
0
letters = [e]
1
1
0 e
Note that slices can easily be aliased so that two slices point to the same underlying memory. The setting to nil
will remove that aliasing.
This method changes the capacity to zero though.
答案2
得分: 152
这完全取决于你对“清空”的定义。其中一个有效的方法是:
slice = slice[:0]
但是有一个问题。如果切片元素的类型是T:
var slice []T
通过上述“技巧”强制len(slice)
为零,并不会使得
slice[:cap(slice)]
中的任何元素有资格进行垃圾回收。这在某些情况下可能是最佳方法。但它也可能导致“内存泄漏”-内存没有被使用,但可能在重新切片'slice'后可达(并且因此无法进行垃圾“回收”)。
英文:
It all depends on what is your definition of 'clear'. One of the valid ones certainly is:
slice = slice[:0]
But there's a catch. If slice elements are of type T:
var slice []T
then enforcing len(slice)
to be zero, by the above "trick", doesn't make any element of
slice[:cap(slice)]
eligible for garbage collection. This might be the optimal approach in some scenarios. But it might also be a cause of "memory leaks" - memory not used, but potentially reachable (after re-slicing of 'slice') and thus not garbage "collectable".
答案3
得分: 6
我为了自己的目的研究了一下这个问题;我有一个包含一些指针的结构体切片,我想确保我弄对了;最后在这个帖子上找到了答案,并想分享我的结果。
为了练习,我做了一个小的Go Playground:
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
我认为这可能是打印被延迟执行的原因(?),但这是一种有趣的内存管理行为的示例,并且对于以下代码的又一个支持:
[]MyStruct = nil
英文:
I was looking into this issue a bit for my own purposes; I had a slice of structs (including some pointers) and I wanted to make sure I got it right; ended up on this thread, and wanted to share my results.
To practice, I did a little go playground:
https://play.golang.org/p/9i4gPx3lnY
which evals to this:
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)
}
Running that code as-is will show the same memory address for both "meow" and "meow2" variables as being the same:
Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
Blahs: []
Blahs: [{1 0x1030e0f0}]
kittenSays: nyan
which I think confirms that the struct is garbage collected. Oddly enough, uncommenting the commented print line, will yield different memory addresses for the meows:
Blahs: []
Blahs: [{1 0x1030e0c0}]
Blahs: [{1 0x1030e0c0} {2 0x1030e0c0}]
kittenSays: meow
Blahs: []
Blahs: [{1 0x1030e0f8}]
kittenSays: nyan
I think this may be due to the print being deferred in some way (?), but interesting illustration of some memory mgmt behavior, and one more vote for:
[]MyStruct = nil
答案4
得分: 5
Go 1.21 (Q3 2023)将提出一个新的内置关键字:clear()
。
issue 56351包含了以下文档:
内置函数
clear
接受一个map、slice或类型参数类型的参数,并删除或将所有元素清零。
调用 参数类型 结果 clear(m)
map[K]T
删除所有条目,结果是一个空的 map (len(m) == 0)
clear(s)
[]T
将 s
的长度之前的所有元素设置为T
的零值clear(t)
类型参数 参见下文 如果参数类型是类型参数,则其类型集中的所有类型必须是map或slice,并且
clear
执行与实际类型参数相对应的操作。如果map或slice为
nil
,clear
不执行任何操作。
英文:
Go 1.21 (Q3 2023) will propose a new builtin keyword: clear()
issue 56351 includes the following documentation:
> The built-in function clear
takes an argument of map, slice or type parameter type, and deletes or zeroes out all elements.
>
> | Call | Argument type | Result > |
> |------------|----------------|--------------------------------------------------------|
> | clear(m)
| map[K]T
| deletes all entries, resulting in an empty map (len(m) == 0)
|
> | clear(s)
| []T
| sets all elements up to the length of s
to the zero value of T
|
> | clear(t)
| type parameter | see below > |
>
> If the argument type is a type parameter, all types in its type set must be maps or slices, and clear
performs the operation corresponding to the actual type argument.
>
> If the map or slice is nil
, clear
is a no-op.
答案5
得分: 0
2023更新
在Go 1.21中,您可以使用clear函数:
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论