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

时间:2015-06-09 12:10:53

标签: string go null optional variant

我希望建模一个可以有两种可能形式的值:absent或string。

执行此操作的自然方式是Maybe StringOptional<String>string option等。但是,Go没有这样的变体类型。

然后我想,在Java,C等之后,替代方案是可空性,或者在Go中nil。但是,nil不是Go中的string类型的成员。

搜索,然后我想使用类型*string。这可能有效,但看起来很尴尬(例如,我不能采用字符串文字的地址,就像我可以获取结构文字的地址一样)。

在Go中模拟这样一个值的惯用方法是什么?

3 个答案:

答案 0 :(得分:7)

您可以使用类似sql.NullString的内容,但我个人会坚持*string。至于尴尬,不幸的是,你不能只是sp := &"foo"。但是有一个workaround

func strPtr(s string) *string {
    return &s
}

应该内联对strPtr("foo")的调用,因此它有效&"foo"

另一种可能性是使用new

sp := new(string)
*sp = "foo"

答案 1 :(得分:6)

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

仅使用string

可选string表示string加1个特定值(或州),表示“不是字符串”(但是null)。

这1个特定值可以存储(发信号通知)在另一个变量(例如bool)中,您可以将stringbool打包到struct中到达包装器,但这不适合“只使用string”(但仍然是一个可行的解决方案)。

如果我们只想坚持string,我们可以从string类型的可能值中取出1个特定值(由于长度不受限制,因此具有“无穷大”可能值(或者可能它必须是int但是没关系)),我们可以将此特定值命名为null值,该值表示“不是字符串”。

指示null的最方便的值是string的零值,即空string""。指定此null元素具有以下便利:无论何时创建string变量而未明确指定初始值,它都将使用""进行初始化。此外,如果密钥不在map中,则查询值为string的{​​{1}}中的元素也会产生""

此解决方案适合许多实际使用案例。例如,如果可选的map应该是某个人的姓名,则空string并不代表有效的人名,因此您不应该首先允许这样做。

当空string确实代表string类型变量的有效值时,可能会出现这种情况。对于这些用例,我们可以选择其他值。

在Go中,string实际上是一个只读的字节切片。请参阅博文Strings, bytes, runes and characters in Go,详细解释了这一点。

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

您可以将此特殊值设为string

const

做这个简短也意味着检查const Null = "\xff" 是否等于此是非常快的 根据此约定,您已经有一个可选的string,它也允许空string

Go Playground上尝试。

string

答案 2 :(得分:0)

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

var myString interface{} // used as type <string>
myString = nil // nil is the default -- and indicates 'empty'
myString = "a value"

引用该值时,通常需要type assertion才能使检查明确。

// 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: (string) hello
b: (int) 123

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