strings.FieldsFunc treats selected Unicode code points as boundaries and returns the non-empty text between them. That behavior fits inputs where separators belong to a class rather than one fixed substring: commas and semicolons, several punctuation marks, or any rune accepted by a deterministic predicate.
fields := strings.FieldsFunc(input, func(r rune) bool {
return r == ',' || r == ';'
})For alpha,,beta;gamma;, the result is []string{"alpha", "beta", "gamma"}. Consecutive matching runes form a boundary region, and matching runes at either edge do not produce empty elements.
The predicate defines boundaries, not accepted fields
The callback returns true for a rune that separates fields. This polarity matters when the rule is expressed in terms of valid token content.
Suppose identifiers may contain Unicode letters and digits while every other rune acts as a separator:
package main
import (
"fmt"
"strings"
"unicode"
)
func main() {
input := "alpha-42 / beta_7;gamma"
fields := strings.FieldsFunc(input, func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsDigit(r)
})
fmt.Printf("%q\n", fields)
}The predicate marks spaces, punctuation, the slash, underscore, and semicolon as boundaries. Letter and digit runs remain as fields. Since the callback receives runes rather than bytes, Unicode classification functions can be used directly.
This is distinct from strings.Split, whose separator is a literal string. Split preserves empty elements created by adjacent separators, while FieldsFunc discards empty regions around runs of matching boundary runes.
Repeated separators collapse into one boundary region
Empty-field suppression is part of the function’s semantics, not a cleanup pass performed after splitting. Consider a loosely formatted list:
input := ",,red;;;green,blue,,"
parts := strings.FieldsFunc(input, func(r rune) bool {
return r == ',' || r == ';'
})
fmt.Printf("%q\n", parts)The result contains red, green, and blue, with no empty strings for the leading, trailing, or repeated punctuation.
That makes the function suitable when repeated separators carry no additional meaning. It is a poor fit for formats where an empty position is data. A record such as red,,blue may represent a missing middle column; collapsing the two commas would erase that position. Literal delimiter functions are a better match for such formats.
Predicate behavior must be stable
strings.FieldsFunc does not promise a particular order for calls to the predicate. The callback is expected to return the same result whenever it receives the same rune.
A predicate based only on its argument has that property:
func boundary(r rune) bool {
return r == ',' || r == ';' || unicode.IsSpace(r)
}A callback that changes behavior from mutable counters, random values, or call order does not satisfy the contract. State used only for immutable configuration is different. A fixed separator set can be captured safely because the result for each rune remains stable:
separators := map[rune]struct{}{
',': {},
';': {},
'|': {},
}
parts := strings.FieldsFunc(input, func(r rune) bool {
_, ok := separators[r]
return ok
})The map is read-only during the operation, so each rune has a consistent classification.
Rune boundaries differ from substring boundaries
The predicate sees one Unicode code point at a time. It cannot directly recognize a multi-rune delimiter such as ::, --, or <-> as an indivisible token. Marking : as a boundary would split on every colon, not only on pairs of colons.
That distinction keeps FieldsFunc focused on character-class tokenization. For exact multi-character separators, strings.Split, strings.Cut, or a parser with explicit delimiter state expresses the format more accurately.
The same boundary also applies to context-sensitive syntax. A comma inside quoted text cannot be classified correctly from the comma rune alone because its meaning depends on parser state. CSV, shell syntax, and similar formats need parsers that model their quoting and escaping rules.
Allocation and iterator variants
strings.FieldsFunc returns a slice, so the call constructs the result slice before the caller receives it. The field strings refer to substrings of the input string; callers should still consider the lifetime of a large source string when retaining a small number of fields for a long period.
Go also provides strings.FieldsFuncSeq in versions that include the iterator API. It yields the same field sequence without constructing the result slice. That variant is useful when fields can be consumed incrementally or processing may stop before every field is needed.
The semantic choice comes before the allocation choice. Both forms collapse runs of matching boundary runes and omit empty fields. Code that needs positional empties or exact substring delimiters needs different parsing semantics rather than only a different return shape.
strings.FieldsFunc is most precise when a token boundary can be decided from a single rune and repeated boundaries are equivalent to one boundary. Within that scope, the predicate makes the format rule explicit while the function handles Unicode iteration and empty-field suppression consistently.