Understanding the fmt Package in GO
Exploring Key Features of the fmt Package in Golang

The fmt package is one of the most fundamental and frequently used packages in Go. Whether you’re printing values, formatting strings, scanning input, or even wrapping errors — fmt is everywhere.
Because of its importance, the fmt package is also a common topic in Go interviews, especially around formatting verbs, stringers, and error wrapping.
In this article, we’ll explore:
What the fmt package does
Printing functions
Formatting verbs
Creating formatted strings
Scanning input
Working with writers (
Fprint,Fprintln,Fprintf)Error wrapping using
fmt.ErrorfImplementing the
fmt.StringerinterfaceWidth, padding & precision
Struct, slice & map formatting
A complete example
This article is ideal for beginners, intermediate developers, and anyone preparing for Go interviews.
What Is the fmt Package?
The fmt package provides a set of functions for:
Formatted output (printing)
Formatted input (scanning)
Working with strings
Working with I/O streams
Error creation & wrapping
Import it using:
import "fmt"
Let’s explore everything in detail.
Printing in Go using fmt
fmt.Println() — Print with newline
fmt.Println("Hello", "World")
Output
Hello World
fmt.Print() — Print without newline
fmt.Print("Hello ")
fmt.Print("World")
fmt.Printf() — Formatted output
Uses formatting verbs like %s, %d, %v, etc.
name := "Rohit"
age := 24
fmt.Printf("My name is %s and I am %d year old\n", name,age)
Important fmt Format Verbs
General Verbs
| Verb | Meaning |
| %v | Default format |
| %+v | Shows struct field names |
| %#v | Go-syntax representation |
| %T | Prints the type |
Example:
type User struct {
Name string
Age int
}
u := User{"Rohit",24}
fmt.Printf("%v\n",u) // Default format
fmt.Printf("%+v/n",u) // Show struct field names
fmt.Printf("%#v\n",u) // Go-syntax representation
fmt.Printf("%T\n",u) // Prints the type
String Verbs
| Verb | Meaning |
| %s | String |
| %q | Quoted string |
| %x | Hex format |
Number verbs
| Verb | Meaning |
| %d | Integer |
| %f | Float |
| %b | Binary |
| %t | Boolean |
Building Strings Instead of Printing
Use the Sprintf family when you want to create a string instead of printing it.
fmt.Sprintf
msg := fmt.Sprintf("Hi %s, score: %d","Rohit",90)
fmt.Println(msg)
Writing to Files, Buffers & HTTP Responses — Fprint, Fprintf, Fprintln
These functions write to any io.Writer, not just stdout.
Example: Writing to a file
file,_ := os.Create("outout.txt")
fmt.Fprintf(file,"Hello %s!", "Rohit")
This is widely used in
Web servers (http.ResponseWriter)
Logging systems
Buffers
Files
Reading Input — Scan ScanIn, Scanf
fmt.Scan — space-separated input
var name string
var age int
fmt.Scan(&name,&age)
fmt.ScanIn (stops at newline)
var city string
fmt.Scanln(&city)
fmt.Scanf (formatted input)
var name string
var age int
fmt.Scanf("%s %d",&name,&age)
Error Handling with fmt.Errorf
creating errors
err := fmt.Errorf("invalid input")
Wrapping errors using %w
err := fmt.Errorf("db error: %w", originalErr)
Then unwrap
errors.Is(err,originalErr)
I have explained this error handling in detail in this article → https://blog.rohitlokhande.in/error-handling-in-go
Implementing fmt.Stringer — Customize Struct Printing
fmt.Stringer is an interface
type Stringer interface {
String() string
}
Implement it on your struct:
type User struct {
Name string
Age int
}
func (u User) String() string {
return fmt.Sprintf("User(Name=%s, Age=%d)",u.Name, u.Age)
}
func main() {
u := User{"Rohit", 24}
fmt.Println(u)
}
User(Name=Rohit, Age=24)
Formatting Width, Alignment,Padding & Precision
Width
fmt.Printf("|%10s|\n", "Go") // right-aligned
fmt.Printf("|%-10s|\n","Go") // left-aligned
Zero-padding
fmt.Printf("%05d\n",42) // 00042
Floating precision
fmt.Printf("%.2f\n",3.14159) // 3.14
Formatting Structs, Maps, Slices
user := map[string]string{"name":"Rohit"}
fmt.Printf("Map:%+v\n",user)
Output
Map:map[name:Rohit]
Using fmt With Buffers (io.Writer)
What is Buffer?
A buffer is a temporary storage area in memory used to hold data before it is processed, written, or read.
Think of a buffer like a bucket.
You collect some data in the bucket (buffer).
Once enough data is collected or when ready, you pour it out (write it somewhere).
This avoids sending small pieces one by one.
Buffers help make input/output operations faster and more efficient.
Why Do We Need Buffers?
Because reading or writing small amounts of data very frequently is slow.
Example:
Writing to a file every time you add a character will slow down the process.
Instead, write to a buffer in memory and then flush it to a file in larger chunks. This speeds up the process.
import (
"fmt";
"bytes"
)
var b bytes.Buffer
fmt.Fprintln(&b,"Hello Buffer")
fmt.Println("Buffer content:",b.String())
We have covered the basic concepts of the fmt package, which are widely used. There are also some less common but useful concepts that we will explore further.
fmt.GoStringer Interface
fmt.GoStringer is an interface similar to fmt.Stringer, but it is used when formatting a type using:
%#v(Go-syntax representation)fmt.GoString()
Interface definition:
type GoStringer interface {
GoString() string
}
If your type implements GoString() string, then %#v will call your custom method.
Example:
package main
import (
"fmt"
)
type User struct {
Name string
Age int
}
// Implementing GoStringer
func (u User) GoString() string {
return fmt.Sprintf("User{Name: %q, Age: %d}", u.Name, u.Age)
}
func main() {
u := User{"Rohit", 24}
fmt.Println("Normal print:")
fmt.Println(u) // does NOT use GoString
fmt.Println("\nGo-syntax print using %#v:")
fmt.Printf("%#v\n", u) // uses GoString()
}
Output
Normal print:
{Rohit 24}
Go-syntax print using %#v:
User{Name: "Rohit", Age: 24}
What happening here?
fmt.Println(u) → Uses the default structure formatting because we did not implement Stringer.
fmt.Printf("%#v", u) → Now Go sees that User implements the GoString() string method, so it prints the custom Go-like struct representation.
Why do we need GoString?
fmt.Stringer is for human-readable output.
fmt.GoStringer is for debugging, logging, and developer-friendly Go syntax output.
fmt.Appendf, Append, Appendln
These functions were added to Go to make it easier to append formatted data to a byte slice without using a bytes.Buffer or strings.Builder.
That means instead of
b := []byte{}
b = append(b,fmt.Sprintf("Hello %s",name))
You can directly do:
b = fmt.Appendf(b, "Hello %s", name)
fmt.Append
Similar to
fmt.Sprint.Converts arguments to string form.
Appends to a byte slice,
No formatting verbs required.
Example
package main
import (
"fmt"
)
func main(){
b := []byte("Start: ")
b = fmt.Append(b,"Hello"," ",124)
fmt.Println(string(b))
}
Output
Start: Hello 123
fmt.Appendln
Similar to
fmt.Sprintln.Appends arguments with spaces and a newline
Example
package main
import "fmt"
func main() {
b := []byte{}
b = fmt.Appendln(b,"Hello","world")
b = fmt.Appendln(b,10, 20, 30)
fmt.Println(string(b))
}
Output
Hello World
10 20 30
fmt.Appendf
Similar to
fmt.SprintfYou can use formatting verbs (
%d,%s,%vetc)
Example
package main
import "fmt"
func main() {
b := []byte("User: ")
b = fmt.Appendf(b, "%s (%d years old)", "Rohit", 25)
fmt.Println(string(b))
}
Output
User: Rohit (25 years old)
When to Use Append Functions?
These functions are useful when:
You want zero allocation or minimal memory overhead.
You want a lightweight alternative to
bytes.Buffer.You want to efficiently build output in a byte slice.
High-performance systems (servers, loggers, serializers).
FAQ: Frequently Asked Questions About Go’s fmt Package
What is the fmt package used for in Go?
The fmt package is used for formatted I/O operations such as printing to the console, formatting strings, scanning input, and writing to buffers or byte slices. It provides functions like
Println,Printf,Sprintf,Scan, and more.What is the difference between
Print,Println, andPrintf?
| Function | Behavior |
| Prints values without newline or formatting | |
| Println | Prints values separated by spaces + automatically adds newline |
| Printf | Prints formatted output using verbs like %s, %d, %v |
What are formatting verbs in fmt?
Formatting verbs are special placeholders (e.g., %d, %s, %v) used to format data.
Example:
%s → string
%d → integer
%f → float
%v → value in default format
What is
fmt.Stringerand why is it importantfmt.Stringeris an interface with the method →String() string.
If a type implements this method, fmt uses it to decide how that type should be printed.
This is very helpful in debugging and logging.What is
fmt.GoStringer?fmt.GoStringeris an interface for generating Go-syntax-friendly output.It uses the
GoString() stringinterface.This result is used when formatting with
%#v.What’s the difference between bus := bytes.Buffer{} and using fmt functions?
| Approach | Best Use Case |
| bytes.Buffer | High-Performace string building, especially in loops |
| fmt.Sprint / fmt.Sprintf | Readable formatting, convenient, but more allocations |
| fmt.Append* functions | Fast, allocation-free appending to byte slices. |
What are fmt.Append, Appendf, and Appendln ?
These functions append formatted data directly to a byte slice.
Append → like Sprint
Appendln → like Sprintln
Appendf → like Sprintf
They are faster and avoid extra allocations, making them useful in logging or serialization.
Is fmt package slow compared other manual string concatenation?
fmt is extremely optimised, but
It is slower than direct concatenation (+)
It is slower than strings.Builder
It is slower than bytes.Buffer
However, it providers:
cleaner code
safe formatting
powerful formatting verbs
For performance-critical sections, avoid using fmt in tight loops.
What happens if formatting verbs don’t match the data type?
fmt does not crash, unlike other languages. Instead, it prints a fallback format.
Example:
fmt.Printf("%d","hello")%!d(string=hello)
Can fmt print coloured output?
No. fmt does not support ANSI colors. Use third-party libraries such as
Why should I implement String() for custom structs?
Because:
Your logs become cleaner
Your debugging becomes easier
fmt.Printf(“%v“) output readable data
Conclusion
The fmt package is one of the first tools every Go developer learns—and for good reason. It simplifies printing, formatting, scanning, and working with different data types. From basic functions like Println to advanced features like Stringer, formatting verbs, and the new Append functions, fmt makes everyday coding much easier and more readable.
If you understand how fmt works, you can write cleaner logs, better debug your programs, and format data more effectively. It’s a small package, but it plays a big role in every Go project.



