This page is for Go beginners who need golang compare strings for equality, ordering, or case-insensitive checks. Go stores strings as read-only byte sequences; == compares those bytes exactly. For “does this string contain…”, see string contains; for general functions, see functions in Go.
Tested with Go 1.24 on Linux.
Quick answer: how to compare strings in Go
Use == for exact equality and != for inequality. Use <, >, <=, >= for lexicographic (byte-wise) ordering. Use strings.EqualFold for case-insensitive equality under Unicode simple case-folding. Use strings.Compare only when you want a single three-way int result; for most code the operators read more clearly.
Compare strings for equality
== compares every byte in both strings. Case and spacing matter: "Go" and "go" are not equal.
package main
import "fmt"
func main() {
a, b, c := "GoLinux", "GoLinux", "golinux"
fmt.Println(a == b, a == c, a != c)
}You should see true false true.
Lexicographic ordering with operators
String ordering uses byte-wise lexicographic order of the UTF-8 encoding, which matches dictionary order for ASCII letters.
package main
import "fmt"
func main() {
fmt.Println("apple" < "banana", "Z" < "a", "01" < "1")
}You should see three true values: apple is before banana, uppercase ASCII letters sort before lowercase, and "01" is less than "1" because the first byte '0' is less than '1'.
strings.Compare (three-way)
strings.Compare(a, b) returns 0 if a == b, -1 if a < b, and +1 if a > b. It is equivalent to the operator chain but easy to misuse when you only need a boolean—prefer == or < for clarity.
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Compare("a", "b")) // -1 : a < b
fmt.Println(strings.Compare("b", "a")) // +1 : b > a
fmt.Println(strings.Compare("x", "x")) // 0 : equal
}You should see -1, 1, and 0 on one line each.
Case-insensitive comparison: strings.EqualFold
strings.EqualFold reports whether two strings are equal under Unicode simple case-folding. Prefer it over strings.ToLower(a) == strings.ToLower(b) to avoid allocating two new strings and to get defined Unicode semantics.
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.EqualFold("GoLinux", "GOLINUX"))
fmt.Println(strings.EqualFold("GoLinux", "GoLiNux"))
fmt.Println("GoLinux" == "GOLINUX")
}You should see true, true, then false for the exact == line.
Length: bytes vs runes
len(s) returns the number of bytes in a string, not necessarily the number of Unicode code points (runes) people think of as “characters.” For ASCII-only text, byte length and rune count usually match; for UTF-8 beyond ASCII, they often do not.
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
ascii := "hello"
hindi := "नमस्ते"
emoji := "😊"
fmt.Println("ascii", len(ascii), "bytes,", utf8.RuneCountInString(ascii), "runes")
fmt.Println("hindi", len(hindi), "bytes,", utf8.RuneCountInString(hindi), "runes")
fmt.Println("emoji", len(emoji), "bytes,", utf8.RuneCountInString(emoji), "runes")
}You should see hello as 5 and 5, Hindi with more bytes than runes, and the smiley as 4 bytes but 1 rune.
Use len(s) when you care about storage size, buffer limits, or wire-format length. Use utf8.RuneCountInString or for _, r := range s when you care about code-point count. Visually “one character” can still span multiple code points (combining marks), so rune count is not always the same as glyph count.
| Goal | Better choice |
|---|---|
| Compare byte size | len(s) |
| Check storage or payload size | len(s) |
| Count UTF-8 code points (runes) | utf8.RuneCountInString(s) |
| Visit each code point | for _, r := range s |
| Validate user-facing length | Prefer rune rules you define; do not assume len is “letters” |
Length checks are byte-based unless you count runes explicitly (with import "unicode/utf8" in real code):
if len(s) > 10 {
// more than 10 bytes, not necessarily more than 10 runes
}
if utf8.RuneCountInString(s) > 10 {
// more than 10 UTF-8 code points
}Short rule:
Use len(s) for bytes.
Use rune counting when limits are defined in code points, not bytes.Whitespace and newlines
User input, files, and environment variables often end with spaces or \n. Compare after strings.TrimSpace (or a more specific trim) when those differences should not matter.
package main
import (
"fmt"
"strings"
)
func main() {
fileLine := "yes\n"
answer := "yes"
fmt.Println(fileLine == answer)
fmt.Println(strings.TrimSpace(fileLine) == answer)
}You should see false then true.
Mistakes to avoid
Using strings.Compare only to get ==—write a == b instead.
Forgetting that == is case-sensitive—use EqualFold when case should be ignored.
Comparing raw stdin or file lines without trimming when trailing newlines break equality.
Using len as a proxy for “number of letters” in non-ASCII text—len counts bytes.
Lowercasing both sides for case-insensitive checks—prefer EqualFold for semantics and allocations.
Go string comparison cheat sheet
| Goal | Approach |
|---|---|
| Exact equality | a == b |
| Not equal | a != b |
| Lexicographic order | <, >, <=, >= |
| Three-way int | strings.Compare |
| Case-insensitive | strings.EqualFold |
| Messy user/file input | strings.TrimSpace (or targeted trim) |
| Byte length | len(s) |
| Rune count | utf8.RuneCountInString or range |
Summary
Golang compare strings with == and != for exact equality, operators for byte-wise ordering, and strings.EqualFold for case-insensitive UTF-8 folding. Reserve strings.Compare for APIs that want -1/0/+1. Remember default comparisons are case-sensitive, raw input may need trimming, and len counts bytes. For deeper UTF-8 background, see Go blog: strings.
References
- Package strings — Compare, EqualFold
- Go blog: strings, bytes, runes and characters in Go
- Package unicode/utf8

