假设一个Go 1.18的程序有quite heavystruct,对它来说,复制被认为是昂贵的:

type MyStruct struct {
    P string
    // a lot of properties
}

现在让我们定义一个函数,将slice个这样的元素作为输入参数,其目标是更新每个切片元素的属性:

func myFunc(sl []MyStruct) {
    for i := range sl {
        p := &sl[i]       // <-- HERE
        p.P = "bar"
        // other properties mutations
    }
}

<-- HERE标记处,Golang编译器是将片元素的temporary copy放入循环的作用域,还是获取片元素in-place的地址?

这样做是为了避免复制整个切片元素.

一个有效的例子:https://go.dev/play/p/jHOC2DauyrQ?v=goprev

推荐答案

&sl[i]不复制Slice元素,它只计算第ith元素的地址.

Slice元素充当变量,&x的计算结果为x个变量的address.想想看:既然&sl[i]是第i个元素的地址,这个地址既不需要也不使用struct值,为什么要复制呢?

如果您的片太大,以至于您担心(隐式)副本的性能影响,您真的应该首先考虑将指针存储在片中,这样您就可以使循环和访问元素变得更加简单,而不必担心副本:

func myFunc(sl []*MyStruct) {
    for _, v := range sl {
        v.P = "bar"
        // other properties mutations
    }
}

另请注意,如果您的切片包含非指针,并且您想要更改切片元素的字段,则索引切片并引用该字段也不涉及复制 struct 元素:

func myFunc(sl []MyStruct) {
    for i := range sl {
        sl[i].P = "bar"
        // other properties mutations
    }
}

是的,如果您必须修改多个字段,这可能会更繁琐,效率可能会更低(但编译器也可能识别和优化多个sl[i]个表达式的求值).

Go相关问答推荐

如何预编译Golang标准库?

如何在Golang中获取mp3文件的持续时间?

Websocket服务器实现与x/net库trowing 403

通过代理从golang连接到ftp

如何修复 Go 中协议缓冲区定义中重新定义的字段?

在 go 中,将接收器 struct 从值更改为指针是否向后兼容?

Golang text/template中的startswith函数 - 入门教程

如何在 Go msgraph-sdk-go 中转发消息并包括抄送和/或密送收件人?

Yocto 无法交叉编译 GoLang Wails 应用程序

当客户端同时是服务器时,需要什么 mTLS 证书?

如何使用 GolangCI 删除未使用的导入

速率限制特定端点

golang pic.ShowImage 为什么它不生成图像而是向我发送base64值

为什么import和ImportSpec之间可以出现多行注释,而PackageName和ImportPath之间不能出现?

如何优雅地映射到 Go 中返回可变长度数组的方法?

GoLang 遍历 yaml 文件

跟踪长时间运行的任务的进度 - 正确的方法

为什么我不能使用来自 gocloak 中 Login() 的访问令牌在 KeyCloak 中创建新客户端?

Scanner.Buffer - 最大值对自定义拆分没有影响?

关于GO的几个问题