I'm trying to represent some points on a 2d plane as pairs of integers. I want the points to be immutable (pass-by-value), but also for each to have a unique identity. To accomplish this, I made a struct with two int
s and a *string
. This works fine:
package main
import "fmt"
func main() {
s1 := ""
s2 := ""
p := Point{1,2,&s1}
p2 := Point{1,2,&s2}
fmt.Println(p2==p) // want false
}
type Point struct{X int; Y int; id *string}
$ go run a.go
false
Since the string
isn't actually used for anything (I only care about telling whether two points are the same), it seems like the canonical solution for making a unique reference like this would be to use a pointer to struct{}
instead:
package main
import "fmt"
func main() {
s1 := struct{}{}
s2 := struct{}{}
p := Point{1,2,&s1}
p2 := Point{1,2,&s2}
fmt.Println(p2==p) // want false
}
type Point struct{X int; Y int; id *struct{}}
However, now the two pointers are equal:
$ go run a.go
true
Why? Can this happen with strings as well? Should I use UUIDs instead?
The empty struct struct{}{}
is special.
See: http://golang.org/ref/spec#Size_and_alignment_guarantees where it says:
A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.
You can probably just put a field in there to get uniqueness. Something like:
package main
import "fmt"
type token struct{ bool }
type Point struct {
X int
Y int
id *token
}
func main() {
p := Point{1, 2, &token{}}
p2 := Point{1, 2, &token{}}
fmt.Println(p2 == p) // want false
}
To distinguish between two different points, even if they have the same coordinates, use addresses. For example,
package main
import "fmt"
type Point struct {
X int
Y int
p *Point
}
func NewPoint(x, y int) Point {
p := Point{X: x, Y: y}
p.p = &p
return p
}
func main() {
x, y := 1, 2
p1 := NewPoint(x, y)
p2 := NewPoint(x, y)
fmt.Println(p1)
fmt.Println(p2)
fmt.Println(p1 == p1)
fmt.Println(p1 == p2)
}
Output:
{1 2 0x10328000}
{1 2 0x10328020}
true
false