为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 Go 中表示可选字符串?

如何在 Go 中表示可选字符串?

Go
RISEBY 2021-10-11 18:50:36
我希望对一个值进行建模,该值可以有两种可能的形式:不存在或字符串。这样做的自然方法是使用Maybe String、 或Optional<String>、 或string option等。但是,Go 没有这样的变体类型。然后我想,继 Java、C 等之后,替代方案是可空性,或者nil在 Go 中。但是,nil不是stringGo 中类型的成员。搜索,然后我想到使用 type *string。这可以工作,但看起来很尴尬(例如,我不能以与获取结构文字地址相同的方式获取字符串文字的地址)。在 Go 中对这样的值进行建模的惯用方法是什么?
查看完整描述

3 回答

?
倚天杖

TA贡献1828条经验 获得超3个赞

一个合乎逻辑的解决方案是使用*stringAinar-G 提到的。另一个答案详细说明了获取指向值的指针的可能性(int64但同样适用string)。包装器是另一种解决方案。


仅使用 string

可选string表示string加 1 的特定值(或状态)表示“不是字符串”(而是 a null)。


这1个特定值可以存储(信号)在另一个变量(例如bool),你可以包装string和bool成struct和我们到达的包装,但这并不适合进入的情况下,“只用string”(但仍然是一个可行的解决方案)。


如果我们只想坚持 a string,我们可以从string类型的可能值中取出 1 个特定值(它具有“无穷大”可能值,因为长度不受限制(或者可能是因为它必须是 anint但仅此而已)对)),我们可以将这个特定的值命名为null值,该值的意思是“不是字符串”。


最方便的指示值null是 的零值string,即空string: ""。指定这个null元素很方便,每当你创建一个string没有明确指定初始值的变量时,它都会被初始化""。还从一个查询的元素时map,其值是string也将产生""如果键不在map。


该解决方案适合许多现实生活中的用例。string例如,如果可选项应该是一个人的名字,那么空string并不意味着一个有效的人名,所以你首先不应该允许它。


当然,可能存在空string值确实表示string类型变量的有效值的情况。对于这些用例,我们可以选择另一个值。


在 Go 中, astring实际上是一个只读的字节片。请参阅博客文章Go 中的字符串、字节、符文和字符,其中详细解释了这一点。


所以 astring是一个字节切片,它是有效文本情况下的 UTF-8 编码字节。假设你想在你的可选中存储一个有效的文本string(如果你不想,那么你可以使用一个[]byte可以有一个nil值的代替),你可以选择一个string代表无效 UTF-8 字节序列的值,因此你赢了甚至不必妥协以从可能的值中排除有效文本。例如,最短的无效 UTF-8 字节序列仅为 1 个字节0xff(还有更多)。注意:您可以使用该utf8.ValidString()函数来判断一个string值是否为有效文本(有效的 UTF-8 编码字节序列)。


您可以将此特殊值设为 a const:


const Null = "\xff"

这么短也意味着检查 a 是否string等于 this会非常快。

按照这个约定,你已经有了一个可选的string,它也允许空的string.


在Go Playground上试一试。


const Null = "\xff"


func main() {

    fmt.Println(utf8.ValidString(Null)) // false


    s := Null

    fmt.Println([]byte(s)) // [255]

    fmt.Println(s == Null) // true

    s = "notnull"

    fmt.Println(s == Null) // false

}


查看完整回答
反对 回复 2021-10-11
?
千万里不及你

TA贡献1784条经验 获得超9个赞

你可以使用类似的东西sql.NullString,但我个人会坚持使用*string. 至于尴尬,确实不能只是sp := &"foo"不幸。但是有一个解决方法:


func strPtr(s string) *string {

    return &s

}

调用strPtr("foo")应该被内联,所以它是有效的&"foo"。


另一种可能性是使用new:


sp := new(string)

*sp = "foo"


查看完整回答
反对 回复 2021-10-11
?
心有法竹

TA贡献1866条经验 获得超5个赞

对于接口类型,您可以使用更自然的赋值语法。


var myString interface{} // used as type <string>

myString = nil // nil is the default -- and indicates 'empty'

myString = "a value"

在引用值时,通常需要类型断言来明确检查。


// checked type assertion

if s, exists := myString.(string); exists {

    useString(s)

}

此外,由于stringers,在某些上下文中将自动处理“可选”类型——这意味着您不需要显式转换该值。该fmt软件包使用此功能:


fmt.Println("myString:",myString) // prints the value (or "<nil>")

警告

分配给值时没有类型检查。


在某些方面,这是一种比处理指针更简洁的方法。但是,因为这使用了接口类型,所以它不限于持有特定的底层类型。风险在于您可能会无意中分配不同的类型——这将nil与上述条件中的处理方式相同。


下面是使用接口进行赋值的演示:


var a interface{} = "hello"

var b = a // b is an interface too

b = 123 // assign a different type


fmt.Printf("a: (%T) %v\n", a, a)

fmt.Printf("b: (%T) %v\n", b, b)

输出:


a:(字符串)你好

b: (int) 123

请注意,接口是通过重复分配的,因此a和b是不同的。


查看完整回答
反对 回复 2021-10-11
  • 3 回答
  • 0 关注
  • 194 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信