英文:
go/types finding if struct implements interface
问题
好的,下面是翻译好的内容:
好的,我看到你想使用go/types、go/parser等来生成一些代码;但是需要找出所有实现特定接口的结构体,这一点我已经弄清楚了。然而,如果结构体函数的定义在结构体上,使用types.Implements就无法匹配。
下面是代码示例:
获取接口
package ifacepkg
const interfacePkg = `package ifacepkg
type MyInterface interface {
    MyFunction() error
}
`
func getIface() *types.Interface {
    fset := token.NewFileSet()
    f, err := parser.ParseFile(fset, "iface.go", interfacePkg, 0)
    if err != nil {
        panic(err)
    }
    config := &types.Config{
        Error: func(e error) {
            fmt.Println(e)
        },
        Importer: importer.Default(),
    }
    info := types.Info{
        Types: make(map[ast.Expr]types.TypeAndValue),
        Defs:  make(map[*ast.Ident]types.Object),
        Uses:  make(map[*ast.Ident]types.Object),
    }
    pkg, e := config.Check("genval", fset, []*ast.File{f}, &info)
    if e != nil {
        fmt.Println(e)
    }
    return pkg.Scope().Lookup("MyInterface").Type().Underlying().(*types.Interface)
}
测试文件
package test
import "ifacepkg"
// User 结构体
type User struct {
    FirstName string
    LastName  string
}
func (u User) MyFunction() error {
    return nil
}
var _ ifacepkg.MyInterface = &User{}
加载测试文件并尝试查看 User 是否实现了 MyInterface
fset := token.NewFileSet()
pkgs, e := parser.ParseDir(fset, "./test", nil, 0)
if e != nil {
    log.Fatal(e)
    // return
}
var astf []*ast.File
for _, pkg := range pkgs {
    fmt.Printf("package %v\n", pkg.Name)
    for fn, f := range pkg.Files {
        fmt.Printf("file %v\n", fn)
        astf = append(astf, f)
    }
}
config := &types.Config{
    Error: func(e error) {
        fmt.Println(e)
    },
    Importer: importer.Default(),
}
info := types.Info{
    Types: make(map[ast.Expr]types.TypeAndValue),
    Defs:  make(map[*ast.Ident]types.Object),
    Uses:  make(map[*ast.Ident]types.Object),
}
pkg, e := config.Check(path, fset, astf, &info)
if e != nil {
    fmt.Println(e)
}
vIface := getIface()
fmt.Println(vIface)
scope := pkg.Scope()
for _, name := range scope.Names() {
    obj := scope.Lookup(name)
    _, ok := obj.Type().Underlying().(*types.Struct)
    imp := types.Implements(obj.Type(), vIface)
    fmt.Println(obj.Name(), ok, imp)
}
好的,fmt.Println(obj.Name(), ok, imp) 打印出 User true true,一切正常!但是,如果我将源文件中的函数从
func (u User) MyFunction() error {
改为
func (u *User) MyFunction() error {
现在它打印出 User true false
所以函数types.Implements报告User没有实现MyInterface,这是不正确的。
所以我的问题是:type.Implements方法是否有问题,还是在调用该函数之前我需要做些什么。
回答
好的,为了解决我的问题,将代码的最后一部分更改为:
scope := pkg.Scope()
for _, name := range scope.Names() {
    obj := scope.Lookup(name)
    _, ok := obj.Type().Underlying().(*types.Struct)
    ptr := types.NewPointer(obj.Type())
    imp := types.Implements(ptr.Underlying(), vIface)
    fmt.Println(obj.Name(), ok, imp)
}
这样可以同时处理指针和非指针接收器。
英文:
OK so looking at using go/types, go/parser... an so forth to generate some code; but need to identify all structs that implement a specific interface which I have figured out, however, if the struct definition on the struct function it does not match using types.Implements.
OK the code examples:
Getting the interface
package ifacepkg
const interfacePkg = `package ifacepkg
type MyInterface interface {
	MyFunction() error
}
`
func getIface() *types.Interface {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "iface.go", interfacePkg, 0)
if err != nil {
	panic(err)
}
config := &types.Config{
	Error: func(e error) {
		fmt.Println(e)
	},
	Importer: importer.Default(),
}
info := types.Info{
	Types: make(map[ast.Expr]types.TypeAndValue),
	Defs:  make(map[*ast.Ident]types.Object),
	Uses:  make(map[*ast.Ident]types.Object),
}
pkg, e := config.Check("genval", fset, []*ast.File{f}, &info)
if e != nil {
	fmt.Println(e)
}
return pkg.Scope().Lookup("MyInterface").Type().Underlying().(*types.Interface)
}
Test File
package test
import "ifacepkg"
// User struct
type User struct {
	FirstName string
	LastName  string
}
func (u User) MyFunction() error {
	return nil
}
var _ ifacepkg.MyInterface = &User{}
Loading Test File & trying to see if User implements MyInterface
fset := token.NewFileSet()
pkgs, e := parser.ParseDir(fset, "./test", nil, 0)
if e != nil {
	log.Fatal(e)
	// return
}
var astf []*ast.File
for _, pkg := range pkgs {
	fmt.Printf("package %v\n", pkg.Name)
	for fn, f := range pkg.Files {
		fmt.Printf("file %v\n", fn)
		astf = append(astf, f)
	}
}
config := &types.Config{
	Error: func(e error) {
		fmt.Println(e)
	},
	Importer: importer.Default(),
}
info := types.Info{
	Types: make(map[ast.Expr]types.TypeAndValue),
	Defs:  make(map[*ast.Ident]types.Object),
	Uses:  make(map[*ast.Ident]types.Object),
}
pkg, e := config.Check(path, fset, astf, &info)
if e != nil {
	fmt.Println(e)
}
vIface := getIface()
fmt.Println(vIface)
scope := pkg.Scope()
	for _, name := range scope.Names() {
		obj := scope.Lookup(name)
		_, ok := obj.Type().Underlying().(*types.Struct)
		imp := types.Implements(obj.Type(), vIface)
		fmt.Println(obj.Name(), ok, imp)
	}
OK so fmt.Println(obj.Name(), ok, imp) prints User true true all good! however if I change the source file function from
func (u User) MyFunction() error {
to
func (u *User) MyFunction() error {
it now prints User true false
so the function types.Implements is reporting that User does not implement MyInterface, which is not true.
So my question is: Is there an issue with the type.Implements method or is there something I have to do to my object prior to calling that function.
Answer
ok to solve my own issue changing final portion of code to
scope := pkg.Scope()
for _, name := range scope.Names() {
    obj := scope.Lookup(name)
    _, ok := obj.Type().Underlying().(*types.Struct)
    ptr := types.NewPointer(obj.Type())
    imp := types.Implements(ptr.Underlying(), vIface)
    fmt.Println(obj.Name(), ok, imp)
}
which works with both Pointer and non-pointer recievers
答案1
得分: 1
你的编译器告诉你的是真的。在Go语言中,*Type和Type是不同的类型。如果你想让*User实现你的接口,那么方法的接收者必须是*User类型;如果你想让User实现接口,那么接收者必须是User类型。我不太知道如何解释得更清楚了... Go语言的类型系统是严格的,这两者并不相同。如果你在指针类型上定义了(u *User) MyFunction(),然后检查MyUser和ptr是否实现了接口,你会更直观地看到这一点:ptr会实现接口,而MyUser则不会。
英文:
What your compiler is telling you is true. *Type != Type in Go. If you want *User to implement your interface then the methods receiver has to be *User and if you want it to be User then it needs to be User. I don't really know how else to explain it... Go's type system is strict and those aren't the same. You can see this more practically if you have (u *User) MyFunction() defined for the pointer type do ptr := &MyUser then check if MyUser and ptr implement the interface, ptr will, MyUser will not.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论