GoLang: When to use string pointers
A string in Go is a value. Thus, a string cannot be nil
.
x := "I am a string!"
x = nil // Won't compile, strings can't be nil in Go
However, a pointer to a string (or *string
) can be nil
.
var x *string
x = nil // Compiles! String pointers in GoLang can be nil
A good rule of thumb is to use normal strings unless you need nil
. Normal strings are easier and safer to use in Go. Pointers require you to write more code because you need to check that a *string
has a value before dereferencing.
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
}
An empty string value ""
and nil
are not the same thing. When programming, if you can’t think of reason you would need nil
, then you probably don’t need it or want it.
So when should I use a pointer to a string?
There may be times when you should use *string
. For instance, you should probably use a *string
for struct properties when deserializing json or yaml (or anything) into a structure.
Consider code where a json document is deserialized into a struct consisting of normal string properties.
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: ''
}
You’ll notice that both Version
and HostName
are stored as empty strings. This is correct behavior for Version
, but should HostName
really be an empty string?
The answer to that question depends on your program. If it is acceptable for a missing property to be unmarshalled into an empty string, then there is no problem. In other words, if you will handle missing json properties and empty json properties the same, then use a normal string.
But what if ""
is a valid configuration value for HostName
, but a missing property is not valid?
Answer: use a *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: ''
}
Notice that with *string
, you can now differentiate between a missing property or null
value from an empty string value in the json document.
tl;dr; Only use a string pointer *string
if you need nil
. Otherwise, use a normal string
If you benefited from this post, please consider subscribing to my newsletter!