-
Hello, I am trying to use dig.As() to provide the same interface as 2 different concrete types. Take a look at this example: Interface
Concrete type 1
Concrete type 2
in main.go
The error: Question: |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
Hey, @stepanen. Yes, Dig allows that but exactly how/what depends on how you're using them. Some of the options are: Value groupsIf you're using them all together, e.g. at some point, you build a For example, container.Provide(NewReceiveOperationV1, dig.As(new(Receiver)), dig.Group("foo"))
container.Provide(NewReceiveOperationV2, dig.As(new(Receiver))), dig.Group("foo"))
// Elsewhere:
type fooParams struct {
dig.In
Receivers []Receiver `group:"foo"`
}
containers.Provide(func(p fooParams) *Foo {
doStuffWith(p.Receivers)
}) This makes use of parameter objects to specify the group. flowchart LR
NewReceiveOperationV1 & NewReceiveOperationV2 --> Group{{"name=foo type=Receiver"}} --> fooParams
Named valuesIf you're using them together or separately, and sometimes need access to both, and know in each consumer which one you want to use, you can use named values. At Provide time, you give them names, and then in consumers, you declare a parameter object (like above) to specify which ones you want: container.Provide(NewReceiveOperationV1, dig.As(new(Receiver)), dig.Name("v1"))
container.Provide(NewReceiveOperationV2, dig.As(new(Receiver))), dig.Name("v2"))
// Elsewhere
type fooParams struct {
dig.In
V1Receiver Receiver `name:"v1"`
V2Receiver Receiver `name:"v2"`
}
container.Invoke(func(p fooParams) { ... }) The params struct can reference one or both by name. flowchart LR
NewReceiveOperationV1 --> V1["Receiver name=v1"]
NewReceiveOperationV2 --> V2["Receiver name=v2"]
V1 & V2 --> fooParams
ScopesThis is the most advanced feature of the three and affords a fair bit of flexibility by scoping what you provide where. As an example, you can provide both concrete types to your parent container, and then create a scope each for the components that use V1 and V2. Inside those scopes, you'll cast the concrete implementations as those interfaces, and consume them, exporting the consumers from that scope if needed. Roughly: container.Provide(NewReceiveOperationV1)
container.Provide(NewReceiveOperationV2)
v1Scope := container.Scope("v1")
v2Scope := container.Scope("v2")
// Provide each implementation as a Receiver
// to its own scope. This will not be available to container.
v1.Provide(func(r *ReceiveOperationV1) Receiver { return r })
v2.Provide(func(r *ReceiveOperationV2) Receiver { return r })
// Add consumers for both interfaces, and export them (or not).
v1.Provide(NewFoo, dig.Export(true)) // given, 'func NewFoo(Receiver) *Foo'
v2.Invoke(doStuff) // given, 'doStuff(Receiver)' flowchart LR
NewReceiveOperationV1; NewReceiveOperationV2
subgraph V1
Receiver1[Receiver]
end
subgraph V2
Receiver2[Receiver] --> doStuff
end
NewReceiveOperationV1 --->Receiver1
NewReceiveOperationV2 --->Receiver2
Receiver1 -->|NewFoo| Foo["*Foo"]
Hope this helps! |
Beta Was this translation helpful? Give feedback.
Hey, @stepanen. Yes, Dig allows that but exactly how/what depends on how you're using them. Some of the options are:
Value groups
If you're using them all together, e.g. at some point, you build a
[]Receiver
and do something with it, you'll want to use value groups. These allow multiple components to provide the same thing to a named group, and then another component to request that entire named group.For example,