translated

This commit is contained in:
geekpi 2017-05-15 13:46:25 +08:00
parent 7ef84699ba
commit 0f59da8269
2 changed files with 324 additions and 326 deletions

View File

@ -1,326 +0,0 @@
translating--geekpi
<header class="post-header" style="text-rendering: optimizeLegibility; font-family: &quot;Noto Serif&quot;, Georgia, Cambria, &quot;Times New Roman&quot;, Times, serif; font-size: 20px; text-align: start; background-color: rgb(255, 255, 255);">[How to use slice capacity and length in Go][14]
============================================================</header>
<aside class="post-side" style="text-rendering: optimizeLegibility; position: fixed; top: 80px; left: 0px; width: 195px; padding-right: 5px; padding-left: 5px; text-align: right; z-index: 300; font-family: &quot;Noto Serif&quot;, Georgia, Cambria, &quot;Times New Roman&quot;, Times, serif; font-size: 20px;"></aside>
Quick pop quiz - what does the following code output?
```
vals := make([]int, 5)
for i := 0; i < 5; i++ {
vals = append(vals, i)
}
fmt.Println(vals)
```
_[Cheat and run it on the Go Playground][1]_
If you guessed `[0 0 0 0 0 0 1 2 3 4]` you are correct.
_Wait, what?_ Why isn't it `[0 1 2 3 4]`?
Don't worry if you got the pop quiz wrong. This is a fairly common mistake when transitioning into Go and in this post we are going to cover both why the output isn't what you expected along with how to utilize the nuances of Go to make your code more efficient.
### Slices vs Arrays
In Go there are both arrays and slices. This can be confusing at first, but once you get used to it you will love it. Trust me.
There are many differences between slices and arrays, but the primary one we want to focus on in this article is that the size of an array is part of its type, whereas slices can have a dynamic size because they are wrappers around arrays.
What does this mean in practice? Well, let's say we have the array `val a [10]int`. This array has a fixed size and that can't be changed. If we were to call `len(a)` it would always return 10, because that size is part of the type. As a result, if you suddenly need more than 10 items in your array you have to create a new object with an entirely different type, such as `val b [11]int`, and then copy all of your values from `a` over to `b`.
While having arrays with set sizes is valuable in specific cases, generally speaking this isn't what developers want. Instead, they want to work with something similar to an array in Go, but with the ability to grow over time. One crude way to do this would be to create an array that is much bigger than it needs to be and then to treat a subset of the array as your array. An example of this is shown in the code below.
```
var vals [20]int
for i := 0; i < 5; i++ {
vals[i] = i * i
}
subsetLen := 5
fmt.Println("The subset of our array has a length of:", subsetLen)
// Add a new item to our array
vals[subsetLen] = 123
subsetLen++
fmt.Println("The subset of our array has a length of:", subsetLen)
```
_[Run it on the Go Playground][2]_
With this code we have an array with a set size of 20, but because we are only using a subset our code can pretend that the length of the array is 5, and then 6 after we add a new item to our array.
This is (very roughly speaking) how slices work. They wrap an array with a set size, much like our array in the previous example has a set size of 20.
They also keep track of the subset of the array that is available for your program to use - this is the `length` attribute, and it is similar to the `subsetLen` variable in the previous example.
Finally, a slice also has a `capacity`, which is similar to the total length of our array (20) in the previous example. This is useful because it tells you how large your subset can grow before it will no longer fit in the array that is backing the slice. When this does happen, a new array will need to be allocated, but all of this logic is hidden behind the `append` function.
In short, combining slices with the `append` function gives us a type that is very similar to arrays, but is capable of growing over time to handle more elements.
Let's look at the previous example again, but this time we will use a slice instead of an array.
```
var vals []int
for i := 0; i < 5; i++ {
vals = append(vals, i)
fmt.Println("The length of our slice is:", len(vals))
fmt.Println("The capacity of our slice is:", cap(vals))
}
// Add a new item to our array
vals = append(vals, 123)
fmt.Println("The length of our slice is:", len(vals))
fmt.Println("The capacity of our slice is:", cap(vals))
// Accessing items is the same as an array
fmt.Println(vals[5])
fmt.Println(vals[2])
```
_[Run it on the Go Playground][3]_
We can still access elements in our slice just like we would arrays, but by using a slice and the `append` function we no longer have to think about the size of the backing array. We are still able to figure these things out by using the `len` and `cap` functions, but we don't have to worry too much about them. Neat, right?
### Back to the pop quiz
With that in mind, let's look back at our pop quiz code to see what went wrong.
```
vals := make([]int, 5)
for i := 0; i < 5; i++ {
vals = append(vals, i)
}
fmt.Println(vals)
```
When calling `make` we are permitted to pass in up to 3 arguments. The first is the type that we are allocating, the second is the `length` of the type, and the third is the `capacity` of the type (_this parameter is optional_).
By passing in the arguments `make([]int, 5)` we are telling our program that we want to create a slice with a length of 5, and the capacity is defaulted to the length provided - 5 in this instance.
While this might seem like what we wanted at first, the important distinction here is that we told our slice that we wanted to set both the `length` and `capacity` to 5, and then we proceeded to call the `append` function which assumes you want to add a new element _after_ the initial 5, so it will increase the capacity and start adding new elements at the end of the slice.
You can actually see the capacity changing if you add a `Println()` statement to your code.
```
vals := make([]int, 5)
fmt.Println("Capacity was:", cap(vals))
for i := 0; i < 5; i++ {
vals = append(vals, i)
fmt.Println("Capacity is now:", cap(vals))
}
fmt.Println(vals)
```
_[Run it on the Go Playground][4]_
As a result, we end up getting the output `[0 0 0 0 0 0 1 2 3 4]` instead of the desired `[0 1 2 3 4]`.
How do we fix it? Well, there are several ways to do this, so we are going to cover two of them and you can pick whichever makes the most sense in your situation.
### Write directly to indexes instead of using `append`
The first fix is to leave the `make` call unchanged and explicitly state the index that you want to set each element to. Doing this, we would get the following code:
```
vals := make([]int, 5)
for i := 0; i < 5; i++ {
vals[i] = i
}
fmt.Println(vals)
```
_[Run it on the Go Playground][5]_
In this case the value we are setting happens to be the same as the index we want to use, but you can also keep track of the index independently.
For example, if you wanted to get the keys of a map you could use the following code.
```
package main
import "fmt"
func main() {
fmt.Println(keys(map[string]struct{}{
"dog": struct{}{},
"cat": struct{}{},
}))
}
func keys(m map[string]struct{}) []string {
ret := make([]string, len(m))
i := 0
for key := range m {
ret[i] = key
i++
}
return ret
}
```
_[Run it on the Go Playground][6]_
This works well because we know that the exact length of the slice we return will be the same as the length of the map, so we can initialize our slice with that length and then assign each element to an appropriate index. The downside to this approach is that we have to keep track of `i` so that we know what index to place every value in.
This leads us to the second approach we are going to cover...
### Use `0` as your length and specify your capacity instead
Rather than keeping track of which index we want to add our values to, we can instead update our `make` call and provide it with two arguments after the slice type. The first, the length of our new slice, will be set to `0`, as we haven't added any new elements to our slice. The second, the capacity of our new slice, will be set to the length of the map parameter because we know that our slice will eventually have that many strings added to it.
This will still construct the same array behind the scenes as the previous example, but now when we call `append` it will know to place items at the start of our slice because the length of the slice is 0.
```
package main
import "fmt"
func main() {
fmt.Println(keys(map[string]struct{}{
"dog": struct{}{},
"cat": struct{}{},
}))
}
func keys(m map[string]struct{}) []string {
ret := make([]string, 0, len(m))
for key := range m {
ret = append(ret, key)
}
return ret
}
```
_[Run it on the Go Playground][7]_
### Why do we bother with capacity at all if `append` handles it?
The next thing you might be asking is, "Why are we even telling our program a capacity if the `append` function can handle increasing the capacity of my slice for me?"
The truth is, in most cases you don't need to worry about this too much. If it makes your code significantly more complicated, just initialize your slice with `var vals []int` and let the `append`function handle the heavy lifting for you.
But this case is different. It isn't an instance where declaring the capacity is difficult; In fact, it is actually quite easy to determine what the final capacity of our slice needs to be because we know it will map directly to the provided map. As a result, we can declare the capacity of our slice when we initialize it and save our program from needing to perform unnecessary memory allocations.
If you want to see what the extra memory allocations look like, run the following code on the Go Playground. Every time capacity increases our program needed to do another memory allocation.
```
package main
import "fmt"
func main() {
fmt.Println(keys(map[string]struct{}{
"dog": struct{}{},
"cat": struct{}{},
"mouse": struct{}{},
"wolf": struct{}{},
"alligator": struct{}{},
}))
}
func keys(m map[string]struct{}) []string {
var ret []string
fmt.Println(cap(ret))
for key := range m {
ret = append(ret, key)
fmt.Println(cap(ret))
}
return ret
}
```
_[Run it on the Go Playground][8]_
Now compare this to the same code but with a predefined capacity.
```
package main
import "fmt"
func main() {
fmt.Println(keys(map[string]struct{}{
"dog": struct{}{},
"cat": struct{}{},
"mouse": struct{}{},
"wolf": struct{}{},
"alligator": struct{}{},
}))
}
func keys(m map[string]struct{}) []string {
ret := make([]string, 0, len(m))
fmt.Println(cap(ret))
for key := range m {
ret = append(ret, key)
fmt.Println(cap(ret))
}
return ret
}
```
_[Run it on the Go Playground][9]_
In the first code sample our capacity starts at `0`, and then increases to `1`, `2`, `4`, and then finally `8`, meaning we had to allocate a new array 5 different times, and on top of that the final array used to back our slice has a capacity of `8`, which is bigger than we ultimately needed.
On the other hand, our second sample starts and ends with the same capacity (`5`) and only needs to allocate it once at the start of the `keys()` function. We also avoid wasting any extra memory and return a slice with the perfect size array backing it.
### Don't over-optimize
As I said before, I typically wouldn't encourage anyone to worry about minor optimizations like this, but in cases where it is really obvious what the final size should be I strongly encourage you to try to set an appropriate capacity or length for your slices.
Not only does it help improve the performance of your application, but it can also help clarify your code a bit by explicitly stating the relationship between the size of your input and the size of your output.
### In summary...
> Hi there! I write a lot about Go, web development, and other topics I find interesting.
>
> If you want to stay up to date with my writing, please [sign up for my mailing list][10]. I'll send you a FREE sample of my upcoming book, Web Development with Go, and an occasional email when I publish a new article (usually 1-2 per week).
>
> Oh, and I promise I don't spam. I hate it as much as you do :)
This article is not meant to be an exhaustive discussion on the differences between slices or arrays, but instead is meant to serve as a brief introduction into how capacity and length affect your slices, and what purpose they serve in the grand scheme of things.
For further reading, I highly recommend the following articles from the Go Blog:
* [Go Slices: usage and internals][11]
* [Arrays, slices (and strings): The mechanics of 'append'][12]
* [Slice Tricks][13]
--------------------------------------------------------------------------------
作者简介:
Jon is a software consultant and the author of the book Web Development with Go. Prior to that he founded EasyPost, a Y Combinator backed startup, and worked at Google.
https://www.usegolang.com
--------------------------------------------------------------------------------
via: https://www.calhoun.io/how-to-use-slice-capacity-and-length-in-go
作者:[Jon Calhoun][a]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]:https://www.calhoun.io/hire-me
[1]:https://play.golang.org/p/7PgUqBdZ6Z
[2]:https://play.golang.org/p/Np6-NEohm2
[3]:https://play.golang.org/p/M_qaNGVbC-
[4]:https://play.golang.org/p/d6OUulTYM7
[5]:https://play.golang.org/p/JI8Fx3fJCU
[6]:https://play.golang.org/p/kIKxkdX35B
[7]:https://play.golang.org/p/h5hVAHmqJm
[8]:https://play.golang.org/p/fDbAxtAjLF
[9]:https://play.golang.org/p/nwT8X9-7eQ
[10]:https://www.calhoun.io/how-to-use-slice-capacity-and-length-in-go/?utm_source=golangweekly&utm_medium=email#mailing-list-form
[11]:https://blog.golang.org/go-slices-usage-and-internals
[12]:https://blog.golang.org/slices
[13]:https://github.com/golang/go/wiki/SliceTricks
[14]:https://www.calhoun.io/how-to-use-slice-capacity-and-length-in-go/

View File

@ -0,0 +1,324 @@
<header class="post-header" style="text-rendering: optimizeLegibility; font-family: &quot;Noto Serif&quot;, Georgia, Cambria, &quot;Times New Roman&quot;, Times, serif; font-size: 20px; text-align: start; background-color: rgb(255, 255, 255);">[在 Go 中如何使用切片的容量和长度][14]
============================================================</header>
<aside class="post-side" style="text-rendering: optimizeLegibility; position: fixed; top: 80px; left: 0px; width: 195px; padding-right: 5px; padding-left: 5px; text-align: right; z-index: 300; font-family: &quot;Noto Serif&quot;, Georgia, Cambria, &quot;Times New Roman&quot;, Times, serif; font-size: 20px;"></aside>
快速测试 - 下面的代码输出什么?
```
vals := make([]int, 5)
for i := 0; i < 5; i++ {
vals = append(vals, i)
}
fmt.Println(vals)
```
_[在 Go Playground 运行一下][1]_
如果你猜测的是 `[0 0 0 0 0 0 1 2 3 4]`,那你是对的。
_等等什么_ 为什么不是 `[0 1 2 3 4]`?
如果你在测试中做错了,你也不用担心。这是在 Go 过渡的过程中相当常见的错误,在这篇文章中,我们将说明为什么输出不是你预期的,以及如何利用 Go 的细微差别来使你的代码更有效率。
### 切片 vs 数组
在 Go 中同时有数组和切片。这可能令人困惑,但一旦你习惯了,你会喜欢上它。请相信我
切片和数组之间存在许多差异,但我们要在本文中重点介绍的内容是数组的大小是其类型的一部分,而切片可以具有动态大小,因为它们是围绕数组的包装。
这在实践中意味着什么?那么假设我们有数组 `val a [10]int`。该数组具有固定大小,且无法更改。如果我们调用 “len(a)”,它总是返回 10因为这个大小是类型的一部分。因此如果你突然在数组中需要超过 10 个项,则必须创建一个完全不同类型的新对象,例如 `val b [11]int`,然后将所有值从 `a` 复制到 `b`
在特定情况下,含有集合大小的数组是有价值的,但一般而言,这不是开发人员想要的。相反,他们希望在 Go 中使用类似于数组的东西,但是随着时间的推移,它们能够随时增长。一个粗略的方式是创建一个比它需要大得多的数组,然后将数组的一个子集视为数组。下面的代码是个例子。
```
var vals [20]int
for i := 0; i < 5; i++ {
vals[i] = i * i
}
subsetLen := 5
fmt.Println("The subset of our array has a length of:", subsetLen)
// Add a new item to our array
vals[subsetLen] = 123
subsetLen++
fmt.Println("The subset of our array has a length of:", subsetLen)
```
_[在 Go Playground 中运行][2]_
在代码中,我们有一个长度为 20 的数组,但是由于我们只使用一个子集,代码中我们可以假定数组的长度是 5然后在我们向数组中添加一个新的项之后是 6。
这是(非常粗略地说)切片是如何工作的。它们包含一个具有设置大小的数组,就像我们前面的例子中的数组一样,它的大小为 20。
它们还跟踪程序中使用的数组的子集 - 这就是 `append` 属性,它类似于上一个例子中的 `subsetLen` 变量。
最后,一个 slice 还有一个 `capacity`类似于前面例子中我们的数组20的总长度。这是很有用的 因为它会告诉你的子集在无法容纳切片数组之前可以增长的大小。当发生这种情况时,需要分配一个新的数组,但所有这些逻辑都隐藏在 `append` 函数的后面。
简而言之,使用 `append` 函数组合切片给我们一个非常类似于数组的类型,但随着时间的推移,它可以处理更多的元素。
我们再来看一下前面的例子,但是这次我们将使用切片而不是数组。
```
var vals []int
for i := 0; i < 5; i++ {
vals = append(vals, i)
fmt.Println("The length of our slice is:", len(vals))
fmt.Println("The capacity of our slice is:", cap(vals))
}
// Add a new item to our array
vals = append(vals, 123)
fmt.Println("The length of our slice is:", len(vals))
fmt.Println("The capacity of our slice is:", cap(vals))
// Accessing items is the same as an array
fmt.Println(vals[5])
fmt.Println(vals[2])
```
_[在 Go Playground 中运行][3]_
我们仍然可以像数组一样访问我们的切片中的元素,但是通过使用切片和 `append` 函数,我们不再需要考虑背后数组的大小。我们仍然可以通过使用 `len``cap` 函数来计算出这些东西,但是我们不用担心太多。简洁吧?
### 回到测试
记住这点,让我们回顾前面的测试,看下什么出错了。
```
vals := make([]int, 5)
for i := 0; i < 5; i++ {
vals = append(vals, i)
}
fmt.Println(vals)
```
当调用 `make` 时,我们允许最多传入 3 个参数。第一个是我们分配的类型,第二个是类型的 `长度`,第三个是类型的 `容量`_这个参数是可选的_
通过传递参数 `make([]int, 5)`,我们告诉程序我们要创建一个长度为 5 的切片,在这种情况下,默认的容量与长度相同 - 本例中是 5。
虽然这可能看起来像我们想要的那样,这里的重要区别是我们告诉我们的切片,我们要将 `长度``容量` 设置为 5假设你想要在初始的 5 个元素_之后_添加新的元素我们接着调用 `append` 函数,那么它会增加容量的大小,并且会在切片的最后添加新的元素。
如果在代码中添加一条 `Println()` 语句,你可以看到容量的变化。
```
vals := make([]int, 5)
fmt.Println("Capacity was:", cap(vals))
for i := 0; i < 5; i++ {
vals = append(vals, i)
fmt.Println("Capacity is now:", cap(vals))
}
fmt.Println(vals)
```
_[在 Go Playground 中运行][4]_
最后,我们最终得到 `[0 0 0 0 0 0 1 2 3 4]` 的输出而不是希望的 `[0 1 2 3 4]`
如何修复它呢?好的,这有几种方法,我们将讲解两种,你可以选取任何一种在你的场景中最有用的方法。
### 直接使用索引写入而不是 `append`
第一种修复是保留 `make` 调用不变,并且显式地使用索引来设置每个元素。这样,我们就得到如下的代码:
```
vals := make([]int, 5)
for i := 0; i < 5; i++ {
vals[i] = i
}
fmt.Println(vals)
```
_[在 Go Playground 中运行][5]_
在这种情况下,我们设置的值恰好与我们要使用的索引相同,但是你也可以独立跟踪索引。
比如,如果你想要获取 map 的键,你可以使用下面的代码。
```
package main
import "fmt"
func main() {
fmt.Println(keys(map[string]struct{}{
"dog": struct{}{},
"cat": struct{}{},
}))
}
func keys(m map[string]struct{}) []string {
ret := make([]string, len(m))
i := 0
for key := range m {
ret[i] = key
i++
}
return ret
}
```
_[在 Go Playground 中运行][6]_
这样做很好,因为我们知道我们返回的切片的长度将与 map 的长度相同,因此我们可以用该长度初始化我们的切片,然后将每个元素分配到适当的索引中。这种方法的缺点是我们必须跟踪 `i`,以便了解每个索引要设置的值。
这就让我们引出了第二种方法。。。
### 使用 `0` 作为你的长度并指定容量
与其跟踪我们要添加的值的索引,我们可以更新我们的 `make` 调用,并在切片类型之后提供两个参数。第一个,我们的新切片的长度将被设置为 `0`,因为我们还没有添加任何新的元素到切片中。第二个,我们新切片的容量将被设置为 map 参数的长度,因为我们知道我们的切片最终会添加许多字符串。
这会如前面的例子那样仍旧会在背后构建相同的数组,但是现在当我们调用 `append`它会将它们放在切片开始处因为切片的长度是0。
```
package main
import "fmt"
func main() {
fmt.Println(keys(map[string]struct{}{
"dog": struct{}{},
"cat": struct{}{},
}))
}
func keys(m map[string]struct{}) []string {
ret := make([]string, 0, len(m))
for key := range m {
ret = append(ret, key)
}
return ret
}
```
_[在 Go Playground 中运行][7]_
### 如果 `append` 处理它,为什么我们还要担心容量呢?
接下来你可能会问:“如果 `append` 函数可以为我增加切片的容量,那我们为什么要告诉程序容量呢?
事实是,在大多数情况下,你不必担心这太多。如果它使你的代码变得更复杂,只需用 `var vals []int` 初始化你的切片,然后让 `append` 函数处理接下来的事。
但这种情况是不同的。它并不是声明容量是困难的例子,实际上这很容易确定我们的切片的最后容量,因为我们知道它将直接映射到提供的 map 中。因此,当我们初始化它时,我们可以声明切片的容量,并免于让我们程序执行不必要的内存分配。
如果要查看额外的内存分配情况,请在 Go Playground 上运行以下代码。每次增加容量,程序都需要做一次内存分配。
```
package main
import "fmt"
func main() {
fmt.Println(keys(map[string]struct{}{
"dog": struct{}{},
"cat": struct{}{},
"mouse": struct{}{},
"wolf": struct{}{},
"alligator": struct{}{},
}))
}
func keys(m map[string]struct{}) []string {
var ret []string
fmt.Println(cap(ret))
for key := range m {
ret = append(ret, key)
fmt.Println(cap(ret))
}
return ret
}
```
_[在 Go Playground 中运行][8]_
现在将此与相同的代码进行比较, 但具有预定义的容量。
```
package main
import "fmt"
func main() {
fmt.Println(keys(map[string]struct{}{
"dog": struct{}{},
"cat": struct{}{},
"mouse": struct{}{},
"wolf": struct{}{},
"alligator": struct{}{},
}))
}
func keys(m map[string]struct{}) []string {
ret := make([]string, 0, len(m))
fmt.Println(cap(ret))
for key := range m {
ret = append(ret, key)
fmt.Println(cap(ret))
}
return ret
}
```
_[在 Go Playground 中运行][9]_
在第一个代码示例中,我们的容量从 `0` 开始,然后增加到 `1`、 `2`、 `4`, 最后是 `8`,这意味着我们不得不分配 5 次数组,最后一个容纳我们切片的数组的容量是 `8`,这比我们最终需要的要大。
另一方面,我们的第二个例子开始和结束都是相同的容量(`5`),它只需要在 `keys()` 函数的开头分配一次。我们还避免了浪费任何额外的内存,并返回一个能放下这个数组的完美大小的切片。
### 不要过分优化
如前所述,我通常不鼓励任何人担心这样的小优化,但如果最后大小的效果真的很明显,那么我强烈建议你尝试为切片设置适当的容量或长度。
这不仅有助于提高程序的性能,还可以通过明确说明输入的大小和输出的大小之间的关系来帮助澄清你的代码。
### 总结
>你好我写了很多关于Go、Web 开发和其他我觉得有趣的话题。
>
>如果你想跟上最新的文章,请[注册我的邮件列表][10]。我会给你发送我新书的样例、Go 的 Web 开发、以及每当有新文章(通常每周 1-2 次)会给你发送邮件。
>
>哦,我保证不会发垃圾邮件。我像你一样讨厌它 :)
本文并不是对切片或数组之间差异的详细讨论,而是简要介绍了容量和长度如何影响切片,以及它们在方案中的用途。
为了进一步阅读,我强烈推荐 Go 博客中的以下文章:
* [Go Slices使用及内部][11]
* [数组、切片和字符串“append” 的机制][12]
* [切片技巧][13]
--------------------------------------------------------------------------------
作者简介:
Jon 是一名软件顾问,也是 “Web Development with Go” 一书的作者。在此之前,他创立了 EasyPost一家 Y Combinator 支持的创业公司,并在 Google 工作。
https://www.usegolang.com
--------------------------------------------------------------------------------
via: https://www.calhoun.io/how-to-use-slice-capacity-and-length-in-go
作者:[Jon Calhoun][a]
译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]:https://www.calhoun.io/hire-me
[1]:https://play.golang.org/p/7PgUqBdZ6Z
[2]:https://play.golang.org/p/Np6-NEohm2
[3]:https://play.golang.org/p/M_qaNGVbC-
[4]:https://play.golang.org/p/d6OUulTYM7
[5]:https://play.golang.org/p/JI8Fx3fJCU
[6]:https://play.golang.org/p/kIKxkdX35B
[7]:https://play.golang.org/p/h5hVAHmqJm
[8]:https://play.golang.org/p/fDbAxtAjLF
[9]:https://play.golang.org/p/nwT8X9-7eQ
[10]:https://www.calhoun.io/how-to-use-slice-capacity-and-length-in-go/?utm_source=golangweekly&utm_medium=email#mailing-list-form
[11]:https://blog.golang.org/go-slices-usage-and-internals
[12]:https://blog.golang.org/slices
[13]:https://github.com/golang/go/wiki/SliceTricks
[14]:https://www.calhoun.io/how-to-use-slice-capacity-and-length-in-go/