如何以不可变性暴露切片成员?

huangapple go评论85阅读模式
英文:

How to expose a slice member with immutability?

问题

我有一个包含切片成员的结构体,并且有一个方法来公开这个切片。但是我不希望调用者能够更改切片的内容。如果我这样做:

type A struct {
	slice []int
}

func (a *A) list() []int {
	return a.slice
}

这是不安全的,因为内容可以很容易地被修改:

a := A{[]int{1, 2, 3}}
_ = append(a.list()[:2], 4)
fmt.Println(a.list()) // [1 2 4]

显然,我可以让list()返回切片的副本来避免这个问题:

func (a *A) list() []int {
	return append([]int{}, a.slice...)
}

但这意味着每次我只想遍历切片时都会创建一个副本,这似乎是浪费的。有没有一种方法可以在不必要复制的情况下做到这一点?

英文:

I have a struct with a slice member, and a method to expose this slice. But I don't want the caller being able to change the content of the slice. If I do this:

type A struct {
	slice []int
}

func (a *A) list() []int {
	return a.slice
}

it is not safe, as the content can be easily modified:

a := A{[]int{1, 2, 3}}
_ = append(a.list()[:2], 4)
fmt.Println(a.list()) // [1 2 4]

Obviously I can let list() return a copy of the slice to avoid this:

func (a *A) list() []int {
	return append([]int{}, a.slice...)
}

but that means every time when I just want to iterate through the slice I created a copy, which seems wasteful. Is there a way to do this without unnecessary copying?

答案1

得分: 3

一旦你通过返回这个切片将其提供给外部调用者,它就可以被修改。如果出于性能原因不接受复制,你可以实现一个访问者模式:

func (a *A) Visit(f func(int)) {
    for _, v := range a.slice {
        f(v)
    }
}

这样完全不会暴露切片,并且允许客户端代码一次性查看切片中的所有项。如果这些项不是指针或其他可变类型,那么这实际上是只读的,因为访问者回调函数将接收到值的副本。

可选地,如果你想要在迭代过程中提前停止,访问者可以返回一个布尔值。

func (a *A) Visit(f func(int) bool) {
    for _, v := range a.slice {
        if !f(v) {
            return
        }
    }
}
英文:

As soon as you provide this slice to an external caller by returning it, it can be modified. If copying isn't acceptable for performance reasons, you can implement a visitor:

func (a *A) Visit(f func(int)) {
    for _, v := range a.slice {
        f(v)
    }
}

This doesn't expose the slice at all, and allows client code to see all items in the slice once. If the items aren't pointers, or other mutable types, this is effectively read-only as the visitor callback will receive a copy of the value.

Optionally, the visitor could return a boolean in case you want to stop the iteration early.

func (a *A) Visit(f func(int) bool) {
    for _, v := range a.slice {
        if !f(v) {
            return
        }
    }
}

huangapple
  • 本文由 发表于 2022年11月21日 07:22:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/74512806.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定