liveBook · Manning

Reference

As explained in Go by Example

A pointer is a Go value that contains the memory address of another value and permits indirect access to it.

A pointer is a Go value that holds another value’s memory address, allowing indirect access to that value. Passing a pointer shares the original data with a function; passing a value gives that function a copy. Choose a pointer when a function must update the caller’s value, and choose a value when independent copies better express the design. section 4.2.3 section 6.3.3

A practical use is configuration parsing. `parseArgs` accepts `*config`, then gives field pointers such as `&c.url` and `&c.n` to type-specific parser builders. Each returned closure retains its field pointer, parses one flag value, and assigns the result to the original configuration field. This makes the parser update one shared configuration value without returning a revised struct after every argument. section 4.2.3

Listing 4.3 Flag parser (hit/cmd/hit/config.go)
package main

import (
    "fmt"
    "strings"
    . . .
)

type config struct {
    url string
    n   int
    c   int
    rps int
}

func parseArgs(c *config, args []string) error {
    flagSet := map[string]parseFunc{  #1
        "url": stringVar(&c.url),  #2
        "n":   intVar(&c.n),  #3
        "c":   intVar(&c.c),   #3
        "rps": intVar(&c.rps),   #3
    }

    for _, arg := range args {
        name, val, _ := strings.Cut(arg, "=")  #4
        name = strings.TrimPrefix(name, "-")  #5

        setVar, ok := flagSet[name]  #6
        if !ok {
           return fmt.Errorf(
                "flag provided but not defined: -%s",
                name,
            )
        }
        if err := setVar(val); err != nil { #7
            return fmt.Errorf(  #8
                "invalid value %q for flag -%s: %w",  #8
                val, name, err,   #8
            )   #8
        }
    }

    return nil
}

Follow the pointer flow from `parseArgs(c *config, ...)` through `stringVar(&c.url)` and `intVar(&c.n)`: parsers capture field addresses and later write parsed values through them. section 4.2.3

Sharing is intentional, not automatic. Avoid a pointer when another function should not alter the original value. For concurrent result processing, `Result` values are passed and yielded as copies because they do not need mutation; each goroutine therefore works with its own value rather than shared state. Pointers can avoid copying pointed-to data, but returning them can trigger heap allocation, so efficiency is a trade-off rather than a rule. section 4.2.3 section 6.3.3

Some APIs require a pointer so they can assign a value for you. `errors.As` searches an error chain and stores a matching error in the variable supplied by the caller. When the desired error type is itself a pointer type, the argument must be a pointer to that pointer. For a SQLite error whose `Error` method has a pointer receiver, `*sqlite.Error` implements `error`; finding it requires `var serr *sqlite.Error` followed by `errors.As(err, &serr)`. Passing a pointer to a non-pointer `sqlite.Error` fails because that value does not implement `error`. section 10.2.4

Finding a pointer-typed error

Declare `serr` as `*sqlite.Error`, then pass `&serr` to `errors.As`. On success, `serr` holds the matched driver error and its code can identify a primary-key violation. The storage layer can translate that condition into an application-level conflict error instead of exposing a SQLite-specific internal error. section 10.2.4

Listing 10.8 Detecting constraint errors (link/sqlite/sqlite.go)
package sqlite

import (
    // _ "modernc.org/sqlite" #1
    . . .
    "errors"
    "modernc.org/sqlite" #2
)

. . .

func isPrimaryKeyViolation(err error) bool {
    var serr *sqlite.Error         #3
    if errors.As(err, &serr) {     #4
        return serr.Code() == 1555 #5
        // See: sqlite.org/rescode.html#constraint_primarykey
    }
    return false
}

This implementation shows the two levels directly: `serr` is `*sqlite.Error`, while `&serr` is the `**sqlite.Error` argument that lets `errors.As` store a match. section 10.2.4

How Go by Example covers it

How deep it goes
Explains pointer sharing, captured field pointers, value-copy trade-offs, and the pointer-to-pointer requirement of errors.As.
Examples
Demonstrates a CLI config parser, copied concurrent Result values, and SQLite primary-key error detection.
What you can do after
Apply it via Go pointers to update caller-owned fields and use errors.As correctly with pointer-typed errors.
How it connects
Connects pointers with closures, pass-by-value, heap allocation, garbage collection, error wrapping, and errors.As.

Also explained in

Compare how 17 books explain reference →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Go by Example. The section links point to the parts of the book it draws on.