samber / do

⚙️ A dependency injection toolkit based on Go 1.18+ Generics.
https://pkg.go.dev/github.com/samber/do
MIT License
1.71k stars 67 forks source link

`InvokeAs` problems #75

Open d-enk opened 2 months ago

d-enk commented 2 months ago
  1. Can't invoke interface (can't cast nil)
    
    package main

import "github.com/samber/do/v2"

func main() { type I interface{}

i := do.New()
do.ProvideValue(i, I("some"))
do.MustInvokeAs[I](i)

}

// panic: DI: could not find service *main.I, available services: *main.I


2. Random invoked, other not checked (https://github.com/samber/do/pull/45#issuecomment-2079226572)
```go
package main

import (
    "fmt"

    "github.com/samber/do/v2"
)

type I interface{ Name() string }

type i struct{}

func (i) Name() string { return "EXPECTED" }

type other struct{}

func (other) Other()       {}
func (other) Name() string { return "OTHER" }

// type
func main() {
    scope := do.New()

    do.ProvideValue(scope, other{})
    do.ProvideValue(scope, i{})

    for range 10 {
        fmt.Println(do.MustInvokeAs[I](scope).Name())
    }
    // output: something like
    // OTHER
    // OTHER
    // OTHER
    // EXPECTED
    // OTHER
    // OTHER
    // OTHER
    // EXPECTED
    // OTHER
    // OTHER
}

I create a function that would temporarily delete InvokedAs result by OverrideNamedValue and tried to check the next not exist.

But I came across both 1 problem and https://github.com/samber/go-type-to-string/issues/2

samber commented 2 months ago

1- I think you should just call do.Invoke[I] instead of do.InvokeAs[i], since your not performing interface matching here. I will improve the error message.

2- I'm not sure to understand the issue here. Your example output seems valid to me.

samber commented 2 months ago

While i review comments on #45, I'm linking your issue to https://github.com/samber/do/pull/45#issuecomment-1993706391

To describe the feedback from @mbark:

Problem: do.InvokeAs will pick a random implementation of IMyService

samber commented 2 months ago

Possible solutions:

@d-enk suggested reporting an error when multiple services match the interface.

Also, I would point out that since services injected in IoC containers are opaque, invoking an interface instead of a struct might lead to loading the wrong service if method signatures overlap. In that situation, an explicit alias is safer: do.AsNamed()

d-enk commented 2 months ago

Another option, maybe you just need a separate method that returns a list of all match? And in InvokeAs allow only one

looks like related https://github.com/samber/do/issues/33

d-enk commented 2 months ago

to fix 1 problem https://github.com/samber/do/issues/81