Go语言中如何调用受多返回类型约束的泛型回调函数?
问题描述
我想要在函数中允许回调参数支持两种返回元组类型,为此使用泛型定义回调的参数类型,代码如下:
func Get[ In any, Out any, TranslateFn func(In) Out | func(In) (Out, error), ]( input In, translate TranslateFn, ) (*Out, error) { // Call translate to convert the input to the output. // If translate is an erroring function, make sure the error is // forwarded to the response of this function. }
由于TranslateFn被约束为两种返回类型(Out 或 (Out, error))之一,我本以为可以直接调用该回调,但尝试用类型断言区分回调类型时出现错误:
invalid operation: cannot use type assertion on type parameter value translate (variable of type TranslateFn constrained by func(In) Out|func(In) (Out, error))
我的尝试代码如下:
func Get[ In any, Out any, TranslateFn func(In) Out | func(In) (Out, error), ]( input In, translate TranslateFn, ) (*Out, error) { if erroringFn, isErroringTranslateFn := translate.(func(In) (Out, error)); isErroringTranslateFn { out, err := erroringFn(input) if err != nil { return nil, err } return &out, nil } if nonErroringFn, isNonErroringTranslateFn := translate.(func(In) Out); isNonErroringTranslateFn { out, err := nonErroringFn(input) if err != nil { return nil, err } return &out, nil } panic("translate function must be either func(In) (Out, error) or func(In) Out") }
请问不使用类型断言的情况下,如何调用该回调函数,或者确定传入的是哪种泛型变体?
解决方案
方法一:转any后做类型断言(兼容原泛型结构)
Go不允许直接对受类型集约束的类型参数做类型断言,但可以先将变量转为any类型绕过限制,这是最直接的修复方式:
func Get[ In any, Out any, TranslateFn func(In) Out | func(In) (Out, error), ]( input In, translate TranslateFn, ) (*Out, error) { // 先转为any再做类型分支判断 switch fn := any(translate).(type) { case func(In) (Out, error): out, err := fn(input) if err != nil { return nil, err } return &out, nil case func(In) Out: out := fn(input) return &out, nil default: panic("translate function must be either func(In) (Out, error) or func(In) Out") } }
方法二:接口封装(完全无类型断言)
如果要彻底避免类型断言,可以定义统一接口抽象两种回调的行为,通过包装类型实现接口:
// 定义统一翻译器接口 type Translator[In, Out any] interface { Translate(In) (Out, error) } // 包装无错误返回的回调 type noErrTranslator[In, Out any] func(In) Out func (f noErrTranslator[In, Out]) Translate(in In) (Out, error) { return f(in), nil } // 包装带错误返回的回调 type errTranslator[In, Out any] func(In) (Out, error) func (f errTranslator[In, Out]) Translate(in In) (Out, error) { return f(in) } // 修改后的Get函数,依赖统一接口 func Get[In any, Out any](input In, translate Translator[In, Out]) (*Out, error) { out, err := translate.Translate(input) if err != nil { return nil, err } return &out, nil }
可以再写一个辅助函数自动包装回调,简化调用:
func WrapTranslator[In any, Out any](fn any) Translator[In, Out] { switch f := fn.(type) { case func(In) Out: return noErrTranslator[In, Out](f) case func(In) (Out, error): return errTranslator[In, Out](f) default: panic("invalid translator function type") } } // 调用示例 func main() { // 无错误回调 strToInt := func(s string) int { num, _ := strconv.Atoi(s) return num } res, _ := Get("123", WrapTranslator[string, int](strToInt)) // 带错误回调 safeStrToInt := func(s string) (int, error) { return strconv.Atoi(s) } res2, _ := Get("456", WrapTranslator[string, int](safeStrToInt)) }
这种方式通过接口统一了两种回调的调用逻辑,Get函数内部完全不需要类型断言。
内容的提问来源于stack exchange,提问作者Nathan F.
相关产品推荐
相关产品推荐

