How to Use `strings.CutPrefix` and `strings.CutSuffix` for Efficient String Manipulation in Go
strings.CutPrefix and strings.CutSuffix combine a prefix or suffix check with trimming into a single atomic operation, returning the remaining string and a boolean indicating whether the cut occurred.
Introduced in Go 1.20, these functions eliminate the redundancy of calling HasPrefix followed by TrimPrefix (or their suffix equivalents) when you need both the match status and the trimmed result. The JetBrains/go-modern-guidelines repository explicitly recommends these helpers to improve code clarity and avoid subtle bugs caused by mismatched check-and-trim operations.
Function Signatures and Return Values
Both functions follow a consistent pattern, returning two values that allow immediate conditional handling:
func CutPrefix(s, prefix string) (rest string, ok bool)
func CutSuffix(s, suffix string) (rest string, ok bool)
ok: Set totrueonly whensstarts withprefix(or ends withsuffix).rest: Contains the substring with the matching prefix or suffix removed. Ifokisfalse,restequals the original strings.
This design pattern enables concise error handling and parsing logic without requiring separate validation calls.
Why Prefer CutPrefix Over Legacy Patterns
The traditional approach requires two separate operations against the string:
// Legacy pattern: two scans of the string
if strings.HasPrefix(s, "pre:") {
result = strings.TrimPrefix(s, "pre:")
// handle result
}
strings.CutPrefix performs the match and trim in a single pass:
// Modern pattern: single scan with clear intent
if result, ok := strings.CutPrefix(s, "pre:"); ok {
// handle result
}
This consolidation provides three concrete benefits:
- Eliminates redundant work: The underlying implementation scans the string only once rather than twice.
- Prevents logic errors: You cannot accidentally trim a string without verifying the prefix exists, a common bug when
HasPrefixandTrimPrefixdrift apart during refactoring. - Improves readability: The code explicitly states "remove the prefix if present, and tell me whether it matched."
Guidelines from JetBrains/go-modern-guidelines
The JetBrains/go-modern-guidelines repository codifies this best practice in internal/guidelines/guidelines.json at lines 1228-1230, explicitly stating: "Use strings.CutPrefix or strings.CutSuffix when you need both the trimmed result and whether it matched."
The guideline is enforced by tests in internal/guidelines/guidelines_test.go at line 41, which flags violations of the strings_bytes_cut_last rule. This ensures that contributions to the repository follow the modern pattern rather than legacy check-then-trim sequences.
Real-World Usage in Version Parsing
The repository demonstrates practical application in internal/goversion/goversion.go (lines 197-201), where the version parser extracts major and minor components. While the file utilizes strings.Cut (another Go 1.20 helper) for initial splitting, the logic structure demonstrates the same philosophy applied throughout: perform one operation to both test and extract content, rather than testing first and extracting second.
This pattern appears when processing semantic version strings where determining the presence of a prefix (like a build marker) and obtaining the remainder must happen atomically to avoid race conditions or double-processing in tight loops.
Practical Implementation Examples
Conditional Header Parsing
Extract error messages from tagged log lines without redundant checks:
lines := []string{
"error: failed to start",
"info: all good",
"error: out of memory",
}
for _, line := range lines {
if msg, ok := strings.CutPrefix(line, "error: "); ok {
fmt.Printf("Critical: %s\n", strings.TrimSpace(msg))
}
}
Safe File Extension Handling
Remove known extensions while confirming the file type:
filename := "report.pdf"
if name, ok := strings.CutSuffix(filename, ".pdf"); ok {
fmt.Printf("Processing PDF: %s\n", name)
} else {
fmt.Println("Unsupported file format")
}
Protocol Stripping
Clean URLs by removing schemes only when present:
url := "https://example.com"
if host, ok := strings.CutPrefix(url, "https://"); ok {
// host now contains "example.com", guaranteed to have had the prefix
handleSecureHost(host)
}
Summary
strings.CutPrefixandstrings.CutSuffixwere added in Go 1.20 to combine matching and trimming into a single operation.- Both functions return
(rest string, ok bool), allowing immediate conditional logic without separateHasPrefix/HasSuffixcalls. - Using these functions eliminates redundant string scans and prevents bugs where validation and trimming logic become decoupled.
- The JetBrains/go-modern-guidelines repository enforces this pattern in
internal/guidelines/guidelines.jsonand validates it throughinternal/guidelines/guidelines_test.go. - Real-world usage in
internal/goversion/goversion.godemonstrates the performance and clarity benefits in production parsing code.
Frequently Asked Questions
When should I use strings.CutPrefix instead of strings.TrimPrefix?
Use strings.CutPrefix when your logic requires knowing whether the prefix existed before processing the remainder. If you only need to remove a prefix unconditionally (and do not care if it was present), strings.TrimPrefix remains appropriate. The JetBrains guidelines specifically recommend CutPrefix when you need both the trimmed result and the match status.
What Go version introduced CutPrefix and CutSuffix?
These functions were introduced in Go 1.20. Codebases targeting earlier versions must continue using the strings.HasPrefix and strings.TrimPrefix combination or implement custom single-pass logic.
Are these functions more performant than separate HasPrefix and TrimPrefix calls?
Yes. According to the implementation details referenced in the JetBrains guidelines, CutPrefix and CutSuffix perform the match only once, whereas the legacy pattern requires scanning the string twice—once for the check and once for the trim. While the performance gain may be negligible for small strings, it becomes measurable when processing high-volume text or large datasets in hot paths.
How does the JetBrains repository enforce these string manipulation guidelines?
The repository enforces the pattern through a combination of documentation in internal/guidelines/guidelines.json (lines 1228-1230) and automated testing in internal/guidelines/guidelines_test.go (line 41). The test suite includes the strings_bytes_cut_last rule, which flags code that uses separate HasPrefix/TrimPrefix calls instead of the modern Cut alternatives.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →