Golang中如何实现泛型

Golang中如何实现泛型 你好

我的英语不好,所以这个话题是用翻译器写的。请理解。

我两周前开始学习Go。我主要使用C语言编程。我对使用void指针的泛型模式很感兴趣。我想在Go中实现类似的形式。

我知道Go不支持泛型语法,但可以通过接口实现类似的功能。

这是我正在阅读的书籍中的一个例子:

type fType func(int, int) int

func errorHandler(fn fType) fType {
    return func(a int, b int) int {
        defer func() {
            if err, ok := recover().(error); ok {
                log.Printf("run time panic: %v", err)
            }
        }()
        return fn(a, b)
    }
}

func divide(a int, b int) int {
    return a / b
}

我想将errorHandler的参数改为泛型风格。我知道interface{}的功能与void *类似。以下是我的代码:

type gfType func(...interface{}) interface{}

func gErrorHandler(gfn gfType) gfType {
	return func(a ...interface{}) interface{} {
		defer func() {
			if err, ok := recover().(error); ok {
				log.Printf("run time panic: %v", err)
			}
		}()
		return gfn(a...)
	}
}

func divide(arg ...interface{}) interface{} {
	a := arg[0].(int)
	b := arg[1].(int)
	return a / b
}

我想知道这是否是Go所倡导的方式。

我也通过接口实现了它,但我不认为这有什么优势。

type runner interface {
	run() interface{}
}

func iErrorHandler(r runner) interface{} {
	defer func() {
		if err, ok := recover().(error); ok {
			log.Printf("run time panic: %v", err)
		}
	}()
	return r.run()
}

type div struct {
	a, b int
}

func (d div) run() interface{} {
	return d.a / d.b
}

感谢你阅读我的话题。


更多关于Golang中如何实现泛型的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

KumKeeHyun:

func divide(arg ...interface{}) interface{}

这个函数签名看起来很奇怪。对于0个、1个、3个及以上参数,除法意味着什么?除了两个数值类型之外,其他任何东西的除法又是什么?

KumKeeHyun: 我想知道这是否是Go语言的发展方向。

不,Go语言并没有朝着这个方向发展。你可以像这样使用interface{},但你应该首先问问自己试图解决什么问题。关于Go语言中泛型的讨论并不新鲜,已经有很多相关的讨论和文章。你可以看看下面的网站获取一些想法。

谁需要泛型?用…代替! - Applied Go

Go语言没有泛型。本文是对可以替代使用的技术的调查。

更多关于Golang中如何实现泛型的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中实现泛型风格,目前主要有两种方式:使用interface{}(Go 1.18之前)和类型参数(Go 1.18+)。你的两种实现都是有效的,但各有特点。

1. 使用interface{}的方式

你的gfType实现确实类似于C的void*,但存在类型安全问题:

func divide(arg ...interface{}) interface{} {
    // 需要运行时类型断言,如果类型不匹配会panic
    a := arg[0].(int)
    b := arg[1].(int)
    return a / b
}

// 使用示例
result := gErrorHandler(divide)(10, 2)
fmt.Println(result) // 5

// 但类型不安全:
// result := gErrorHandler(divide)("10", "2") // 运行时会panic

2. 使用接口的方式

你的接口实现更符合Go的惯用法,提供了更好的类型安全:

type Runner interface {
    Run() interface{}
}

type ErrorHandler struct{}

func (eh ErrorHandler) Handle(r Runner) interface{} {
    defer func() {
        if err, ok := recover().(error); ok {
            log.Printf("run time panic: %v", err)
        }
    }()
    return r.Run()
}

// 具体实现
type DivRunner struct {
    a, b int
}

func (d DivRunner) Run() interface{} {
    if d.b == 0 {
        panic("division by zero")
    }
    return d.a / d.b
}

// 使用
handler := ErrorHandler{}
result := handler.Handle(DivRunner{a: 10, b: 2})

3. Go 1.18+ 泛型实现

如果你使用Go 1.18或更高版本,可以使用类型参数实现真正的泛型:

package main

import (
    "fmt"
    "log"
)

type Operation[T any] func(T, T) T

func ErrorHandler[T any](op Operation[T]) Operation[T] {
    return func(a, b T) T {
        defer func() {
            if err, ok := recover().(error); ok {
                log.Printf("run time panic: %v", err)
            }
        }()
        return op(a, b)
    }
}

// 整数除法
func DivideInt(a, b int) int {
    return a / b
}

// 浮点数除法
func DivideFloat(a, b float64) float64 {
    return a / b
}

func main() {
    // 类型安全,编译时检查
    intDiv := ErrorHandler(DivideInt)
    result1 := intDiv(10, 2)
    fmt.Println(result1) // 5
    
    floatDiv := ErrorHandler(DivideFloat)
    result2 := floatDiv(10.0, 2.0)
    fmt.Println(result2) // 5.0
}

4. 更通用的泛型错误处理器

type GenericFunc[In, Out any] func(...In) Out

func GenericErrorHandler[In, Out any](fn GenericFunc[In, Out]) GenericFunc[In, Out] {
    return func(args ...In) (result Out) {
        defer func() {
            if err, ok := recover().(error); ok {
                log.Printf("run time panic: %v", err)
                // 可以返回零值或处理错误
            }
        }()
        return fn(args...)
    }
}

// 使用
func Add(a, b int) int {
    return a + b
}

func Concat(a, b string) string {
    return a + b
}

func main() {
    safeAdd := GenericErrorHandler(Add)
    fmt.Println(safeAdd(10, 20)) // 30
    
    safeConcat := GenericErrorHandler(Concat)
    fmt.Println(safeConcat("Hello, ", "World!")) // Hello, World!
}

总结

  1. Go 1.18之前:你的接口实现是推荐的方式,它提供了更好的类型安全和可读性
  2. Go 1.18+:使用类型参数实现真正的泛型,既有类型安全又有灵活性
  3. interface{}方式虽然灵活,但失去了类型安全,需要谨慎使用

对于错误处理模式,建议结合泛型和接口,根据具体场景选择最合适的实现方式。

回到顶部