Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to work with un-exported interfaces #366

Open
rbroggi opened this issue Jun 22, 2022 · 1 comment
Open

How to work with un-exported interfaces #366

rbroggi opened this issue Jun 22, 2022 · 1 comment

Comments

@rbroggi
Copy link

rbroggi commented Jun 22, 2022

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.

@jayzhuang
Copy link
Collaborator

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants