Golang编译时避免重复文本查找值的反馈与探讨

Golang编译时避免重复文本查找值的反馈与探讨 我希望在编译时为本地包提供唯一的查找字符串。

我目前有以下代码:

type Key int

const (
	txt Key = iota + 1
	greeting
	// 此处无法再次添加 txt - **编译**将失败
	newKey // 尚未使用
)

var id map[Key]string

func init() {
	id = map[Key]string{txt: "txt", greeting: "greeting"}
	// 无法添加 txt: "anything",因为**编译**将失败
	// 可以添加 newKey: "txt" - 但这需要**故意犯傻**

	library.NewBlockType("server.log", "misc", setup).Add(
		text.New("Log "),
		stringinput.New(id[greeting]).Empty("greeting"),
		// 无法阻止 id 的错误重用,例如:
		// stringinput.New(id[greeting]).Empty("duplicate greeting"),
		// 然而生成的 UI 是可见的,因此这应该非常明显
		text.New(" "),
		stringinput.New(id[txt]).Empty("message"))
}

这里使用 Key 类型来将字符串查找限制在常量值上。

这能在编译时提供对复制/粘贴错误的部分(而非全部)检测。

请提供任何想法或建议。

我也很想知道这是否是惯用的代码/模式,或者是否在其他地方见过/使用过…


更多关于Golang编译时避免重复文本查找值的反馈与探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

你好。为什么不使用 stringer 呢?

更多关于Golang编译时避免重复文本查找值的反馈与探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


好主意——Stringer 或许能行——谢谢——安迪

如果有人对此感兴趣的话——我没有使用 Stringer,因为它不太符合我的使用场景。相反,我选择了一条不同的(痛苦的)路线,即对结构体使用反射。所以我现在有一个结构体,它定义了(唯一的)字段,包括一些我也使用的结构体标签,例如:

type EveryDefn struct {
	TypeName struct{}                `_:"system.every"`
	Class    struct{}                `_:"misc"`
	_        text.Text               `txt:"Every " iconify:"true"`
	Secs     numberinput.NumberInput `empty:"seconds" min:"0" max:"999" width:"4" default:"1"`
	_        text.Text               `txt:"seconds"`
	Callback idinput.IdInput
}

这定义了唯一的字段名(并且允许我需要的结构体标签)。缺点是这可能在运行时出错,而不是在 go generate 时。好处是不必运行 go generate

在我的例子中,我运行这个:

	every := &EveryDefn{}
	...New(every)

这会创建一个结构体实例,然后通过反射遍历字段并访问结构体标签来初始化实例,例如,设置 Secs.Empty = “seconds” 和 Secs.Min = 0 (int)。

但正如我所说——这特定于我的使用场景

这是一个很好的编译时安全模式,在Go中确实有类似的实践。你的方法通过类型系统和常量枚举来防止键的重复定义,这是有效的。让我展示一个更完整的实现示例:

package main

import "fmt"

// Key 类型防止字符串键的意外重用
type Key int

const (
    txt Key = iota + 1
    greeting
    message
    // 编译时会阻止重复的常量定义
)

// 包级变量,确保初始化一次
var keyStrings = map[Key]string{
    txt:      "txt",
    greeting: "greeting",
    message:  "message",
}

// GetString 提供类型安全的字符串查找
func GetString(k Key) string {
    if s, ok := keyStrings[k]; ok {
        return s
    }
    return ""
}

// 编译时验证函数
func init() {
    // 验证所有常量都有对应的字符串
    constKeys := []Key{txt, greeting, message}
    for _, k := range constKeys {
        if _, ok := keyStrings[k]; !ok {
            panic(fmt.Sprintf("missing string for key %v", k))
        }
    }
}

func main() {
    // 使用示例 - 这些在编译时都是类型安全的
    fmt.Println(GetString(txt))      // 输出: txt
    fmt.Println(GetString(greeting)) // 输出: greeting
    
    // 以下代码会导致编译错误:
    // GetString("txt")          // 类型不匹配
    // GetString(999)           // 未定义的Key值
}

这种模式在需要编译时键安全性的场景中很常见。另一个变体是使用go:generate来确保同步:

//go:generate go run generate_keys.go

type Key string

const (
    TxtKey      Key = "txt"
    GreetingKey Key = "greeting"
    MessageKey  Key = "message"
)

var validKeys = map[Key]struct{}{
    TxtKey:      {},
    GreetingKey: {},
    MessageKey:  {},
}

func IsValidKey(k Key) bool {
    _, ok := validKeys[k]
    return ok
}

你的方法确实是惯用的Go代码,它利用了Go的强类型系统在编译时捕获错误。这种模式在配置管理、国际化(i18n)键、API端点定义等场景中都有应用。

回到顶部