TL; DR:
Ознакомьтесь с полной рабочей демо здесь: https://github.com/jvmatl/go-plugindemo
Длинный, но (надеюсь!) Информативный ответ:
Плагины хитрые по нескольким причинам, и ответ @ icza является полностью правильным, но чтобы понять, почему он правильный и как он применяется к вашему вопросу, вам необходимо понять, что гибкий характер интерфейсов go не применим к сложным типам.
Вы, вероятно, уже сталкивались с этим в других контекстах:
Это разрешено в Go:
var a interface{}
var b int
a = b // yep, an int meets the spec for interface{} !
Но это не так:
var aa []interface{}
var bb []int
aa = bb // cannot use bb (type []int) as type []interface {} in assignment
Аналогично, с функциями это допустимо:
type Runner interface {
Run()
}
type UsainBolt struct{}
func (ub *UsainBolt) Run() {
fmt.Println("Catch me if you can!")
}
var a Runner
var b *UsainBolt
a = b // Yep, a (pointer to) Usain Bolt is a runner!
Но это не так:
var aa func() Runner
var bb func() *UsainBolt
aa = bb // cannot use bb (type func() *UsainBolt) as type func() Runner in assignment
Теперь давайте посмотрим на определенные типы функций. Вот где это становится действительно интересным:
type RunnerGetter func() Runner
var rg RunnerGetter
rg = getUsain // <-- Nope: doesn't compile: "cannot use getUsain (type func() *UsainBolt) as type RunnerGetter in assignment"
rg = getRunner // <-- This *assignment* is allowed: getRunner is assignable to a type RunnerGetter
var i interface{} = getRunner
rg = i.(RunnerGetter) // compiles, but panics at runtime: "interface conversion: interface {} is func() main.Runner, not main.RunnerGetter"
Другими словами, язык в порядке с присвоением func getRunner() Runner
переменной типа RunnerGetter
, но утверждение типа не выполняется, потому что утверждение типа задает вопрос: действительно ли это переменная типа RunnerGetter ? И ответ - нет, это func() Runner
, что близко, но не совсем верно, поэтому мы паникуем.
Но это работает:
var rg RunnerGetter
var i interface{}
i = rg // after this assignment, i *is* a RunnerGetter
rg = i.(RunnerGetter) // so this assertion passes.
Хорошо, со всем этим фоном проблема в том, что символ, который вы ищете из вашего плагина, должен быть точно того же типа, что и ваше утверждение типа, но не достаточно близко -в-разрешительное присваивание.
Как уже говорилось @icza, у вас есть несколько вариантов:
Вариант 1: Быстрый и грязный, выполняет свою работу
В вашем плагине
func GetGeneric() interface{} {
return &Processor{}
}
В вашем основном: (обработка ошибок пропущена для ясности)
p, _ := plugin.Open(pluginFile) // load plugin
newIntf, _ := p.Lookup("Getgeneric") // find symbol
newProc, _ := newIntf.(func() interface{}) // assert symbol to generic constructor
shoutProc, _ := newProc().(processors.Processor) // call generic constructor, type assert the return value
// Now use your new plugin!
shoutProc.Init(map[string]interface{}{"log_everything": true})
output := shoutProc.Process([]byte("whisper"))
Вариант 2: чище, лучше, если у вас много плагинов
Объявите интерфейс, все ваши плагины должны встретиться в другой пакет:
package processors
// Every plugin must be able to give me something that meets this interface
type Processor interface {
Init(map[string]interface{}) error
Process(buf []byte) []byte
}
В вашем плагине:
type ShoutProcessor struct {
configured bool
logEverything bool
}
func NewProcessor() processors.Processor {
return &ShoutProcessor{}
}
В вашей главной:
p, _ := plugin.Open(pluginFile) // load plugin
newProcIntf, _ := p.Lookup("NewProcessor") // lookup constructor
newProc, _ := newProcIntf.(func() processors.Processor) // assert the type of the func
shoutProc := newProc() // call the constructor, get a new ShoutProcessor
// ready to rock and roll!
shoutProc.Init(map[string]interface{}{"log_everything": true})
output := shoutProc.Process([]byte("whisper"))