strings.Map wendet eine benutzerdefinierte Mapping-Funktion func(rune) rune auf jede einzelne Rune eines Eingabestrings an und gibt das Ergebnis als neuen String zurück. Die Besonderheit: gibt die Mapping-Funktion einen negativen Wert zurück, wird die entsprechende Rune aus dem Ergebnis-String entfernt. Damit ist strings.Map gleichzeitig ein Transformations- und ein Filter-Werkzeug.
Die Funktion arbeitet rune-orientiert, nicht byte-orientiert — Multi-Byte-Sequenzen werden korrekt erkannt und an die Mapping-Funktion als komplette rune (also int32-Code-Point) übergeben. Das macht strings.Map zur idiomatischen Wahl, wenn Operationen pro Code-Point benötigt werden: Whitespace-Entfernung, ASCII-Filter, Case-Manipulation auf Unicode-Ebene oder ROT13-artige Transformationen.
Die Signatur ist kompakt und nimmt eine Mapping-Funktion als erstes Argument entgegen, gefolgt vom Eingabestring.
func Map(mapping func(rune) rune, s string) stringDie Reihenfolge ist bewusst so gewählt, dass Closures direkt am Aufrufort inline definiert werden können und die Mapping-Logik am Anfang sichtbar steht. Der Eingabestring s kommt zuletzt — das passt zum typischen Pipeline-Stil, wo Daten am Ende übergeben werden.
Das wichtigste Detail der Semantik: gibt mapping einen negativen rune-Wert zurück, wird die ursprüngliche Rune aus dem Ergebnis entfernt. Idiomatisch wird dafür -1 verwendet, aber jeder negative Wert funktioniert. Damit lassen sich Filter und Transformation in einer einzigen Funktion kombinieren.
package main
import (
"fmt"
"strings"
"unicode"
)
func main() {
// Alle Whitespaces entfernen, sonst unverändert lassen
stripWS := func(r rune) rune {
if unicode.IsSpace(r) {
return -1
}
return r
}
s := "Hallo Welt\tmit\nUmbrüchen"
fmt.Println(strings.Map(stripWS, s))
}HalloWeltmitUmbrüchenDer Mechanismus ist elegant, weil derselbe Funktionswert sowohl „keep" (positive Rune zurückgeben) als auch „drop" (negative Rune zurückgeben) und „transform" (andere positive Rune zurückgeben) ausdrücken kann. Es gibt keinen separaten Filter-Schritt — ein Aufruf, ein Pass über den String.
strings.Map iteriert über den Eingabestring rune-weise, nicht byte-weise. Das ist der entscheidende Unterschied zu bytes.Map und macht die Funktion UTF-8-sicher. Multi-Byte-Sequenzen werden als ein einzelner rune-Wert an die Mapping-Funktion übergeben.
package main
import (
"fmt"
"strings"
)
func main() {
// Jede Rune wird einmal aufgerufen — auch Umlaute und Emoji
count := 0
strings.Map(func(r rune) rune {
count++
fmt.Printf("rune #%d = %q (%U)\n", count, r, r)
return r
}, "Hä!🚀")
}rune #1 = 'H' (U+0048)
rune #2 = 'ä' (U+00E4)
rune #3 = '!' (U+0021)
rune #4 = '🚀' (U+1F680)Ungültige UTF-8-Sequenzen im Input werden durch utf8.RuneError (U+FFFD) ersetzt, bevor die Mapping-Funktion aufgerufen wird — das schützt vor unerwarteten Byte-Mustern und stellt sicher, dass die Mapping-Logik immer mit gültigen Code-Points arbeitet.
strings.Map allokiert intern einen strings.Builder mit der Kapazität des Eingabestrings. Wenn keine Rune geändert oder entfernt wird (Identitäts-Mapping), wird trotzdem ein neuer String erzeugt — anders als bei strings.Replace, das im No-Op-Fall den Originalstring durchreicht.
Faustregel: strings.Map lohnt sich, wenn die Transformation pro Rune individuell ist und nicht durch ein fixes Pattern beschrieben werden kann. Für feste Substitutionen ist strings.Replacer schneller, weil dieser eine Trie-Suche statt eines Funktionsaufrufs pro Rune nutzt. Für ein einzelnes Substring-Replace ist strings.ReplaceAll schneller.
Ein klassischer Einsatz: aus einem Titel einen URL-tauglichen Slug bauen. Großbuchstaben werden zu Kleinbuchstaben, Leerzeichen zu Bindestrichen, alles andere wird entweder durchgereicht oder entfernt.
package main
import (
"fmt"
"strings"
"unicode"
)
func slug(s string) string {
return strings.Map(func(r rune) rune {
switch {
case unicode.IsLetter(r):
return unicode.ToLower(r)
case unicode.IsDigit(r):
return r
case unicode.IsSpace(r) || r == '-':
return '-'
default:
return -1
}
}, s)
}
func main() {
fmt.Println(slug("Hallo Welt!"))
fmt.Println(slug("Büro für Übersetzungen — Köln, 2026"))
}hallo-welt
büro-für-übersetzungen--köln-2026Das Beispiel zeigt sowohl die Transformation (ToLower), den Filter (-1 für Satzzeichen) als auch das Mapping (Whitespace zu -) in einer einzigen Funktion. Für „echte" Slugs müssten doppelte Bindestriche noch zusammengefasst und Umlaute transliteriert werden — beides idealerweise in einem zweiten Pass mit strings.ReplaceAll oder einer Transliterations-Library.
Das strings-Paket bietet strings.Map als die idiomatische Variante für character-weise Verschlüsselungen wie ROT13. Die Standardbibliothek liefert sogar strings.NewReader zusammen mit golang.org/x/text/transform — aber für eine reine Lehrbuch-Version ist strings.Map perfekt geeignet.
package main
import (
"fmt"
"strings"
)
func rot13(r rune) rune {
switch {
case r >= 'A' && r <= 'Z':
return 'A' + (r-'A'+13)%26
case r >= 'a' && r <= 'z':
return 'a' + (r-'a'+13)%26
}
return r
}
func main() {
enc := strings.Map(rot13, "Hallo Welt! 1234")
fmt.Println(enc)
fmt.Println(strings.Map(rot13, enc)) // zweimal angewandt = original
}Unyyb Jryg! 1234
Hallo Welt! 1234ROT13 ist symmetrisch: zweimal angewandt ergibt sich der Originalstring. Das macht das Beispiel zu einem schönen Selbsttest, weil keine separate Decode-Funktion nötig ist. Ziffern, Satzzeichen und Whitespace werden unverändert durchgereicht — die switch-Anweisung greift nur bei Buchstaben.
Negativer Rune-Wert = Filter
Gibt die Mapping-Funktion -1 zurück, wird die Rune aus dem Ergebnis entfernt — Transformation und Filter in einem Aufruf.
Rune-orientiert, nicht byte-orientiert
Die Funktion iteriert über UTF-8-Runen; Multi-Byte-Sequenzen kommen als einzelner Code-Point in die Mapping-Funktion.
Ungültiges UTF-8 wird zu RuneError
Defekte Byte-Sequenzen werden vor dem Mapping zu U+FFFD ersetzt — kein Crash, kein Stille-Datenverlust.
Allokiert immer
Auch bei Identitäts-Mapping entsteht ein neuer String, anders als bei Replace ohne Treffer.
Replacer für feste Paare schneller
Bei statischen Substitutionsregeln ist strings.NewReplacer performanter, weil keine Funktion pro Rune aufgerufen wird.
Inline-Closures idiomatisch
Die Signatur stellt die Mapping-Funktion an die erste Stelle, damit Closures direkt am Aufrufort lesbar definiert werden können.
Pendant in bytes.Map ist byte-orientiert
bytes.Map arbeitet byteweise, strings.Map rune-weise — bei UTF-8-Daten ist strings.Map die richtige Wahl.
Threadsafe
Strings sind in Go immutable und strings.Map mutiert nichts — beliebig viele Goroutinen können parallel mappen.
Weiterführende Ressourcen
Externe Quellen
strings.Mapbytes.Map(byte-orientiertes Pendant)- Go Blog — Strings, bytes, runes and characters