我遇到了一个我不明白的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
函数。
返回testpl
的函数打印预期的输出:
You're now connected to: my name is Test-Plugin
Plugin says hello to user
Plugin says goodby to user
其他功能以sym.(iface.IPlugin)
panic: error binding plugin to interface
goroutine 1 [running]:
main.main()
/home/../../../main.go:27 +0x343
exit status 2
有人可以解释为什么这不可能吗?如果在这种情况下不允许您构建插件,那么创建插件会不会更容易?
答案 0 :(得分:1)
你想要什么是可能的,但背景中有一些东西阻止它工作。
这就是您要从插件中查找名为Greeter
的变量。 Plugin.Lookup()
将返回指向此变量的指针!如果不这样做,你只能检查它的值,但是你无法改变它。
您只需打印sym
:
fmt.Printf("%T\n", sym)
在您的第一个案例func getPlugin() testpl
中,输出将为:
*main.testpl
在您的第二种情况func getPlugin() iface.IPlugin
中,输出将为:
*iface.IPlugin
(是的,它是指向界面的指针!)
在第三种情况func getPlugin() interface{}
中,输出将是:
*interface {}
所以你的第一个例子是有效的,因为sym
中存储的值是*main.testpl
类型,它也实现了iface.IPlugin
(因为main.testpl
实现了它,指针类型也是如此)。
回到第二个例子:func getPlugin() iface.IPlugin
sym
中存储的值属于*iface.IPlugin
类型。接口的指针类型的值永远不会满足任何接口(空接口除外),因此尝试从类型iface.IPlugin
的值中键入断言*iface.IPlugin
将永远不会成功。您必须键入assert *iface.IPlugin
类型,您可以在取消引用后获取类型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
包返回指向值的指针,而如果是变量则返回值。一个简单的函数类型,没有指向接口的功夫。使用它来获得欢迎会是:
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")
查看相关/类似问题:go 1.8 plugin use custom interface