strings.TrimRight arbeitet ausschließlich am rechten Rand der Eingabe und entfernt dort Zeichen, solange diese im übergebenen Cutset vorkommen. Sobald das erste Zeichen von rechts nicht mehr Mitglied der Cutset-Menge ist, stoppt der Abbau — alles weiter links bleibt unangetastet. Die Funktion ist die exakte Spiegelung von TrimLeft und teilt mit ihr die wichtigste Falle: der zweite Parameter ist eine Menge einzelner Runes, kein Substring.

Damit eignet sich TrimRight besonders gut für Aufgaben wie das Entfernen von Trailing-Newlines aus zeilenweise gelesenem Input, das Abschneiden von Padding-Bytes oder das Normalisieren von URLs durch Stripping nachgestellter Slashes. Wer einen ganzen Suffix-String (z. B. .go) entfernen will, greift stattdessen zu strings.TrimSuffix.

Die Signatur ist minimal und folgt dem Muster der gesamten Trim-Familie: Eingabe-String plus Cutset, Rückgabewert ist ein neuer String. Das Original bleibt durch Go-Semantik unverändert, weil Strings immutable sind.

Go signatur.go
func TrimRight(s, cutset string) string

Beide Parameter werden als UTF-8 interpretiert, sodass auch Mehrbyte-Runes korrekt als ein einzelnes Cutset-Element gelten. Ein leerer Cutset liefert die Eingabe unverändert zurück.

Die häufigste Verwechslung passiert genau hier: cutset ist eine Zeichenmenge, nicht ein zu entfernender Suffix. Im folgenden Beispiel werden vom Ende her so lange Zeichen abgeräumt, wie sie zum Set {o, g} gehören — das sind drei Zeichen, nicht der String "ogo" als Block.

Go cutset_falle.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	// Cutset = {'o', 'g'}
	fmt.Printf("%q\n", strings.TrimRight("hello.go", "ogo"))

	// Reihenfolge im Cutset egal, Mengensemantik:
	fmt.Printf("%q\n", strings.TrimRight("hello.go", "go"))

	// Punkt ist nicht im Cutset → Stopp davor:
	fmt.Printf("%q\n", strings.TrimRight("hello.go", "."))
}
Output
"hello."
"hello."
"hello.go"

Wer wirklich exakt den String ".go" am Ende entfernen will, ist mit strings.TrimSuffix(s, ".go") richtig. TrimRight würde stattdessen den Cutset {., g, o} interpretieren und unter Umständen viel zu aggressiv schneiden, falls weitere dieser Zeichen davor stehen.

Cutset und Eingabe werden rune-weise verarbeitet, nicht byte-weise. Ein deutsches ä belegt in UTF-8 zwei Bytes, zählt aber als genau eine Rune — und genau so erkennt es TrimRight auch im Cutset.

Go runes.go
package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Printf("%q\n", strings.TrimRight("dataäää", "ä"))
	fmt.Printf("%q\n", strings.TrimRight("café→→→", "→"))
}
Output
"data"
"café"

Damit ist TrimRight für alle UTF-8-Texte sicher; es entstehen keine kaputten halben Codepoints am Rand, wie man es bei naivem Byte-Slicing befürchten müsste.

Die drei Funktionen werden gerne verwechselt, lösen aber unterschiedliche Probleme. Die folgende Tabelle stellt die wichtigsten Unterschiede gegenüber:

FunktionZweiter ParameterWas wird entfernt?Wiederholung
TrimRight(s, cutset)Menge von RunesRechtsrandzeichen, solange in der Mengemehrfach, bis Stop
TrimSuffix(s, suffix)Exakter SubstringGenau dieser Suffix, nur falls vorhandengenau einmal
Trim(s, cutset)Menge von RunesLinks- und Rechtsrandmehrfach, beidseitig

Faustregel: Wenn das Ziel ein konkretes Wortende ist (Dateierweiterung, fester Marker), nimm TrimSuffix. Wenn beliebige Füllzeichen weg sollen (Whitespace, Slashes, Newlines), nimm TrimRight.

Beim Lesen von Zeilen über bufio.Reader.ReadString('\n') oder beim Einlesen kompletter Dateien hängt am Ende fast immer ein \n (auf Windows zusätzlich \r). Genau dafür ist TrimRight mit Cutset "\r\n" das idiomatische Werkzeug — eine einzige Zeile räumt beide Varianten in einem Rutsch ab.

Go trailing_newline.go
package main

import (
	"bufio"
	"fmt"
	"strings"
)

func main() {
	r := bufio.NewReader(strings.NewReader("erste Zeile\r\nzweite Zeile\n"))

	for {
		line, err := r.ReadString('\n')
		if line == "" && err != nil {
			break
		}
		clean := strings.TrimRight(line, "\r\n")
		fmt.Printf("[%s]\n", clean)
		if err != nil {
			break
		}
	}
}
Output
[erste Zeile]
[zweite Zeile]

Im Gegensatz zu TrimSpace bleiben hier echte Tabs und Leerzeichen am Zeilenende erhalten — wichtig, wenn der Inhalt z. B. mit signifikantem Whitespace formatiert ist (Markdown-Tabellen, fixed-width-Logs).

Beim Bauen kanonischer URL-Formen will man https://mibeon.de/docs/ und https://mibeon.de/docs als dieselbe Ressource behandeln. TrimRight(url, "/") löscht alle nachgestellten Slashes auf einmal und liefert eine vergleichbare Basisform für Routing oder Caching.

Go url_normalize.go
package main

import (
	"fmt"
	"strings"
)

func canonical(u string) string {
	return strings.TrimRight(u, "/")
}

func main() {
	fmt.Printf("%q\n", canonical("https://mibeon.de/docs/"))
	fmt.Printf("%q\n", canonical("https://mibeon.de/docs///"))
	fmt.Printf("%q\n", canonical("https://mibeon.de/docs"))

	// Achtung: reiner Root wird komplett geleert
	fmt.Printf("%q\n", canonical("/"))
}
Output
"https://mibeon.de/docs"
"https://mibeon.de/docs"
"https://mibeon.de/docs"
""

Die letzte Zeile zeigt eine Stolperstelle: ein Pfad, der nur aus Slashes besteht, wird zu einem leeren String. Wer eine Route wie / als Root behalten möchte, muss diesen Fall vorher abfangen, etwa mit if u == "/" { return u }.

cutset ist eine Menge, kein Suffix

TrimRight(s, "ogo") entfernt am Ende beliebige Kombinationen aus {o, g}, nicht den Block ogo — exakte Spiegelung der TrimLeft-Semantik.

Verwechslungsgefahr mit TrimSuffix

Für das Entfernen eines konkreten Wortendes wie .go immer TrimSuffix nehmen; TrimRight würde hier {., g, o} als Menge interpretieren und zu viel abschneiden.

Mehrfach-Matching am Rand

Solange das jeweils letzte Zeichen Mitglied des Cutsets ist, wird weiter abgebaut — TrimRight("aaab", "a") ergibt aaab, aber TrimRight("baaa", "a") ergibt b.

Rune-aware durch UTF-8

Mehrbyte-Zeichen wie ä oder gelten als einzelne Cutset-Elemente; es entstehen keine zerschnittenen Codepoints am Stringende.

Leerer cutset = Identität

TrimRight(s, "") liefert s unverändert zurück; nützlich als No-Op in dynamisch gebauten Trim-Konfigurationen.

Idealfall: Trailing-Newlines

TrimRight(line, "\r\n") ist der idiomatische Cleanup für zeilenweise gelesenen Input und deckt LF- wie CRLF-Endings ab.

Vorsicht bei Cutset /

TrimRight("/", "/") ergibt "" — Root-Pfade müssen separat behandelt werden, sonst geht die Wurzel-Information verloren.

Threadsafe und allokationsschonend

Reine Funktion ohne geteilten Zustand; gibt einen neuen String zurück (Allokation nur, wenn tatsächlich getrimmt wurde, sonst wird s direkt zurückgegeben).

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht