fmt.Print ist die schlankste Funktion der Print-Familie: keine Format-Verben, kein automatisches Newline, nur eine Reihe von Operanden, die nacheinander auf os.Stdout landen. Sie wirkt auf den ersten Blick wie das Python-print ohne Klammern, verhält sich aber bei Trennzeichen grundsätzlich anders — und genau diese Abweichung führt zu den meisten Anfängerfehlern.

In der Praxis ist Print die am häufigsten unterschätzte und gleichzeitig am häufigsten falsch eingesetzte Funktion des fmt-Pakets. Wer sie bewusst wählt, tut das fast immer wegen ihres fehlenden Newlines — alle anderen Anwendungen sind in der Regel bei Println oder Printf besser aufgehoben.

Signatur

fmt.Print ist eine variadische Funktion: func Print(a ...any) (n int, err error). Sie nimmt beliebig viele Argumente beliebigen Typs entgegen und schreibt sie ohne Formatierungsanweisung nach os.Stdout. Die Rückgabe besteht aus der Anzahl tatsächlich geschriebener Bytes sowie einem error-Wert, der bei os.Stdout praktisch nie auftritt, aber Teil des einheitlichen Print-Vertrags ist.

Im Vergleich zu Printf fehlt der erste format string-Parameter, im Vergleich zu Println fehlt das angehängte Newline. Die variadische Signatur ...any (in älterem Code ...interface{}) erlaubt jede Mischung von Typen — Strings, Zahlen, Structs, Slices — die intern über die %v-Standardformatierung dargestellt werden.

Die Leerzeichen-Regel

Das überraschendste Detail von fmt.Print ist seine Whitespace-Heuristik: Leerzeichen werden zwischen zwei aufeinanderfolgenden Operanden nur dann automatisch eingefügt, wenn keiner der beiden Operanden ein String ist. Sobald einer der beiden ein String ist, werden die Werte ohne Trennzeichen direkt konkateniert — eine Regel, die in keiner anderen weit verbreiteten Print-Funktion so existiert.

Diese Regel wirkt willkürlich, hat aber einen pragmatischen Hintergrund: Strings enthalten oft selbst schon Whitespace (Doppelpunkte, Leerzeichen, Bindestriche), während rohe Zahlen oder Booleans visuell zusammenkleben würden, wenn sie ohne Trenner gedruckt würden.

Go spacing.go
package main

import "fmt"

func main() {
    fmt.Print("a", "b")
    fmt.Print("\n")
    fmt.Print(1, 2)
    fmt.Print("\n")
    fmt.Print("a", 1)
    fmt.Print("\n")
    fmt.Print(1, "b")
    fmt.Print("\n")
    fmt.Print(1, 2, 3)
    fmt.Print("\n")
    fmt.Print(1, "x", 2)
    fmt.Print("\n")
}
Output
ab
1 2
a1
1b
1 2 3
1x2

Beachtenswert ist der letzte Fall: Sobald ein einziger String zwischen Nicht-String-Operanden steht, fallen alle Leerzeichen weg — die Regel wird paarweise zwischen direkten Nachbarn ausgewertet. Wer hier zuverlässig Trennzeichen will, muss sie explizit selbst einbauen oder direkt zu Println oder Printf greifen.

Vergleich zu Println

Println macht zwei Dinge, die Print nicht macht: Es fügt immer ein Leerzeichen zwischen je zwei Operanden ein — unabhängig davon, ob es Strings sind oder nicht — und hängt am Ende immer ein \n an.

Go vergleich.go
package main

import "fmt"

func main() {
    fmt.Print("Hallo", "Welt", 42)
    fmt.Print("\n--\n")
    fmt.Println("Hallo", "Welt", 42)
    fmt.Print("--\n")
    fmt.Printf("%v %v %v\n", "Hallo", "Welt", 42)
}
Output
HalloWelt42
--
Hallo Welt 42
--
Hallo Welt 42

Print liefert HalloWelt42 ohne jedes Trennzeichen, weil zwei der drei Operanden Strings sind und damit die Whitespace-Heuristik komplett ausschaltet. Println setzt zwischen jedes Paar ein Space und schließt mit \n ab — genau das Verhalten, das in 90 % der Debugging-Sessions gewünscht ist.

Vergleich zu Printf

Printf ist das genaue Gegenstück zu Print: ein Format-String steuert exakt, wie jeder Operand gerendert wird, welche Trenner dazwischen stehen und ob am Ende ein Newline kommt. Während Print mit einer impliziten und teilweise überraschenden Regel arbeitet, ist bei Printf jedes Whitespace und jedes Zeichen vom Aufrufer kontrolliert.

Die praktische Daumenregel: Printf ist die richtige Wahl, sobald man Werte mit Kontext ausgibt ("x = %d, y = %d"), Breiten oder Präzisionen braucht oder eine bestimmte Reihenfolge mit %[N]v referenziert. Print lohnt sich nur dann, wenn man tatsächlich genau die Whitespace-Heuristik will — was selten ist — oder wenn man bewusst kein Newline an den Output anhängen möchte.

Häufige Verwirrung

Die häufigste Stolperfalle ist die Erwartung, fmt.Print verhielte sich wie Pythons print() oder JavaScripts console.log() — also mit automatischem Leerzeichen zwischen allen Argumenten. In Go ist das genau dann nicht der Fall, wenn mindestens einer der direkten Nachbarn ein String ist.

Go anfaengerfalle.go
package main

import (
    "fmt"
    "strings"
)

func main() {
    fmt.Print("Hallo", "Welt", "\n")
    fmt.Println("Hallo", "Welt")
    fmt.Printf("%s %s\n", "Hallo", "Welt")

    worte := []string{"Hallo", "Welt", "aus", "Go"}
    fmt.Println(strings.Join(worte, " "))
}
Output
HalloWelt
Hallo Welt
Hallo Welt
Hallo Welt aus Go

Welche der drei Lösungen passt, hängt vom Kontext ab: Println ist die schnellste Korrektur, Printf der saubere Weg bei festen Strukturen und strings.Join die richtige Antwort, wenn eine variable Anzahl an Strings mit immer demselben Trenner verbunden werden soll.

Rückgabewerte

Wie bei Printf und Println liefert Print ein Paar aus geschriebenen Bytes und einem error zurück. In den allermeisten Programmen werden beide Werte stillschweigend verworfen, weil der Output nach os.Stdout geht und ein Fehler dort nur in absoluten Ausnahmesituationen — etwa bei geschlossenem Deskriptor — überhaupt auftritt.

Relevant werden die Rückgabewerte erst, wenn man die Print-Logik auf andere Writer übertragen will. Genau dafür existiert fmt.Fprint, das denselben Whitespace-Algorithmus benutzt, aber gegen ein beliebiges io.Writer-Ziel schreibt.

Wann Print wirklich Sinn macht

Es gibt drei Situationen, in denen Print die objektiv bessere Wahl gegenüber Println oder Printf ist, und alle drei haben einen gemeinsamen Nenner: Man will kein Newline am Ende. Der erste Use Case ist die schlichte Konkatenation ohne Format-String. Der zweite ist die Aktualisierung derselben Zeile per \r-Carriage-Return — typisch für Spinner und Progress-Bars. Der dritte sind Tools, deren Output in eine Shell-Pipeline geht und deren letzte Zeile bewusst ohne abschließendes \n enden soll.

Praxis 1 — Spinner-Output

Ein klassischer Anwendungsfall ohne Newline ist ein Loading-Indikator, der immer dieselbe Zeile im Terminal überschreibt. Dafür schreibt man am Anfang jeder Aktualisierung ein \r (Carriage Return), das den Cursor an den Zeilenanfang setzt, ohne in die nächste Zeile zu springen.

Go spinner.go
package main

import (
    "fmt"
    "time"
)

func main() {
    frames := []rune{'|', '/', '-', '\\'}
    for i := 0; i <= 100; i += 25 {
        frame := frames[(i/25)%len(frames)]
        fmt.Print("\rFortschritt ", string(frame), " ", i, "%")
        time.Sleep(50 * time.Millisecond)
    }
    fmt.Println()
}
Output
Fortschritt \ 100%

Im Terminal sieht man während des Laufs jede Zwischenstufe an derselben Position — gespeichert bleibt am Ende nur die letzte Zeile. Das abschließende fmt.Println() ohne Argumente sorgt dafür, dass der Cursor nach Ende des Spinners in die nächste Zeile springt.

Praxis 2 — Pipe-freundlicher Output

Kommandozeilen-Tools, deren Output in eine Shell-Pipeline weitergereicht wird, profitieren manchmal davon, kein abschließendes Newline zu schreiben.

Go uuid-pipe.go
package main

import "fmt"

func main() {
    id := "550e8400-e29b-41d4-a716-446655440000"
    fmt.Print(id)
}
Output
550e8400-e29b-41d4-a716-446655440000

Aufgerufen als go run uuid-pipe.go | pbcopy landet exakt die UUID in der Zwischenablage — ohne ein angehängtes Newline, das in vielen Eingabefeldern als unsichtbares Zeichen Probleme macht.

Interessantes

Leerzeichen nur zwischen Nicht-String-Operanden

fmt.Print setzt automatische Leerzeichen ausschließlich zwischen zwei direkt benachbarten Operanden, von denen keiner ein String ist.

Print fügt kein Newline an

Im Gegensatz zu Println schreibt Print nie ein abschließendes \n. Der Cursor bleibt am Ende des letzten Operanden.

Print mit Strings: kontinuierliche Konkatenation

Aufrufe wie fmt.Print(&quot;Hallo&quot;, &quot;Welt&quot;) liefern HalloWelt, nicht Hallo Welt.

Println: immer Spaces, immer Newline

fmt.Println ergänzt immer Leerzeichen zwischen Operanden und hängt am Ende immer ein Newline an.

Printf bei Format, Print bei reiner Konkatenation

Sobald Werte mit Kontext oder Format-Verben ausgegeben werden, ist Printf die richtige Wahl.

Rückgabewerte meist verworfen

Auf os.Stdout schlägt Print praktisch nie fehl. Üblich ist das stille Ignorieren der Rückgabe.

Spinner und Progress: Print + Carriage-Return

Für Statuszeilen, die dieselbe Terminalzeile überschreiben, ist Print zusammen mit \r das richtige Werkzeug.

Pipe-Output ohne abschließendes Newline

Wenn der Output sauber in eine Shell-Pipe fließen soll, ist das fehlende \n von Print ein Feature.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht