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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions