Formatting & interpolation
"Next train: {$dest}, platform {$platform}". You've typed thousands of these. Go doesn't have them. No Go string interpolates anything, so "{$dest}" is just six characters of text.
What Go has instead is the fmt package and a format string, which is PHP's sprintf with a few extra tricks. Your %d and %05.2f muscle memory comes along for the ride.
The print family
fmt names its functions in a pattern, so once you know the pattern you know them all:
| What | Prints | Returns a string |
|---|---|---|
| Values, with spaces | Println |
Sprintln |
| Values, as they are | Print |
Sprint |
| A format string | Printf |
Sprintf |
The S versions are the ones you'll use most, because they hand back a string instead of printing it: Sprintf is sprintf, and Printf is printf. (There are F versions too, which write to a file or network connection. Lesson 7.4 covers those.)
msg := fmt.Sprintf("%s, platform %d", dest, platform) fmt.Printf("Next train: %s\n", msg)
Printf doesn't add a newline, so end the format with \n. Println adds one, and puts spaces between its arguments.
Print and Sprint are the odd ones out. They add a space between two arguments only when neither of them is a string, so fmt.Sprint(1, 2, "a", 3, 4) is "1 2a3 4". Nobody remembers this rule. Use Sprintf when the layout matters.
The verbs
| Verb | Prints | Example |
|---|---|---|
%v |
any value, in its default format | {Epping 3} |
%+v |
structs with field names | {Dest:Epping Platform:3} |
%#v |
the value as Go code | main.Departure{…} |
%T |
the type | main.Departure |
%d |
an integer | 42 |
%s |
a string | Epping |
%q |
a string in quotes, escaped | "Köln" |
%f, %.2f |
a float | 3.14 |
%g |
a float, no trailing zeros | 12.5 |
%t |
a bool | true |
%x |
hex | ff |
%% |
a literal % |
% |
%v is the one to remember. It prints anything, from ints to structs to the slices and maps in Unit 2, and %+v is the nearest thing Go has to print_r. %q is great when debugging strings, because it shows the empty string as "" and makes stray spaces and \ns visible.
Widths and flags work as they do in PHP. A number between the % and the verb sets the width, a - left-aligns, and a 0 pads with zeros:
fmt.Sprintf("%5d|", 42) // " 42|" fmt.Sprintf("%-6s|", "Bank") // "Bank |" fmt.Sprintf("%02d", 7) // "07"
Widths count characters, not bytes, so £ and ö take up one place each. After 1.5, that's a relief.
When you get a verb wrong
PHP's sprintf throws an ArgumentCountError if you pass too few arguments. Go's fmt never panics. It writes its complaint into the output instead:
| Expected | Platform 3 |
|---|---|
| Got | Platform %!d(MISSING) |
| go vet | I did try to tell you. |
A wrong type gives you %!d(string=Epping), and a missing argument gives %!d(MISSING). Both turn up in your output rather than as an error, which means they reach users. Luckily go vet checks every format string against its arguments, and Submit runs vet, so you'll see fmt.Sprintf format %d has arg "Epping" of wrong type string well before anyone else does.
Mind the gap
PHP still wins on number_format. Go's fmt has no thousands separator and no locale-aware formatting. For 1,234,567.89 you either write a small helper or use the golang.org/x/text/message module. Money is usually stored in pence, as an int (floats and money don't mix), and formatted with %d.%02d, which is exactly what the exercise does.
For gluing a few strings together, + is still fine, and strconv.Itoa(n) turns an int into its digits. Reach for Sprintf once there's any formatting involved.
Recap
- No interpolation.
Sprintfbuilds a string,Printfprints one, and neither adds a newline. %vprints anything,%+vadds field names,%qquotes strings,%Tshows the type.- Widths and flags work like PHP's:
%5d,%-20s,%02d. They count characters, not bytes. - A wrong verb gives
%!d(…)in the output rather than an error.go vetcatches it.