# How to Use `strings.CutPrefix` and `strings.CutSuffix` for Efficient String Manipulation in Go

> Learn to efficiently manipulate strings in Go using strings.CutPrefix and strings.CutSuffix. Discover how these functions perform checks and trim simultaneously.

- Repository: [JetBrains/go-modern-guidelines](https://github.com/jetbrains/go-modern-guidelines)
- Tags: how-to-guide
- Published: 2026-08-30

---

**`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:

```go
func CutPrefix(s, prefix string) (rest string, ok bool)
func CutSuffix(s, suffix string) (rest string, ok bool)

```

- **`ok`**: Set to `true` only when `s` starts with `prefix` (or ends with `suffix`).
- **`rest`**: Contains the substring with the matching prefix or suffix removed. If `ok` is `false`, `rest` equals the original string `s`.

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:

```go
// 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:

```go
// 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 `HasPrefix` and `TrimPrefix` drift 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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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:

```go
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:

```go
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:

```go
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.CutPrefix` and `strings.CutSuffix`** were 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 separate `HasPrefix`/`HasSuffix` calls.
- 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.json`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) and validates it through [`internal/guidelines/guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines_test.go).
- Real-world usage in [`internal/goversion/goversion.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/goversion/goversion.go) demonstrates 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`](https://github.com/JetBrains/go-modern-guidelines/blob/main/internal/guidelines/guidelines.json) (lines 1228-1230) and **automated testing** in [`internal/guidelines/guidelines_test.go`](https://github.com/JetBrains/go-modern-guidelines/blob/main/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.