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.

Go signatur.go
func Map(mapping func(rune) rune, s string) string

Die 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.

Go filter_negativ.go
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))
}
Output
HalloWeltmitUmbrüchen

Der 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.

Go rune_semantik.go
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ä!🚀")
}
Output
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.

Go slug_simple.go
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"))
}
Output
hallo-welt
büro-für-übersetzungen--köln-2026

Das 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.

Go rot13.go
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
}
Output
Unyyb Jryg! 1234
Hallo Welt! 1234

ROT13 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

/ Weiter

Zurück zu Das strings-Paket — String-Manipulation

Zur Übersicht