di
packageAPI reference for the di
package.
Imports
(3)Container
Container manages dependency injection with thread-safe access.
type Container struct
Example
c := di.New()
c.Provide("db", &Database{})
db := di.Get[*Database](c, "db")
Methods
Provide registers a dependency by name.
Parameters
func (*Container) Provide(name string, instance any)
{
c.mu.Lock()
defer c.mu.Unlock()
c.providers[name] = instance
}
Get retrieves a dependency by name.
Parameters
Returns
func (*Container) Get(name string) (any, bool)
{
c.mu.RLock()
defer c.mu.RUnlock()
v, ok := c.providers[name]
return v, ok
}
MustGet retrieves a dependency and panics if not found.
Parameters
Returns
func (*Container) MustGet(name string) any
{
v, ok := c.Get(name)
if !ok {
panic("di: dependency not found: " + name)
}
return v
}
Has checks if a dependency is registered.
Parameters
Returns
func (*Container) Has(name string) bool
{
c.mu.RLock()
defer c.mu.RUnlock()
_, ok := c.providers[name]
return ok
}
Inject populates struct fields with registered dependencies. Uses `inject:"name"` tags to match fields with providers. Falls back to field name if no inject tag is present.
Parameters
func (*Container) Inject(target any)
{
val := reflect.ValueOf(target)
if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
return
}
elem := val.Elem()
elemType := elem.Type()
c.mu.RLock()
defer c.mu.RUnlock()
for i := 0; i < elem.NumField(); i++ {
fieldType := elemType.Field(i)
fieldVal := elem.Field(i)
if !fieldVal.CanSet() {
continue
}
name := fieldType.Tag.Get("inject")
if name == "" {
name = fieldType.Name
}
if dep, ok := c.providers[name]; ok {
depVal := reflect.ValueOf(dep)
if depVal.Type().AssignableTo(fieldType.Type) {
fieldVal.Set(depVal)
}
}
}
}
Clone creates a shallow copy of the container.
Returns
func (*Container) Clone() *Container
{
c.mu.RLock()
defer c.mu.RUnlock()
clone := New()
for k, v := range c.providers {
clone.providers[k] = v
}
return clone
}
Keys returns all registered dependency names.
Returns
func (*Container) Keys() []string
{
c.mu.RLock()
defer c.mu.RUnlock()
keys := make([]string, 0, len(c.providers))
for k := range c.providers {
keys = append(keys, k)
}
return keys
}
Fields
| Name | Type | Description |
|---|---|---|
| providers | map[string]any | |
| mu | sync.RWMutex |
New
New creates an empty DI container.
Returns
func New() *Container
{
return &Container{
providers: make(map[string]any),
}
}
testDB
type testDB struct
Fields
| Name | Type | Description |
|---|---|---|
| Name | string |
testService
type testService struct
Fields
| Name | Type | Description |
|---|---|---|
| DB | *testDB | inject:"db" |
| Logger | string | inject:"logger" |
TestContainer_ProvideAndGet
Parameters
func TestContainer_ProvideAndGet(t *testing.T)
{
c := New()
db := &testDB{Name: "test"}
c.Provide("db", db)
got, ok := c.Get("db")
if !ok {
t.Fatal("expected to find 'db'")
}
gotDB, ok := got.(*testDB)
if !ok {
t.Fatal("expected *testDB type")
}
if gotDB.Name != "test" {
t.Errorf("got %q, want %q", gotDB.Name, "test")
}
}
TestContainer_Has
Parameters
func TestContainer_Has(t *testing.T)
{
c := New()
c.Provide("exists", "value")
if !c.Has("exists") {
t.Error("Has should return true for 'exists'")
}
if c.Has("missing") {
t.Error("Has should return false for 'missing'")
}
}
TestContainer_Inject
Parameters
func TestContainer_Inject(t *testing.T)
{
c := New()
db := &testDB{Name: "injected"}
c.Provide("db", db)
c.Provide("logger", "stdout")
svc := &testService{}
c.Inject(svc)
if svc.DB != db {
t.Errorf("DB not injected correctly")
}
if svc.Logger != "stdout" {
t.Errorf("Logger: got %q, want %q", svc.Logger, "stdout")
}
}
TestContainer_InjectFallbackToFieldName
Parameters
func TestContainer_InjectFallbackToFieldName(t *testing.T)
{
type noTagService struct {
DB *testDB
}
c := New()
db := &testDB{Name: "fallback"}
c.Provide("DB", db)
svc := &noTagService{}
c.Inject(svc)
if svc.DB != db {
t.Error("DB not injected via field name fallback")
}
}
TestContainer_Clone
Parameters
func TestContainer_Clone(t *testing.T)
{
c := New()
c.Provide("key", "value")
clone := c.Clone()
clone.Provide("new", "added")
if !clone.Has("key") {
t.Error("clone should have 'key'")
}
if !clone.Has("new") {
t.Error("clone should have 'new'")
}
if c.Has("new") {
t.Error("original should not have 'new'")
}
}
TestContainer_Keys
Parameters
func TestContainer_Keys(t *testing.T)
{
c := New()
c.Provide("a", 1)
c.Provide("b", 2)
keys := c.Keys()
if len(keys) != 2 {
t.Errorf("got %d keys, want 2", len(keys))
}
}
TestContainer_MustGet_Panic
Parameters
func TestContainer_MustGet_Panic(t *testing.T)
{
c := New()
defer func() {
if r := recover(); r == nil {
t.Error("MustGet should panic for missing key")
}
}()
c.MustGet("missing")
}
TestResolve
Parameters
func TestResolve(t *testing.T)
{
c := New()
c.Provide("num", 42)
got, ok := Resolve[int](c, "num")
if !ok {
t.Fatal("expected to resolve 'num'")
}
if got != 42 {
t.Errorf("got %d, want 42", got)
}
}
TestResolve_TypeMismatch
Parameters
func TestResolve_TypeMismatch(t *testing.T)
{
c := New()
c.Provide("num", 42)
_, ok := Resolve[string](c, "num")
if ok {
t.Error("should return false for type mismatch")
}
}
TestMustResolve_Panic
Parameters
func TestMustResolve_Panic(t *testing.T)
{
c := New()
defer func() {
if r := recover(); r == nil {
t.Error("MustResolve should panic for missing key")
}
}()
MustResolve[int](c, "missing")
}
Resolve
Resolve retrieves a typed dependency from the container.
Returns zero value and false if not found or type mismatch.
Parameters
Returns
func Resolve[T any](c *Container, name string) (T, bool)
{
var zero T
v, ok := c.Get(name)
if !ok {
return zero, false
}
typed, ok := v.(T)
if !ok {
return zero, false
}
return typed, true
}
MustResolve
MustResolve retrieves a typed dependency and panics if not found.
Parameters
Returns
func MustResolve[T any](c *Container, name string) T
{
v, ok := Resolve[T](c, name)
if !ok {
panic("di: cannot resolve " + name)
}
return v
}