fmt.Println ist die Funktion, mit der so gut wie jede Go-Karriere beginnt — fmt.Println("Hallo, Welt") steht in jedem Einsteiger-Tutorial. Sie schreibt ihre Argumente positional nach Stdout, formatiert jeden Operand mit dem Default-Verb %v, trennt sie durch Leerzeichen und beendet die Zeile mit einem Newline.

Genau diese drei Eigenheiten machen Println zur idiomatischen Wahl, wenn man einfach etwas ausgeben will, ohne über Format-Strings nachzudenken: immer Leerzeichen zwischen Operanden, immer Newline am Ende, immer %v als Formatierung.

Signatur

func Println(a ...any) (n int, err error) — variadisch, akzeptiert beliebig viele Argumente beliebigen Typs. Die Rückgabewerte sind dieselben wie bei Print und Printf: die Anzahl geschriebener Bytes und ein optionaler Fehler.

Wichtig ist das ...any (früher ...interface{}): jeder Operand darf einen beliebigen Typ haben, und Println entscheidet zur Laufzeit über Reflection, wie er formatiert wird. Genau wie bei Print interessieren uns die Rückgabewerte in der Praxis selten — fast jeder Aufruf in echtem Code lässt n und err ungenutzt.

Leerzeichen-Regel — anders als Print

Der wichtigste Unterschied zu fmt.Print: Println fügt immer Leerzeichen zwischen allen Operanden ein — unabhängig von deren Typ. Während Print nur dann ein Leerzeichen einschiebt, wenn beide Nachbarn keine Strings sind, ist Println in dieser Hinsicht radikal einfach: zwischen je zwei Argumenten landet genau ein Space im Output.

Go spacing.go
package main

import "fmt"

func main() {
	fmt.Println("a", "b")
	fmt.Println(1, 2)
	fmt.Println("a", 1, "c")
	fmt.Println("Name:", "Klaus")
}
Output
a b
1 2
a 1 c
Name: Klaus

Im letzten Beispiel sieht man, warum diese Regel so angenehm ist: Println("Name:", "Klaus") liefert Name: Klaus — das Leerzeichen kommt geschenkt. Bei Print müsste man entweder "Name: " mit Trailing-Space schreiben oder einen Nicht-String-Operand dazwischen schmuggeln.

Newline am Ende

Println hängt immer ein \n an die ausgegebene Zeile. Das ist nicht konfigurierbar, nicht abschaltbar, nicht unterdrückbar. Wer das Newline nicht will, ist bei Println falsch und greift zu Print oder Printf.

Go newline.go
package main

import "fmt"

func main() {
	fmt.Println("eins")
	fmt.Println("zwei")
	fmt.Println("drei")
}
Output
eins
zwei
drei

Ein nachgestelltes \n im String selbst (Println("eins\n")) ist überflüssig und produziert eine Leerzeile, weil Println sein eigenes Newline trotzdem dranhängt.

Operand-Formatierung

Jeder Operand wird so formatiert, als hätte man Printf mit dem Default-Verb %v aufgerufen. Für Strings: unverändert. Für Zahlen: dezimal. Für Structs: feldweise in geschweiften Klammern. Für Typen mit String() string-Methode (Stringer-Interface): genau diese Methode.

Go stringer.go
package main

import "fmt"

type Ampel int

const (
	Rot Ampel = iota
	Gelb
	Gruen
)

func (a Ampel) String() string {
	switch a {
	case Rot:
		return "ROT"
	case Gelb:
		return "GELB"
	case Gruen:
		return "GRÜN"
	}
	return "?"
}

func main() {
	fmt.Println("Aktuelle Phase:", Rot)
	fmt.Println("Reihenfolge:", Rot, Gelb, Gruen)
}
Output
Aktuelle Phase: ROT
Reihenfolge: ROT GELB GRÜN

Ohne String()-Methode hätte Println die rohen Integer-Werte 0, 1, 2 ausgegeben. So aber liefert es genau die sprechende Repräsentation, die der Typ selbst definiert.

Vergleich zu Print und Printf

Am klarsten wird Println im direkten Vergleich mit seinen beiden Geschwistern. Dieselbe Eingabe, drei Funktionen, drei deutlich unterschiedliche Ergebnisse.

Go vergleich.go
package main

import "fmt"

func main() {
	fmt.Print("a", "b", 1, "\n")
	fmt.Println("a", "b", 1)
	fmt.Printf("%v%v%v\n", "a", "b", 1)
}
Output
ab 1
a b 1
ab1

Print schiebt nur zwischen "b" und 1 ein Leerzeichen ein, Println liefert sauber a b 1, und Printf ohne Spaces im Format-String klebt alles zusammen.

Anwendungsbereiche

Println ist überall dort die richtige Wahl, wo man keinen Format-String braucht und eine Zeile pro Aufruf möchte. Vier Szenarien: Hello-World, Debug-Output während der Entwicklung, einfache CLI-Output-Zeilen, und überall dort, wo der Output-Inhalt ohnehin schon strukturiert vorliegt (z. B. durch einen Stringer).

In Production-Code für Server, Daemons oder größere Tools verschiebt sich der Fokus: dort will man strukturiertes Logging mit Levels, Kontextfeldern und maschinenlesbarem Output. Dafür ist log/slog aus der Standardbibliothek die richtige Adresse — Println bleibt das Werkzeug für Entwickler-Output und kleine, menschenlesbare CLIs.

Println vs Printf

Eine häufige Verwechslung: „Ich nehme einfach Printf("%v %v\n", a, b) statt Println." Das funktioniert, ist aber länger, fehleranfälliger und nicht idiomatisch.

Go vs-printf.go
package main

import "fmt"

func main() {
	name := "Klaus"
	alter := 42

	fmt.Printf("%v %v\n", name, alter)
	fmt.Println(name, alter)
}
Output
Klaus 42
Klaus 42

Gleicher Output, aber Println spart Format-String, Newline und mentale Last. Sobald jedoch echte Formatierung ins Spiel kommt — feste Breiten, Hex, Float-Präzision — ist Printf wieder das richtige Werkzeug. Die Faustregel: Format nötig → Printf. Nur Werte ausgeben → Println.

Println bei Slices und Maps

Println formatiert auch zusammengesetzte Typen vollständig — Slices erscheinen in eckigen Klammern, Maps in geschweiften, jeweils mit space-separierten Elementen.

Go slices-maps.go
package main

import "fmt"

func main() {
	zahlen := []int{1, 2, 3, 4}
	preise := map[string]float64{"Apfel": 0.5, "Birne": 0.7}

	fmt.Println("Zahlen:", zahlen)
	fmt.Println("Preise:", preise)
}
Output
Zahlen: [1 2 3 4]
Preise: map[Apfel:0.5 Birne:0.7]

Bei kleinen Datenstrukturen ist das perfekt. Bei großen, tief verschachtelten Structs wird der Output schnell unleserlich — dann lohnt sich fmt.Sprintf("%+v", x) mit anschließender Übergabe an einen Logger, oder direkt slog.Info("...", "key", x).

Praxis 1 — Vom Hello-World zur strukturierten Ausgabe

Der typische Lernweg mit Println beginnt mit einem einzigen Argument und wächst dann organisch mit.

Go praxis-progression.go
package main

import "fmt"

type Nutzer struct {
	Name  string
	Alter int
}

func (n Nutzer) String() string {
	return fmt.Sprintf("%s (%d)", n.Name, n.Alter)
}

func main() {
	fmt.Println("Hallo")
	fmt.Println("Hallo,", "Welt")
	fmt.Println("Antwort:", 42, "von", 100)

	user := Nutzer{Name: "Klaus", Alter: 42}
	fmt.Println("Nutzer:", user)

	user2 := Nutzer{Name: "Anna", Alter: 35}
	fmt.Println("Vergleich:", user, "vs.", user2)
}
Output
Hallo
Hallo, Welt
Antwort: 42 von 100
Nutzer: Klaus (42)
Vergleich: Klaus (42) vs. Anna (35)

Der Schlüssel zur Progression: dieselbe Funktion trägt vom ersten Println("Hallo") bis zur strukturierten Vergleichszeile. Custom-Typen werden über String() formatiert, gemischte Argumente automatisch durch Spaces getrennt, jede Zeile durch das eingebaute Newline abgeschlossen.

Praxis 2 — Debug-Output während der Entwicklung

Eine der häufigsten echten Anwendungen von Println ist das explorative Debugging: man steckt mitten in einer Funktion, weiß nicht genau, welcher Wert wo ankommt, und streut schnell ein paar Println-Zeilen ein.

Go praxis-debug.go
package main

import (
	"fmt"
	"strings"
)

func normalisiereName(roh string) string {
	fmt.Println("Eingabe:", roh)

	getrimmt := strings.TrimSpace(roh)
	fmt.Println("Nach Trim:", getrimmt)

	klein := strings.ToLower(getrimmt)
	fmt.Println("Nach ToLower:", klein)

	mitBindestrich := strings.ReplaceAll(klein, " ", "-")
	fmt.Println("Endergebnis:", mitBindestrich)

	return mitBindestrich
}

func main() {
	_ = normalisiereName("  Klaus Müller  ")
}
Output
Eingabe:   Klaus Müller  
Nach Trim: Klaus Müller
Nach ToLower: klaus müller
Endergebnis: klaus-müller

Jede Zwischenstufe wird sichtbar — mit Label, Wert und automatischem Newline. Wichtig zur Abgrenzung: in Produktivcode haben solche Println-Zeilen nichts zu suchen. Dort übernimmt log/slog, das Levels, strukturierte Felder und konfigurierbare Senken bietet.

Interessantes

Immer Leerzeichen zwischen Operanden

Println fügt zwischen jedem Argumentpaar genau ein Space ein — unabhängig vom Typ. Das ist der entscheidende Unterschied zu fmt.Print.

Immer ein Newline am Ende

Println hängt grundsätzlich \n an. Nicht konfigurierbar — wer kein Newline will, nimmt Print oder Printf.

Erste Wahl für Hello-World und Quick-Debug

Wenn kein Format-String nötig ist und der Output zeilenweise sein soll, ist Println idiomatisch und am kürzesten.

Operanden werden via %v formatiert

Jedes Argument durchläuft Default-Formatierung. Typen mit String() string werden über diese Methode dargestellt — Custom-Repräsentationen funktionieren automatisch.

Rückgabewerte meist verworfen

(n int, err error) interessieren in der Praxis selten — bei Bedarf mit _, _ = fmt.Println(...) explizit ignorieren.

Production: lieber log/slog

Println kennt keine Levels, keine Kontextfelder. Für Server und langlebige Dienste ist log/slog der richtige Weg.

Println vs Printf

Wenn man nur Werte ausgeben will, ist Println kürzer. Sobald echte Formatierung dazukommt, gewinnt Printf.

Slices und Maps werden vollständig ausgegeben

Praktisch für schnelles Debugging. Bei großen Strukturen lieber Sprintf("%+v", x) an einen Logger geben.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht