Some notes about Golang Generics and Abstraction based on my working experience.
WARNING
Under construction.
Use ‘generic interface’ when you need to self-reference the interface in the methods of your interface
TIP
Important references:
For Example:
Before we have generics:
type MyInterface interface {
DoSomething(MyInterface)
}
type MyConcreteType struct{}
func (m MyConcreteType) DoSomething(m2 MyInterface) {} // Here we have to use 'MyInterface' instead of 'MyConcreteType' !
Note that m2 MyInterface
argument. We can’t use ‘concrete’ type here.
The consequence is that we will have to do a dynamic type assertion to get the concrete type
for every implementation.
With generics:
type MyInterface[T any] interface {
DoSomething(T)
}
type MyConcreteType struct{}
func (m MyConcreteType) DoSomething(m2 MyConcreteType) {} // Here we can use 'MyConcreteType' directly !
NOTE
注意这个东西,it is used as the type constraint in the type parameter in the code of the generic interface user.
但是你肯定有疑问:DoSomething 如果不是 receiver 和 argument 不是同一个 type,不是还是有这个问题吗? 确实如此:
type GenericInterface[T any] interface {
DoSomething(T)
}
type ConcreteType struct{}
func (m ConcreteType) DoSomething(m2 ConcreteType) {}
type AnotherType struct{}
func (a AnotherType) DoSomething(m2 ConcreteType) {}
var (
_ GenericInterface[ConcreteType] = ConcreteType{}
// The exact example of receiver type != argument type.
_ GenericInterface[ConcreteType] = AnotherType{}
)
但是可惜,这方面只能是这样了。实际上只能在 user end 来让 compiler 知道 “对于这个 interface,receiver type 和 argument type 应该一样“
func FuncUserOfGenericInterface[T MyInterface[T]](arg T) {
arg.DoSomething(arg) // This is OK.
}
type TypeUserOfGenericInterface[T MyInterface[T]] struct {
value T
}
func (t TypeUserOfGenericInterface[T]) method() {
t.value.DoSomething(t.value) // This is OK.
}
In the example above:
- The
T MyInterface[T]
enforced the reciver type and the argument type must be the same.
Functor and single-method interface conversion
这两个虽然有很多相似的地方,而且如果 functor 可以直接当作 implementation for single-method interace,可能很多代码可以简化。
但是从 type system 的角度看,就是不一样的东西。两者之间也没有办法互相 cast。
Here is the not-planned proposal for Golang 2: https://github.com/golang/go/issues/21670
但是现实中我们可以看到很多地方用了 http.HandlerFunc
这个 functor type 来当作 http.Handler
的 implementation. 为什么呢?
Because: code
// Yes, this is a functor type.
type HandlerFunc func(ResponseWriter, *Request)
// But it also has a specific method to implement the handler interface
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
So, if you do want to let a ‘functor’ type to implement a single-method interface, you still need to explicitly implement the method for the interface:
type yourFunctorType func(xxx) yyy
// This is needed to implement interface `YourInterface` which has a method: `YourInterfaceMethod(xxx) yyy`
func (f yourFunctorType) YourInterfaceMethod(xxx) yyy {
}