
在 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),同时保持资源系统完全解耦于具体实现细节。










