如何找到函数类型的实现?

In Go, it's possible to create function types (https://golang.org/ref/spec#Function_types) like this

type Printer func(s string)

How can I find all of the functions which satisfy this type? For example, if I had the file below, how could I find out that consolePrinter is a Printer?

package main

import "fmt"

func main() {
    printToScreen(consolePrinter)
}

// Printer defines a function that prints a string.
type Printer func(s string)

func printToScreen(p Printer) {
    p("Hello")
}

// consolePrinter is a Printer (the function signature is identical).
func consolePrinter(s string) {
    fmt.Println(s)
}

I tried out guru, but the implements feature doesn't seem to support function types.

guru implements ./main.go:#134
/Users/adrian/go/gurutest/main.go:10.6-10.12: signature type Printer implements only interface{}

You are looking for functions that are assignable to Printer.

The guru tool does not support an "assignable to" query.

You can write a program to find functions that are assignable to Printer using the go/types package. Follow the tutorial to type check code and use AssignableTo to find the functions.

If you are only interested in finding functions declared at package level in gofmted code, then searching your code with a regexp might be good enough. This approach is not accurate, but is simple to do in many editors or the command line.

Here consolePrinter is not of type Printer it is required to declare consolePrinter to be of type Printer.You see console.Printer is a function with same underlying type that's why it can be pass to printToScreen which takes Printer type. Have a Look at the difference with their types

package main

import (
    "fmt"
    "reflect"
)

func main() {
    printToScreen(consolePrinter)
    fmt.Println(reflect.TypeOf(consolePrinter)) // Prints func(string) as its type
}

// Printer defines a function that prints a string.
type Printer func(s string)

func printToScreen(p Printer) {
    var consoleprint Printer // declare to be of type Printer
    fmt.Println(reflect.TypeOf(consoleprint)) // Prints main.Printer
    p("Hello")
}

// consolePrinter is a Printer (the function signature is identical).
func consolePrinter(s string) {
    fmt.Println(s)
}

Playground Example

I'm sure there are better ways for the specific case of printing, but in general you may be better to take a more idiomatic go approach of using interfaces.

Declare an interface and types that implement the interface:

type StringPrinter interface {
    PrintString(string)
}

type Console struct {
    // console specific stuff
}

func (c *Console) PrintString(s string) {
    // Printing code
}

type Paper struct {
    // paper specific stuff
}

func (p *Paper) PrintString(s string) {
    // Printing code
}

Then to print in different ways you can access through an interface in a generic way:

func main() {
    var sp StringPrinter

    sp = &Console{ /* member inits */ }
    sp.PrintString("Hello on console") 

    sp = &Paper{ /* member inits */ }
    sp.PrintString("Hello on paper")
}

You will be able to use guru on this form of code to find objects that implement the StringPrinter interface.

Working example, based on Thundercat's answer. guru would need to do something like this to provide support for looking up suitable functions to pass in (e.g. implementations of http.HandleFunc).

 package main

 import (
    "fmt"
    "go/ast"
    "go/importer"
    "go/parser"
    "go/token"
    "go/types"
    "log"
 )

 const hello = `package main

 import "fmt"

 const x = 1;

 func main() {
         fmt.Println("Hello, world")
 }

 // Printer defines a function that prints a string.
 type Printer func(s string)

 func consolePrinter(s string) {
    fmt.Println(s)
 }
 `

 func main() {
    fset := token.NewFileSet()

    // Parse the input string, []byte, or io.Reader,
    // recording position information in fset.
    // ParseFile returns an *ast.File, a syntax tree.
    f, err := parser.ParseFile(fset, "hello.go", hello, 0)
    if err != nil {
        log.Fatal(err) // parse error
    }

    // A Config controls various options of the type checker.
    // The defaults work fine except for one setting:
    // we must specify how to deal with imports.
    conf := types.Config{Importer: importer.Default()}

    // Type-check the package containing only file f.
    // Check returns a *types.Package.
    pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil)
    if err != nil {
        log.Fatal(err) // type error
    }

    names, signatures := getFunctionTypes(pkg)
    for i, name := range names {
        fmt.Println("Functions which implement", name)
        for _, implementor := range getFunctionsWhichImplement(signatures[i], pkg) {
            fmt.Println(implementor)
        }
    }
 }

 func getFunctionTypes(pkg *types.Package) (names []string, signatures []*types.Signature) {
    for _, name := range pkg.Scope().Names() {
        o := pkg.Scope().Lookup(name)

        if _, isType := o.(*types.TypeName); !isType {
            continue
        }
        var sig *types.Signature
        var isFunc bool
        if sig, isFunc = o.Type().Underlying().(*types.Signature); !isFunc {
            continue
        }
        signatures = append(signatures, sig)
        names = append(names, name)
    }
    return
 }

 func getFunctionsWhichImplement(sig *types.Signature, pkg *types.Package) (fns []types.Object) {
    for _, name := range pkg.Scope().Names() {
        o := pkg.Scope().Lookup(name)
        if _, isType := o.(*types.TypeName); isType {
            continue
        }
        var csig *types.Signature
        var isFunc bool
        if csig, isFunc = o.Type().Underlying().(*types.Signature); !isFunc {
            continue
        }

        if types.AssignableTo(sig, csig) {
            fns = append(fns, o)
        }
    }
    return
 }

The output of this code is shown below:

 Functions which implement Printer
 func cmd/hello.consolePrinter(s string)