【问题标题】:Make a struct "range-able"?使结构“可范围”?
【发布时间】:2015-01-26 10:35:55
【问题描述】:
type Friend struct {
    name string
    age  int
}

type Friends struct {
    friends []Friend
}

我想让Friends 可以范围化,这意味着,如果我有一个my_friends 类型为Friends 的变量,我可以通过它循环:

for i, friend := range my_friends {
    // bla bla
}

在 Go 中可以吗?

【问题讨论】:

  • 你能说明一个真实的用例吗?即,您为什么要这样做而不是 range Friends.friendsrange Friends.List()(仅示例)?
  • 示例:稍后您将列表升级为家庭(朋友组)列表,并且您不想更改现有的客户端代码,该代码可以完美地迭代单个朋友。

标签: go


【解决方案1】:

Friends 是一个结构体吗?否则干脆做

type Friends []Friend

【讨论】:

  • 我认为这是一个很好的答案。如果Friends struct 仅显示内容,则将类型定义为type Friends []Friend。 Go 可以在这样的类型上使用方法,而不仅仅是在 structs 上,这与大多数语言有点不同。
  • 我可能会晚一点参加聚会,但是如果我想遍历我自己的链表或我自己的集合实现怎么办?
【解决方案2】:

小心:正如deft_code 提到的,当循环中断时,这段代码会泄漏一个通道和一个goroutine。不要将其用作一般模式。


在 go 中,没有办法使任意类型与range 兼容,因为 range 仅支持切片、数组、通道和映射。

您可以使用range 对通道进行迭代,如果您想迭代动态生成的数据而无需使用切片或数组,这将非常有用。

例如:

func Iter() chan *Friend {
   c := make(chan *Friend)
   go func() {
      for i:=0; i < 10; i++ {
        c <- newFriend()
      }
      close(c)
   }()
   return c
}

func main() {
  // Iterate 
  for friend := range Iter() {
    fmt.Println("A friend:", friend)
  }
}

这是使某些东西“可调整”的最接近的方法。

所以一种常见的做法是在你的类型上定义一个方法Iter() 或类似的东西,然后 将其传递给range

请参阅the spec 以进一步阅读range

【讨论】:

  • 当然,缺点是在这里使用通道与在切片上测距相比效率不高。每个通道读/写都可能导致运行时切换到不同的 goroutine,而在大多数情况下,您可以跨越一个切片而不会被其他 goroutine 抢占。
  • 这是正确的,直到构建切片非常昂贵或不受欢迎。例如,在cbfs 中,我有时会遍历所有文件(或某个范围内的文件)。它们可能有数十亿个,并且需要针对数据库的批量请求和类似的有趣内容。
  • 如果范围没有迭代完成,这会泄漏一个通道和一个 goroutine。泄漏导致竞争或死锁的简单解决方案。一个完整的强大解决方案非常复杂和微妙。 Google 内部 Go 邮件列表的共识是,这是一种反模式。
  • @deft_code 你是对的,这段代码泄露了。我添加了一个警告,不要将其用作一般模式,尽管我喜欢它。
【解决方案3】:

例如,

var my_friends Friends

for i, friend := range my_friends.friends {
    // bla bla
}

【讨论】:

  • 这并不是对原始问题的真正答案。
  • @simonmenke 是的,但它可能更清楚地表明不需要 OP 要求的内容。
猜你喜欢
  • 1970-01-01
  • 2016-10-11
  • 1970-01-01
  • 2012-06-24
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 2013-09-27
相关资源
最近更新 更多