Skip to content

Adding dig.In to any struct #442

@maranqz

Description

@maranqz

MR

Is your feature request related to a problem? Please describe.

When a constructor needs dig.In (or dig.Out) but I want my
application code to depend on a plain struct (no dig import),
I have to add extra boilerplate: create a StructIn wrapper
with dig.In and then write a Fix function that maps it to
the plain struct.

This gets repetitive across modules and makes it harder to keep
the domain/app layer clean.

type StructIn struct {
	dig.In

	Buffer *bytes.Buffer
}

func Fix(st StructIn) Struct {
	return Struct{
		Buffer: st.Buffer,
	}
}

Describe the solution you'd like

Add a small helper, for example:

  • dig.AsIn(T{}) and dig.AsIn(reflect.TypeOf(T{}))

that returns a constructor function which:

  • takes an auto generated dig.In struct with the same fields
  • returns T with fields copied over
  • can be passed directly to c.Provide(...)

So the user can do:

c.Provide(dig.AsIn(Struct{}))

instead of defining StructIn and Fix.

Describe alternatives you've considered

  1. Current approach:
  • define type StructIn struct { dig.In; ... }

  • define func Fix(StructIn) Struct

  • provide Fix

  1. Generic does not help.

  2. Code generation (too heavy for this small mapping and dig bases on reflection).

Is this a breaking change?

No. Existing APIs and behavior stay unchanged.

Additional context

Code example (old vs proposed) is below:

main.go

package main  
  
import (  
    "bytes"  
    "fmt"    "log"  
    "go.uber.org/dig")  
  
// AsIn removes the need to create StructIn and Fix.  
  
type StructIn struct {  
    dig.In  
  
    Buffer *bytes.Buffer  
}  
  
func Fix(st StructIn) Struct {  
    return Struct{  
       Buffer: st.Buffer,  
    }  
}  
  
func main() {  
    c := dig.New()  
  
    err := c.Provide(func() *bytes.Buffer {  
       return bytes.NewBufferString("old version")  
    })  
    checkErr(err)  
  
    err = c.Provide(Fix)  
    checkErr(err)  
  
    err = c.Invoke(Run)  
    checkErr(err)  
  
    // As in version  
    cWithAsIn := dig.New()  
  
    err = cWithAsIn.Provide(func() *bytes.Buffer {  
       return bytes.NewBufferString("AsIn version")  
    })  
    checkErr(err)  
  
    err = cWithAsIn.Provide(dig.AsIn(Struct{}))  
    checkErr(err)  
  
    err = cWithAsIn.Invoke(Run)  
    checkErr(err)  
}  
  
func checkErr(err error) {  
    if err != nil {  
       panic(err)  
    }  
}  
  
// Placed in separate module as fx docs recommend it.  
// https://uber-go.github.io/fx/modules.html#packages  
type Struct struct {  
    Buffer *bytes.Buffer  
}  
  
func Run(st Struct) {  
    if st.Buffer == nil {  
       log.Fatalf("struct should be filled\n")  
    }  
  
    fmt.Printf("struct is filled: %v\n", st)  
}

go.mod

module ex  
  
go 1.20  
  
require go.uber.org/dig v1.19.0  
  
replace go.uber.org/dig => github.com/maranqz/dig v0.0.0-20260101180718-ec25e1f28ca2

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions