英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论