std::flag
std::flag provides a small, robust command
line argument parser modeled after Go’s flag package, but shaped to match
std:: conventions (explicit Result(...) errors, no leaky out-params, and a
clear separation between flags, positionals, and -- rest arguments).
std::flag is intended for programs that can build a std::args::Args view,
typically from a native hosted entrypoint:
fn main (argc: int, argv: u64) -> int { ... }
and the current std::args::Args view.
On wasm32-wasi, executable entrypoints remain fn main () -> int; use
std::args::current() (or std::args::Args.init(std::args::argc(), std::args::argv)) to construct the same Args value before calling
FlagSet.parse_args(...).
See also:
- args (argv helpers)
- conventions (error/ownership conventions)
- result (
Result(T, E)andOk(...)/Err(...)match usage)
Parsing rules#
Given an argv slice args[start..] (typically start = 1 to skip argv[0]):
--terminates flag parsing; arguments after--are captured as rest and are never interpreted as flags.- Before
--: - tokens beginning with
--or-are parsed as flags whenever they match a declared flag name or alias (--name,--name=value,-name,-name=value,-a,-a=value), - a lone
-is captured as a positional token, - tokens that do not begin with
-are captured as positional tokens, - tokens that lexically look like negative integer literals (for example
-7) are captured as positional tokens when they do not match a declared flag. - Unknown dash-prefixed tokens that are neither declared flags nor negative
integer literals are rejected as
UnknownFlag.
This keeps flags usable before or after subcommands without losing typo
checking. When a CLI needs to pass arbitrary dash-prefixed positional strings,
use -- to start the raw rest segment explicitly.
Exported API#
module std::flag;
import std::args;
import std::result;
enum FlagErrorKind { ... }
enum FlagValueKind { Bool, Int, I64, U64, String }
struct FlagFailed {
code: int,
arg_index: int,
detail: i64,
}
impl FlagFailed {
public fn kind (self: &FlagFailed) -> FlagErrorKind;
public fn arg_opt (self: &FlagFailed, args: &std::args::Args) -> string?;
public fn arg (self: &FlagFailed, args: &std::args::Args) -> string;
}
struct BoolOptions { name: string, alias: string, default_value: bool, usage: string }
struct IntOptions { name: string, alias: string, default_value: int, usage: string }
struct I64Options { name: string, alias: string, default_value: i64, usage: string }
struct U64Options { name: string, alias: string, default_value: u64, usage: string }
struct StringOptions { name: string, alias: string, default_value: string, usage: string }
struct FlagSet { ... }
struct BoolFlag { index: i64 }
struct IntFlag { index: i64 }
struct I64Flag { index: i64 }
struct U64Flag { index: i64 }
struct StringFlag { index: i64 }
struct FlagInfo {
index: i64,
kind: FlagValueKind,
name: string,
alias: string,
usage: string,
}
struct FlagIter { ... }
struct PosString { index: i64 }
struct PosInt { index: i64 }
struct PosI64 { index: i64 }
struct PosU64 { index: i64 }
struct ParsedArgs { ... }
export type ParseResult = std::result::Result(ParsedArgs, FlagFailed);
export type BoolFlagResult = std::result::Result(BoolFlag, FlagFailed);
export type IntFlagResult = std::result::Result(IntFlag, FlagFailed);
export type I64FlagResult = std::result::Result(I64Flag, FlagFailed);
export type U64FlagResult = std::result::Result(U64Flag, FlagFailed);
export type StringFlagResult = std::result::Result(StringFlag, FlagFailed);
export type PosStringResult = std::result::Result(PosString, FlagFailed);
export type PosIntResult = std::result::Result(PosInt, FlagFailed);
export type PosI64Result = std::result::Result(PosI64, FlagFailed);
export type PosU64Result = std::result::Result(PosU64, FlagFailed);
impl FlagSet {
public fn get_flag_name (self: &FlagSet, index: i64) -> string;
public fn get_flag_alias (self: &FlagSet, index: i64) -> string;
public fn get_flag_usage (self: &FlagSet, index: i64) -> string;
public fn iter (self: &FlagSet) -> FlagIter;
}
impl FlagIter as std::interfaces::Iterator(FlagInfo) {
public fn next (mut self: &FlagIter) -> FlagInfo?;
}
Notes:
- Flag/positional “handles” (
BoolFlag,PosString, …) are small, copyable indices into the owningFlagSet. This keeps the API explicit and avoids exporting raw pointers. - Handle structs have safe defaults (their
indexfield defaults to an invalid sentinel).FlagSet.get_*methods treat invalid handles as “missing” and return zero values (false,0, or"") rather than reading out of bounds. - Declared flag metadata may be retrieved from the owning
FlagSetviaget_flag_name(handle.index),get_flag_alias(handle.index), andget_flag_usage(handle.index)when building usage/help output (or viahandle.usage(fs)for usage text specifically). FlagSet.iter()enumerates declared flags in declaration order and yieldsFlagInfovalues carrying the declared index, value kind, name, alias, and usage string.FlagIterborrows metadata storage owned by theFlagSet. Keep theFlagSetalive, and do not declare more flags on it, while an iterator snapshot is in use.ParsedArgsprovides views of:- all positional tokens before and after interspersed flags (including the
--rest segment), - and raw rest tokens (after
--). ParsedArgsborrows its positional index bookkeeping from the owningFlagSet; keep theFlagSetalive until you are done reading positional tokens fromParsedArgs.- Typed values are retrieved from the
FlagSetvia the returned handles. - Flag declarations prefer options structs (
BoolOptions,IntOptions, ...). - Options structs use
default_valuebecausedefaultis a reserved keyword.
Example#
import std::args;
import std::flag;
import { println } from "std/io";
fn main (argc: int, argv: u64) -> int {
let a = std::args::Args.init(argc, argv);
let mut fs = std::flag::FlagSet.init();
let verbose_r = fs.bool({ name: "verbose", alias: "v", default_value: false, usage: "enable verbose logging" });
let out_r = fs.string({ name: "out", alias: "", default_value: "out.txt", usage: "output path" });
let input_r = fs.positional_string("input", "input file");
match (verbose_r) {
Ok(verbose) => {
match (out_r) {
Ok(out) => {
match (input_r) {
Ok(input) => {
let parsed_r = fs.parse_args(a, 1);
match (parsed_r) {
Err(e) => {
println("flag parse error: kind={} at argv[{}]: {}", e.kind(), e.arg_index, e.arg(a));
fs.drop();
return 2;
},
Ok(p) => {
if fs.get_bool(verbose) {
println("out={}", fs.get_string(out));
}
println("input={}", fs.get_pos_string(input));
// `--` rest args (for forwarding).
var i: int = 0;
while i < p.rest_count() {
println("rest[{}]={}", i, p.rest(a, i));
i += 1;
}
fs.drop();
return 0;
}
}
},
Err(_) => {
fs.drop();
return 2;
},
}
},
Err(_) => {
fs.drop();
return 2;
},
}
},
Err(_) => {
fs.drop();
return 2;
},
}
}
Declared flag metadata#
FlagSet now exposes the declared flag surface directly:
get_flag_name(index)returns the canonical long name.get_flag_alias(index)returns the declared alias, or""when the flag has no alias or the index is invalid.get_flag_usage(index)returns the usage text, or""for an invalid index.iter()returns a non-destructive iterator over all declared flags in declaration order.
This is intended for help/usage generation and for generic tooling that only
has a FlagSet plus flag handles.
let mut fs = std::flag::FlagSet.init();
let verbose_r = fs.bool({ name: "verbose", alias: "v", default_value: false, usage: "enable verbose logging" });
let out_r = fs.string({ name: "out", alias: "o", default_value: "out.txt", usage: "output path" });
if verbose_r.is_ok() && out_r.is_ok() {
for info in fs.iter() {
if info.alias != "" {
println("--{} (-{}): {}", info.name, info.alias, info.usage);
} else {
println("--{}: {}", info.name, info.usage);
}
}
}
Design goals#
- Typed flags: parse
bool,int,i64,u64, andstringvalues. - Typed positionals: declare and parse positional arguments (required and optional), separate from flags.
--rest: support--to stop parsing flags and expose the remaining arguments as a “rest” list for forwarding to subcommands/tools.- Interspersed flags: continue recognizing declared flags before
--even after earlier positional tokens, so CLI subcommands can still accept global flags after the subcommand name. - Stable errors: return a structured
FlagFailedvalue (noerrno, no sentinel returns, no hidden error state). - No hidden allocation: the parser stores only string views into the original argv bytes; it does not copy argument strings.
Source repository · Edit this page · View Markdown