首页 > 解决方案 > Cannot use 'func(c Container) interface{} { return &MyService{} }' (type func(c Container) interface{}) as type ContainerFunc

问题描述

I'm trying to understand how to build a service container to go. Though it's not a recommended process still I want to learn something from it.

But after structuring code like this image below.

enter image description here

I'm getting an error message like this below. enter image description here

Source code:

    package unit

    import (
        "testing"
        "github.com/stretchr/testify/assert"
        framework "../../framework"
    )
    type ContainerFunc func(container Container) interface{}

    type MyService struct {
        Name string
    }

    type Container framework.Container

    func TestContainerSingleton(t *testing.T) {

        c := framework.New()

        assert.False(t, c.Has("test.service.name"))

        assert.Equal(t, []string{}, c.GetKeys())

        c.Set("my.service", func(c Container) interface{} {
            return &MyService{}
        })

    }

container.go

package framework

import (
    "fmt"
    "log"
    "reflect"
    "sync"
)

// Container is for containing any services
type Container interface {
    Set(name string, f ContainerFunc)
    Has(name string) bool
    Get(name string) interface{}
    GetKeys() []string
    Fill(name string, ds interface{})
    Extend(name string, f ExtenderFunc)
}

// ContainerFunc func will generate interfaces based on need
type ContainerFunc func(container Container) interface{}

// ExtenderFunc means interface
type ExtenderFunc interface{}

type container struct {
    values   map[string]ContainerFunc
    extends  map[string][]reflect.Value
    services map[string]interface{}
    mtx      *sync.RWMutex
}

//New method initialize a new Container and returns it.
func New() Container {
    return &container{
        services: make(map[string]interface{}),
        values:   make(map[string]ContainerFunc),
        extends:  make(map[string][]reflect.Value),
        mtx:      &sync.RWMutex{},
    }
}

/*
In Set method storing the container
*/
func (c *container) Set(name string, f ContainerFunc) {
    c.mtx.Lock()
    defer c.mtx.Unlock()

    //Checking if the service is in the format, if service is there throw a panic
    if _, ok := c.services[name]; ok {
        log.Panic("Can not overwrite initialized service")
    }
    c.values[name] = f
    fmt.Printf("%s service has been registered", name)
}

/*
Has Checks if the name exists in map and return boolean value
it first locks the c.values for reading then checks and at last using
defer it release the lock
*/
func (c *container) Has(name string) bool {
    //applying read lock on value map
    c.mtx.RLock()
    //releasing lock after return call
    defer c.mtx.RUnlock()
    // Checking if the value exists or not, and put the boolean value into "ok" variable
    if _, ok := c.values[name]; ok {
        return true
    }

    return false
}

func (c *container) Get(name string) interface{} {
    //locks reading from c.values
    c.mtx.RLock()
    _, ok := c.values[name]
    //unlocking it after reading and put the boolean value into the ok variable
    c.mtx.RUnlock()
    //if its false panic a error
    if !ok {
        panic(fmt.Sprintf("The service does not exist: %s", name))
    }

    //if panic is not triggered
    //read lock services from reading
    c.mtx.RLock()
    //check if the name is in the services
    _, ok = c.services[name]
    //read unlock the service map
    c.mtx.RUnlock()

    // the ok (boolean) is false type define container as c in "v" variable
    if !ok {
        v := c.values[name](c)

        c.mtx.RLock()
        c.services[name] = v
        c.mtx.RUnlock()
        // it itterates through the extends map and ....
        if extends, ok := c.extends[name]; ok {
            for _, extend := range extends {
                //creating an slice of reflect value
                result := extend.Call([]reflect.Value{reflect.ValueOf(v), reflect.ValueOf(c)})
                c.mtx.Lock()
                c.services[name] = result[0].Interface()
                c.mtx.Unlock()
            }
        }
    }

    c.mtx.RLock()
    defer c.mtx.RUnlock()
    return c.services[name]
}

// Fill Returns error if anything happens
func (c *container) Fill(name string, dst interface{}) {
    // getting the struct
    obj := c.Get(name)
    // added element the dst interface using fill funciton
    if err := fill(obj, dst); err != nil {
        log.Panic(err)
    }
}

//Extend mainly have control over the c.extends , it is appending a callback function
func (c *container) Extend(name string, f ExtenderFunc) {
    c.mtx.Lock()
    defer c.mtx.Unlock()

    if _, ok := c.services[name]; ok {
        log.Panic("Cannnot extend initialized service")
    }

    if _, ok := c.values[name]; !ok {
        log.Panicf("Cannot extend %s service", name)
    }

    c.extends[name] = append(c.extends[name], reflect.ValueOf(f))
}

// Get keys mainly creates a empty map , fill the map with all the value with string
//by appending and return the map
func (c *container) GetKeys() []string {
    c.mtx.RLock()
    defer c.mtx.RUnlock()

    keys := make([]string, 0)

    for k := range c.values {
        keys = append(keys, k)
    }
    return keys
}

//fill method just add an element to the dest interface (which is being injected)
func fill(src, dest interface{}) (err error) {
    defer func() {
        if r := recover(); r != nil {
            d := reflect.TypeOf(dest)
            s := reflect.TypeOf(src)
            err = fmt.Errorf("The fill destination should be a pointer to a %s , but you used a %s", s, d)
        }
    }()
    reflect.ValueOf(dest).Elem().Set(reflect.ValueOf(src))
    return err
}

the error message I'm getting, Any idea how to solve this?:

Cannot use 'func(c Container) interface{} { return &MyService{} }' (type func(c Container) interface{}) as type ContainerFunc

标签: gogoland

解决方案


如果我尝试通过Framework猜测构建我自己的情况来重建你的情况(使用 Go 1.13)——如果你向我们展示了其中的内容会有所帮助Framework——我能得到的最接近的是:

./unit.go:25:22: cannot use func literal (type func(Container) interface {})
as type framework.ContainerFunc in argument to c.Set

这是由于这一行:

type Container framework.Container

将其更改为:

type Container = framework.Container

因此,这是现有类型的别名使得代码构建。(最好只framework.Container在适当的地方使用,而不是定义自己的unit.Container类型。)

(如果您的 IDE 正在从 中删除framework.前缀framework.Container,或者在此处缩短 Go 编译器的错误消息,那就可以解释了。)


推荐阅读