sort.Slice() passing slice through interface{}

I’m trying to understand two questions for Go 1.8 sort.Slice() func.

  1. In func(i, j int) bool, how does this function get access to “people” so it can do the operation of people[i]? The “people” is passed as a interface{} in Slice() function to me but I don’t understand how less func() can in turn access it?

  2. If “people” is passed as interface{}, how it’s possible to do indexing for a interface{}? Why complier doesn’t complain “type interface {} does not support indexing”?

    people := []struct {
    Name string
    Age int
    }{
    {“Gopher”, 7},
    {“Alice”, 55},
    {“Vera”, 24},
    {“Bob”, 75},
    }
    sort.Slice(people, func(i, j int) bool { return people[i].Name < people[j].Name })
    fmt.Println(“By name:”, people)

    sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age })
    fmt.Println(“By age:”, people)

The second function passed into slice needs to be a closure. I don’t really know of a short and easy way to describe this so I’m going to suggest you check out a few articles I have written on this topic - https://www.calhoun.io/what-is-a-closure/

Then if you have any questions feel free to ask them here and I’ll be happy to answer them :slight_smile:

2 Likes

Thanks Jon. Great blog and book/videos!

I’m still trying to understand more on the case where a closure func is passed as an argument and the func can access other arguments as you mentioned in one of the closure use cases in post part II.

It seems to me that most of Closure examples are NOT for the above use case. How do I tell if a function passed in is a closure or not? How is it possible behind the scene that a closure func passed as the 2nd arg can access the 1st arg as in my original question and below? Does it access 1st argument as the interface{} type or original []struct type?

func Slice(slice interface{}, less func(i, j int) bool)

package main

import (  
  "fmt"
  "sort"
)

func main() {  
  numbers := []int{1, 11, -5, 8, 2, 0, 12}
  sort.Ints(numbers)
  fmt.Println("Sorted:", numbers)

  index := sort.Search(len(numbers), func(i int) bool {
    return numbers[i] >= 7
  })
  fmt.Println("The first number >= 7 is at index:", index)
  fmt.Println("The first number >= 7 is:", numbers[index])
}

How do I tell if a function passed in is a closure or not?

It will be a function literal that accesses one or more variables directly (e.g., not passed to the function as variables) from the immediately surrounding scope. In this case the function literal passed to sort.Search is a closure because it directly accesses numbers, which is in main's scope.

How is it possible behind the scene that a closure func passed as the 2nd arg can access the 1st arg as in my original question and below?

It doesn’t. It can access its arguments ( i int in the search example) and the variables in the other scopes it has access to (in this case, main's scope, the global scope, and the universal scope).

Does it access 1st argument as the interface{} type or original []struct type?

sort.Search accesses its 1st argument as an int. The function you passed accesses its 1st argument as an int.

1 Like

You can use sort.Slice with an abbreviated form of the original sort implementations - just define Less without Len and Swap for a more conventional approach: https://play.golang.org/p/1CuFWPO-MZ

Wrt. passing the first arg as an interface{} value, the sort.Slice function uses reflect to identify the type … here’s the source:

func Slice(slice interface{}, less func(i, j int) bool) {
   rv := reflect.ValueOf(slice)
   swap := reflect.Swapper(slice)
   length := rv.Len()
   quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length))
}

As it says in the documentation for reflect.Swapper: “Swapper panics if the provided interface is not a slice.”

2 Likes

Thank you Nathan and Charles. Great info and example and I see how the closure func in my case accessed the “main” scope. I was thinking it “jumped” into the 1st argument originally and that confused me.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.