3

I'm puzzled as why this code doesn't work:

package main

import (
    "fmt"
    "sort"
)

type T [2]int

func (t T) Len() int           { return len(t) }
func (t T) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t T) Less(i, j int) bool { return t[i] < t[j] }

func main() {
    var x = [2]int{1, 0}
    fmt.Println(x)
    sort.Sort(T(x))
    fmt.Println(x)
}

It outputs (incorrectly):

[1 0]
[1 0]

Changing the type of T to slices does the correct thing.

9
  • IMO, Swap does not do anything to the receiver, i.e. t := T(x) t.Swap(0, 1) will cause nothing. Commented Jan 8, 2014 at 14:48
  • 1
    Everything in Go is passed (and assigned) by value, and arrays are not an exception. Slices have the so-called "reference semantics" meaning that their values contain a reference to the underlying data storage and hence passing a slice to a function copies the slice value but the copy shares the underlying data storage* with the original slice value. Commented Jan 8, 2014 at 15:20
  • kostix, your link mentions that "everything in Go is passed by value, but there are three buit-in "reference types" which are passed by value as well but internally they hold references to separately maintained data structure: maps, slices, channels". But I can't find anything mentioning that in the official Go spec. Have I missed something? Thanks. Commented Jan 8, 2014 at 16:31
  • @epsylon, no, I think you didn't. My take on this is that the spec is kind of a legal document: it might be too succinct but in exchange it's minimal -- it only defines what formally matters. The concept of types whose values have reference semantics is called here to help people understand why they behave in the way they behave; they might very well not exist formally in the language, and so they do. Commented Jan 8, 2014 at 21:43
  • @epsylon, you might also look at this from another angle: the spec does not explain "whys" and nether does it describe how a concept it defines behaves differently to a similar concept found in other specs. Say, if assignment of maps cloned those maps, the spec would supposedly define that explicitly, but since they don't, the spec is silent on this. Commented Jan 8, 2014 at 21:44

1 Answer 1

8

Slices are inherently reference types, meaning the slice header contains a pointer to the backing array, so they can be mutated without a pointer receiver. Arrays not being a reference type, are copied in full when calling your methods.

In order to do this with an array you need to change everything to use pointers, so your code might look something like:

package main

import (
    "fmt"
    "sort"
)

type T [2]int

func (t *T) Len() int           { return len(t) }
func (t *T) Swap(i, j int)      { t[i], t[j] = t[j], t[i] }
func (t *T) Less(i, j int) bool { return t[i] < t[j] }

func main() {
    var x = T([2]int{1, 0})
    fmt.Println(x)
    sort.Sort(&x)
    fmt.Println(x)
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.