go泛型使用指针接收器
背景
在使用go泛型时,想实现一个能接收泛型实参,并能改变实参struct成员值,即指针接收器的方法。先看一个简单例子,定义一个Adder加法器,返回int结果(不要纠结)。
1. Adder实现
// 一个加法interface,用于计算两个数的和
type Adder interface {
Add() int
Set(a, b any)
}
// Adder的实现,整数
type IntAdder struct {
A int
B int
}
// 实现Adder接口的Set方法
func (i *IntAdder) Set(a, b any) {
i.A = a.(int)
i.B = b.(int)
}
func (i *IntAdder) Add() int {
return i.A + i.B
}
// Adder的实现,浮点数
type FloatAdder struct {
A float64
B float64
}
// 实现Adder接口的Set方法
func (f *FloatAdder) Set(a, b any) {
f.A = a.(float64)
f.B = b.(float64)
}
func (f *FloatAdder) Add() int {
return int(f.A + f.B)
}
2. 泛型实现int和float的add
如果要实现一个泛型的add方法,看看有哪些直接的方法
// 泛型创建一个通用的Add方法
func Add[T Adder, P any](a, b P) int {
ad := new(T)
ad.Set(a, b)
return ad.Add()
}
# go run
./general-point-golang.go:69:5: ad.Set undefined (type *T is pointer to type parameter, not type parameter)
./general-point-golang.go:70:12: ad.Add undefined (type *T is pointer to type parameter, not type parameter)
泛型形参是Adder,那Adder interface有定义[*Adder]Set/Add吗? 没有 这个报错是:Set/Add方法是被*T new(T)调用的,但这两个方法是被申明为Adder,而adder并没有[*Adder]Set/Add; 那我们改下,不用指针,解决编译错误
// 通过非指针接收器,使用generic
// 不符合预期,set不会生效,结果是00
func AddNotPoint[T Adder, P any](a, b P) int {
var av T
av.Set(a, b)
return av.Add()
}
但这部分代码并不能直接调用指针接收器的以上方法,无法实例化,类型实参传什么?(IntAdd, *IntAdd--和new效果类似了)
fmt.Println(AddNotPoint[StringAdder, string]("1", "2"))
//能调用,但是无效果, StringAdder,方法都是值作为接收器
fmt.Println(AddNotPoint[IntAdder, int](1, 2))
//IntAdder does not implement Adder (method Add has pointer receiver)compilerInvalidTypeArg
3. 泛型interface,解决以上问题
以上问题说明
实例化时:必须实例化出指针的IntAdd和FloatAdd 类型实参必须是包含Set/Add的方法,且非指针
// 泛型的interface
type AdderPoint[T any] interface {
// 这个AdderPoint泛型interface,可接受一个指针的T或一个值类型的T
*T
// Adder,让这个interface继承Adder的接口
// AddPoint就有了Add方法,Set方法,关键在new(Adder的具体实现)可直接转成AdderPoint[T]
Adder
}
type AddType interface {
IntAdder | FloatAdder | StringAdder
}
func AddPoint[T AddType, P any, ap AdderPoint[T]](a, b P) int {
var tt ap = new(T) // 实例化出指针, 指针可调用Add, Set
tt.Set(a, b)
return tt.Add()
}
int main() {
fmt.Println(AddPoint[IntAdder, int](1, 2))
fmt.Println(AddPoint[FloatAdder, float64](1.8, 2.8))
}
参考
- ? 以上实例debug代码[1] https://go.dev/play/p/bxrUHnBPgQI
- ? Using Generics with Pointer Receivers in Go[2]
- ? go generic tutorial[3]
欢迎关注我的公众号“风oneo”,原创内容第一时间推送。
引用链接
[1] 以上实例debug代码:(https://go.dev/play/p/bxrUHnBPgQI
[2] Using Generics with Pointer Receivers in Go: https://medium.com/@reetas/using-generics-with-pointer-receivers-in-go-39ee237d7475
[3] go generic tutorial: https://go.dev/doc/tutorial/generics