golang实现可选结构体字段和变量的插件库optional的使用

Golang 实现可选结构体字段和变量的插件库 optional 的使用

安装

go get github.com/kazhuravlev/optional

快速开始

package main

import (
	"encoding/json"
	"fmt"
	"github.com/kazhuravlev/optional"
)

type User struct {
	Name      string               `json:"name"`
	AvatarURL optional.Val[string] `json:"avatar_url,omitempty"`
}

func main() {
	// JSON with optional field
	data := []byte(`{"name": "Alice", "avatar_url": "https://example.com/avatar.jpg"}`)
	
	var user User
	json.Unmarshal(data, &user)
	
	// Check if value exists
	if avatarURL, ok := user.AvatarURL.Get(); ok {
		fmt.Printf("Avatar URL: %s\n", avatarURL)
	}
	
	// Or use a default
	url := user.AvatarURL.ValDefault("https://example.com/default.jpg")
	fmt.Printf("URL with default: %s\n", url)
}

主要特性

  • 类型安全:泛型实现防止运行时类型错误
  • 零值友好:不会将缺失值与零值混淆
  • 多格式支持:支持 JSON、YAML、SQL 和自定义编组器
  • 简单 API:直观的方法如 Get()Set()HasVal()Reset()

API 概览

var opt optional.Val[string]

// Set a value
opt.Set("hello")

// Check and get
if val, ok := opt.Get(); ok {
    fmt.Println(val) // "hello"
}

// Get with default
val := opt.ValDefault("default") // "hello"

// Reset to empty
opt.Reset()

// Convert to pointer (nil if empty)
ptr := opt.AsPointer() // *string

完整示例

package main

import (
	"encoding/json"
	"fmt"
	"github.com/kazhuravlev/optional"
)

type Profile struct {
	Username  string               `json:"username"`
	Email     optional.Val[string] `json:"email"`
	Age       optional.Val[int]    `json:"age"`
	IsPremium optional.Val[bool]   `json:"is_premium"`
}

func main() {
	// 创建带有可选字段的结构体
	profile := Profile{
		Username: "john_doe",
		Email:    optional.Set("john@example.com"),
		// Age 和 IsPremium 保持未设置状态
	}

	// 序列化为 JSON
	jsonData, _ := json.Marshal(profile)
	fmt.Println(string(jsonData))
	// 输出: {"username":"john_doe","email":"john@example.com"}

	// 反序列化 JSON
	var newProfile Profile
	json.Unmarshal([]byte(`{"username":"jane_doe","age":30}`), &newProfile)

	// 检查可选字段
	if age, ok := newProfile.Age.Get(); ok {
		fmt.Printf("Age: %d\n", age) // 输出: Age: 30
	}

	if email, ok := newProfile.Email.Get(); ok {
		fmt.Printf("Email: %s\n", email)
	} else {
		fmt.Println("Email not provided") // 输出: Email not provided
	}

	// 使用默认值
	isPremium := newProfile.IsPremium.ValDefault(false)
	fmt.Printf("Is premium: %v\n", isPremium) // 输出: Is premium: false
}

更多关于golang实现可选结构体字段和变量的插件库optional的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现可选结构体字段和变量的插件库optional的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 可选字段与变量:使用 optional 库

在 Go 语言中,由于没有内置的可选字段或变量概念,我们通常需要使用指针或特殊值来表示可选状态。optional 库提供了一种更优雅的方式来处理这种情况。

安装 optional 库

go get github.com/markphelps/optional

基本用法

1. 可选基本类型

package main

import (
	"fmt"
	"github.com/markphelps/optional"
)

func main() {
	// 创建一个有值的可选字符串
	strOpt := optional.NewString("hello")
	fmt.Println(strOpt.Get()) // 输出: hello true

	// 创建一个空的可选字符串
	emptyStr := optional.String{}
	fmt.Println(emptyStr.Get()) // 输出:  false

	// 检查是否有值
	if strOpt.Present() {
		fmt.Println("Value exists:", strOpt.MustGet())
	}

	// 设置新值
	strOpt.Set("world")
	fmt.Println(strOpt.MustGet()) // 输出: world

	// 清除值
	strOpt.Clear()
	fmt.Println(strOpt.Present()) // 输出: false
}

2. 可选结构体字段

package main

import (
	"fmt"
	"github.com/markphelps/optional"
)

type User struct {
	ID       int
	Name     string
	Email    optional.String
	Age      optional.Uint
	Address  optional.String
}

func main() {
	// 创建用户,部分字段可选
	user := User{
		ID:    1,
		Name:  "Alice",
		Email: optional.NewString("alice@example.com"),
		// Age 和 Address 留空
	}

	// 检查并获取可选字段
	if email, ok := user.Email.Get(); ok {
		fmt.Println("Email:", email)
	} else {
		fmt.Println("Email not provided")
	}

	// 使用 OrElse 提供默认值
	age := user.Age.OrElse(30)
	fmt.Println("Age:", age) // 输出: 30

	// 更新可选字段
	user.Address.Set("123 Main St")
	fmt.Println(user.Address.MustGet()) // 输出: 123 Main St
}

3. 高级用法

package main

import (
	"fmt"
	"github.com/markphelps/optional"
)

func main() {
	// 使用 IfPresent 执行回调
	opt := optional.NewInt(42)
	opt.IfPresent(func(value int) {
		fmt.Println("Value is present:", value)
	})

	// 链式操作
	result := optional.NewString(" hello ")
		.Map(func(s string) string { return strings.TrimSpace(s) })
		.Filter(func(s string) bool { return len(s) > 0 })
		.OrElse("default")
	fmt.Println(result) // 输出: hello

	// 合并两个可选值
	opt1 := optional.NewString("first")
	opt2 := optional.String{}
	merged := optional.FirstPresent(opt1, opt2)
	fmt.Println(merged.MustGet()) // 输出: first
}

自定义可选类型

如果需要处理自定义类型,可以这样实现:

package main

import (
	"fmt"
	"github.com/markphelps/optional"
)

type CustomType struct {
	Field1 string
	Field2 int
}

func main() {
	// 创建自定义类型的可选值
	customOpt := optional.New(CustomType{"test", 123})

	// 使用自定义类型
	if val, ok := customOpt.Get(); ok {
		fmt.Printf("Custom value: %+v\n", val)
	}

	// 或者使用结构体中的可选字段
	type Config struct {
		Timeout optional.Optional[time.Duration]
		Retries optional.Optional[int]
	}

	cfg := Config{
		Timeout: optional.New(5 * time.Second),
	}
	fmt.Println(cfg.Timeout.OrElse(1 * time.Second)) // 输出: 5s
}

与 JSON 的交互

optional 库还支持 JSON 序列化和反序列化:

package main

import (
	"encoding/json"
	"fmt"
	"github.com/markphelps/optional"
)

type Profile struct {
	Name    string
	Age     optional.Int
	Website optional.String `json:",omitempty"`
}

func main() {
	// 序列化
	profile := Profile{
		Name:    "Bob",
		Website: optional.NewString("https://example.com"),
	}
	data, _ := json.Marshal(profile)
	fmt.Println(string(data)) // 输出: {"Name":"Bob","Website":"https://example.com"}

	// 反序列化
	var p Profile
	json.Unmarshal([]byte(`{"Name":"Bob"}`), &p)
	fmt.Println(p.Age.Present()) // 输出: false
}

总结

optional 库为 Go 提供了处理可选字段和变量的优雅方式,主要优点包括:

  1. 更清晰的语义表示可选值
  2. 避免使用指针带来的 nil 检查
  3. 提供丰富的操作方法(Map、Filter、IfPresent 等)
  4. 支持 JSON 序列化/反序列化
  5. 类型安全,减少运行时错误

对于需要处理可选字段的场景,特别是 API 响应、配置解析等,optional 库是一个很好的选择。

回到顶部