11

How to use function as map's key? for example:

type Action func(int)
func test(a int) { }
func test2(a int) { }

func main() {
  x := map[Action]bool{}
  x[test] = true
  x[test2] = false
}

those code would show an error: invalid map key type Action

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Kokizzu
  • 24,974
  • 37
  • 137
  • 233

8 Answers8

13

You cannot use a function as a map key. The language specification clearly says:

The comparison operators == and != must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice.

Tore Olsen
  • 2,153
  • 2
  • 22
  • 35
7

You can't use functions as keys in maps : the key type must be comparable.

From Go blog :

map keys may be of any type that is comparable. The language spec defines this precisely, but in short, comparable types are boolean, numeric, string, pointer, channel, and interface types, and structs or arrays that contain only those types. Notably absent from the list are slices, maps, and functions; these types cannot be compared using ==, and may not be used as map key

What you might use, depending on your precise use case, is an interface.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
7

You can use reflect.

    import (
       "reflect"
       "math"
    )


    func foo () {
       table := make(map[uintptr] string)
       table[reflect.ValueOf(math.Sin)] = "Sin"
       table[reflect.ValueOf(math.Cos)] = "Cos"
       println(table[reflect.ValueOf(math.Cos)])
    }
David Tolpin
  • 86
  • 1
  • 1
6

While functions can't be keys, function pointers can.

package main

import "fmt"

type strFunc *func() string

func main() {

    myFunc := func() string { return "bar" }
    m := make(map[strFunc]string)
    m[(strFunc)(&myFunc)] = "f"

    for f, name := range m {
        fmt.Println((*f)(), name)
    }
}

http://play.golang.org/p/9DdhYduX7E

  • 4
    This only works because you're using address of a variable, which is not the same as "function pointer". Here's your example with the same function assigned to another variable, and the resulting map has two entries, because variable addresses are different: https://play.golang.org/p/8DBIR4h1jU – Yuri Shkuro Mar 03 '17 at 21:16
4

Functions cannot be keys:

The comparison operators == and != must be fully defined for operands of the key type; thus the key type must not be a function, map, or slice.

Source

Makpoc
  • 3,921
  • 1
  • 19
  • 19
3

You cannot do this directly, as mentioned already, but you can sort of fake it you do something like this:

package main

import "fmt"

func a(i int) int {
    return i + 1
}

func b(i int) int {
    return i + 2
}

type Function func(int)int
type FunctionWrapper struct {
    f *Function
}

var fnMap = make(map[string]FunctionWrapper)

// MakeFunctionWrapper returns a unique FunctionWrapper per Function pointer, using fnMap to avoid having multiple values for the same function
func MakeFunctionWrapper(f Function) FunctionWrapper {
    key := fmt.Sprintf("%#v", f)
    data, ok := fnMap[key]
    if !ok {
        data = FunctionWrapper{&f}
        fnMap[key] = data
    }
    return data
}

func main() {
    functions := make(map[FunctionWrapper]bool)
    fa := MakeFunctionWrapper(a)
    fb := MakeFunctionWrapper(b)
    fb2 := MakeFunctionWrapper(b)
    functions[fa] = true
    functions[fb] = true
    functions[fb2] = false              // This overwrites the previous value since fb is essentially the same as fb2

    fmt.Println(functions[fa])          // "true"
    fmt.Println(functions[fb])          // "false"
    fmt.Println(functions[fb2])         // "false"
}

Check it out on the Go playground

This is a bit cumbersome, and I honestly think it's a very bad idea to essentially use the string version of a pointer as your map's key. But ... it's at least an option if you really need it.

Nerdmaster
  • 4,287
  • 1
  • 22
  • 16
  • This works only as long as you keep your function wrapper instances around and always re-use them. Creating 2 wrappers around the same function do not yield the same result: http://play.golang.org/p/DdwzCTORBf – chakrit Apr 04 '15 at 02:25
2

A clean way to achieve this is to wrap the functions in a struct, then use a pointer to this struct as the map key:

type FuncPack struct {
    TheFunc func(a int)
}

func Test(a int)  {}
func Test2(a int) {}

func main() {
    theMap := make(map[*FuncPack]bool)
    theMap[&FuncPack{TheFunc: Test}] = true
    theMap[&FuncPack{TheFunc: Test2}] = false
}
rodrigocfd
  • 6,450
  • 6
  • 34
  • 68
1

Use function pointers as map keys instead of functions.

The following example stores dependencies of function in a map, calculates the execution order and executes the functions in the correct order:

package main

import (
    "fmt"
)

type Action func()

var Premises = make(map[*Action][]*Action)

func OnSetup(action Action, premises ...*Action) *Action {
    ap := &action
    Premises[ap] = premises
    return ap
}

func rank(action *Action) int {
    if len(Premises[action]) == 0 {
        return 0
    }
    max := 0
    for _, p := range Premises[action] {
        r := rank(p)
        if r > max {
            max = r
        }
    }
    return max + 1
}

func Setup() {
    ranks := make(map[int][]*Action)
    for action := range Premises {
        r := rank(action)
        ranks[r] = append(ranks[r], action)
    }
    for r := 0; r < len(ranks); r++ {
        fmt.Println("Rank:", r)
        actions := ranks[r]
        for a := range actions {
            (*(actions[a]))()
        }
    }
}

func main() {
    a := OnSetup(func() { fmt.Println("a") })
    b := OnSetup(func() { fmt.Println("b") }, a)
    OnSetup(func() { fmt.Println("c") }, b)
    OnSetup(func() { fmt.Println("d") })
    Setup()
}
ceving
  • 21,900
  • 13
  • 104
  • 178