wire icon indicating copy to clipboard operation
wire copied to clipboard

How to work with un-exported interfaces

Open rbroggi opened this issue 3 years ago • 1 comments

You can use go bug to have a cool, automatically filled out bug template, or fill out the template below.

Describe the bug

Some Providers rely on locally-defined non-exported interfaces. In Go the interface contract is loosely coupled and therefore those interfaces do not need to be exported in order for the provider/constructor to be called as the resolution will be dynamic at compile-time.

In the documentation, wire.Bind is the adviced approach to wire Structs into interface arguments, the issue is that in case the interface argument is not exported wire compilation fails because it has no visibility over the non-exported interface.

To Reproduce

Project structure:

❯ tree
.
├── bar
│   └── bar.go
├── foo
│   └── foo.go
├── go.mod
├── go.sum
├── main.go
├── wire.go
└── wire_gen.go

bar.go:

package bar

import "fmt"

type sayer interface {
	Say() string
}

func NewBar(s sayer) *Bar {
	return &Bar{
		sayer: s,
	}
}

type Bar struct {
	sayer sayer
}

func (b *Bar) Start() {
	fmt.Printf("%s\n", b.sayer.Say())
}

foo.go

package foo

func NewFoo() *Foo {
	return &Foo{}
}

type Foo struct {
}

func (f *Foo) Say() string {
	return "I'm foo"
}

main.go

package main

func main() {
	b := InitializeBar()
	b.Start()
}

wire.go (does not compile)

//go:build wireinject
// +build wireinject

package main

import (
	"github.com/google/wire"
	"wire-test/bar"
)
import "wire-test/foo"

func InitializeBar() *bar.Bar {
	wire.Build(foo.NewFoo, wire.Bind(new(bar.sayer), new(*foo.Foo)), bar.NewBar)
	return &bar.Bar{}
}
❯ wire
wire: /Users/rodrigo.broggi/repo/wire-test/wire.go:13:43: sayer not exported by package bar
wire: generate failed

Interestingly enough if we change the bar interface to be exported everything compiles correctly and code is well generated, furthermore, the generated code makes no references at all to the exported interface so theoretically one could refactor the generated code along with the interface to make those non-exporeted again.

Is there a way to overcome that?

Expected behavior

A way to generate injectors also for providers that use non-exported interfaces.

Version

latest (0.5.0)

Additional context

Add any other context about the problem here.

rbroggi avatar Jun 22 '22 12:06 rbroggi

The explicit binding is there so when multiple structs implement the same interface, wire doesn't have to guess user's intent. The final generated code doesn't really need to reference that interface because of the loose coupling you mentioned. That FAQ points to #242, which I think would solve this problem as well. I'm not sure about the status of #242.

Besides #242, I can't think of a solution other than either exporting the interface (which may not be preferred, or even impossible), or refactor after generation.

jayzhuang avatar Jul 05 '22 02:07 jayzhuang