Wire 报 "returns cleanup but injection does not return cleanup function" 怎么排查 injector 签名?
【免费下载链接】wireCompile-time Dependency Injection for Go项目地址: https://gitcode.com/GitHub_Trending/wi/wire
当你在 Go 项目中用 Wire 做编译期依赖注入,在包含 injector 的包目录下执行wire生成代码时,可能收到这样的错误:
example.com/foo/wire.go:x:y: inject injectFoo: provider for example.com/foo.Foo returns cleanup but injection does not return cleanup function(以上为仓库测试数据中的期望输出,行号x:y是测试占位,实际运行时会显示真实位置。)
这条错误的含义是:provider set 里有一个 provider 返回了 cleanup 函数,但 injector 函数的返回值中没有对应的 cleanup 函数。本文基于 Wire 仓库的用户指南(docs/guide.md)和internal/wire/testdata下的官方测试用例,给出从报错定位到修复、再验证生成的完整排查路径。
报错信息怎么读
错误信息本身已经给出了定位线索。对照仓库测试用例 InjectorMissingCleanup 的期望输出,格式为:
<文件位置>: inject <injector名>: provider for <包名>.<类型名> returns cleanup but injection does not return cleanup functioninject injectFoo:出问题的 injector 函数名;provider for example.com/foo.Foo:提供类型Foo的那个 provider——问题就出在它返回了 cleanup 函数;- 后半句说明根因:injector 的签名没有声明对应的 cleanup 返回值。
所以排查的第一步不是通读全部 provider,而是根据报错里的类型名找到具体那个 provider,确认它的返回值。
核对 provider 的返回值
在测试用例 internal/wire/testdata/InjectorMissingCleanup/foo 中,provider 定义在 foo.go:
func provideFoo() (Foo, func()) { return Foo(42), func() {} }第二个返回值func()就是 cleanup 函数。用户指南(docs/guide.md 的 "Cleanup functions" 一节)规定:cleanup 函数必须具有func()签名,并且保证在任何该 provider 的输入 provider 的 cleanup 函数之前被调用。
而同一目录的 injector 声明(wire.go)却是:
func injectFoo() Foo { // provideFoo returns a cleanup, but injectFoo does not. wire.Build(provideFoo) return Foo(0) }injector 只返回Foo,没有 cleanup 返回值——这正是报错的原因。
判定规则来自根目录 wire.go 中Build的文档注释:
the first return value is the output of the injector function, the optional second return value is a cleanup function, and the optional last return value is an error. If any of the provider functions in the injector function's provider set return errors or cleanup functions, the corresponding return value must be present in the injector function template.
也就是说:provider set 中只要有 provider 返回 cleanup 或 error,injector 模板的返回值里就必须有对应的 cleanup(第二个返回值)和/或error(最后一个返回值)。
修改 injector 签名
修复方式是把 cleanup 函数加进 injector 的返回值。对上面的例子,改法如下:
func injectFoo() (Foo, func()) { wire.Build(provideFoo) return Foo(0), nil }如果 provider 同时返回 error(即provideXxx() (T, func(), error)形式),injector 必须同时声明 cleanup 和 error,且顺序固定为func()在error之前。仓库测试用例 PartialCleanup 展示了这种签名:
func injectBaz() (Baz, func(), error) { wire.Build(provideFoo, provideBar, provideBaz) return 0, nil, nil }只涉及 cleanup、不涉及 error 时,参考测试用例 Cleanup 的 injector 签名:
func injectBar() (*Bar, func()) { wire.Build(provideFoo, provideBar) return nil, nil }注意 injector 模板体里的return只起占位作用(返回值的类型必须正确,值会被生成代码忽略),所以从return Foo(0)改成return Foo(0), nil即可。另外,承载 injector 的文件要带wireinject构建标签(如//+build wireinject),避免 stub 参与最终构建,这一点上述测试用例文件头部均有体现。
重新生成并验证
修复签名后,在 injector 所在的包目录下重新运行:
wire成功条件有两个:
wire不再输出returns cleanup but injection does not return cleanup function错误;- 生成的
wire_gen.go中出现了聚合的 cleanup 函数。
以 Cleanup/want/wire_gen.go 的期望输出为例(文档示例),生成结果类似:
func injectBar() (*Bar, func()) { foo, cleanup := provideFoo() bar, cleanup2 := provideBar(foo) return bar, func() { cleanup2() cleanup() } }可以看到两个要点:cleanup 被聚合成一个func()返回给调用方;调用顺序是后创建的 provider 先清理(cleanup2()先于cleanup())。如果某个 provider 在注入过程中返回 error,生成代码会在错误路径上调用已收集到的 cleanup 并返回 error——PartialCleanup/want/wire_gen.go 展示了这种带 error 分支的生成形式,可作为你验证自己生成结果的对照。
wire_gen.go生成之后,后续可以用go generate重新生成(wire_gen.go头部带有//go:generate指令,见上方生成示例)。
相关限制
- cleanup 函数必须是
func()类型,不能带参数或返回值(docs/guide.md、wire.go)。 - 错误信息与 cleanup 规则是对称的:provider 返回 error 而 injector 没有返回
error时,Build文档注释中的同一判定规则同样适用,修复方式(在 injector 返回值末尾加上error)与本文一致。 - injector 返回的聚合 cleanup 由调用方负责调用,例如 Cleanup/foo/foo.go 的
main中在用完bar后显式调用cleanup()。
如果你的 provider set 较复杂,报错只会指出触发该条错误的那个 provider;按"读报错 → 核对 provider 返回值 → 补全 injector 返回值 → 重新运行wire"的顺序处理,直到wire无错误输出且wire_gen.go按上述形式生成。
【免费下载链接】wireCompile-time Dependency Injection for Go项目地址: https://gitcode.com/GitHub_Trending/wi/wire
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考