Golang中使用泛型进行错误处理的实践
Golang中使用泛型进行错误处理的实践 大家好
我正在尝试使用泛型的新测试版 1.18.1。我有一个函数,它返回一个泛型类型,但也可以返回一个错误。 如果我需要返回一个错误,我需要提供一个包含泛型值和一个错误的元组。我该如何创建一个泛型值呢?
以下是我的尝试:
type Option[T any] struct {
value T
isSome bool
}
// Val returns the value or default and error if represents null
func (o Option[T]) Val() (T, error) {
if !o.isSome {
return T{}, errors.New("Opt is none")
}
return o.value, nil
}
更多关于Golang中使用泛型进行错误处理的实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html
3 回复
NobbZ:
var t T
Go 中的错误处理通常比较冗长。 感谢您的回答。有效!
更多关于Golang中使用泛型进行错误处理的实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
if !o.isSome {
var t T
return t, errors.New("Opt is none")
}
对我个人品味来说有点冗长,但它是有效的。我已经用 Option[string]、Option[int] 和 Option[bool] 测试过了,我推测更复杂的类型应该也能正常工作。
在Go 1.18+中处理泛型错误返回时,确实需要创建泛型类型的零值。你的实现基本正确,但可以进一步优化。以下是几种实践方式:
1. 使用泛型零值(你的方案改进版)
package main
import (
"errors"
"fmt"
)
type Option[T any] struct {
value T
isSome bool
}
// Val 返回值或零值,并附带错误信息
func (o Option[T]) Val() (T, error) {
if !o.isSome {
var zero T
return zero, errors.New("option is none")
}
return o.value, nil
}
// 使用示例
func main() {
// 字符串类型
strOpt := Option[string]{value: "hello", isSome: true}
if val, err := strOpt.Val(); err == nil {
fmt.Printf("String value: %s\n", val)
}
// 整数类型
intOpt := Option[int]{isSome: false}
if val, err := intOpt.Val(); err != nil {
fmt.Printf("Error: %v, Zero value: %v\n", err, val)
}
}
2. 使用泛型函数返回错误
package main
import (
"errors"
"fmt"
)
// Result 泛型结果类型
type Result[T any] struct {
value T
err error
}
// NewResult 创建成功结果
func NewResult[T any](value T) Result[T] {
return Result[T]{value: value}
}
// NewErrorResult 创建错误结果
func NewErrorResult[T any](err error) Result[T] {
var zero T
return Result[T]{value: zero, err: err}
}
// Unwrap 解包结果
func (r Result[T]) Unwrap() (T, error) {
return r.value, r.err
}
// 使用示例
func ParseInt[T int | int32 | int64](s string) Result[T] {
// 模拟解析逻辑
if s == "" {
return NewErrorResult[T](errors.New("empty string"))
}
// 实际解析代码...
var zero T
return NewResult(zero)
}
func main() {
// 成功案例
res1 := ParseInt[int]("123")
if val, err := res1.Unwrap(); err == nil {
fmt.Printf("Parsed value: %d\n", val)
}
// 错误案例
res2 := ParseInt[int]("")
if val, err := res2.Unwrap(); err != nil {
fmt.Printf("Error: %v, Zero value: %v\n", err, val)
}
}
3. 使用泛型接口处理错误
package main
import (
"errors"
"fmt"
)
// Option 泛型接口
type Option[T any] interface {
IsSome() bool
Unwrap() (T, error)
UnwrapOr(defaultVal T) T
}
// Some 有值的情况
type Some[T any] struct {
value T
}
func (s Some[T]) IsSome() bool { return true }
func (s Some[T]) Unwrap() (T, error) {
return s.value, nil
}
func (s Some[T]) UnwrapOr(defaultVal T) T {
return s.value
}
// None 无值的情况
type None[T any] struct{}
func (n None[T]) IsSome() bool { return false }
func (n None[T]) Unwrap() (T, error) {
var zero T
return zero, errors.New("none value")
}
func (n None[T]) UnwrapOr(defaultVal T) T {
return defaultVal
}
// 使用示例
func Divide[T ~int | ~float64](a, b T) Option[T] {
if b == 0 {
return None[T]{}
}
return Some[T]{value: a / b}
}
func main() {
// 成功除法
result1 := Divide(10.0, 2.0)
if val, err := result1.Unwrap(); err == nil {
fmt.Printf("Division result: %.2f\n", val)
}
// 除零错误
result2 := Divide(10, 0)
if val, err := result2.Unwrap(); err != nil {
fmt.Printf("Error: %v\n", err)
}
// 使用默认值
defaultVal := result2.UnwrapOr(999)
fmt.Printf("Default value: %v\n", defaultVal)
}
4. 泛型工厂函数模式
package main
import (
"errors"
"fmt"
)
// Result 泛型结果
type Result[T any] struct {
Value T
Err error
}
// Try 泛型尝试执行函数
func Try[T any](fn func() (T, error)) Result[T] {
val, err := fn()
return Result[T]{Value: val, Err: err}
}
// Map 泛型转换
func Map[T, U any](r Result[T], fn func(T) (U, error)) Result[U] {
if r.Err != nil {
var zero U
return Result[U]{Value: zero, Err: r.Err}
}
return Try(func() (U, error) {
return fn(r.Value)
})
}
// 使用示例
func ParseString(s string) (string, error) {
if s == "" {
return "", errors.New("empty string")
}
return s, nil
}
func StringLength(s string) (int, error) {
return len(s), nil
}
func main() {
// 链式调用
result := Try(func() (string, error) {
return ParseString("hello")
})
lengthResult := Map(result, StringLength)
if lengthResult.Err != nil {
fmt.Printf("Error: %v\n", lengthResult.Err)
} else {
fmt.Printf("Length: %d\n", lengthResult.Value)
}
}
这些示例展示了在Go泛型中处理错误的不同方法。你的原始方案已经基本正确,只需要使用 var zero T 来显式创建零值,这样代码意图更清晰。

