Inspired by #40135, I have another proposal for an addition to the strings package (and by extension, I suppose, the bytes package).
The strings functions I most frequently wish for are variants of TrimPrefix and TrimSuffix which report whether they did any trimming.
// TrimmedPrefix returns s without the provided leading prefix string
// and reports whether it found the prefix.
// If s doesn't start with prefix, TrimmedPrefix returns s, false.
// If prefix is the empty string, TrimmedPrefix returns s, true.
func TrimmedPrefix(s, prefix string) (trimmed string, ok bool)
Lacking these functions, Go authors resort to fragile code, often writing a prefix/suffix literal twice or hard-coding its length:
if strings.HasPrefix(s, "myprefix") {
s = strings.TrimPrefix(s, "myprefix")
...
}
if strings.HasPrefix(s, "myprefix") {
s = s[len("myprefix"):]
...
}
if strings.HasPrefix(s, "myprefix") {
s = s[8:]
...
}
if t := strings.TrimPrefix(s, "myprefix"); s != t {
// had prefix
s = t
...
}
Of course, a function like TrimmedPrefix is easy to write and can exist outside of the standard library. At my company we have these functions in an internal string helper package and they see regular use. But certainly I wouldn't pull in a dependency for such a tiny helper function and so when I'm working on my own projects I generally just use the above workarounds.
Here are some examples from the Go source tree along with how they could be altered to use TrimmedPrefix/TrimmedSuffix:
-
src/cmd/go/internal/modload/load.go:
if strings.HasPrefix(suffix, "/vendor/") {
return strings.TrimPrefix(suffix, "/vendor/")
}
becomes
if v, ok := strings.TrimmedPrefix(suffix, "/vendor/"); ok {
return v
}
-
src/cmd/go/proxy_test.go:
if !strings.HasSuffix(name, ".txt") {
continue
}
name = strings.TrimSuffix(name, ".txt")
becomes
name, ok := strings.TrimmedSuffix(name, ".txt")
if !ok {
continue
}
-
src/testing/benchmark.go:
if strings.HasSuffix(s, "x") {
n, err := strconv.ParseInt(s[:len(s)-1], 10, 0)
...
}
becomes
if s, ok := strings.TrimmedSuffix(s, "x"); ok {
n, err := strconv.ParseInt(s, 10, 0)
...
}
-
src/testing/fstest/mapfs.go:
if strings.HasPrefix(fname, prefix) {
felem := fname[len(prefix):]
...
}
becomes
if felem, ok := strings.TrimmedPrefix(fname, prefix); ok {
...
}
-
src/mime/mediatype.go:
if !strings.HasPrefix(rest, ";") {
return "", "", v
}
rest = rest[1:] // consume semicolon
becomes
rest, ok := strings.TrimmedPrefix(rest, ";")
if !ok {
return "", "", v
}
-
test/run.go:
if strings.HasPrefix(line, "//") {
line = line[2:]
} else {
continue
}
becomes
line, ok := strings.TrimmedPrefix(line, "//")
if !ok {
continue
}
-
test/run.go:
if strings.HasPrefix(m, "LINE+") {
delta, _ := strconv.Atoi(m[5:])
n += delta
...
}
becomes
if d, ok := strings.TrimmedPrefix(m, "LINE+"); ok {
delta, _ := strconv.Atoi(d)
n += delta
...
}
-
src/runtime/testdata/testprog/traceback_ancestors.go:
if strings.HasPrefix(tb[pos:], "goroutine ") {
id := tb[pos+len("goroutine "):]
...
}
becomes
if id, ok := strings.TrimmedPrefix(tb[pos:], "goroutine "); ok {
...
}
Update 2022-03-26: Changed proposed names to TrimmedPrefix/TrimmedSuffix (per @ianlancetaylor's suggestion).
Inspired by #40135, I have another proposal for an addition to the strings package (and by extension, I suppose, the bytes package).
The strings functions I most frequently wish for are variants of
TrimPrefixandTrimSuffixwhich report whether they did any trimming.Lacking these functions, Go authors resort to fragile code, often writing a prefix/suffix literal twice or hard-coding its length:
Of course, a function like
TrimmedPrefixis easy to write and can exist outside of the standard library. At my company we have these functions in an internal string helper package and they see regular use. But certainly I wouldn't pull in a dependency for such a tiny helper function and so when I'm working on my own projects I generally just use the above workarounds.Here are some examples from the Go source tree along with how they could be altered to use
TrimmedPrefix/TrimmedSuffix:src/cmd/go/internal/modload/load.go:becomes
src/cmd/go/proxy_test.go:becomes
src/testing/benchmark.go:becomes
src/testing/fstest/mapfs.go:becomes
src/mime/mediatype.go:becomes
test/run.go:becomes
test/run.go:becomes
src/runtime/testdata/testprog/traceback_ancestors.go:becomes
Update 2022-03-26: Changed proposed names to
TrimmedPrefix/TrimmedSuffix(per @ianlancetaylor's suggestion).