String Types
A string type represents the set of string values. A string value is a (possibly empty) sequence of bytes. The number of bytes is called the length of the string and is never negative. Strings are immutable: once created, it is impossible to change the contents of a string. The predeclared string type is
string
; it is a defined type.
Anyone who’s ever written even just “Hello, World” in virtually any language has had experience with strings. So there’s not a lot to say about the concept here.
What makes strings in Go stand out, though, is that they are immutable.
Although the langauge does a pretty good job of hiding that detail from us, the results can be a bit surprising in a few cases.
In short, once a string is created, it cannot be changed. That sounds simple enough. But we see code like this all the time:
str := "Hello, World" // Create a string
// Then later...
str = "Howdy, Pardner" // Apparently "changing" the string... what gives?
This conflict, which may appear to conflict with the spec, doesn’t actually. But what’s happening is subtle.
The above code actually creates two distinct strings, but assigns them, one at a time, to a variable of type string
, called str
.
Eh, what?
Line one creates a variable, str
, and also creates a string in memory with the bytes that make up “Hello, World”, and sets the str
variable to point to that memory location.
Then on line three, we create a new, immutable string in memory, this time with the bytes that make up “Howdy, Pardner”, then we update the str
variable to point to that new memory location. The first string is then eligible for garbage collection (assuming it’s not referenced elsewhere).
Where this often bites people is when doing string concatentation. Especially if you come from a language like Perl or JavaScript, where code like this is fairly common:
func generateBody(name string ) string {
var body string
body += "<html>"
body += "<body>"
body += fmt.Sprintf("<h1>Hello, %s!</h1>", name)
body += "</body>"
body += "<html>"
return body
}
This seemlingly innocent code actually creates 6 distinct strings in memory, each a bit longer than the previous, then discards the first 5 via garbage collection, and returns only the 6th.
The strings.Builder function helps us work around the quirk of the language, by building strings gradually in a container type that doesn’t do any new memory allocations until the final string is requested.
func generateBody(name string ) string {
var b strings.Builder
b.WriteString("<html>")
b.WriteString("<body>")
fmt.Fprintf(b, "<h1>Hello, %s!</h1>", name)
b.WriteString("</body>")
b.WriteString("</html>")
return b.String()
}
Quotes from The Go Programming Language Specification, Version of January 19, 2023