Formatted printing

You have been using fmt.Println since the first lesson. It is the "just print my values" function: every argument becomes its default text representation, separated by spaces, followed by a newline. That is fine for debugging but often too loose. When you want control over how numbers round, how strings are quoted, or where things line up, you reach for fmt.Printf.

Printf and the format string

Printf takes a format string as its first argument, and fills in the placeholders from the arguments that follow:

name := "Kamran"
age := 30

fmt.Printf("name=%s age=%d\n", name, age)
// name=Kamran age=30

Each placeholder starts with % and is called a verb. %s is replaced by the string argument. %d is replaced by the decimal-integer argument. Literal text around the verbs (name=, age=) appears verbatim in the output.

Notice the \n at the end of the format string. Unlike Println, Printf does not add a newline for you; if you want one, you write it yourself.

Gotcha

What if you pair the wrong verb with a value? Printf does not refuse to compile or crash at runtime. It prints a visible marker in the output so you can see the mismatch:

fmt.Printf("%d\n", "hello")   // %!d(string=hello)
fmt.Printf("%s\n", 42)         // %!s(int=42)

The %!VERB(TYPE=VALUE) pattern tells you which verb was misused, what type the argument actually had, and the value itself. Odd output like %!d(string=hello) is how you usually notice a mistyped verb. On your own machine, go vet (covered in the tooling chapter) also flags these mismatches at build time before you run the program.

The verbs worth knowing

These cover the vast majority of printing needs:

Verb Meaning Example value Example output
%s string "hello" hello
%d decimal integer 42 42
%f float (default) 3.14159 3.141590
%.2f float, 2 decimals 3.14159 3.14
%t boolean true true
%v default format any varies
%q quoted string "hello" "hello"
%T type of the value 42 int

%v is the one to remember when you are writing debug prints and do not care which verb is strictly correct. It picks something sensible for any type, including slices and structs that you will meet in later chapters.

%T is handy when you are unsure what type a value has; it prints the type name Go inferred.

fmt.Printf("%.2f\n", 3.14159)   // 3.14
fmt.Printf("%q\n", "hello")     // "hello"
fmt.Printf("%T\n", 42)          // int

Println, Printf, and Sprintf

The three print variants you will use most are:

  • fmt.Println(args...) prints values with spaces between them and a trailing newline. No format string.
  • fmt.Printf(format, args...) prints with a format string. You control everything, including whether there is a newline.
  • fmt.Sprintf(format, args...) is the same as Printf but returns the formatted string instead of printing it. Use it when you want to build a string and do something with it later.
name := "Kamran"
greeting := fmt.Sprintf("Hello, %s!", name)
fmt.Println(greeting)   // Hello, Kamran!

There are others (Fprintf to any writer, Errorf for wrapped errors) that come up in later chapters. For most code, these three are enough.

Reach for %v when in doubt

%v prints any value in a reasonable default form, so it is a safe choice when you do not care about precise formatting. It is especially handy for debugging slices and structs once the course gets there.

Task

Extend the starter with a second Printf call that prints the type of age:

  • Use the format string "type of age: %T\n".
  • Pass age as the argument.

The output should include the line type of age: int.

Expected output
name=Kamran age=30 pi=3.14
type of age: int