go 1.8 plugin use custom interface

Custom interfaces work just fine.

But one important thing: you can only type assert types from values looked up from plugins that are defined outside of the plugin (you can’t refer types defined in plugins). This also applies to each component of “composite types”, for example you can only type assert a function type whose parameter and result types are also defined outside of the plugin.

1. With a common package outside of the plugin

One solution is to define the interface in a package outside of the plugin, and both the plugin and your app can import it and refer to it.

Define it in package filter:

package filter

type Filter interface {
    Name() string
    Age() int
}

The plugin is in package pq and imports package filter:

package main

import (
    "fmt"
    "filter"
)

type plgFilter struct{}

func (plgFilter) Name() string { return "Bob" }
func (plgFilter) Age() int     { return 23 }

func GetFilter() (f filter.Filter, err error) {
    f = plgFilter{}
    fmt.Printf("[plugin GetFilter] Returning filter: %T %v\n", f, f)
    return
}

And the main app that also imports (the same) package filter, loads the plugin, looks up GetFilter(), calls it and also uses the returned Filter:

package main

import (
    "fmt"
    "filter"
    "plugin"
)

func main() {
    p, err := plugin.Open("pg/pg.so")
    if err != nil {
        panic(err)
    }

    GetFilter, err := p.Lookup("GetFilter")
    if err != nil {
        panic(err)
    }
    filter, err := GetFilter.(func() (filter.Filter, error))()
    fmt.Printf("GetFilter result: %T %v %v\n", filter, filter, err)
    fmt.Println("\tName:", filter.Name())
    fmt.Println("\tAge:", filter.Age())
}

Output:

[plugin GetFilter] Returning filter: main.plgFilter {}
GetFilter result: main.plgFilter {} <nil>
        Name: Bob
        Age: 23

2. With plugin returning interface{}, and interface defined in main app

Another solution is to have the plugin function return a value of type interface{}. Your main app can define the interface it expects, and it can use type assertion on the interface{} value returned by the plugin.

No filter package this time.

The plugin is in package pq:

package main

import (
    "fmt"
)

type plgFilter struct{}

func (plgFilter) Name() string { return "Bob" }
func (plgFilter) Age() int     { return 23 }

func GetFilterIface() (f interface{}, err error) {
    f = plgFilter{}
    fmt.Printf("[plugin GetFilterIface] Returning filter: %T %v\n", f, f)
    return
}

And the main app:

package main

import (
    "fmt"
    "plugin"
)

func main() {
    p, err := plugin.Open("pg/pg.so")
    if err != nil {
        panic(err)
    }

    GetFilterIface, err := p.Lookup("GetFilterIface")
    if err != nil {
        panic(err)
    }
    filterIface, err := GetFilterIface.(func() (interface{}, error))()
    fmt.Printf("GetFilterIface result: %T %v %v\n", filterIface, filterIface, err)
    myfilter := filterIface.(MyFilter)
    fmt.Println("\tName:", myfilter.Name())
    fmt.Println("\tAge:", myfilter.Age())
}

type MyFilter interface {
    Name() string
    Age() int
}

Output:

[plugin GetFilterIface] Returning filter: main.plgFilter {}
GetFilterIface result: main.plgFilter {} <nil>
        Name: Bob
        Age: 23

Also see related question: How do Go plugin dependencies work?

Leave a Comment