Question
Why does Go not provide standard startswith and endswith functions? Are there built-in-library functions for checking whether a string begins or ends with a specified value?
Short Answer
Go does provide these operations in its standard library, but they are named strings.HasPrefix and strings.HasSuffix rather than startswith and endswith. You will learn how to use them, why they live in packages, and when to use the equivalent functions for byte slices.
Concept
Go keeps many operations outside of built-in types and organizes them into focused packages. String prefix and suffix checks are available in the standard strings package:
strings.HasPrefix(s, prefix)
strings.HasSuffix(s, suffix)
Both functions return a bool:
truewhen the requested text matchesfalsewhen it does not
For example, strings.HasPrefix("report.csv", "report") is true, and strings.HasSuffix("report.csv", ".csv") is also true.
The names use Go's usual exported-identifier style: HasPrefix and HasSuffix use capital letters because they are public functions. Go favors package functions for many operations rather than attaching a large collection of methods to every type. This keeps the core string type small while making related functionality easy to find in the strings package.
For []byte values, use the equivalent functions in the bytes package: bytes.HasPrefix and bytes.HasSuffix.
Mental Model
Think of a string as a label on a package.
- A prefix is the beginning of the label.
- A suffix is the ending of the label.
strings.HasPrefix asks: “Does this label begin with these characters?”
strings.HasSuffix asks: “Does this label end with these characters?”
For a file named photo.png, .png is a suffix. For a URL path such as /api/users, /api/ is a prefix.
Syntax and Examples
Import the strings package, then pass the full string first and the text to look for second.
package main
import (
"fmt"
"strings"
)
func main() {
filename := "invoice.pdf"
isPDF := strings.HasSuffix(filename, ".pdf")
startsWithInvoice := strings.HasPrefix(filename, "invoice")
fmt.Println(isPDF) // true
fmt.Println(startsWithInvoice) // true
}
The function signatures are:
func HasPrefix(s, prefix string) bool
func HasSuffix(s, suffix string) bool
The comparison is exact and case-sensitive:
fmt.Println(strings.HasPrefix("Hello", "He")) // true
fmt.Println(strings.HasPrefix("Hello", "he")) // false
If case-insensitive matching is appropriate, normalize both strings first:
name := "PHOTO.PNG"
isPNG := strings.HasSuffix(strings.ToLower(name), ".png")
fmt.Println(isPNG) // true
Step by Step Execution
Consider this example:
package main
import (
"fmt"
"strings"
)
func main() {
path := "/api/orders"
isAPIPath := strings.HasPrefix(path, "/api/")
isJSONFile := strings.HasSuffix(path, ".json")
fmt.Println(isAPIPath)
fmt.Println(isJSONFile)
}
Step by step:
pathstores"/api/orders".strings.HasPrefix(path, "/api/")compares the beginning ofpathwith"/api/".- The beginning matches, so
isAPIPathbecomestrue. strings.HasSuffix(path, ".json")compares the end ofpathwith".json".- The path ends in
"orders", not".json", soisJSONFilebecomesfalse. - The program prints:
true
false
Real World Use Cases
Prefix and suffix checks appear in many practical tasks:
- HTTP routing: Check whether a request path begins with
/api/or/assets/. - File handling: Accept files ending in
.json,.csv, or.go. - Configuration: Read environment variables with a prefix such as
APP_. - Logging: Identify lines beginning with
ERROR:orWARN:. - Protocol parsing: Detect a URL beginning with
https://. - Data imports: Ignore temporary files ending with
~or.tmp.
Example: process only JSON files.
if strings.HasSuffix(filename, ".json") {
// Read and decode the JSON file.
}
Real Codebase Usage
In production code, prefix and suffix checks are usually combined with validation and early returns.
Validate input early
func loadConfig(filename string) error {
if !strings.HasSuffix(filename, ".json") {
return fmt.Errorf("config file must have a .json extension")
}
// Continue only after validation succeeds.
return nil
}
An early return prevents deeply nested if statements and makes invalid input handling clear.
Filter a collection
var jsonFiles []string
for _, filename := range filenames {
if strings.HasSuffix(filename, ".json") {
jsonFiles = append(jsonFiles, filename)
}
}
Use the package that matches the data type
Network and file APIs often provide data as []byte. Avoid converting to string solely for a prefix check when you can use bytes.HasPrefix:
if bytes.HasPrefix(data, []byte("GET ")) {
// Handle an HTTP GET request line.
}
Treat extensions carefully
A suffix check can be useful for preliminary filtering, but it does not prove file contents are safe or valid. For example, a file named image.png may not actually contain PNG data. Validate or parse the content when security or correctness matters.
Common Mistakes
Looking for methods on string
Go does not use a method call such as name.StartsWith(...) for this operation.
// Does not compile.
name := "report.csv"
if name.StartsWith("report") {
}
Use the package function instead:
if strings.HasPrefix(name, "report") {
}
Forgetting the import
import "strings"
Without this import, strings.HasPrefix is undefined.
Reversing the arguments
The full text is first; the prefix or suffix to test is second.
// Incorrect meaning: asks whether ".go" ends with "main.go".
strings.HasSuffix(".go", "main.go")
// Correct.
strings.HasSuffix("main.go", ".go")
Assuming comparisons ignore case
They are case-sensitive.
strings.HasSuffix("PHOTO.JPG", ".jpg") // false
Normalize deliberately when your domain requires case-insensitivity:
strings.HasSuffix(strings.ToLower("PHOTO.JPG"), ".jpg") // true
Using a suffix check as complete file validation
Checking a filename extension is not a security check and does not validate file contents. Parse the data or inspect its format when that guarantee is needed.
Forgetting empty-string behavior
Every string has the empty string as both a prefix and a suffix:
strings.HasPrefix("hello", "") // true
strings.HasSuffix("hello", "") // true
Validate an expected prefix or suffix first if an empty value would be invalid in your program.
Comparisons
| Need | Recommended Go function | Notes |
|---|---|---|
Check a string beginning | strings.HasPrefix(s, prefix) | Exact, case-sensitive comparison |
Check a string ending | strings.HasSuffix(s, suffix) | Useful for extensions and markers |
Check a []byte beginning | bytes.HasPrefix(b, prefix) | Avoids unnecessary string conversion |
Check a []byte ending | bytes.HasSuffix(b, suffix) | Equivalent byte-slice operation |
| Find text anywhere | strings.Contains(s, sub) | Does not require a start or end match |
| Remove a known start once | strings.TrimPrefix(s, prefix) | Returns the original string if no match |
| Remove a known end once | strings.TrimSuffix(s, suffix) | Returns the original string if no match |
HasPrefix and Contains answer different questions:
text := "user-admin-settings"
fmt.Println(strings.HasPrefix(text, "admin")) // false
fmt.Println(strings.Contains(text, "admin")) // true
Use HasPrefix when position matters. Use Contains when the text may appear anywhere.
Cheat Sheet
import "strings"
strings.HasPrefix(s, prefix) // bool
strings.HasSuffix(s, suffix) // bool
strings.HasPrefix("golang", "go") // true
strings.HasSuffix("main.go", ".go") // true
Rules:
- First argument: complete string to inspect.
- Second argument: required prefix or suffix.
- Matching is case-sensitive.
- An empty prefix or suffix matches every string.
- Use
bytes.HasPrefixandbytes.HasSuffixfor[]byte. - Use
strings.TrimPrefixorstrings.TrimSuffixwhen you need to remove a verified portion.
clean := strings.TrimPrefix("Bearer token123", "Bearer ")
// "token123"
FAQ
Does Go have startswith and endswith functions?
Yes, under different names: strings.HasPrefix and strings.HasSuffix.
Why are they not methods on Go strings?
Go commonly places operations in packages rather than adding many methods to basic types. String-related helpers are grouped in the strings package.
Are strings.HasPrefix and strings.HasSuffix case-sensitive?
Yes. Convert both values with strings.ToLower or strings.ToUpper if your use case requires case-insensitive matching.
What is the equivalent for byte slices in Go?
Use bytes.HasPrefix and bytes.HasSuffix for []byte values.
Does an empty prefix match a string in Go?
Yes. strings.HasPrefix(s, "") is true for every string, including an empty string.
Can I use HasSuffix to verify that a file is safe?
No. It only checks the filename text. Validate the file contents and apply appropriate security controls when needed.
How do I remove a prefix after checking it?
Use strings.TrimPrefix. It removes one matching prefix and otherwise returns the original string unchanged.
Mini Project
Description
Build a small filename classifier for an upload or import tool. The program examines filenames and decides whether they are accepted data files, temporary files to ignore, or unsupported files. It demonstrates practical prefix and suffix checks with the strings package.
Goal
Classify a list of filenames using strings.HasPrefix and strings.HasSuffix.
Requirements
Use a list containing CSV, JSON, temporary, hidden, and unsupported filenames.
Ignore names beginning with ..
Ignore names ending with .tmp.
Accept names ending with .csv or .json.
Print a clear classification for every filename.
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.