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

时间:2018-04-27 10:01:10

标签: go

在Go中,可以像这样创建函数类型(https://golang.org/ref/spec#Function_types

type Printer func(s string)

如何找到满足此类型的所有功能?例如,如果我有下面的文件,我怎么能发现consolePrinterPrinter

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)
}

我尝试了大师,但implements功能似乎不支持功能类型。

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

4 个答案:

答案 0 :(得分:3)

您正在寻找assignablePrinter的功能。

guru工具不支持“可分配给”查询。

您可以使用go/types程序包编写程序以查找可分配给Printer的函数。按照tutorial键入检查代码,然后使用AssignableTo查找功能。

如果您只想在gofmted代码中查找在包级别声明的函数,那么使用regexp搜索代码可能就足够了。这种方法不准确,但在许多编辑器或命令行中都很简单。

答案 1 :(得分:0)

此处consolePrinter不属于Printer类型,需要将consolePrinter声明为Printer类型。您看到console.Printer是一个具有相同功能的函数基础类型,它可以传递到printToScreen的{​​{1}}。Printer type。看看他们的类型的差异

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

答案 2 :(得分:0)

工作示例,基于Thundercat的回答。 guru需要执行类似的操作,以便为查找要传入的合适函数提供支持(例如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
 }

此代码的输出如下所示:

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

答案 3 :(得分:-2)

我确信有更好的方法来处理特定的打印情况,但总的来说,采用更加惯用的方法来使用界面可能会更好。

声明实现接口的接口和类型:

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
}

然后以不同的方式打印,您可以通过以下通用方式访问界面:

func main() {
    var sp StringPrinter

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

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

您将能够在这种代码形式上使用guru来查找实现StringPrinter接口的对象。