fmt.Scanln liest wie fmt.Scan aus der Standardeingabe und weist die gefundenen Tokens den per Pointer übergebenen Zielen zu — mit einem entscheidenden Unterschied: Die Funktion stoppt am ersten Newline-Zeichen. Endet die aktuelle Zeile, bevor alle erwarteten Argumente befüllt sind, liefert Scanln einen Fehler. Damit eignet sich die Funktion für interaktive Prompts, in denen jede Frage genau eine Zeile Antwort erwartet — und gerade nicht für mehrzeilige oder gepufferte Eingaben.
Signatur und Rückgabewerte
fmt.Scanln hat dieselbe Signatur wie fmt.Scan und unterscheidet sich nur im Trennverhalten. Der erste Rückgabewert n ist die Anzahl der erfolgreich gelesenen und zugewiesenen Werte, der zweite Rückgabewert beschreibt den Fehler — oder ist nil, wenn alle Pointer befüllt wurden und die Zeile sauber endete.
func Scanln(a ...any) (n int, err error)Wichtig: n zählt nur die tatsächlich befüllten Argumente. Bricht Scanln mittendrin ab — etwa weil das Newline vor dem dritten Argument kommt — bleibt der Rest der Pointer auf seinen Nullwerten stehen, und n spiegelt exakt diesen Fortschritt wider.
Newline als hartes Stoppsignal
Wo fmt.Scan Whitespace inklusive Zeilenumbruch als gleichwertigen Token-Trenner behandelt, zieht Scanln an dieser Stelle eine harte Grenze. Sobald das erste \n im Eingabestrom auftaucht, hört die Funktion auf zu lesen. Wenn dann noch Pointer offen sind, ist das ein Fehler — nicht etwa ein Grund, in der nächsten Zeile weiterzusuchen.
package main
import "fmt"
func main() {
var a, b, c int
n, err := fmt.Scanln(&a, &b, &c)
fmt.Println("n =", n)
fmt.Println("err =", err)
fmt.Println("a,b,c =", a, b, c)
}Eingabe: 10 20⏎
n = 2
err = unexpected newline
a,b,c = 10 20 0Das Ergebnis zeigt das Verhalten exemplarisch: Zwei Werte wurden gelesen, der dritte bleibt auf 0, und der Fehler trägt die typische Meldung unexpected newline. Wer dieses Verhalten ignoriert und ungeprüft mit a, b, c weiterarbeitet, baut sich stille Datenfehler ein.
Pointer-Pflicht — kein Spielraum
Genau wie bei fmt.Scan müssen alle Argumente Pointer sein. Übergibt man stattdessen den Wert selbst, kann Scanln ihn nicht beschreiben und meldet zur Laufzeit einen Fehler. Der Compiler hält das nicht auf, da ...any jede Form akzeptiert — der Fehler tritt erst beim Aufruf zutage.
package main
import "fmt"
func main() {
var name string
// Falsch: name statt &name
n, err := fmt.Scanln(name)
fmt.Println(n, err)
}0 Scanln: type not a pointer: stringDie Regel ist hart und kennt keine Ausnahmen: Wer Scanln benutzt, schreibt vor jedes Argument ein &. Strukturen, Slices oder Maps lassen sich nicht direkt füllen — dafür gibt es entweder pro Feld einen eigenen Pointer oder, für komplexere Eingaben, einen bufio.Scanner mit anschließendem Parsing.
Typische Verwendung — der einzeilige Prompt
Der einzige Bereich, in dem Scanln wirklich glänzt, sind interaktive Prompts mit genau einer Eingabezeile pro Frage. Das Newline ist hier kein Hindernis, sondern das natürliche Signal dafür, dass der Nutzer mit der Antwort fertig ist. Im Skript- oder Pipe-Kontext zeigt sich dagegen schnell, dass Scanln zu strikt ist — dort ist bufio.Scanner deutlich robuster.
package main
import "fmt"
func main() {
var name string
fmt.Print("Wie heißt du? ")
fmt.Scanln(&name)
fmt.Printf("Hallo, %s!\n", name)
}Wie heißt du? Lukas
Hallo, Lukas!Solange der Nutzer einen Wert pro Zeile eingibt, verhält sich Scanln genau wie erwartet. Sobald aber Leerzeichen im Namen vorkommen (Anna Maria), kippt das Verhalten — siehe Praxis-Beispiele unten.
Vergleich: Scan vs. Scanln
Der direkte Vergleich zeigt, dass beide Funktionen denselben Lesemechanismus teilen und sich nur am Zeilenende unterscheiden. Genau dieser Unterschied entscheidet aber, welche Funktion für welchen Anwendungsfall passt.
| Aspekt | fmt.Scan | fmt.Scanln |
|---|---|---|
| Token-Trenner | Whitespace inkl. \n | Whitespace |
| Stopp | Bei n Tokens oder EOF | Bei n Tokens oder \n |
| Newline mittendrin | Weiterlesen in nächster Zeile | Fehler unexpected newline |
| Zu viele Tokens pro Zeile | Akzeptiert (verbleiben im Puffer) | Fehler expected newline |
| Typischer Einsatz | Mehrzeilige Eingaben, Pipes | Interaktive Einzeilen-Prompts |
Die Faustregel: Scan ist toleranter, Scanln ist strikter. Wer jede Form von Zeilenstruktur erzwingen will, nutzt Scanln — wer flexibel über mehrere Zeilen lesen möchte, nimmt Scan oder bewusster bufio.Scanner.
Edge-Case: zu viele Tokens auf derselben Zeile
Scanln ist nicht nur dann unzufrieden, wenn zu wenige Tokens kommen — auch ein „zu viel" wird als Fehler gemeldet. Stehen nach dem letzten erwarteten Argument noch weitere Tokens auf derselben Zeile, signalisiert die Funktion, dass das Format nicht passt.
package main
import "fmt"
func main() {
var x, y int
n, err := fmt.Scanln(&x, &y)
fmt.Println("n =", n)
fmt.Println("err =", err)
fmt.Println("x,y =", x, y)
}Eingabe: 10 20 30⏎
n = 2
err = expected newline
x,y = 10 20Interessant: Die beiden ersten Werte sind korrekt zugewiesen — n ist 2, x und y enthalten die erwarteten Zahlen. Trotzdem ist err gesetzt, weil das zusätzliche 30 vor dem Newline steht. Wer den Fehler ignoriert, verliert die Information über die überschüssige Eingabe und arbeitet möglicherweise mit verstümmelten Daten weiter.
Praxis 1 — Interaktive Yes/No-Bestätigung
Eine klassische CLI-Bestätigung ist genau das Szenario, in dem Scanln seine Stärken ausspielt: ein einzelnes Wort pro Zeile, gefolgt von Enter. Die strikte Newline-Semantik schützt sogar vor versehentlichen Doppeleingaben, weil ein zweites Token sofort einen Fehler erzeugt.
package main
import (
"fmt"
"strings"
)
func confirm(prompt string) bool {
var answer string
fmt.Printf("%s [j/N]: ", prompt)
_, err := fmt.Scanln(&answer)
if err != nil {
// Leere Eingabe oder Fehler => Default "nein"
return false
}
answer = strings.ToLower(strings.TrimSpace(answer))
return answer == "j" || answer == "ja" || answer == "y" || answer == "yes"
}
func main() {
if confirm("Datei wirklich löschen?") {
fmt.Println("=> gelöscht")
} else {
fmt.Println("=> abgebrochen")
}
}Datei wirklich löschen? [j/N]: ja
=> gelöschtBemerkenswert ist hier der Umgang mit dem Fehlerpfad: Drückt der Nutzer nur Enter, liefert Scanln unexpected newline — die Funktion confirm interpretiert das als „nein" und folgt damit dem üblichen CLI-Idiom, bei leerem Input den sicheren Default zu wählen.
Praxis 2 — Mehrere Werte auf einer Zeile
Auch ein strukturiertes Format wie „Vorname Nachname Alter" auf einer einzigen Zeile lässt sich mit Scanln sauber einlesen. Die Funktion erzwingt dabei sogar, dass alle drei Felder auf derselben Zeile stehen — verteilt der Nutzer sie auf mehrere Zeilen, gibt es einen Fehler.
package main
import "fmt"
func main() {
var vorname, nachname string
var alter int
fmt.Print("Vorname Nachname Alter: ")
n, err := fmt.Scanln(&vorname, &nachname, &alter)
if err != nil {
fmt.Printf("Eingabe ungültig (gelesen: %d): %v\n", n, err)
return
}
fmt.Printf("=> %s %s, %d Jahre\n", vorname, nachname, alter)
}Vorname Nachname Alter: Anna Schmidt 34
=> Anna Schmidt, 34 JahreDer Haken: Sobald jemand Anna Maria Schmidt 34 eingibt, scheitert das Schema. Scanln weist Anna an vorname und Maria an nachname zu, versucht dann Schmidt in den int alter zu parsen und liefert einen Konvertierungsfehler. Für Eingaben mit variabler Tokenzahl ist deshalb bufio.Scanner plus eigenes Parsing die robustere Wahl — Scanln lebt von der starren Spaltenstruktur.
Stoppt am ersten Newline
Anders als fmt.Scan liest Scanln niemals über einen Zeilenumbruch hinaus. Das Newline ist das harte Ende der Eingabe.
Pointer-Pflicht
Alle Argumente müssen Pointer sein. Wer den Wert direkt übergibt, bekommt zur Laufzeit type not a pointer.
Strenger als Scan
Überschüssige Tokens auf derselben Zeile erzeugen expected newline — auch wenn alle Pointer befüllt sind.
Ideal für interaktive Single-Line-Prompts
Yes/No-Fragen, einzelne Namen, kurze Bestätigungen: hier passt Scanln perfekt zum Enter-Verhalten der Nutzer.
Zu wenige Tokens = Fehler
Endet die Zeile vor dem letzten Pointer, liefert Scanln unexpected newline und lässt die restlichen Pointer auf ihren Nullwerten.
Production: bufio.Scanner bevorzugen
Für CLI-Tools, Pipes oder beliebige Texteingaben ist bufio.Scanner deutlich robuster und besser zu testen als Scanln.
n zeigt erfolgreich gelesene Werte
Der Rückgabewert n ist auch bei Fehlern aussagekräftig — er zeigt exakt, wie weit Scanln gekommen ist, bevor es aufgegeben hat.
Newline als „Eingabe fertig“
Konzeptionell modelliert Scanln das, was Nutzer mit Enter signalisieren: „Diese Zeile ist meine vollständige Antwort."