fmt.Appendln ist die Println-Variante der Append-Familie: Sie nimmt einen vorhandenen []byte-Buffer, fügt die Operanden mit garantierten Spaces dazwischen an und schließt das Ganze mit einem \n ab. Der erweiterte Buffer wird zurückgegeben — kein Zwischen-String, keine zweite Kopie, kein manueller Newline.

Die Funktion ist seit Go 1.19 Teil des fmt-Pakets und schließt eine Lücke, die Sprintln zuvor mit einer unnötigen String-Allokation füllen musste. Wer zeilenbasierten Output direkt in einen wiederverwendbaren Buffer schreibt — NDJSON-Writer, Logger-Backends, Netzwerk-Protokolle — bekommt mit Appendln den passenden Hot-Path.

Signatur

func Appendln(b []byte, a ...any) []byte — der erste Parameter b ist der Ziel-Buffer (typischerweise ein langlebiges []byte, das in einer Schleife wiederverwendet wird). Die Operanden in a werden nacheinander mit der Default-Formatierung (%v) serialisiert. Der Rückgabewert ist derselbe Buffer, möglicherweise nach interner Reallocation — deshalb muss er immer wieder zugewiesen werden.

Verhalten

Appendln verhält sich semantisch wie Println, schreibt aber nicht in os.Stdout, sondern hängt an einen Buffer an. Zwischen allen Operanden steht ein Space — unabhängig davon, ob die Nachbarn Strings, Zahlen oder Strukturen sind. Am Ende kommt genau ein \n.

Go verhalten.go
package main

import (
	"fmt"
	"os"
)

func main() {
	buf := make([]byte, 0, 64)
	buf = fmt.Appendln(buf, "user", 42, true)
	buf = fmt.Appendln(buf, "next", "line")
	os.Stdout.Write(buf)
}
Output
user 42 true
next line

Auffällig: Auch zwischen "user" und der Zahl 42 steht ein Space. Das ist der entscheidende Unterschied zu Append — dort wird nur dann ein Space eingefügt, wenn beide Nachbarn keine Strings sind. Appendln zieht diese Regel nicht durch, sondern setzt konsequent Spaces zwischen alle Felder.

Vergleich Append / Appendln / Appendf

FunktionSpaces zwischen OperandenTrailing NewlineFormat-String
fmt.Appendnur zwischen Nicht-Stringsneinnein
fmt.Appendlnimmerja (\n)nein
fmt.Appendfgesteuert durch Formatgesteuert durch Formatja

Appendln ist also der „Println-Modus" der Familie: zeilenorientiert, vorhersehbar, ohne Format-Bastelei. Für strukturierten Output mit präzisen Verbs ist Appendf die richtige Wahl, für rohes Zusammenfügen ohne Whitespace-Magie Append.

Use Case: NDJSON oder zeilenbasierter Output

Newline-Delimited-Formate (NDJSON, klassische Log-Zeilen, Plain-Text-Streams) leben davon, dass jede Zeile in sich abgeschlossen ist und mit \n endet. Hier spart Appendln zwei Handgriffe gleichzeitig: Die Spaces zwischen den Feldern sind gesetzt, und der Zeilenumbruch wird nicht vergessen.

Go ndjson_like.go
package main

import (
	"fmt"
	"os"
)

type Event struct {
	ID   int
	Kind string
}

func (e Event) String() string {
	return fmt.Sprintf("event(id=%d kind=%s)", e.ID, e.Kind)
}

func main() {
	events := []Event{
		{1, "login"},
		{2, "click"},
		{3, "logout"},
	}

	buf := make([]byte, 0, 256)
	for _, ev := range events {
		buf = fmt.Appendln(buf, ev)
	}
	os.Stdout.Write(buf)
}
Output
event(id=1 kind=login)
event(id=2 kind=click)
event(id=3 kind=logout)

Jeder Schleifendurchlauf hängt eine vollständige Zeile an — inklusive Newline. Im Gegensatz zu Sprintln entsteht kein Zwischen-String, der danach wieder verworfen würde.

Idiomatic Pattern

Das typische Appendln-Muster ist eine Schleife mit wiederverwendetem Buffer. Nach jedem Flush wird der Buffer per buf[:0] auf Länge null zurückgesetzt, behält aber die zugrundeliegende Kapazität.

Go reuse.go
buf := make([]byte, 0, 4096)

for batch := range incoming {
	for _, item := range batch {
		buf = fmt.Appendln(buf, item.ID, item.Name, item.Status)
	}
	if _, err := out.Write(buf); err != nil {
		return err
	}
	buf = buf[:0]
}

Der Effekt ist messbar: Nach den ersten Iterationen wächst der Buffer nicht mehr, die append-Operationen innerhalb von Appendln finden in bereits allozierter Kapazität statt.

NDJSON-Writer ohne Sprintf-Umweg

Ein simpler Logger schreibt Items als Zeilen in einen Buffer und gibt den Buffer am Ende an einen io.Writer weiter. Appendln ersetzt hier sowohl den Sprintln-Zwischenschritt als auch das manuelle Anhängen eines \n.

Go praxis_ndjson.go
package main

import (
	"fmt"
	"os"
)

type Item struct {
	ID   int
	Name string
}

func main() {
	items := []Item{
		{101, "alpha"},
		{102, "beta"},
		{103, "gamma"},
	}

	buf := make([]byte, 0, 128)
	for _, item := range items {
		buf = fmt.Appendln(buf, item.ID, item.Name)
	}
	os.Stdout.Write(buf)
}
Output
101 alpha
102 beta
103 gamma

Die Zeilen sind durch das automatische Space getrennt, und der Newline pro Zeile kommt aus Appendln selbst. Kein fmt.Sprintf("%d %s\n", ...), kein append(buf, '\n') — die Funktion erledigt beides.

Zeilenbasiertes Protokoll im Buffer aufbauen

Text-Protokolle wie SMTP, IMAP oder Redis (RESP) bestehen aus Befehlen, die jeweils mit Newline terminiert sind. Wer eine Sequenz von Kommandos in einem Rutsch rausschicken will, baut sie zuerst im Buffer auf und schreibt dann eine Socket-Operation.

Go praxis_protocol.go
package main

import (
	"fmt"
	"os"
)

func main() {
	buf := make([]byte, 0, 256)

	buf = fmt.Appendln(buf, "EHLO", "client.example.com")
	buf = fmt.Appendln(buf, "MAIL", "FROM:<sender@example.com>")
	buf = fmt.Appendln(buf, "RCPT", "TO:<recipient@example.com>")
	buf = fmt.Appendln(buf, "DATA")

	os.Stdout.Write(buf)
}
Output
EHLO client.example.com
MAIL FROM:<sender@example.com>
RCPT TO:<recipient@example.com>
DATA

Jede Zeile ist ein Aufruf, die Spaces zwischen Verb und Argument fallen automatisch an, und das \n am Zeilenende ist garantiert. Der Vorteil gegenüber einzelnen Write-Calls auf den Socket: ein einziger Syscall am Ende, deutlich weniger Round-Trip-Overhead.

Interessantes

Go 1.19+ vorausgesetzt

fmt.Appendln existiert erst seit Go 1.19. In älteren Toolchains gibt es nur Sprintln mit String-Zwischenschritt.

Auto-Spaces und Newline wie Println

Zwischen allen Operanden steht ein Space, am Ende ein \n. Anders als bei Append gilt die Regel ausnahmslos.

Append-Semantik: Buffer wächst

Appendln ruft intern append auf dem übergebenen Buffer auf. Es entsteht kein neuer String, keine zweite Kopie.

%v auf Operanden, Stringer respektiert

Jeder Operand wird mit dem Default-Verb %v formatiert. Implementiert ein Typ fmt.Stringer, ruft Appendln String() auf.

Buffer-Reset mit buf[:0]

Nach dem Flush den Buffer per buf = buf[:0] zurücksetzen. Länge wird null, Kapazität bleibt.

Newline ist automatisch — nicht doppelt

Wer nach Appendln noch ein eigenes \n anhängt, bekommt Leerzeilen.

Für NDJSON, Logger, Line-Protokolle

Überall, wo zeilenbasierter Output in einen Buffer fließt, ist Appendln der idiomatische Hot-Path.

Spart Result-String gegenüber Sprintln

Sprintln alloziert einen String; Appendln schreibt direkt in den Buffer.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

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

Zur Übersicht