Skip to main content

Command Palette

Search for a command to run...

Understanding the fmt Package in GO

Exploring Key Features of the fmt Package in Golang

Updated
9 min read
Understanding the fmt Package in GO

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.Errorf

  • Implementing the fmt.Stringer interface

  • Width, 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

VerbMeaning
%vDefault format
%+vShows struct field names
%#vGo-syntax representation
%TPrints 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

VerbMeaning
%sString
%qQuoted string
%xHex format

Number verbs

VerbMeaning
%dInteger
%fFloat
%bBinary
%tBoolean

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.Sprintf

  • You can use formatting verbs (%d,%s,%v etc)

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

  1. 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.

  2. What is the difference between Print, Println, and Printf?

FunctionBehavior
PrintPrints values without newline or formatting
PrintlnPrints values separated by spaces + automatically adds newline
PrintfPrints formatted output using verbs like %s, %d, %v
  1. 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

  1. What is fmt.Stringer and why is it important

    fmt.Stringer is 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.

  2. What is fmt.GoStringer?

    fmt.GoStringer is an interface for generating Go-syntax-friendly output.

    It uses the GoString() string interface.

    This result is used when formatting with %#v.

  3. What’s the difference between bus := bytes.Buffer{} and using fmt functions?

ApproachBest Use Case
bytes.BufferHigh-Performace string building, especially in loops
fmt.Sprint / fmt.SprintfReadable formatting, convenient, but more allocations
fmt.Append* functionsFast, allocation-free appending to byte slices.
  1. 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.

  1. 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.

  1. 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)
    
  1. Can fmt print coloured output?

    No. fmt does not support ANSI colors. Use third-party libraries such as

  2. 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.

Go Deep with Golang

Part 3 of 11

Go beyond the basics! This series explores how Go works under the hood — from memory management to goroutines, channels, and design principles that make Go ideal for modern backend development.

Up next

Mastering Error Handling in Go - The Art of Simplicity and Clarity

“Don’t panic, just handle it.“ — This phrase beautifully sums up how Go treats errors.