Skip to Content
Course content

108: Verb Reference: %v, %+v, %#v, %T

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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 ApiResponse with fields: StatusCode (int), Payload (map[string]string), and RequestID (string).
  • Initialize an instance of ApiResponse with some sample data.
  • Use fmt.Printf to print the response in four different ways:
    1. Using %v to see the raw values.
    2. Using %+v to identify which value belongs to which field.
    3. Using %#v to see the Go-syntax representation (including the package name).
    4. Using %T to confirm the type of the variable.

Compare the outputs in your terminal to see how each verb changes the level of detail provided.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.