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.
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"))
}ab
1 2
Wert: 42
42 Punkte
1 2 3
x=1 2yDie 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).
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)
}[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
| Funktion | Spacing | Newline | Format-String |
|---|---|---|---|
Sprint | nur zwischen Nicht-String-Paaren | nein | nein |
Sprintln | immer zwischen allen Operanden | ja | nein |
Sprintf | exakt wie im Format-String | nein | ja |
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))
}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.
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)
}[APP] Server gestartet auf Port 8080
[APP] Verbindungen: 3 aktiv, 12 idle
[APP] Fehlerquote 0.034 — Schwellwert 0.05Bemerkenswert 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.
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)
}<NUM:42>
err: leerer Input bei pos 0In 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.