ZetCode

Golang fmt.Append function

last modified May 8, 2025

This tutorial explains how to use the fmt.Append function in Go. We'll cover string building basics with practical examples of efficient string manipulation.

The fmt.Append function is used to efficiently build strings by appending formatted data to byte slices. It provides a performance advantage over string concatenation for complex string building operations.

In Go, fmt.Append is part of the fmt package and was introduced in Go 1.19. It works similarly to fmt.Printf but writes to a byte slice instead of an io.Writer.

Basic fmt.Append example

The simplest use of fmt.Append appends formatted strings to a byte slice. This example demonstrates basic string appending.
Note: The function returns a new slice, it doesn't modify the original.

basic_append.go
package main

import (
    "fmt"
)

func main() {
    buf := []byte("Hello, ")
    buf = fmt.Append(buf, "World!")
    
    fmt.Println(string(buf)) // Convert back to string for printing
    
    // Append multiple values
    nums := []byte("Numbers: ")
    nums = fmt.Append(nums, 1, 2, 3)
    fmt.Println(string(nums))
}

The function appends formatted values to the byte slice. The original slice may be empty or contain existing data.

Appending with formatting verbs

fmt.Append supports the same formatting verbs as fmt.Printf. This example shows formatted number appending.

formatting_verbs.go
package main

import (
    "fmt"
)

func main() {
    buf := []byte("Formatted values: ")
    
    // Append with different format verbs
    buf = fmt.Appendf(buf, "Int: %d, Float: %.2f, Bool: %t", 42, 3.14159, true)
    
    fmt.Println(string(buf))
    
    // Hexadecimal example
    hexBuf := []byte("Hex: ")
    hexBuf = fmt.Appendf(hexBuf, "%x", 255)
    fmt.Println(string(hexBuf))
}

The Appendf variant allows precise control over formatting. Format verbs work exactly like in other fmt package functions.

Appending slices and arrays

fmt.Append can append entire slices and arrays. This example shows how to efficiently build strings from collections.

append_slices.go
package main

import (
    "fmt"
)

func main() {
    names := []string{"Alice", "Bob", "Charlie"}
    buf := []byte("Names: ")
    
    // Append each name with separator
    for _, name := range names {
        buf = fmt.Append(buf, name, ", ")
    }
    
    // Remove trailing comma and space
    if len(names) > 0 {
        buf = buf[:len(buf)-2]
    }
    
    fmt.Println(string(buf))
    
    // Alternative with fmt.Appendln
    buf = []byte("Names:\n")
    for _, name := range names {
        buf = fmt.Appendln(buf, name)
    }
    fmt.Println(string(buf))
}

The example shows two approaches: comma-separated values and line-separated. The function handles each element's string conversion automatically.

Performance comparison with strings.Builder

fmt.Append can be more efficient than strings.Builder for some use cases. This example compares the performance characteristics.

performance_comparison.go
package main

import (
    "fmt"
    "strings"
    "time"
)

func buildWithBuilder(count int) string {
    var b strings.Builder
    for i := 0; i < count; i++ {
        fmt.Fprintf(&b, "Item %d, ", i)
    }
    return b.String()
}

func buildWithAppend(count int) string {
    buf := []byte{}
    for i := 0; i < count; i++ {
        buf = fmt.Append(buf, "Item ", i, ", ")
    }
    return string(buf)
}

func main() {
    const count = 10000
    
    start := time.Now()
    buildWithBuilder(count)
    builderTime := time.Since(start)
    
    start = time.Now()
    buildWithAppend(count)
    appendTime := time.Since(start)
    
    fmt.Printf("strings.Builder: %v\n", builderTime)
    fmt.Printf("fmt.Append: %v\n", appendTime)
}

The performance difference depends on the use case. fmt.Append often wins for many small appends, while strings.Builder may be better for fewer, larger writes.

Appending custom types

fmt.Append works with any type that implements the Stringer interface. This example demonstrates custom type appending.

custom_types.go
package main

import (
    "fmt"
)

type Point struct {
    X, Y int
}

func (p Point) String() string {
    return fmt.Sprintf("(%d,%d)", p.X, p.Y)
}

func main() {
    buf := []byte("Points: ")
    
    p1 := Point{10, 20}
    p2 := Point{30, 40}
    
    buf = fmt.Append(buf, p1, " ", p2)
    fmt.Println(string(buf))
    
    // Works with pointers too
    buf = []byte("Pointer points: ")
    buf = fmt.Append(buf, &p1, " ", &p2)
    fmt.Println(string(buf))
}

The String method is automatically called when appending custom types. This provides flexible formatting for user-defined data structures.

Error handling with fmt.Append

fmt.Append doesn't return errors, making it simpler than some other formatting functions. This example shows error-free usage patterns.

error_handling.go
package main

import (
    "fmt"
)

func main() {
    // No error handling needed
    buf := []byte{}
    buf = fmt.Append(buf, "This", " ", "works", " ", 123)
    fmt.Println(string(buf))
    
    // Even with invalid format strings, it won't panic
    buf = []byte{}
    buf = fmt.Appendf(buf, "Value: %d", "not a number") // %d expects number
    fmt.Println(string(buf)) // Still produces output (with wrong format)
    
    // For critical formatting, validate first
    value := "not a number"
    if _, err := fmt.Sscanf(value, "%d", new(int)); err == nil {
        buf = fmt.Appendf(buf, "Valid number: %d", value)
    } else {
        buf = fmt.Append(buf, "Invalid number: ", value)
    }
    fmt.Println(string(buf))
}

While fmt.Append won't panic on format mismatches, the output may not be as expected. For critical formatting, validate inputs first.

Building complex strings

fmt.Append excels at building complex strings incrementally. This example shows a multi-step string construction.

complex_strings.go
package main

import (
    "fmt"
    "time"
)

func buildReport() []byte {
    buf := []byte("REPORT\n")
    buf = fmt.Appendf(buf, "Generated: %s\n\n", time.Now().Format(time.RFC1123))
    
    // Add header
    buf = fmt.Append(buf, "ITEM".padRight(20), "QUANTITY".padRight(10), "PRICE\n")
    buf = fmt.Append(buf, "----".padRight(20), "--------".padRight(10), "-----\n")
    
    // Add items
    items := []struct {
        name     string
        quantity int
        price    float64
    }{
        {"Widget", 5, 12.99},
        {"Gadget", 2, 29.95},
        {"Thingy", 10, 1.49},
    }
    
    for _, item := range items {
        buf = fmt.Appendf(buf, "%-20s%-10d$%.2f\n", 
            item.name, item.quantity, item.price)
    }
    
    // Add footer
    buf = fmt.Append(buf, "\nTotal items: ", len(items), "\n")
    return buf
}

func main() {
    report := buildReport()
    fmt.Println(string(report))
}

// Helper function for padding
func (s string) padRight(width int) string {
    if len(s) >= width {
        return s
    }
    return s + strings.Repeat(" ", width-len(s))
}

The example demonstrates building a formatted report with headers, data rows, and footer. fmt.Append efficiently handles each part.

Source

Go fmt package documentation

This tutorial covered the fmt.Append function in Go with practical examples of efficient string building and formatting.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Golang tutorials.