使用接口指针作为泛型参数时触发空指针解引用panic问题求助
问题:泛型接口指针接收者引发nil指针解引用Panic
问题重现
定义泛型Storage接口及指针接收者实现的Base结构体:
type Storage[T any] interface { Allocate(size int) } type Base[T any] struct { data []T } func (b *Base[T]) Allocate(size int) { b.data = make([]T, size) }
尝试用该接口约束Tensor泛型参数并初始化时触发panic:
type Tensor[T any, U Storage[T]] struct { data U } func NewTensor[T any, U Storage[T]](size int) *Tensor[T, U] { result := &Tensor[T, U]{} result.data = *new(U) result.data.Allocate(size) return result } func main() { t := NewTensor[float32, *Base[float32]](10) fmt.Println(t) }
Panic信息:
panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x1 addr=0x8 pc=0x47de63] goroutine 1 [running]: main.(*Base[...]).Allocate(...) .../goten/debug/debug.go:14 main.NewTensor[...](0x60) .../goten/debug/debug.go:24 +0x47 main.main() .../goten/debug/debug.go:29 +0x25 exit status 2
问题原因
当U为*Base[float32]时,new(U)返回的是**Base[float32]类型指针,解引用*new(U)得到的是nil(指针类型零值为nil)。此时调用result.data.Allocate(size)本质是对nil指针调用方法,触发nil指针解引用panic。
解决方案
方案1:反射初始化兼容指针/值类型
利用反射创建U的有效实例,无需修改现有接口,兼容所有Storage实现:
import "reflect" func NewTensor[T any, U Storage[T]](size int) *Tensor[T, U] { result := &Tensor[T, U]{} uType := reflect.TypeOf((*U)(nil)).Elem() var u U if uType.Kind() == reflect.Ptr { // 创建指针类型的非nil实例 u = reflect.New(uType.Elem()).Interface().(U) } else { // 值类型直接零值初始化 u = reflect.Zero(uType).Interface().(U) } result.data = u result.data.Allocate(size) return result }
方案2:添加构造约束
给Storage相关类型添加构造方法,通过约束保证可初始化:
type Storage[T any] interface { Allocate(size int) } // 定义构造约束 type StorageConstructor[T any, U Storage[T]] interface { New() U } // 给Base指针类型实现构造方法 func (*Base[T]) New() *Base[T] { return &Base[T]{} } type Tensor[T any, U Storage[T]] struct { data U } func NewTensor[T any, U Storage[T], C StorageConstructor[T, U]](size int) *Tensor[T, U] { result := &Tensor[T, U]{} var c C result.data = c.New() result.data.Allocate(size) return result } // 调用时传入构造类型 func main() { t := NewTensor[float32, *Base[float32], *Base[float32]](10) fmt.Println(t) }
最终可行代码示例
采用反射方案的完整可运行代码:
package main import ( "fmt" "reflect" ) type Storage[T any] interface { Allocate(size int) } type Base[T any] struct { data []T } func (b *Base[T]) Allocate(size int) { b.data = make([]T, size) } type Tensor[T any, U Storage[T]] struct { data U } func NewTensor[T any, U Storage[T]](size int) *Tensor[T, U] { result := &Tensor[T, U]{} uType := reflect.TypeOf((*U)(nil)).Elem() var u U if uType.Kind() == reflect.Ptr { u = reflect.New(uType.Elem()).Interface().(U) } else { u = reflect.Zero(uType).Interface().(U) } result.data = u result.data.Allocate(size) return result } func main() { t := NewTensor[float32, *Base[float32]](10) fmt.Printf("Tensor data length: %d\n", len(t.data.data)) }
运行输出:
Tensor data length: 10
内容的提问来源于stack exchange,提问作者user3483203
相关产品推荐
相关产品推荐

