golang通用可选值与结果类型处理插件库valor的使用

Golang通用可选值与结果类型处理插件库valor的使用

介绍

valor模块提供了可选值和结果类型,它们可能包含一个值(因此命名为"valor",是"value or"的缩写)。这不是为了让Go代码看起来不像Go,而是为了将Go已经鼓励的"comma ok"和"errors are values"原则编码化。

安装

go get github.com/phelmkamp/valor

类型

Value

optional.Value模拟了"comma ok"的习惯用法。它包含一个值(ok)或什么都没有(not ok)。

m := map[string]int{"foo": 42}
val := optional.OfIndex(m, "foo")
fmt.Println(val.IsOk()) // true

var foo int
fmt.Println(val.Ok(&foo), foo) // true 42

valStr := optional.Map(val, strconv.Itoa)
fmt.Println(valStr) // {42 true}

val = optional.OfIndex(m, "bar")
fmt.Println(val.Or(-1))                          // -1
fmt.Println(val.OrZero())                        // 0
fmt.Println(val.OrElse(func() int { return 1 })) // 1

Result

result.Result包含一个值或一个错误。

// 传统方式
if res := result.Of(w.Write([]byte("foo"))); res.IsError() {
    fmt.Println(res.Error())
    return
}

// 尝试获取值,如果不ok则打印包装的错误
// 注意:处理之后只有相关值在作用域内
var n int
if res := result.Of(w.Write([]byte("foo"))); !res.Value().Ok(&n) {
    fmt.Println(res.Errorf("Write() failed: %w").Error())
    return
}

// 与上面相同,但有多个值
var s string
var b bool
if res := two.TupleResultOf(multi(false)); !res.Value().Do(
    func(t two.Tuple[string, bool]) { s, b = t.Values() },
).IsOk() {
    fmt.Println(res.Errorf("multi() failed: %w").Error())
    return
}

// errors.Is
if res := result.Of(w.Write([]byte("foo"))); res.ErrorIs(io.ErrShortWrite) {
    fmt.Println(res.Error())
    return
}

// errors.As
if res := result.Of(w.Write([]byte("foo"))); res.IsError() {
    var err *fs.PathError
    if res.ErrorAs(&err) {
        fmt.Println("path=" + err.Path)
    }
    fmt.Println(res.Error())
    return
}

// errors.Unwrap
if res := result.Of(mid(true)); res.IsError() {
    fmt.Println(res.ErrorUnwrap().Error())
    return
}

Tuples

unit.Type, singleton.Set, two.Tuple, three.Tuplefour.Tuple分别包含零到四个值。它们使ValueResult能够包含可变数量的值。

get := func(string, int, bool) {
    return "a", 1, true
}
val := two.TupleValueOf(get())
fmt.Println(val) // {{a 1} true}

Enum

enum.Enum是一个枚举类型。它用一组允许的值初始化,然后每个"副本"可选地包含一个当前选择的值。

const (
    Clubs    = "clubs"
    Diamonds = "diamonds"
    Hearts   = "hearts"
    Spades   = "spades"
)
var Suit = enum.OfString(Clubs, Diamonds, Hearts, Spades)
func main() {
    fmt.Println(Suit.Values())          // [clubs diamonds hearts spades]
    fmt.Println(Suit.ValueOf("Foo"))    // { false}
    fmt.Println(Suit.ValueOf(Hearts))   // {hearts true}
}

其他语言中的类似概念

Rust

Value类似于Rust的OptionResult类似于Rust的Result

switch语句提供了与Rust的match类似的语义:

var foo int
switch val {
case val.OfOk():
    foo = val.MustOk()
    fmt.Println("Ok", foo)
case optional.OfNotOk[int]():
    fmt.Println("Not Ok")
    return
}

var n int
switch res {
case res.OfOk():
    n = res.Value().MustOk()
    fmt.Println("Ok", n)
case res.OfError():
    fmt.Println("Error", res.Error())
    return
}

Java

Value类似于Java的Optional

发布

该模块目前处于v0版本,但将尽一切努力避免破坏性更改。相反,功能将根据需要被弃用,并计划在v1中删除。

Linter

valorcheck是一个linter,用于检查对可选值的访问是否针对值不存在的情况进行了保护。


更多关于golang通用可选值与结果类型处理插件库valor的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang通用可选值与结果类型处理插件库valor的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang通用可选值与结果类型处理插件库valor使用指南

valor是一个用于处理可选值和结果类型的Golang库,提供了类似Rust中Option和Result的功能,让错误处理更优雅。

核心概念

valor提供了两种主要类型:

  • Option[T]:表示一个可能存在或不存在的值
  • Result[T, E]:表示一个可能成功或失败的操作

安装

go get github.com/chronos-tachyon/valor

Option类型使用

创建Option

import "github.com/chronos-tachyon/valor"

func main() {
    // 有值的Option
    someValue := valor.Some(42)
    
    // 无值的Option
    noneValue := valor.None[int]()
    
    // 从指针创建
    var ptr *int
    optFromPtr := valor.FromPtr(ptr) // None
    
    num := 10
    optFromPtr2 := valor.FromPtr(&num) // Some(10)
}

操作Option

// 检查是否有值
if someValue.IsSome() {
    fmt.Println("有值")
}

// 获取值(不安全)
value := someValue.Unwrap()

// 安全获取值
value, ok := someValue.Get()

// 提供默认值
value := noneValue.UnwrapOr(100)

// 链式操作
result := valor.Some(5).
    Map(func(x int) int { return x * 2 }).
    Filter(func(x int) bool { return x > 5 }).
    UnwrapOr(0)

Result类型使用

创建Result

func divide(a, b int) valor.Result[int, error] {
    if b == 0 {
        return valor.Error[int](errors.New("除数不能为零"))
    }
    return valor.OK(a / b)
}

操作Result

res := divide(10, 2)

// 检查是否成功
if res.IsOK() {
    fmt.Println("操作成功")
}

// 获取值或错误
value, err := res.Get()

// 安全获取值
value := res.Unwrap()

// 提供默认值
value := res.UnwrapOr(0)

// 错误处理
value := res.UnwrapOrElse(func(err error) int {
    log.Printf("错误发生: %v", err)
    return 0
})

// 链式操作
result := divide(10, 2).
    Map(func(x int) int { return x * 2 }).
    AndThen(func(x int) valor.Result[int, error] {
        return divide(x, 3)
    })

实际应用示例

package main

import (
	"errors"
	"fmt"
	"log"

	"github.com/chronos-tachyon/valor"
)

type User struct {
	ID   int
	Name string
}

func findUser(id int) valor.Result[*User, error] {
	if id < 0 {
		return valor.Error[*User](errors.New("无效的用户ID"))
	}
	
	// 模拟数据库查询
	if id == 1 {
		return valor.OK(&User{ID: 1, Name: "Alice"})
	}
	
	return valor.Error[*User](errors.New("用户未找到"))
}

func getUserName(id int) valor.Result[string, error] {
	return findUser(id).Map(func(user *User) string {
		return user.Name
	})
}

func main() {
	// 处理成功情况
	result := getUserName(1)
	if name, err := result.Get(); err == nil {
		fmt.Printf("用户名: %s\n", name)
	} else {
		log.Printf("错误: %v\n", err)
	}

	// 处理错误情况
	result = getUserName(-1)
	result.Match(
		func(name string) {
			fmt.Printf("用户名: %s\n", name)
		},
		func(err error) {
			log.Printf("错误: %v\n", err)
		},
	)

	// 链式操作示例
	length := getUserName(1).
		Map(func(name string) int {
			return len(name)
		}).
		UnwrapOr(0)
	fmt.Printf("名字长度: %d\n", length)
}

优势

  1. 类型安全:编译时检查,避免运行时错误
  2. 明确意图:代码清晰表达可能缺失或错误的情况
  3. 组合操作:支持链式调用,简化复杂逻辑
  4. 减少if err != nil:更优雅的错误处理方式

valor库特别适合需要明确处理缺失值和错误场景的应用程序,能显著提高代码的可读性和健壮性。

回到顶部