Git Product home page Git Product logo

do's People

Contributors

aflybird0 avatar dezren39 avatar graveland avatar greyxor avatar hesining avatar ilharp avatar maddiesch avatar rainydew avatar samber avatar suzuki-safie avatar syuparn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

do's Issues

feature request: auto injection with struct tags

I used some other framework that could auto inject with struct tags.

Something like this:

type Bar interface {
  DoSomething()
}

type Foo struct {
  Bar `inject:""` // or we can use named value here `inject:"name"`
}

do.Provide(injector,  func() ( Bar, err){...})

f, err := do.Invoke[Foo]() // f.Bar should be injected automatically
f.Bar.DoSomething()

[Feature Request] Method aliases skipping "injector" argument (default injector)

  • Service register
    func ProvideNamedValue[T any](i *Injector, name string, value T) {
    func Provide[T any](i *Injector, provider Provider[T]) {

  • Service lookup
    func InvokeNamed[T any](i *Injector, name string) (T, error) {

for these two group method, it can accept a nil Injector, in this case then it will use system default Injector.

can we supply another group of API without the ** Injector* parameter to make a simpler API?

Possible use of closed services in main() on shutdown

Hi. Your example code shows us, that it is OK to define services by specifying factories and next get it from the container with the function do.MustInvoke[...](x).

Lets imagine we have following code (based on readme example)

func main() {
    injector := do.New()

    // provides CarService
    do.Provide(injector, NewCarService)

    // provides EngineService
    do.Provide(injector, NewEngineService)

    car := do.MustInvoke[*CarService](injector)
    ch := make(chan struct{})
 
    // run background goroutine with stop chan
    go (func(){
        for {
        select {
        case <-ch:
            return
        default:
            car.Beep()  // service may be already closed
            time.Sleep(1s)
        }
        }
    })()

    injector.ShutdownOnSIGTERM()    // will block until receiving sigterm signal
    close(ch)  // stop the goroutine, but only after container shutdown
}

Here we have no API to let the main() code know that container is wanting to shutdown and await the reaction from main() for this event BEFORE starting the shutdown. This may trigger misuse of the container by users.

Solutions.

  • Force users to don't start background goroutines from main() code which uses services spawned by the container. Instead, users should implement services with right Shutdown() implementation to allow the container manage services life times. I.e. users should implement glue code services.
  • Implement API that allows to define Shutdown function for the main() code, to allow the main() stop working with the services before they starts shutdown.

Add telemetry, OTEL, logger...

Debugging IoC is a pain.

An OTEL plugin listening to lifecycle events would be very nice.

Requires #73.

I imagine an API similar to this:

import (
    "github.com/samber/do/v2"
    "github.com/samber/do/observability/otel/v2"
)

injector := do.New()
otel.Listen(injector, ...) // <-- handling every available hooks

Invoke returns panic because of nil instance

Hey! I have a problem. I have a config struct and want to provide some instances for my application.

main.go:

di := do.New()

do.Provide[*redis.Client](di, func(i *do.Injector) (*redis.Client, error) {
  db, err := c.ConnectRedis()
  if err != nil {
	  return nil, err
  }
  return db, nil
})

do.Provide[*bun.DB](di, func(i *do.Injector) (*bun.DB, error) {
  db, err := c.ConnectPostgres()
  if err != nil {
	  return nil, err
  }
  return db, nil
})

And the strange thing is that the Redis client was created successfully and I can invoke it, but the Postgres client instance is nil (found with debugger). What is the logic of that? Because I didn't understand and can't do the job, because some of my clients weren't built.

Example of invocation

redisClient := do.MustInvoke[*redis.Client](i) // ok, I can use it
postgresClient := do.MustInvoke[*bun.DB](i) // panic here and in debugger: built = false, instance = nil

`InvokeAs` problems

  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`
  1. Random invoked, other not checked (#45 (comment))
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 samber/go-type-to-string#2

Proposal: do.Shutdown returning shutdown time ?

My monolith application relies a lot on do.Shutdownable interface. Some tasks call database or third-parties, and flush buffer.

I would like to measure the time of the do.Shutdown processing.

API proposal: duration, err := do.Shutdown()

duration would be either a time.Duration or:

type ShutdownDuration struct {
    All time.Duration
    Services map[string]time.Duration
}

WDYT ?

Improvements on dependency graph

Today we only keep an ordered list of service inception.

It would be awesome to keep track of a real dependency graph in a doubly-linked list or anything similar.

Other improvements:

  • circular DI detection (at provide() time)
  • print dependency graph
  • shutdown a single graph branch
  • provide then invoke a group of services needed for a larger module

Could not find service `main-router`, available services: `main-router`

Hello, thanks for your great works.

here the specs :

github.com/samber/do v1.2.0 
go 1.18

Maybe I don't understand something ?
I tried to generate gin router and retrieved it to run the server.
But it doesn't work :'(

Here what I tried :

package main

import (
	"github.com/gin-gonic/gin"
	glog "github.com/magicsong/color-glog"
	"github.com/samber/do"
)

func generateContainer() *do.Injector {
	injector := do.New()

	do.ProvideNamedValue(injector, "main-router", gin.Default())
	
	return injector
}

func main() {
	container := generateContainer()

	router := do.MustInvokeNamed[gin.Engine](container, "main-router")
	if err := router.Run(); err != nil {
		glog.Fatal(err)
	}
}

And the error :

panic: DI: could not find service `main-router`, available services: `main-router`

Is it possible to have some help.

How can I InvokeNamed T to any

Demo(use v2):

func TestInvokeAny(t *testing.T) {
	do.ProvideNamed(nil, "test", func(i do.Injector) (int, error) {
		return 1, nil
	})

	int1, err := do.InvokeNamed[int](nil, "test")
	if err != nil {
		t.Errorf("invoke error: %v", err)
		return
	}
	if int1 != 1 {
		t.Errorf("invoke response error: %v", int1)
		return
	}
	int2, err := do.InvokeNamed[any](nil, "test")
	if err != nil {
		t.Errorf("invoke error: %v", err)
		return
	}
	t.Log(int2)
}

do.InvokeNamed[any](nil, "test") not work

image

Why do I have to do this?

I want to generic call my local service, I just known service name

localService, err := do.InvokeNamed[any](nil, "ServiceName")
if err != nil {
return nil, err
}
methodValue := reflect.ValueOf(localService).MethodByName("FuncName")

invokeAnyByName Is what I want, can you Export It ?

thank you

Transform hooks into slices of hooks

We should be able to add multiple callbacks for each hook:

type InjectorOpts struct {
	HookAfterRegistration []func(scope *Scope, serviceName string)
	HookAfterShutdown     []func(scope *Scope, serviceName string)
        Logf                  []func(format string, args ...any)
}

instead of

type InjectorOpts struct {
	HookAfterRegistration func(scope *Scope, serviceName string)
	HookAfterShutdown     func(scope *Scope, serviceName string)
        Logf                  func(format string, args ...any)
}

Requirement for #70

Feature request: service lookup & Singleton

1: Does this framework ensure singleton instance?

2: Support service lookup:
There is scenario that we just want to get a service when in a function call but I dont want to new a new instance of the target service.

in java ecosystem the famous springboot support above features.

I am choosing a DI framework for my project and find I need above two features. the case is

** source code**

interface A {
  M1()
  M2()
}
var _ A = (*ImplA)(nil)
struct ImplA {
}

func NewImplaA() {
}
....

but right now I want to test another function which depends on the interface(ImplA) with Mock. if this framework can supply above features than I can inject a mocked implementation and then the tested method can looup the mock object.

Can we do auto inject?

I want to know if we has a construct function, how to make it be auto injected?
Should i always code like this bellow?

do.Provide(builder, func(i *do.Injector) (*appcmd.CmdUser, error) {
        db := do.MustInvoke[trans.ItfDB](i)
        log := do.MustInvoke[logger.ItfLogger](i)
        userRepo := do.MustInvoke[reposititf.ItfRepoUser](i)
        accountRepo := do.MustInvoke[reposititf.ItfRepoAccount](i)
        publisher := do.MustInvoke[publisheritf.ItfPublisher](i)
        productRemote := do.MustInvokeNamed[clientitf.ItfProxyProduct](i, "productRemote")
        productLocal := do.MustInvokeNamed[clientitf.ItfProxyProduct](i, "productLocal")
        
        return appcmd.NewCmdUser(db, log, userRepo, accountRepo, publisher, productRemote, productLocal), nil
})

func NewCmdUser(
	db trans.ItfDB,
	log logger.ItfLogger,
	userRepo reposititf.ItfRepoUser,
	accoutRepo reposititf.ItfRepoAccount,
	pub publisheritf.ItfPublisher,
	productRemote clientitf.ItfProxyProduct,
	productLocal clientitf.ItfProxyProduct) *CmdUser {
	return &CmdUser{
		db:                 db,
		userRepo:           userRepo,
		accountRepo:        accoutRepo,
		log:                log,
		publisher:          pub,
		productRemoteProxy: productRemote,
		productLocalProxy:  productLocal,
	}
}

Performance Question about service instnace : getInstance(i Injector) (T, error)

func (s *serviceEager[T]) getInstance(i Injector) (T, error) {
	frame, ok := stacktrace.NewFrameFromCaller()
	if ok {
		s.mu.Lock()
		s.invokationFrames = append(s.invokationFrames, frame) // @TODO: potential memory leak
		s.mu.Unlock()
	}

	return s.instance, nil
}

above code is from https://github.com/samber/do/blob/v2-%F0%9F%9A%80/service_eager.go,

right now I am using do by generate a global singleton scope and get the service on demand. but there is a call to get call
stack everytime I try to get the instnace
frame, ok := stacktrace.NewFrameFromCaller(), in fact this methos is not exported just for internal usagae. It will decrease the system performance. is there way to optimize the design?

More hooks

Currently, we listen to the following events:

  • AfterRegistration
  • AfterShutdown

I would add:

  • BeforeRegistration
  • BeforeShutdown
  • BeforeClone
  • AfterClone
  • BeforeInvocation
  • AfterInvocation

Is there an equivalent to dig.As() ?

I have a constructor that returns a concrete type which implements more than one interface and I would like to register the same instance as providing multiple interfaces.

In dig, it is possible using dig.As().

Middleware

Would you be interested in a PR that adds standard http based middleware? The ideal is that it provides middleware that adds the injector to the request context and then, in each request, you can quickly fetch not just the injector instance itself but also request specific services out of the injector while handling requests. Seems like it would be generally useful for anybody doing web development using do and it doesn't add any dependencies since it is based on the standard library.

I've written such a thing and it would be useful to include here since otherwise I'd have to make a tiny package and publish it just to reuse it across projects.

Add support of Value Groups

@matdurand :

Hey, great work on the lib!

Any chance you have plans to support a concept similar to groups in Dig? (aka, registering a bunch of things of the same type, and asking for the list later). Pretty useful to do something like register a bunch of Routes associated with controllers in a DI container and then asking for all the routes to register them. That is super useful to decouple route creation, controller creation and servers for example.

https://uber-go.github.io/fx/value-groups/

Proposal: allow multiple implementation for the same interface and being able to return all them

Hi! I am doing some research on DI for Golang and I find this library to be the perfect middle ground of simplicity, efficiency and static stability. There's one missing feature that at the moment doesn't seem to be possible (correct me please if I'm wrong).

There's no way right now to register multiple implementation of the same interface and then being able to retrieve all of them.
I come from Java, where with Springboot, you can inject a Collection<IService>.

Would that make sense to do it here too?
If you wondering "ok, but how do you handle the case of having 2 implementation of the same interface but your struct only needs one?"
In Springboot in this case you would have an error, because the framework wouldn't know which one to inject.. And we could have the same here too.

Proposal: way to automate invoke deps in do.Provider for any type constructor

package main

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


type Type struct {
	int
	uint64
	string
}

func NewType(i int, u uint64, s string) Type {
	return Type{i, u, s}
}


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

	do.ProvideValue(scope, int(1))
	do.ProvideValue(scope, uint64(2))
	do.ProvideValue(scope, string("str"))

	// instead
	do.Provide(scope, func(i do.Injector) (Type, error) {
		return NewType(
			do.MustInvoke[int](i),
			do.MustInvoke[uint64](i),
			do.MustInvoke[string](i),
		), nil
	})

	// something like
	do.ProvideAny[Type](scope, NewType)

	_ = do.MustInvoke[Type](scope)
}

Where

func ProvideAny[T any](i do.Injector, fun any) {
	fn := reflect.ValueOf(fun)
	
	// check that fn is func(...) T or func(...) (T, error)

	do.Provide[T](i, func(i do.Injector) (res T, err error) {
		inputTypes := []reflect.Value{}
		// inputTypes - invoked input by type name

		out := fn.Call(inputTypes)

		// res = out[0]
		// err = out[1]

		return
	})
}

Also the function can have variadic names ... string
With a match for each function parameter, to use it instead of the type name.
Analog do:"name" in struct

If interested I can send pull request.
We can discuss the interface.

Now it just

func ToProvider[T any](fun any) do.Provider[T]

do.Provide(scope, ToProvider[Type](NewType))

proposal: Add factory provide mode

First, this is a amazing project.

But now, both Provide and ProvideValue only support singleton likely mode, for Provide which only create value when first invoked. In sometimes, it's useful to create a new instance whenever it invoked.

V2 Feature Request: add lifecycle interface support

I have Java background , in springboot there is a interface named InitializingBean this interface give an opportunity to do some one-time initialization. We can introduce similar interface, service implement this interface will call the method automatically once after the service creation.

Not all services shutting down

Hello. Thank you for this package. I've tried it on my project with grpc client and i wanted to close client connection after injector shutting down. The key problem is that grpc client doesn't implements do.Shutdownable by default, so i needed to wrap it in my struct. But. I found that this connection not shutting down.

I wrote some simple code, which visualises that service A will never shutdown.

package main

import (
	"log"

	"github.com/samber/do"
)

type ServiceA struct{}

// doesn't have Shutdown method, so, we need make it shutdownable
func (*ServiceA) Close() error {
	return nil
}

type ServiceB struct{}

func (*ServiceB) Shutdown() error {
	log.Println("Shutted down B")
	return nil
}

type ServiceC struct{}

func (*ServiceC) Shutdown() error {
	log.Println("Shutted down C")
	return nil
}

// Simple, ok, because it shutdownable
func NewServiceC(i *do.Injector) (*ServiceC, error) {
	return &ServiceC{}, nil
}

func NewServiceB(i *do.Injector) (*ServiceB, error) {
	return &ServiceB{}, nil
}

// Make shutdownable service A
type ShutdownableServiceA struct {
	a *ServiceA
}

// implemenint do.Shutdownable
func (v *ShutdownableServiceA) Shutdown() error {
	return v.a.Close()
}

// Custruct shutdownable
func MakeShutdownableServiceA(a *ServiceA) do.Provider[*ShutdownableServiceA] {
	return func(i *do.Injector) (*ShutdownableServiceA, error) {
		return &ShutdownableServiceA{
			a: a,
		}, nil
	}
}

func main() {
	injector := do.New()

	do.Provide(injector, NewServiceC)

	do.Provide(injector, MakeShutdownableServiceA(&ServiceA{}))
	do.Provide(injector, NewServiceB)

	do.MustInvoke[*ServiceC](injector)
	do.MustInvoke[*ShutdownableServiceA](injector)
	do.MustInvoke[*ServiceB](injector)

	injector.Shutdown()
}

Result:

2022/05/28 16:01:05 Shutted down B
2022/05/28 16:01:05 Shutted down C

Data race when invoking services after shutdown

I am not sure if this is a problem or a feature, but invoking dependencies after shutdown creates a race condition (runnable code at https://goplay.tools/snippet/vIQ3Pwu4lw1):

package main

import (
	"log"
	"sync"
	"time"

	"github.com/samber/do"
)

type Service struct{}

func main() {
	var wg sync.WaitGroup

	i := do.New()

	do.Provide(i, func(injector *do.Injector) (*Service, error) {
		return &Service{}, nil
	})

	// uncommenting the line below gives an error: `Invoking after shutdown error: DI: could not find service `*main.Service`, available services:`
	//_ = do.MustInvoke[*Service](i)

	wg.Add(1)

	go func() {
		defer wg.Done()

		time.Sleep(time.Second * 1)

		_, err := do.Invoke[*Service](i)
		if err != nil {
			log.Printf("Invoking after shutdown error: %v\n", err)
			return
		}
	}()

	err := i.Shutdown()
	if err != nil {
		log.Printf("Shutdown error: %v\n", err)
	}

	wg.Wait()

	log.Println("Finished")
}

When running with go run -race . it detects a race condition:

==================
WARNING: DATA RACE
Write at 0x00c0000b60c8 by goroutine 6:
  github.com/samber/do.(*Injector).onServiceInvoke()
      /Users/ivand/go/pkg/mod/github.com/samber/[email protected]/injector.go:278 +0x120
  github.com/samber/do.InvokeNamed[...]()
      /Users/ivand/go/pkg/mod/github.com/samber/[email protected]/di.go:128 +0xc8
  github.com/samber/do.Invoke[...]()
      /Users/ivand/go/pkg/mod/github.com/samber/[email protected]/di.go:101 +0x6c
  main.main.func2()
      /Users/ivand/IdeaProjects/untitled1/main.go:32 +0x7c

Previous read at 0x00c0000b60c8 by main goroutine:
  github.com/samber/do.(*Injector).Shutdown()
      /Users/ivand/go/pkg/mod/github.com/samber/[email protected]/injector.go:124 +0x1b0
  main.main()
      /Users/ivand/IdeaProjects/untitled1/main.go:39 +0x130

Goroutine 6 (running) created at:
  main.main()
      /Users/ivand/IdeaProjects/untitled1/main.go:27 +0x128
==================
2023/04/28 12:55:27 Finished
Found 1 data race(s)
exit status 66

Is this the intended way it supposed to work? Is the shutdown procedure expected to be a final call with no invoking of services after it?

I haven't dug deeply in the source code, but apparently the shutdown procedure does not operate under the write lock. Is there a particular reason for that?

shutdown all services even on error

Currently when Injector.Shutdown receives an error while calling Shutdown on a service, it aborts and never calls Shutdown on the following services. It would be nice to have it behave like Injector.HealthCheck were all services are called regardless of their returned errors.

Warm up the container

Hello, I would like to test that all services are wired correctly inside my DIC and also warm up when booting the app.
I would like to do something like this

func TestAllServicesInDI(t *testing.T) {
	injector := do.New()
	do.ProvideNamedValue(injector, "first", 1)
	do.ProvideNamedValue(injector, "second", 2)
	do.ProvideNamedValue(injector, "third", 3)
	do.ProvideNamedValue(injector, "string", "Hello World!")

	for _, v := range injector.ListProvidedServices() {
		_, err := do.InvokeNamed[any](injector, v)
		assert.NilError(t, err)
	}
}

It will fail with assertion failed: error is not nil: DI: could not find service "third", available services: "third", "string", "first", "second"

The easiest way how to accomplish this would be to add InvokeNamedUntyped(i, name) or something that would return any but invoke the service. WDYT?

Is it true that this library doesn't use the reflection package? You lied.

I thought there was some new hack to implement dependency injection without reflection and only through generics. A quick look at the code revealed that this was a lie.

// service.go:27
func generateServiceName[T any]() string {
	var t T

	// struct
	name := fmt.Sprintf("%T", t)   // <-- Here you use reflection !!!
	if name != "<nil>" {
		return name
	}

	// interface
	return fmt.Sprintf("%T", new(T))
}

feat: InjectorOpts.HookBeforePanic

Is it possible to allow a func before panic from DI functions such as Must*?

Basically, to log out the service name that caused panic.

v2: Feature Request - expose a method for add lifecycle hook

right now life cycle hook can only be set when build a container as below
`injector := do.NewWithOpts(&do.InjectorOpts{
HookAfterRegistration: func(injector *do.Injector, serviceName string) {
fmt.Printf("Service registered: %s\n", serviceName)
},
HookAfterShutdown: func(injector *do.Injector, serviceName string) {
fmt.Printf("Service stopped: %s\n", serviceName)
},

Logf: func(format string, args ...any) {
    log.Printf(format, args...)
},

})`

my case is that I am using do as DI container build some basic infra in the group for other projects. it's obvious that other teams(projects) also need hooks for project specific design. I export the DI but they can not register hook.

proposal:
if HookAfterRegistration and HookAfterShutdown are slcie of functions and di expose a new method to add a new hook method then it will work in my case

of course there is gotcha: this new added method which for adding hook will throw error when the DI is not emtpy

thank you!

Add hook at the scope level

AfterRegistration and AfterShutdown are currently defined globally.

It would be nice to listen events at the scope level.

In a big project, having tons of libs and modules, a developer should be able to create listeners locally, in its own scope.

Question: do we need to listen to events coming from the current scope or events coming from children as well?

Data race: unsynchronized read of `Injector.services` in `serviceNotFound`

When a service is not found in Invoke/MustInvoke/InvokeNamed, serviceNotFound is called

func (i *Injector) serviceNotFound(name string) error {
	// @TODO: use the Keys+Map functions from `golang.org/x/exp/maps` as
	// soon as it is released in stdlib.
	servicesNames := keys(i.services)
	servicesNames = mAp(servicesNames, func(name string) string {
		return fmt.Sprintf("`%s`", name)
	})

	return fmt.Errorf("DI: could not find service `%s`, available services: %s", name, strings.Join(servicesNames, ", "))
}

which calls keys(i.services). keys performs a read on i.services but serviceNotFound hasn't acquired the mutex for reading. This trips the race detector in some of my tests.

Solution is probably to just acquire the mutex for reading at the beginning of either serviceNotFound or InvokeNamed.

data race:

WARNING: DATA RACE
Write at 0x00c000f51440 by goroutine 20:
  runtime.mapaccess2_faststr()
      /opt/homebrew/Cellar/go/1.21.1/libexec/src/runtime/map_faststr.go:108 +0x42c
  github.com/samber/do.(*Injector).set()
      /Users/devin/go/pkg/mod/github.com/samber/[email protected]/injector.go:239 +0xa4
  github.com/samber/do.ProvideValue[go.shape.interface { Sleep(time.Duration) }]()
      /Users/devin/go/pkg/mod/github.com/samber/[email protected]/di.go:42 +0x188
  # proprietary code which calls ProvideValue

Previous read at 0x00c000f51440 by goroutine 21:
  github.com/samber/do.keys[go.shape.string,go.shape.interface {}]()
      /Users/devin/go/pkg/mod/github.com/samber/[email protected]/utils.go:14 +0x54
  github.com/samber/do.(*Injector).serviceNotFound()
      /Users/devin/go/pkg/mod/github.com/samber/[email protected]/injector.go:264 +0x34
  github.com/samber/do.InvokeNamed[go.shape.interface { Sleep(time.Duration) }]()
      /Users/devin/go/pkg/mod/github.com/samber/[email protected]/di.go:115 +0x20c
  github.com/samber/do.Invoke[go.shape.interface { Sleep(time.Duration) }]()
      /Users/devin/go/pkg/mod/github.com/samber/[email protected]/di.go:101 +0x6c
# proprietary code which calls Invoke

ServiceLazy shutdowm method

func (s *ServiceLazy[T]) shutdown() error {
	instance, ok := any(s.instance).(Shutdownable)
	if ok {
		return instance.Shutdown()
	}

	s.built = false
	s.instance = empty[T]()
	return nil

}

Is it necessary for ‘shutdown’ not return, continue to set the 'build' and 'instance' field?

Anonymous Invocation

Hi, thanks for the great library.
I'm wondering if anonymous invocation is possible because I'm following Go proverbs with accept interfaces, return structs.
What I am expecting is that:

  • Providers return structs
  • Invocation with [any] is possible
  • Casting results (any) to wanted interfaces.
    Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.