GoLang|何时使用字符串指针?

3,455 阅读2分钟

Go中的字符串是一个值。因此,一个字符串不能被nil

x := "I am a string!"

x = nil // Won't compile, strings can't be nil in Go

然而,一个指向字符串的指针(或*string )可以是nil

var x *string

x = nil // Compiles! String pointers in GoLang can be nil

一个好的经验法则是使用普通字符串,除非你需要 nil 。正常的字符串在Go中使用起来更容易、更安全。指针需要你写更多的代码,因为你需要在取消引用之前检查*string 是否有一个值。

func UseString(s *string) error {
    if s == nil {
        temp := "" // *string cannot be initialized
        s = &temp // in one statement
    }
    value := *s // safe to dereference the *string
}

一个空的字符串值""nil 是不一样的。在编程时,如果你想不出需要nil 的原因,那么你可能不需要或不想要它。

那么,什么时候我应该使用字符串的指针呢?

有的时候你应该使用*string 。例如,当把json或yaml(或任何东西)反序列化为一个结构时,你可能应该对结构属性使用*string

考虑一下这样的代码:一个json文档被反序列化成一个由普通字符串属性组成的结构。

package main

import (
    "encoding/json"
    "fmt"
)

type Config struct {
    Environment string
    Version     string
    HostName    string

}

func (c *Config) String() string {
    return fmt.Sprintf("Environment: '%v'\nVersion:'%v'\nHostName: '%v'", 
    c.Environment, c.Version, c.HostName)
}

func main() {

    jsonDoc :=`
        {
            "Environment" : "Dev",
            "Version" : ""
        }`

    conf := &Config{}
    json.Unmarshal([]byte(jsonDoc), conf)
    fmt.Println(conf) // Prints 
                      //   Environment: 'Dev'
                      //   Version:''
                      //   HostName: ''

}

你会注意到,VersionHostName 都被存储为空字符串。对于Version ,这是正确的行为,但是HostName 真的应该是一个空字符串吗?

这个问题的答案取决于你的程序。如果一个缺失的属性被解读为一个空字符串是可以接受的,那么就没有问题了。换句话说,如果你将处理缺失的json属性和空的json属性是一样的,那么就使用一个正常的字符串。

但是,如果"" 是一个有效的配置值HostName ,但是缺失的属性是无效的呢?

答案是:使用一个*string

package main

import (
    "encoding/json"
    "fmt"
)

type ConfigWithPointers struct {
    Environment *string // pointer to string
    Version     *string
    HostName    *string
}

func (c *ConfigWithPointers) String() string {
    var envOut, verOut, hostOut string
    envOut = "<nil>"
    verOut = "<nil>"
    hostOut = "<nil>"

    if c.Environment != nil { // Check for nil!
        envOut = *c.Environment
    }

    if c.Version != nil {
        verOut = *c.Version
    }

    if c.HostName != nil {
        hostOut = *c.HostName
    }

    return fmt.Sprintf("Environment: '%v'\nVersion:'%v'\nHostName: '%v'",
        envOut, verOut, hostOut)
}

func main() {

    jsonDoc :=
        `
        {
            "environment" : "asdf",
            "hostName" : ""
        }
        `

    conf := &ConfigWithPointers{}
    json.Unmarshal([]byte(jsonDoc), conf)
    fmt.Println(conf) // Prints the following:
                      // Environment: 'asdf'
                      // Version:'<nil>'
                      // HostName: ''

}

请注意,使用*string ,你现在可以区分缺失的属性或null 值与json文档中的空字符串值。

tl;dr; 如果你需要nil ,才使用字符串指针*string 。否则,使用一个普通的字符串