相比JavaScript里对象使用引用传递(passing-by-reference),Go里面,所有的变量传递都是值传递(passing-by-value)(和C语言一样)。
由于所有变量都没有按引用传递,Go当中的比较,自然也都是值比较,也就意味着只要两个数组的内容相同,则==
的结果就为true:
package main
import (
"fmt"
)
func main() {
foo := [2]int{1, 2}
bar := foo // 这里发生了拷贝
fmt.Printf("foo and bar pointing to the same array? : %v\n", &foo == &bar) // false
fmt.Printf("foo == bar: %v\n", foo == bar) // true
bar[0] = 3
fmt.Printf("foo == bar: %v\n", foo == bar) // false
}
不仅对数组有效,对对象也有效:
package main
import "fmt"
type Foobar struct {
privateVar int
PublicVar int
}
func main() {
f1 := Foobar{
privateVar: 123,
PublicVar: 456,
}
f2 := Foobar{
privateVar: 123,
PublicVar: 456,
}
fmt.Printf("f1 and f2 pointing to the same object? : %v\n", &f1 == &f2) // false
fmt.Printf("f1 == f2: %v\n", f1 == f2) // true
f2.privateVar = 888
fmt.Printf("f1 == f2: %v\n", f1 == f2) // false
}
struct 或 array 作为 map 的 key 的时候,也是采用比较的逻辑的,只要==
的结果为true,那它们就是同个key(这一点上 Javascript 和 Go 是一样的)。
如果你的目的是判断某个具体对象是否在cacheMap里面存在的话,一个选择是使用它的指针作为key(指针其实就是对其的一个引用),即map[*[2]int]bool
。
另一个选择是直接使用map[interface{}]
(Go1.18使用map[any]
),然后插入时传入指针作为key。这样使得可以使用任何类型的指针作为key。
注意如果这么干的话,你必须自己用额外代码确保插入的key都是指针,而不包括普通的非指针值,这是由于 Golang 的设计选择使得 interface{} 可以接受任何指针或非指针值,但在这里我们只希望接受指针。
这个本质是 Go 为了简化语言规则和编译器设计,没有提供 covariant, 所以采用了让 interface{} 接受包括指针在内的任何类型的值的方案,而不是使得*interface{}可以接受任何类型的指针的方案。
这又是一个值得吐槽的点了,方便了编译器的设计者,但是苦了使用者(导致一些场景下类型不清晰,很多库需要大量 reflect 代码对 interface{} 进行判断,又长又影响性能)。