
在 go 中,函数回调需返回接口类型而非具体结构体指针;因 go 的类型系统要求函数签名严格匹配,即使结构体实现了接口,func(String) *t 与 func(string) Interface 仍被视为不同类型,无法直接赋值。
Go 的接口是静态声明、动态实现的抽象机制,但其类型系统对函数签名具有严格的类型身份(type identity)要求。问题核心在于:AddTextureLoader 期望接收一个 func(string) *Texture 类型的参数(注意:此处 *Texture 是指向接口的指针,本身即错误用法),而 NewDDSTexture 返回的是 *DDSTexture —— 一个具体结构体指针。尽管 *DDSTexture 可隐式转换为 Texture 接口值,但 func(string) *DDSTexture 和 func(string) Texture(或 func(string) *Texture)在类型层面完全不兼容。
✅ 正确做法:函数返回接口类型,而非结构体指针
首先,修正 Resource.go 中的函数签名,将参数类型从 func(string) *Texture 改为 func(string) Texture:
// resource/resource.go package resource var ( tex_types map[string]func(string) Texture = make(map[string]func(string) Texture) ) type Texture interface { Texture() (uint32, error) Width() int Height() int } func AddTextureLoader(ext string, fn func(string) Texture) { tex_types[ext] = fn }
⚠️ 注意:*Texture 是反模式!接口本身已是引用类型,*Texture 表示“指向接口变量的指针”,通常毫无必要且易引发混淆和内存误用。
接着,在 dds.go 中,让工厂函数返回 Texture 接口值(而非 *DDSTexture):
// texture/dds.go package texture import "your-module/resource" type DDSTexture struct { path string _tid uint32 height uint32 width uint32 } func NewDDSTexture(filename string) resource.Texture { return &DDSTexture{ path: filename, _tid: 0, height: 0, width: 0, } } func (d *DDSTexture) Texture() (uint32, error) { /* 实现 */ } func (d *DDSTexture) Width() int { return int(d.width) } func (d *DDSTexture) Height() int { return int(d.height) } func init() { resource.AddTextureLoader("dds", NewDDSTexture) }
✅ 此时 NewDDSTexture 类型为 func(string) resource.Texture,与 AddTextureLoader 参数签名完全一致,编译通过。
? 关键原理说明
- Go 不支持“协变返回类型”(covariant return types):func() *DDSTexture ≠ func() Texture,即使 *DDSTexture 实现了 Texture。
- 接口值本质是 (type, value) 对,可安全持有任何满足接口的实例(包括 *DDSTexture);因此工厂函数应直接返回该接口值。
- 使用 &DDSTexture{} 而非 DDSTexture{} 通常更合理(避免拷贝大结构体,且方法集完整),但返回类型必须是 Texture,而非 *DDSTexture。
? 总结
| 错误写法 | 正确写法 |
|---|---|
| func(string) *Texture(接口指针) | func(string) Texture(接口值) |
| func(string) *DDSTexture | func(string) Texture(由 *DDSTexture 隐式转换) |
| AddTextureLoader(“dds”, NewDDSTexture)(类型不匹配) | ✅ 类型一致,可直接注册 |
遵循这一模式,即可灵活注册任意 Texture 实现(如 PNGTexture、JPEGTexture),同时保持资源系统完全解耦于具体实现细节。