I wish to model a value which can have two possible forms: absent, or a string.
The natural way to do this is with Maybe String
, or Optional<String>
, or string option
, etc. However, Go does not have variant types like this.
I then thought, following Java, C, etc., that the alternative would be nullability, or nil
in Go. However, nil
is not a member of the string
type in Go.
Searching, I then thought to use the type *string
. This could work, but seems very awkward (e.g. I cannot take the address of the string literal in the same way that I can take the address of a struct literal).
What is the idiomatic way to model such a value in Go?
A logical solution would be to use
*string
as mentioned by Ainar-G. This other answer details the possibilities of obtaining a pointer to a value (int64
but the same works forstring
too). A wrapper is another solution.Using just
string
An optional
string
means astring
plus 1 specific value (or state) saying "not a string" (but anull
).This 1 specific value can be stored (signaled) in another variable (e.g
bool
) and you can pack thestring
and thebool
into astruct
and we arrived to the wrapper, but this doesn't fit into the case of "using just astring
" (but is still a viable solution).If we want to stick to just a
string
, we can take out 1 specific value from the possible values of astring
type (which has "infinity" possible values as the length is not limited (or maybe it is as it must be anint
but that's all right)), and we can name this specific value thenull
value, the value which means "not a string".The most convenient value for indicating
null
is the zero value ofstring
, which is the emptystring
:""
. Designating this thenull
element has the convenience that whenever you create astring
variable without explicitly specifying the initial value, it will be initialized with""
. Also when querying an element from amap
whose value isstring
will also yield""
if the key is not in themap
.This solution suits many real-life use-cases. If the optional
string
is supposed to be a person's name for example, an emptystring
does not really mean a valid person name, so you shouldn't allow that in the first place.There might be cases of course when the empty
string
does represent a valid value of a variable ofstring
type. For these use-cases we can choose another value.In Go, a
string
is in effect a read-only slice of bytes. See blog post Strings, bytes, runes and characters in Go which explains this in details.So a
string
is a byte slice, which is the UTF-8 encoded bytes in case of a valid text. Assuming you want to store a valid text in your optionalstring
(if you wouldn't, then you can just use a[]byte
instead which can have anil
value), you can choose astring
value which represents an invalid UTF-8 byte sequence and thus you won't even have to make a compromise to exclude a valid text from the possible values. The shortest invalid UTF-8 byte sequence is 1 byte only, for example0xff
(there are more). Note: you can use theutf8.ValidString()
function to tell if astring
value is a valid text (valid UTF-8 encoded byte sequence).You can make this exceptional value a
const
:Being this short also means it will be very fast to check if a
string
equals to this.And by this convention you already have an optional
string
which also allows the emptystring
.Try it on the Go Playground.
You could use something like
sql.NullString
, but I personally would stick to*string
. As for awkwardness, it's true that you can't justsp := &"foo"
unfortunately. But there is a workaround for this:Calls to
strPtr("foo")
should be inlined, so it's effectively&"foo"
.Another possibility is to use
new
: