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
















