Golang的...语法真的只是可变参数吗?

3

我有一个简单的例子,虽然它可能看起来毫无意义,但这些例子已经与我的认知大相径庭。

有人能告诉我发生了什么吗?

我使用...语法将[]int中的元素传递给参数args,但是当我更改形式参数args时,[]int实际参数也会被更改。

我修改了args的顺序,但[]int的顺序也受到影响:

package main

import (
    "fmt"
)

func bubbleSort(args ...int) {
    for i := 0; i < len(args); i++ {
        for j := 0; j < len(args)-1; j++ {
            if args[j] > args[j+1] {
                args[j], args[j+1] = args[j+1], args[j]
            }
        }
    }
}

func main() {
    isle := []int{3, 6, 1, 2, 5}
    bubbleSort(isle...)

    fmt.Printf("%v\n", isle)
}

// [1 2 3 5 6]

如果我传入一个参数会怎样?显然,[]int 不受 args 影响:
package main

import (
    "fmt"
)

func bubbleSort(args ...int) {
    for i := 0; i < len(args); i++ {
        for j := 0; j < len(args)-1; j++ {
            if args[j] > args[j+1] {
                args[j], args[j+1] = args[j+1], args[j]
            }
        }
    }
}

func main() {
    isle := []int{3, 6, 1, 2, 5}
    bubbleSort(isle[0], isle[1], isle[2], isle[3], isle[4])

    fmt.Printf("%v\n", isle)
}

// [3 6 1 2 5]

4
请参阅规范。在第一个示例中,将isle传递给函数。函数修改了isle的后备数组。在第二个示例中,传递了一个带有后备数组的新切片到函数中。 - Charlie Tumahai
@CeriseLimón 很好的回答,解决了我的疑惑。这确实很令人困惑。。 - YunYaJun
1
可变参函数实际上只是以切片作为最终参数的函数,具有在调用点构建切片的语法快捷方式。 - Hymns For Disco
在 Golang 中,如果你发送了一个切片到某个地方,你必须意识到它可能会被修改。 - comdiv
1个回答

1

Golang并不像Rust那样关注"变量安全性",因此您不能认为发送到函数的切片不会被破坏。

如果您想要使用可变参数函数但发送切片,请先将其复制:

func myUnsafeFunc(args ...int) { 
   // it will change args
}
func wantToSaveSliceFunc() {
   myLovingSlice := []int{1,2,3}
   myUnsafeFunc(append([]int(nil), slice...)...)
}

https://freshman.tech/snippets/go/copy-slices/

如果您记得 扩展操作符... 只是一个语法糖,那么它并不会令人困惑。

除非有充分的理由(例如 fmt.Printf(...)),不要在自己的 API 中使用它,而是优先使用显式的 x []type 表示法,而不是 x ...type

func myUnsafeFunc(args []int) { 
   // it will change args
}
func wantToSaveSliceFunc() {
   myLovingSlice := []int{1,2,3}
   myUnsafeFunc(append([]int(nil), slice...))
}

仍需复制切片,但语义更为清晰


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接