我遇到了我不了解的Go行为。我的想法是导入一个插件,该插件实现了两个软件包都没有的接口。如果返回一个结构,它可以正常工作,但是要确保它实现了该接口,我想返回一个失败的接口。
接口定义:
package iface type IPlugin interface{ SayHello(string) SayGoodby(string) WhatsYourName() string }
主程序如下所示:
package main import ( "plugin" "plugin_test/iface" "errors" "fmt" ) //go:generate go build -buildmode=plugin -o ./pg/test.so ./pg/test.go func main(){ path := "pg/test.so" plug, err := plugin.Open(path) if err != nil { panic(err) } sym, err := plug.Lookup("Greeter") if err != nil { panic(err) } var pg iface.IPlugin pg, ok := sym.(iface.IPlugin) if !ok { panic(errors.New("error binding plugin to interface")) } fmt.Printf("You're now connected to: %s \n", pg.WhatsYourName()) pg.SayHello("user") pg.SayGoodby("user") }
插件(存储在pg / test.go中)
package main import ( "fmt" "plugin_test/iface" ) type testpl struct {} func(pl testpl) SayHello(s string){ fmt.Printf("Plugin says hello to %s \n", s) } func(pl testpl) SayGoodby(s string){ fmt.Printf("Plugin says goodby to %s \n", s) } func(pl testpl) WhatsYourName() string{ return "my name is Test-Plugin" } /* This function works */ func getPlugin() testpl{ return testpl{} } /* This function doesn't work */ func getPlugin() iface.IPlugin{ return testpl{} } /* This function also doesn't work */ func getPlugin() interface{}{ return testpl{} } var Greeter = getPlugin()
我尝试了每个getPlugin功能。
getPlugin
返回的函数将testpl输出预期的输出:
testpl
You're now connected to: my name is Test-Plugin Plugin says hello to user Plugin says goodby to user
其他功能结束于 sym.(iface.IPlugin)
sym.(iface.IPlugin)
panic: error binding plugin to interface goroutine 1 [running]: main.main() /home/../../../main.go:27 +0x343 exit status 2
有人可以解释为什么这不可能吗?如果在这种情况下不让您构建插件,那么创建插件会不会更容易?
您想要的东西是可能的,但是在后台有一些东西使它无法工作。
这即是要查找一个 变量 命名Greeter从插件。Plugin.Lookup()将返回一个指向该变量的指针!如果不能,则只能检查其值,但不能更改它。
Greeter
Plugin.Lookup()
您可以通过简单地打印存储在中的值的类型来验证这一点sym:
sym
fmt.Printf("%T\n", sym)
在您的第一种情况下func getPlugin() testpl,输出将是:
func getPlugin() testpl
*main.testpl
在第二种情况下func getPlugin() iface.IPlugin,输出将是:
func getPlugin() iface.IPlugin
*iface.IPlugin
(是的,它是一个接口的指针!)
在第三种情况下func getPlugin() interface{},输出将是:
func getPlugin() interface{}
*interface {}
因此,您的第一个示例之所以有效,sym是因为其中存储的值的类型*main.testpl也是,它也实现了iface.IPlugin(因为main.testpl实现了,指针类型也实现了)。
iface.IPlugin
main.testpl
回到您的第二个示例: func getPlugin() iface.IPlugin
存储在中的值sym是类型*iface.IPlugin。指向接口的指针类型的值永远不会满足任何接口(空接口除外),因此尝试从type iface.IPlugin值进行类型断言*iface.IPlugin将永远不会成功。您必须输入assert *iface.IPlugintype,然后可以对其进行引用以获取type的值iface.IPlugin。它可能看起来像这样:
pgPtr, ok := sym.(*iface.IPlugin) if !ok { panic(errors.New("error binding plugin to interface")) } pg := *pgPtr
现在一切都按预期工作!
为避免此类麻烦和混乱,您可以实现插件以公开返回以下内容的 函数Greeter:
func Greeter() iface.IPlugin { return testpl{} }
然后Greeter当然要摆脱全局变量。如果这样做,您可以查找以下Greeter类型的符号:
func() iface.IPlugin
区别在于查找函数不需要plugin包返回指向该值的指针,而对于变量则需要。简单的函数类型,没有指向接口的功夫指针。用它来获得迎宾员将是:
plugin
Greeter, err := p.Lookup("Greeter") if err != nil { panic(err) } greeterFunc, ok := GetFilter.(func() iface.IPlugin) if !ok { panic(errors.New("not of expected type")) } greeter := greeterFunc() // And using it: fmt.Printf("You're now connected to: %s \n", greeter.WhatsYourName()) greeter.SayHello("user") greeter.SayGoodby("user")