The strings package is Go’s everyday toolbox for working with text. Anything you’d do with a string — search inside it, split it, change its case, join a list of them — lives here. This page is a quick reference to the functions you’ll reach for most, plus the one performance trick (strings.Builder) that interviewers love to ask about.
Import it like this:
import "strings"
At a glance
| Need to… | Use |
|---|---|
| Check if text contains something | Contains, HasPrefix, HasSuffix |
| Find where something is | Index, LastIndex, Count |
| Split text into pieces | Split, SplitN, Fields |
| Combine pieces back together | Join |
| Change case | ToLower, ToUpper |
| Swap one thing for another | Replace, ReplaceAll |
| Clean up whitespace or characters | TrimSpace, Trim, TrimPrefix, TrimSuffix |
| Build a string in a loop (fast) | strings.Builder |
| Compare without caring about case | EqualFold |
The rest of this page walks through each group with examples.
Searching and testing
Contains, HasPrefix, HasSuffix
The “does this string contain X?” family. All return bool.
strings.Contains("hello world", "world") // true
strings.ContainsAny("hello", "xyz!l") // true (any one char matches)
strings.HasPrefix("main.go", "main") // true
strings.HasSuffix("main.go", ".go") // true
Index, LastIndex, Count
The “where is X?” family. Return an int (-1 if not found).
strings.Index("hello", "ll") // 2
strings.LastIndex("banana", "a") // 5
strings.Count("banana", "a") // 3
strings.Count("banana", "") // 7 (matches between every char)
EqualFold — case-insensitive equality
The Go way to do "Hello".equalsIgnoreCase("hello"):
strings.EqualFold("Hello", "hello") // true
strings.EqualFold("Go", "go") // true
Faster and more correct than ToLower(a) == ToLower(b) for Unicode.
Splitting and joining
Split and Fields
Split cuts text at a separator you choose. Fields cuts at any run of whitespace.
strings.Split("a,b,c", ",") // ["a", "b", "c"]
strings.SplitN("a,b,c,d", ",", 2) // ["a", "b,c,d"] (max 2 pieces)
strings.Fields(" hello world ") // ["hello", "world"] (whitespace-aware)
strings.Fields("a\tb\nc") // ["a", "b", "c"]
Use
Fieldswhen splitting on whitespace, notSplit(s, " ").Splitkeeps empty strings around tabs and double-spaces.Fieldsdoes what you actually want.
Join — the reverse of Split
parts := []string{"a", "b", "c"}
strings.Join(parts, ", ") // "a, b, c"
strings.Join(parts, "") // "abc"
Transforming
Case
strings.ToLower("Hello World") // "hello world"
strings.ToUpper("Hello World") // "HELLO WORLD"
Replace and ReplaceAll
strings.Replace("banana", "a", "X", 2) // "bXnXna" (replace first 2)
strings.Replace("banana", "a", "X", -1) // "bXnXnX" (-1 means all)
strings.ReplaceAll("banana", "a", "X") // "bXnXnX" (same as -1, clearer)
Trimming
strings.TrimSpace(" hello \n") // "hello"
strings.Trim("***hello***", "*") // "hello"
strings.TrimLeft("---hi", "-") // "hi"
strings.TrimRight("hi---", "-") // "hi"
strings.TrimPrefix("Mr. Smith", "Mr. ") // "Smith"
strings.TrimSuffix("file.go", ".go") // "file"
TrimPrefixis not the same asTrim.Trim("xxhello", "x")removes everyxfrom the start.TrimPrefix("xxhello", "xx")removes the exact prefix"xx"once.
Repeat
strings.Repeat("ab", 3) // "ababab"
strings.Repeat("-", 20) // "--------------------"
Building strings the fast way
This is the most important section for interviews. Go strings are immutable — every time you do s = s + "x", Go creates a brand new string and copies all the old bytes plus the new ones. In a loop, this gets quadratically slow.
The slow way
s := ""
for i := 0; i < 10000; i++ {
s += "x" // creates a new string every iteration
}
The fast way — strings.Builder
strings.Builder writes to an internal buffer that grows as needed, so only the final .String() call copies the bytes once.
var b strings.Builder
for i := 0; i < 10000; i++ {
b.WriteString("x")
}
result := b.String()
The full Builder API:
var b strings.Builder
b.WriteString("hello ") // append a string
b.WriteRune('🎉') // append a single rune
b.WriteByte('!') // append a single byte
b.Len() // current length in bytes
b.Grow(1024) // pre-allocate capacity (optional, but helpful)
b.Reset() // empty it and reuse
s := b.String() // get the final string
Interview answer to “how would you build a big string?”: “Use
strings.Builder— strings are immutable so+=in a loop is O(n²); Builder amortizes to O(n).”
Reading strings character by character
Go strings are sequences of bytes, not characters. For ASCII text this doesn’t matter. For anything else (accents, emoji, non-English text), it does.
s := "héllo"
len(s) // 6 (bytes — é takes 2 bytes in UTF-8)
utf8.RuneCountInString(s) // 5 (actual characters)
Byte iteration — fast but byte-level
for i := 0; i < len(s); i++ {
fmt.Printf("%d=%c ", i, s[i])
}
// For "héllo" this prints garbled output for the é
Rune iteration — correct for any text
for i, r := range s {
fmt.Printf("i=%d r=%c\n", i, r)
}
The i here is the byte index (so it can skip from 1 to 3 when crossing a 2-byte character), and r is the actual rune.
Interview gotcha:
s[i]returns abyte(uint8), not arune. If someone asks you to reverse a string, byte-reversing breaks UTF-8. Convert to[]runefirst:func reverse(s string) string { r := []rune(s) for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { r[i], r[j] = r[j], r[i] } return string(r) }
Common interview patterns
Check if a string is a palindrome (case-insensitive)
func isPalindrome(s string) bool {
s = strings.ToLower(s)
r := []rune(s)
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
if r[i] != r[j] {
return false
}
}
return true
}
Count words in a string
words := strings.Fields("hello world go")
count := len(words) // 3
Build a comma-separated list from numbers
nums := []int{1, 2, 3}
parts := make([]string, len(nums))
for i, n := range nums {
parts[i] = strconv.Itoa(n)
}
result := strings.Join(parts, ", ") // "1, 2, 3"
Check if a string starts with any of several prefixes
func hasAnyPrefix(s string, prefixes ...string) bool {
for _, p := range prefixes {
if strings.HasPrefix(s, p) {
return true
}
}
return false
}
Quick rules of thumb
- Building in a loop? →
strings.Builder - Splitting by whitespace? →
Fields, notSplit(s, " ") - Case-insensitive comparison? →
EqualFold, not lowercasing both sides - Removing an exact prefix/suffix? →
TrimPrefix/TrimSuffix, notTrim - Iterating over characters? →
for i, r := range s, notfor i := 0; i < len(s); i++
Common mistakes
- Using
+=to build big strings — quadratic time. UseBuilder. s[i]thinking it gives a character — it gives a byte. Use[]rune(s)orrange.Split(s, " ")to break on whitespace — leaves empty strings around tabs/double-spaces. UseFields.ToLower(a) == ToLower(b)— works for ASCII, misses some Unicode edge cases. UseEqualFold.