Go Module 2 – Basics: values, types and control flow
Formatted output with fmt: verbs and widths
Print Go values with Println, Printf and Sprintf, choose verbs such as %v, %+v, %#v, %T, %q and %x, and line up columns with width, precision and flags.
What you will learn
- Choose between Print, Println, Printf, Sprintf and Fprintf for a piece of output
- Choose the verb that shows a value as needed: %v, %+v, %#v, %T, %q, %x, %d or %f
- Format columns with width, precision, padding and the - flag
- Recognise the markers a wrong verb prints, and find the mistake with go vet
- Write a String method that %v uses, without the recursion trap
Before you start
What you’ll learn · Before you start
What you will learn
- Choose between Print, Println, Printf, Sprintf and Fprintf for a piece of output
- Choose the verb that shows a value as needed: %v, %+v, %#v, %T, %q, %x, %d or %f
- Format columns with width, precision, padding and the - flag
- Recognise the markers a wrong verb prints, and find the mistake with go vet
- Write a String method that %v uses, without the recursion trap
Before you start
On this page
A bill that prints Samosa 3 54.00 on one line and Filter coffee 1 40.50 on the next is hard to read; the same
numbers in columns are not. Go’s fmt package writes such columns with a format string: %-14s puts a string
left-aligned in 14 characters, %4d a number right-aligned in 4, and verbs like them decide how each value appears.
You will choose between Println, Printf and Sprintf, pick the verb that shows a value the way you need, and line up a table with widths and padding. The programs below ran on Go 1.27.1, and each shows what it printed.
Println, Printf and Sprintf differ in spaces, newlines and destination
The print functions come in families. Print and Println write their operands in their default format; Printf follows a format string: The first argument of Printf, Sprintf and their relatives: text in which each verb, such as %d or %s, marks where the next argument goes and how it is shown. The rest of the text is copied as it is.; the S versions return the text, and the F versions write to any destination that accepts bytes:
// print_funcs.go: where each print function puts its text, and its spaces.
package main
import (
"fmt"
"os"
"strings"
)
func main() {
fmt.Print("total", 3, 4, "\n") // a space only between two operands that are not strings
fmt.Println("total", 3, 4) // a space between all operands, then a newline
fmt.Printf("total %d+%d\n", 3, 4) // a format string; the newline is yours to write
line := fmt.Sprintf("%d items", 7) // returns the text instead of printing it
fmt.Println(len(line), "bytes:", line)
var b strings.Builder
fmt.Fprintf(&b, "order %d", 15) // writes to any io.Writer
fmt.Fprintln(os.Stdout, b.String())
fmt.Println("total %d", 3) // Println prints a verb as it is
} Output
total3 4 total 3 4 total 3+4 7 bytes: 7 items order 15 total %d 3
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run print_funcs.go
Print added a space only between 3 and 4, the two operands that are not strings, so total and 3 ran together.
Println put a space between every operand and ended the line. Printf printed exactly what its format said, its
newline included, and Sprintf returned a 7-byte string instead of printing it. Fprintf wrote into a
strings.Builder; the same call writes to os.Stderr, a file or a network connection.
A verb in Println
fmt.Println("total %d", 3) printed total %d 3: Println does not read verbs. Use Printf whenever the text holds a
verb, and remember the \n that Println would have added.
Checkpoint
Check: Print, Println, Printf, Sprintf and Fprintf
A verb chooses how each value is shown
In a format string, % and a letter form a verb: In a format string, a percent sign and a letter, such as %d, %s or %v, that takes the next argument and says how to show it. Flags, a width and a precision may stand between the two., and each verb takes the next argument.
%v works for every type, which is why Println uses it. The others choose a view:
// verbs.go: one value, several verbs.
package main
import "fmt"
type Ticket struct {
ID int
Route string
Fare float64
}
func main() {
t := Ticket{ID: 42, Route: "Pune-Mumbai", Fare: 355.5}
fmt.Printf("%v\n", t)
fmt.Printf("%+v\n", t)
fmt.Printf("%#v\n", t)
fmt.Printf("%T\n", t)
fmt.Printf("%d %s %q %t\n", 42, "seat", "seat 4", true)
fmt.Printf("%x %X %o %b\n", 255, 255, 8, 5)
fmt.Printf("%f %.2f %e %g\n", 355.5, 355.5, 355.5, 355.5)
fmt.Printf("%c %U %q\n", '₹', '₹', '₹')
} Output
{42 Pune-Mumbai 355.5}
{ID:42 Route:Pune-Mumbai Fare:355.5}
main.Ticket{ID:42, Route:"Pune-Mumbai", Fare:355.5}
main.Ticket
42 seat "seat 4" true
ff FF 10 101
355.500000 355.50 3.555000e+02 355.5
₹ U+20B9 '₹'
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run verbs.go
| Verb | Shows | In the run |
|---|---|---|
%v |
the default format | {42 Pune-Mumbai 355.5} |
%+v |
structs with field names | {ID:42 Route:… Fare:355.5} |
%#v |
Go syntax for the value | main.Ticket{ID:42, …} |
%T |
the type | main.Ticket |
%d %x %o %b |
an integer in base 10, 16, 8 or 2 | 42, ff, 10, 101 |
%f %e %g |
a float with a point, with an exponent, or %e for large exponents and %f otherwise |
355.500000, 3.555000e+02, 355.5 |
%s %q |
a string as it is, or quoted and escaped | seat, "seat 4" |
%c %U |
a rune as a character, or its code point | ₹, U+20B9 |
%+v is the one to reach for when you debug a struct, because it names every field, and %#v prints text you can
paste into a test. A type can also choose its own text for %v with a String method.
Checkpoint
Check: Choosing a verb
Width, precision and flags line up columns
Between % and the letter, a number sets the width, the least number of characters to fill, and a dot with a number
sets the precision. Flags change the padding: - puts the spaces on the right, 0 pads numbers with zeros, +
always shows a sign. Here the amounts stay in whole paise, hundredths of a rupee, and print through %d.%02d:
// invoice.go: columns lined up with widths, and rupees printed from paise.
package main
import "fmt"
type line struct {
item string
qty int
paise int64 // the price of one, in paise (₹1 = 100 paise)
}
func main() {
lines := []line{{"Masala chai", 2, 2500}, {"Samosa", 3, 1800}, {"Filter coffee", 1, 4050}}
fmt.Printf("%-14s %4s %10s\n", "Item", "Qty", "Amount ₹")
var total int64
for _, l := range lines {
amount := int64(l.qty) * l.paise
total += amount
fmt.Printf("%-14s %4d %7d.%02d\n", l.item, l.qty, amount/100, amount%100)
}
fmt.Printf("%-14s %4s %7d.%02d\n", "Total", "", total/100, total%100)
fmt.Printf("[%6s] [%-6s] [%06d] [%+d]\n", "₹50", "₹50", 42, 42)
} Output
Item Qty Amount ₹ Masala chai 2 50.00 Samosa 3 54.00 Filter coffee 1 40.50 Total 144.50 [ ₹50] [₹50 ] [000042] [+42]
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run invoice.go
Step through how Printf builds the Samosa line:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- The first verb. %-14s takes the first argument, the string Samosa. Its 6 characters are padded with 8 spaces to a width of 14, and the - flag puts the spaces on the right, so the text starts at the left edge.
- A space, then a number. The space after the verb is plain text and is copied. %4d takes the next argument, 3, and pads it on the left to 4 characters, because numbers are right-aligned by default.
- The rupees. Another copied space, then %7d takes 54, the whole rupees of 5400 paise. Five spaces of padding bring it to 7 characters, under the Amount header.
- The paise. The dot is copied as it is. %02d takes 0, the paise left over, and the 0 flag pads with zeros instead of spaces, so the line shows 54.00, never 54.0.
- The end of the line. \n is the last plain text of the format. Printf writes only what the format says, so without it the next line would start right after 54.00.
Every amount ends at the same column because %7d.%02d makes each amount here 10 characters wide, as wide as the
header’s %10s. Width counts characters, not bytes: "₹50" is 5 bytes, yet %6s padded it with 3 spaces, which
differs from C’s printf. For a float, %.2f keeps two digits after the point; for a string, precision cuts it.
Key idea
The width is the least room a value takes, and the precision how much of it is shown: digits for a float, characters for a string.
Checkpoint
Check: Width, precision and flags
Wrong verbs, missing arguments and go vet, reproduced Pro: opens with a Pro, Premium or Ultimate pass
fmt finds a mismatch between a verb and its argument only while the program runs, and then prints a marker into the output instead of stopping. This part reproduces six of these markers, then lets go vet find the same mistakes before anything runs.
In this part:
- What Printf prints when a verb and its argument disagree, six markers reproduced
- Text from data used as a format string breaks on its first percent sign
- Your own Printf-like functions print the same markers
- go vet reports these before the program runs, wrappers included
4 runnable examples (Go, Python) · about 5 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Go syntax, argument indexes and String methods, measured Pro: opens with a Pro, Premium or Ultimate pass
%v, %+v and %#v follow pointers into structs, slices and maps, an argument index reuses one value several times,
and a String method replaces what %v prints. This part measures each, with the pointer receiver that keeps a String
method from being used and the recursion vet warns about.
In this part:
- Pointers, slices, maps and nil values under %v, %+v and %#v
- Argument indexes and widths read from arguments
- A String method changes what %v prints, if the value has the method
- String methods inside slices, maps and struct fields
1 diagram · 6 runnable examples (Go, Python) · about 6 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Practise
Practice on the print functions, the verbs and the widths opens with whichever of the three your answers so far leave weakest, and every choice you pick is explained on the spot:
Practice that adapts to you
Practise this lesson
8 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): What is the difference between %v, %+v and %#v?
All three print a value. %v uses the default format, such as {42 Pune-Mumbai 355.5} for a struct. %+v adds the
field names of structs, and %#v prints the value in Go syntax, with its type, which you could paste into code.
Three longer questions of the module bank build on this lesson:
-
Fresher to mid
A service writes request details with log.Printf(msg), where msg is built from what the client sent. Some log lines contain %!d(MISSING) or %!s(MISSING). What causes it, how serious is it, and how do you fix it and keep it fixed?
-
Fresher to mid
Print a table of item names, quantities and amounts kept in paise so that the columns line up, for names of any length. Which verbs and widths do you use, and what goes wrong when a name is longer than its column?
-
Fresher to mid
A type Paise int64 has func (p *Paise) String() string, which writes 35550 paise as rupees and paise. fmt.Println(&p) uses it, but fmt.Println(p) and a printed []Paise show bare numbers. Explain why, fix it, and say how you keep such a method from breaking.
Key takeaways
- Print adds spaces only between non-strings, Println always adds spaces and a newline, Printf follows its format string, Sprintf returns the text and Fprintf writes it to any writer.
- A verb takes the next argument:
%vshows any value,%+vadds field names,%#vgives Go syntax and%Tthe type. - A width is the least room a value takes,
-aligns it left,0pads numbers with zeros, and precision sets the digits of a float or cuts a string.
Terms in this lesson
- format string
- The first argument of Printf, Sprintf and their relatives: text in which each verb, such as %d or %s, marks where the next argument goes and how it is shown. The rest of the text is copied as it is.
- verb
- In a format string, a percent sign and a letter, such as %d, %s or %v, that takes the next argument and says how to show it. Flags, a width and a precision may stand between the two.
References
- Package fmt (The Go Authors)
- Package fmt: Stringer (The Go Authors)
- Command vet: printf (The Go Authors)
- Go 1.24 Release Notes: Vet (The Go Authors)
- Command go: Test packages (The Go Authors)
- Package strings: Builder (The Go Authors)
- Effective Go: Printing (The Go Authors)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress