Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
108: Verb Reference: %v, %+v, %#v, %T
A few years ago, I was helping a junior dev debug a production issue where a configuration struct was being populated incorrectly from a JSON file. He kept adding fmt.Printf("%v", config) to his logs, but all he was seeing was a string of values like {8080 localhost 30s true}. He spent nearly an hour trying to map those values back to the struct fields in his head, wondering if the 30s was the timeout or the keep-alive interval. I told him to just add a plus sign to the verb, and suddenly the logs showed {Port:8080 Host:localhost Timeout:30s Enabled:true}. The bug—a swapped port and timeout value—became obvious in seconds.
The Default and the Detailed
In Go, %v is your "value" catch-all. It's the most common verb because it tells Go, "Just print this in a way that makes sense." For a string, it's a string; for an int, it's an int. But when you hit a struct, %v only prints the values, omitting the field names. It's clean, but as my colleague found out, it's often too clean for debugging.
That's where %+v comes in. The plus sign is a modifier that tells Go to include the field names for structs. If you're logging a data structure to see why a specific field is empty or wrong, %+v should be your default choice. It transforms a cryptic list of values into a labeled map of your data.
type User struct {
ID int
Name string
Email string
}
u := User{ID: 1, Name: "Alice", Email: "alice@example.com"}
fmt.Printf("%v\n", u) // Output: {1 Alice alice@example.com}
fmt.Printf("%+v\n", u) // Output: {ID:1 Name:Alice Email:alice@example.com}
Going Deeper with Go-Syntax
Sometimes, even field names aren't enough. If you're dealing with custom types that wrap primitives—like type Duration int—or if you need to know exactly how to initialize that object in code, %#v is the tool you want. I call this the "compiler's view."
The # flag tells Go to print the value as it would appear in Go source code. It includes the package name, the type name, and the field names. It's incredibly useful when you're unsure if a variable is a pointer to a struct or the struct itself, because the output will explicitly show the & symbol if it's a pointer.
fmt.Printf("%#v\n", u)
// Output: main.User{ID:1, Name:"Alice", Email:"alice@example.com"}
Identifying the Type
Finally, there's %T. While the other verbs focus on the value, %T focuses exclusively on the type. You won't see the data inside the variable; you'll only see the name of the type it satisfies.
I find %T most useful when I'm working with interfaces. If a function returns an interface{} and you're not sure which concrete struct was actually returned, %T will strip away the interface wrapper and tell you exactly what you're holding. It's a quick way to verify that your type assertions aren't about to panic.
var i interface{} = u
fmt.Printf("%T\n", i) // Output: main.User
📋 Practical Task
Exercise: Debugging the API Response Wrapper
You are building a tool that processes API responses. The API returns data in a generic wrapper, and you need to verify exactly what is happening inside the response object before it gets passed to the database layer.
Create a program with the following requirements:
- Define a struct called
ApiResponsewith fields:StatusCode (int),Payload (map[string]string), andRequestID (string). - Initialize an instance of
ApiResponsewith some sample data. - Use
fmt.Printfto print the response in four different ways:- Using
%vto see the raw values. - Using
%+vto identify which value belongs to which field. - Using
%#vto see the Go-syntax representation (including the package name). - Using
%Tto confirm the type of the variable.
- Using
Compare the outputs in your terminal to see how each verb changes the level of detail provided.
There are no comments for now.