fmt.Sprint ist die String-zurückgebende Schwester von fmt.Print: gleiche variadische Operanden-Liste, gleiche eigenwillige Whitespace-Heuristik, aber kein I/O — statt nach Stdout zu schreiben, liefert die Funktion das Ergebnis als string zurück. Damit ist Sprint das, was Sprintf wäre, wenn man keinen Format-String hätte: rein positionale Konkatenation der Operanden mit der Print-Regel, dass Leerzeichen nur dort eingefügt werden, wo zwei aufeinanderfolgende Operanden beide nicht vom Typ string sind.

In der Praxis ist Sprint die am wenigsten genutzte der vier Schwester-Funktionen: meistens will man entweder klare Formatierung (Sprintf) oder eine Zeile mit Newline (Sprintln). Trotzdem lohnt der genaue Blick — die Funktion taucht in Logger-Wrappern, in errors.New(fmt.Sprint(...))-Konstrukten und in Stringer-Implementierungen auf.

Signatur

func Sprint(a ...any) string — variadische Operanden vom Typ any gehen rein, ein fertig formatierter string kommt raus. Da die Funktion nichts schreibt, gibt es auch nichts, was schiefgehen könnte; der einzige Rückgabewert ist der zusammengebaute String. fmt wendet auf jeden Operanden intern das Standardverb %v an, respektiert dabei fmt.Stringer und error, und fügt zwischen den Stücken ggf. ein Leerzeichen ein.

Whitespace-Regel — geerbt von Print

Die einzige Eigenheit, die man bei Sprint wirklich kennen muss, ist die Spacing-Heuristik: ein Leerzeichen wird nur dann zwischen zwei Operanden eingefügt, wenn keiner von beiden ein string ist. Sobald links oder rechts ein String steht, klebt Sprint die Argumente ohne Trennzeichen aneinander.

Go whitespace.go
package main

import "fmt"

func main() {
	fmt.Println(fmt.Sprint("a", "b"))
	fmt.Println(fmt.Sprint(1, 2))
	fmt.Println(fmt.Sprint("Wert: ", 42))
	fmt.Println(fmt.Sprint(42, " Punkte"))
	fmt.Println(fmt.Sprint(1, 2, 3))
	fmt.Println(fmt.Sprint("x=", 1, 2, "y"))
}
Output
ab
1 2
Wert: 42
42 Punkte
1 2 3
x=1 2y

Die letzte Zeile ist die lehrreichste: "x=1 2y". Zwischen "x=" und 1 kein Space (links ist String), zwischen 1 und 2 ein Space (beides Nicht-Strings), zwischen 2 und "y" kein Space (rechts ist String). Wer das Verhalten nicht kennt, hält das für einen Bug — wer es kennt, weiß, dass Sprintf mit explizitem Format-String fast immer die saubere Antwort darauf ist.

Kein Newline am Ende

Sprint hängt nichts an das Ergebnis an — kein \n, kein zusätzlicher Space, nichts. Wer einen Zeilenumbruch braucht, greift zu Sprintln, das pauschal ein Newline anhängt und zusätzlich zwischen allen Operanden ein Leerzeichen einfügt (also ohne die String-Sonderregel).

Go kein-newline.go
package main

import "fmt"

func main() {
	s := fmt.Sprint("Hallo", " ", "Welt")
	fmt.Printf("[%s]\n", s)

	t := fmt.Sprintln("Hallo", "Welt")
	fmt.Printf("[%s]", t)
}
Output
[Hallo Welt]
[Hallo Welt
]

Bei Sprint schließt die rechte Klammer direkt am Wort an, bei Sprintln rutscht sie eine Zeile nach unten, weil das \n mit im String steckt.

Sprint, Sprintln, Sprintf im direkten Vergleich

FunktionSpacingNewlineFormat-String
Sprintnur zwischen Nicht-String-Paarenneinnein
Sprintlnimmer zwischen allen Operandenjanein
Sprintfexakt wie im Format-Stringneinja
Go vergleich.go
package main

import "fmt"

func main() {
	a, b, c := 1, "x", 2

	fmt.Printf("Sprint:   %q\n", fmt.Sprint(a, b, c))
	fmt.Printf("Sprintln: %q\n", fmt.Sprintln(a, b, c))
	fmt.Printf("Sprintf:  %q\n", fmt.Sprintf("%d %s %d", a, b, c))
}
Output
Sprint:   "1x2"
Sprintln: "1 x 2\n"
Sprintf:  "1 x 2"

Die %q-Ausgabe macht die unsichtbaren Anteile sichtbar: Sprint produziert "1x2" (der String in der Mitte unterdrückt beide Spaces), Sprintln produziert "1 x 2\n" (Spaces überall plus Newline), Sprintf produziert exakt das, was im Format-String steht.

Wann Sprint überhaupt sinnvoll ist

In der Praxis ist Sprint selten erste Wahl. Es gibt aber zwei Nischen, in denen Sprint tatsächlich glänzt:

Logger-Wrapper mit variadischer API. Viele Logger-Bibliotheken (auch das Standard-log-Paket) implementieren intern eine Funktion, die ...any entgegennimmt und daraus eine Log-Zeile baut. Genau für diesen Anwendungsfall ist Sprint gemacht — die Print-Whitespace-Regel liefert hier sogar die gleiche Erfahrung wie log.Print selbst.

Lose Konkatenation gemischter Typen für Debugging. Wenn man schnell mal errors.New(fmt.Sprint("unerwartet: ", val)) schreibt, ist das kürzer als Sprintf("unerwartet: %v", val) — und für einen Wegwerf-Fehlertext akzeptabel.

Außerhalb dieser zwei Fälle ist die Faustregel: wenn du dich fragst, ob du Sprint brauchst, brauchst du wahrscheinlich Sprintf.

Performance

Sprint durchläuft den gleichen Reflection-basierten Formatierungspfad wie Sprintf und Print. Für jeden Operanden wird per Type-Switch und ggf. reflect.Value geprüft, dann das Standardverb %v angewendet, und alles in einen intern verwalteten Buffer geschrieben, der am Ende zu einem string konvertiert wird — was eine zusätzliche Allokation kostet. In Hot-Paths ist jede fmt-Funktion deutlich langsamer als typspezifische Alternativen wie strconv.Itoa oder strings.Builder. Für normales Application-Logging und gelegentliche Fehlertexte ist Sprint dagegen völlig unproblematisch.

Logger-Wrapper

Ein realistischer Anwendungsfall: ein dünner Logger, der ...any akzeptiert und für die String-Erzeugung auf fmt.Sprint zurückgreift. Die Print-Whitespace-Regel ist hier ein Feature, kein Bug — sie sorgt dafür, dass der Wrapper sich genauso verhält wie log.Print.

Go logger.go
package main

import (
	"fmt"
	"os"
)

type Logger struct {
	prefix string
}

func (l *Logger) Log(args ...any) {
	msg := fmt.Sprint(args...)
	fmt.Fprintf(os.Stdout, "[%s] %s\n", l.prefix, msg)
}

func main() {
	l := &Logger{prefix: "APP"}

	l.Log("Server gestartet auf Port ", 8080)
	l.Log("Verbindungen: ", 3, " aktiv, ", 12, " idle")
	l.Log("Fehlerquote ", 0.034, " — Schwellwert ", 0.05)
}
Output
[APP] Server gestartet auf Port 8080
[APP] Verbindungen: 3 aktiv, 12 idle
[APP] Fehlerquote 0.034 — Schwellwert 0.05

Bemerkenswert ist die zweite Log-Zeile. Hier liefert die Print-Regel exakt das gewünschte Ergebnis — die String-Stücke bringen ihre eigenen Spaces mit, und Sprint fügt keine zusätzlichen ein. Mit Sprintln käme an jeder Stelle ein doppelter Space heraus.

Schnelle Debug-Konkatenation

Der zweite Praxisfall ist weniger glamourös: Debug-Strings, in denen man die Print-Whitespace-Regel bewusst ausnutzt, um gemischte Operanden ohne Format-String zusammenzukleben.

Go debug-concat.go
package main

import (
	"errors"
	"fmt"
)

type Token struct {
	Kind  string
	Value any
}

func (t Token) String() string {
	return fmt.Sprint("<", t.Kind, ":", t.Value, ">")
}

func parse(s string) (Token, error) {
	if s == "" {
		return Token{}, errors.New(fmt.Sprint("leerer Input bei pos ", 0))
	}
	return Token{Kind: "IDENT", Value: s}, nil
}

func main() {
	t := Token{Kind: "NUM", Value: 42}
	fmt.Println(t)

	_, err := parse("")
	fmt.Println("err:", err)
}
Output
<NUM:42>
err: leerer Input bei pos 0

In Token.String() ist die Print-Regel praktisch: alle String-Literale unterdrücken jeglichen Auto-Space, und das numerische Value rutscht ohne Trennzeichen dazwischen. Trotzdem wäre fmt.Errorf("leerer Input bei pos %d", 0) hier die idiomatischere Variante — Sprint ist eine Abkürzung, kein architektonisches Statement.

Interessantes

Print-Whitespace-Regel

Sprint fügt nur dann ein Leerzeichen zwischen zwei Operanden ein, wenn keiner der beiden ein string ist.

Kein Newline am Ende

Im Gegensatz zu Sprintln hängt Sprint nichts an das Ergebnis an. Wer eine Zeile mit \n braucht, nimmt Sprintln.

Keine Format-Verben

Sprint kennt keinen Format-String und keine Verben. Für %d, %v, %x etc. ist Sprintf zuständig.

Liefert String, schreibt nichts

Anders als Print oder Fprint macht Sprint keinerlei I/O — Rückgabewert ist allein der zusammengebaute string, daher auch kein error.

%v auf Operanden

Intern formatiert Sprint jeden Operanden mit dem Standardverb %v. Damit verhalten sich Strukturen und Pointer genauso wie bei Println.

In Hot-Paths langsam

Reflection-basierter Formatierungspfad plus String-Allokation: für innere Schleifen ungeeignet.

In der Praxis selten verwendet

Die meisten Anwendungen sind mit Sprintf oder strings.Builder besser bedient. Sprint glänzt vor allem in variadischen Logger-Wrappern.

Stringer-Interface wird respektiert

Implementiert ein Operand fmt.Stringer, wird die String()-Methode aufgerufen — genauso wie bei Print und Sprintf %v.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Das fmt-Paket — Formatierte I/O

Zur Übersicht