In Go's flag package, why does flag.String return a *string, and when may you read it?
answer
- the flag exists before argv is read
- you get a handle, not a value
- one call fills every registered flag
- dereference only after that call returns
basics
~20 sflag.String registers a flag before the command line has been read, so it can only hand back a pointer to storage it will fill in later. Call flag.Parse() first, then read the value as *ptr.
solid answer
~40 sPackage `flag` works in three ordered steps: define, parse, read. `flag.String("name", "world", "who to greet")` runs at a point where nothing has looked at the command line yet, so all it can do is allocate a string, store the default in it, and return a `*string` it promises to overwrite. `flag.Parse()` reads `os.Args[1:]` and assigns into every registered flag; only after it returns is `*name` meaningful. The `Var` forms — `flag.StringVar(&cfg.Name, "name", "world", ...)` — bind to a variable you already own instead of allocating, which is how you fill a config struct directly. Whatever is left over after parsing is available as `flag.Args()`, with `flag.NArg()` for its length and `flag.Arg(0)` for the first operand. Two ordering mistakes cover most bugs: reading a flag before Parse, and defining one after Parse has already run.
code
go · 8 linesname := flag.String("name", "world", "who to greet")
var quiet bool
flag.BoolVar(&quiet, "quiet", false, "suppress output")
flag.Parse() // reads os.Args[1:]
// Only meaningful from here on.
fmt.Println(*name, quiet, flag.NArg(), flag.Args())go deeper
Be ready to write the three lines from memory: define the flag, call flag.Parse, then dereference. Say out loud that reading the pointer before Parse gives you the default.
Explain why the pointer exists at all — the flag is declared before the command line has been read — and show the Var forms filling a config struct instead.
Show that you know the failure is silent: an unparsed or late-defined flag raises no error, it just serves the default. Mention flag.Value and flag.Var for the types the package does not cover.
Own the shape of what you register: which values are flags, which have defaults safe enough to ship, and whether the parsed values land in one config struct that the rest of the program can be tested against.
## The three-step contract Go's standard library parses command lines with package `flag`, and the whole package is built around one ordering rule: **define every flag, then parse once, then read**. ### 1. Define ```go name := flag.String("name", "world", "who to greet") ``` This call happens long before anybody has looked at the command line. There is nothing for it to return except a *handle*: it allocates a `string`, writes the default `"world"` into it, records the flag under the name `name` on the default flag set, and returns the address. The same shape exists for the other supported types — `flag.Int`, `flag.Int64`, `flag.Uint`, `flag.Float64`, `flag.Bool`, `flag.Duration` (which reads values like `1500ms` or `2h` into a `time.Duration`) — each returning a pointer to its own type. Every one of them has a `Var` twin that takes the address of a variable you already have: ```go var cfg struct { Name string Quiet bool } flag.StringVar(&cfg.Name, "name", "world", "who to greet") flag.BoolVar(&cfg.Quiet, "quiet", false, "suppress output") ``` The `Var` forms are what you use when the parsed values should land in a config struct rather than in a scatter of package-level pointers. They are otherwise identical: same names, same defaults, same usage strings. ### 2. Parse `flag.Parse()` reads `os.Args[1:]` and walks it, assigning into the registered flags. Until it is called, every pointer you were handed still holds its default. That is the single most common beginner bug in this package, and it is silent: nothing panics, nothing logs, the program simply behaves as if the user passed nothing. The mirror-image bug is defining a flag *after* `Parse` has returned. Registration succeeds, but the parse that would have filled it has already happened, so the flag holds its default forever. Define everything first — usually in `main` before any other work, or in package-level `var` declarations. ### 3. Read After `Parse` returns, `*name` is the value the user asked for, or the default if they said nothing. There is deliberately no way to tell those two apart from the value alone — `flag.Visit` exists for that. ## What is left over Flags are only part of a command line; the rest are *operands*. After parsing: - `flag.Args()` returns the remaining arguments as a `[]string` - `flag.NArg()` is `len(flag.Args())` - `flag.Arg(i)` indexes it safely, returning `""` when `i` is out of range None of these include the program name — that is `os.Args[0]`, and it never reaches the parser. ## Extending it: the Value interface The fixed set of types runs out quickly. A flag of any type at all is possible through `flag.Var`, which accepts anything implementing: ```go type Value interface { String() string Set(string) error } ``` `Set` is called once for **each occurrence** of the flag on the command line, which is exactly how you build a repeatable flag: append in `Set` and you get a slice. `String` is called to render the value in usage text, so it must tolerate being called on a zero value of the type. `flag.Func` is a shortcut for the same idea when you only need the `Set` half and no accumulated state. ## Why the pointer, once more Some people find the returned pointer awkward and reach for `os.Args` directly. That trade is a bad one: hand-parsing loses the generated `-h` usage text, the type conversion, the default values, and the consistent syntax other Go tools share. The pointer is simply the price of the flag being declared before the data exists — and if you dislike it, the `Var` forms remove it entirely.
- What is the difference between flag.String and flag.StringVar?`flag.String` allocates a string, seeds it with the default, and returns a `*string`. `flag.StringVar` takes the address of a variable you already own and writes into that instead. Everything else — name, default, usage text, parsing — is identical. Use the `Var` form when the values belong in a config struct rather than in loose package-level pointers.
- How would you accept a flag the user can repeat, such as -exclude twice?Define a named slice type with `String() string` and `Set(string) error` methods, so it satisfies `flag.Value`, and register it with `flag.Var`. `Set` is invoked once per occurrence of the flag, so appending inside `Set` accumulates every value. `flag.Func` does the same job when you need only the `Set` behaviour and no stored state.
- What does flag.Args() give you, and how does it differ from os.Args?`os.Args` is the raw argument vector, with the program path at index 0 and nothing interpreted. `flag.Args()` is what the parser did not consume — the positional operands left after flag parsing stopped, with no program name. `flag.NArg()` is its length and `flag.Arg(i)` indexes it, returning an empty string when `i` is out of range.
Registering a flag is like handing someone an empty envelope with your address on it: you get the envelope immediately, but there is nothing inside until the mail is actually sorted.
saying these in an interview costs you the question
- Reads the flag pointer before calling flag.Parse
- Defines flags after flag.Parse has already run
- Thinks flag.Args() includes the program name
- Believes flag.String returns the value rather than a pointer
- Hand-parses os.Args instead of registering flags