> For the complete documentation index, see [llms.txt](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/05.0/05.9/05.9.3.md).

# 05.9.3 使用 container/ring

这一节中将使用 `conRing.go` 中的 Go 语言代码阐述 `container/ring` 包的用法，下面分为四个部分介绍。注意，`container/ring` 比 `container/list` 和 `container/heap` 都简单多了，也就是说这个包中的函数比另外两个包中的要少一些。

`conRing.go` 中的第一个代码段如下：

```go
package main

import (
    "container/ring"
    "fmt"
)

var size int = 10
```

`size` 变量存储了要创建的环的大小。

`conRing.go` 的第二部分包含如下 Go 代码：

```go
func main() {
    myRing := ring.New(size + 1)
    fmt.Println("Empty ring:", *myRing)

    for i := 0; i < myRing.Len()-1; i++ {
        myRing.Value = i
        myRing = myRing.Next()
    }

    myRing.Value = 2
```

从上面可知，创建新的环需要使用 `ring.New()` 函数，它需要接受一个提供环的大小的参数。最后的 `myRing.Value = 2` 语句向环中加入了 2 这个值。不过前面的 `for` 循环中已经向环中加入了那个值。最后，环的零值指的是只有一个值为 `nil` 的元素的环。

`conRing.go` 的第三部分如下：

```go
    sum := 0
    myRing.Do(func(x interface{}) {
        t := x.(int)
        sum = sum + t
    })
    fmt.Println("Sum:", sum)
```

`ring.Do()` 函数可以对环上的每个元素依次调用一个函数。然而 `ring.Do()` 没有定义对环进行修改的行为。`x.(int)` 语句称为**类型断言**。第 7 章“反射和接口”中将详细介绍类型断言。目前，你只用知道这表示 `x` 是 `int` 类型的就行了。

`conRing.go` 的最后一部分程序如下：

```go
    for i := 0; i < myRing.Len()+2; i++ {
        myRing = myRing.Next()
        fmt.Print(myRing.Value, " ")
    }
    fmt.Println()
}
```

使用环会遇到的唯一的问题就是你可以无限调用 `ring.Next()`，所以你需要找到停下来的办法。这种情况下就需要用到 `ring.Len()` 函数。就个人而言，我比较倾向于使用 `ring.Do()` 函数来迭代环上的所有元素，因为这样代码更简洁，但用 `for` 循环其实也不错！

执行 `conRing.go` 将会生成如下输出：

```bash
$ go run conRing.go
Empty ring: {0x42000a080 0xc42000a1a0 <nil>}
Sum: 47
0 1 2 3 4 5 6 7 8 9 2 0 1
```

输出的结果证明环上可以存在重复的值，也就是说你只能通过 `ring.Len()` 函数才能安全地获取到环的大小。
