std.arith
std.arith collects explicit integer overflow policies. Use it when ordinary operators are not the right contract and the call site should say whether overflow is checked, reported, clamped, or wrapped. It also documents the standard string-to-number parsing contract used by builtin numeric type calls and the lower-level std.arith.parse optional parsers.
Contract
The policy modules are imported directly; importing std.arith does not make their short names available:
std.arith.checkedreturnsT?; overflow returnsnull.std.arith.overflowingreturns(T, bool); the value is the wrapped result and the flag reports whether overflow occurred.std.arith.saturatingclamps to the numeric minimum or maximum.std.arith.wrappingreturns the wrapped result.
All helpers are generic over builtin integer marker interfaces. Signed and unsigned integer types are supported by separate overloads where needed. The helpers do not allocate and do not throw.
Use these helpers where overflow behavior is part of the public contract, such as allocation size calculation, hash arithmetic, counters, and codec bounds. Plain arithmetic operators keep the language's checked/trapping behavior.
import std.arith.checked;
import std.arith.overflowing;
import std.arith.saturating;
import std.arith.wrapping;
fn main() {
let checked_result = checked.add(@max(u8), 1 as u8);
let (wrapped, did_overflow) = overflowing.add(@max(u8), 1 as u8);
let saturated = saturating.add(@max(u8), 1 as u8);
let wrapped_only = wrapping.add(@max(u8), 1 as u8);
assert checked_result == null;
assert wrapped == 0 as u8;
assert did_overflow;
assert saturated == @max(u8);
assert wrapped_only == 0 as u8;
}Parsing
Builtin numeric type calls are the primary string-to-number API. For a supported builtin numeric type T, T(text) takes one str argument and has type T throws(std.arith.parse.ParseError). Use try or catch at the call site:
import std.arith.parse;
fn read_port(text: str) throws(parse.ParseError) -> u16 {
let count: i32 = try i32("123");
let port: u16 = try u16(text);
let ratio: f64 = try f64("3.14");
assert count == 123;
assert ratio > 3.0;
return port;
}Numeric type calls are supported for the fixed integer types, isize/usize, custom integer spellings iN/uN for 1 <= N <= 65535, and f32/f64. A malformed input, an unsupported sign, trailing or leading whitespace, a radix prefix, a digit separator, or a value outside the target type's range throws std.arith.parse.ParseError.Invalid. The calls do not allocate.
Aliases and generic numeric construction are follow-up work. type Port = u16; try Port("8080") and try T(text) in generic code are not part of this API; call the concrete builtin numeric type directly, such as try u16(text) or try u24(text).
The parsing rules match the lower-level std.arith.parse parsers:
- Base 10 only. There is no prefix syntax for hexadecimal, octal, or binary, and digit separators or grouping are not accepted.
- Integer parsers accept an optional leading sign followed by one or more ASCII digits. Signed parsers accept
+and-; unsigned parsers accept+only, so a leading-fails. - Surrounding whitespace is not allowed. Leading or trailing non-digit bytes, including spaces, fail.
- Overflow is a failure, not a wrap or trap. The narrow integer parsers parse over the full 64-bit range first and then range-check the result, so a value outside the target type's range fails.
- Float parsing is backed by the runtime float engine and accepts the existing runtime float syntax, including an optional sign, a fraction, an exponent, and the
inf/nanforms. Leading whitespace or trailing garbage fails.
std.arith.parse remains available as a lower-level compatibility API. Each function takes a borrowed str and returns the matching optional type, so a successful parse yields the value and any failure yields null. The parsers do not throw and do not allocate.
There is no generic parse_int<T> entry point. Select the parser for the target type directly when using the optional API. The widest integer parsers are parse_i128 and parse_u128; custom-width parsing is exposed through builtin type-call syntax, for example try i24("123").
A successful parse returns the value wrapped in an optional. Check for null, then bind the result to read the parsed value:
import std.arith.parse;
fn main() {
let count = parse.parse_i32("123");
assert count != null;
if count != null {
let value: i32 = count;
assert value == 123;
}
let signed = parse.parse_i64("-9000000000");
assert signed != null;
if signed != null {
let value: i64 = signed;
assert value == -9000000000;
}
let byte = parse.parse_u8("255");
assert byte != null;
if byte != null {
let value: u8 = byte;
assert value == 255;
}
let real = parse.parse_float("100.25");
assert real != null;
if real != null {
let value: f64 = real;
assert value == 100.25;
}
}The optional parsers return null for the same inputs that make numeric type calls throw ParseError.Invalid. Empty input, a lone sign, non-digit bytes, surrounding whitespace, out-of-range values, and (for unsigned types) a leading minus sign all fail:
import std.arith.parse;
fn main() {
// Empty input, a lone sign, and non-digit bytes all fail.
assert parse.parse_i32("") == null;
assert parse.parse_i32("-") == null;
assert parse.parse_i32("12a") == null;
assert parse.parse_i32(" 12") == null;
assert parse.parse_i32("1.5") == null;
// Out-of-range values fail instead of wrapping.
assert parse.parse_i8("128") == null;
assert parse.parse_u8("256") == null;
// Unsigned parsers reject a leading minus sign.
assert parse.parse_u8("-1") == null;
// Float parsing rejects trailing garbage and non-numeric text.
assert parse.parse_float("abc") == null;
assert parse.parse_float("1.5x") == null;
}API Reference
Modules
| Module | Operations | Result |
|---|---|---|
std.arith.checked | add, sub, mul | T?, with null on overflow. |
std.arith.overflowing | add, sub, mul | (T, bool), with wrapped value plus overflow flag. |
std.arith.saturating | add, sub, mul, shl | T, clamped at integer bounds. |
std.arith.wrapping | add, sub, mul | T, wrapped modulo the integer width. |
std.arith.parse | parse_i8..parse_i128, parse_isize, parse_u8..parse_u128, parse_usize, parse_float | T?, with null on failure or overflow. |
Numeric Type Calls
Each supported builtin numeric type T accepts one str argument and returns T throws(std.arith.parse.ParseError).
| Type calls | Return type | Description |
|---|---|---|
i8(s) i16(s) i32(s) i64(s) i128(s) isize(s) | signed integer T throws(ParseError) | Parses decimal signed integer text into the target type. |
u8(s) u16(s) u32(s) u64(s) u128(s) usize(s) | unsigned integer T throws(ParseError) | Parses decimal unsigned integer text into the target type. |
f32(s) f64(s) | float T throws(ParseError) | Parses runtime float text into the target type. |
Parse Errors
| Error | Variants | Description |
|---|---|---|
std.arith.parse.ParseError | Invalid | Malformed numeric text, unsupported sign, disallowed whitespace/separators/radix prefixes, or overflow/out-of-range for the target type. |
Parsing Functions
Each lower-level parser takes a borrowed str and returns the optional target type.
| Function | Signature | Returns | Description |
|---|---|---|---|
parse_i8 | parse_i8(s: str) -> i8? | i8? | Parses a decimal i8; null on failure or out-of-range. |
parse_i16 | parse_i16(s: str) -> i16? | i16? | Parses a decimal i16; null on failure or out-of-range. |
parse_i32 | parse_i32(s: str) -> i32? | i32? | Parses a decimal i32; null on failure or out-of-range. |
parse_i64 | parse_i64(s: str) -> i64? | i64? | Parses a decimal i64 over the full range; null on failure or out-of-range. |
parse_i128 | parse_i128(s: str) -> i128? | i128? | Parses a decimal i128 over the full range; null on failure or out-of-range. |
parse_isize | parse_isize(s: str) -> isize? | isize? | Parses a decimal isize; null on failure or out-of-range. |
parse_u8 | parse_u8(s: str) -> u8? | u8? | Parses a decimal u8; null on failure or out-of-range. |
parse_u16 | parse_u16(s: str) -> u16? | u16? | Parses a decimal u16; null on failure or out-of-range. |
parse_u32 | parse_u32(s: str) -> u32? | u32? | Parses a decimal u32; null on failure or out-of-range. |
parse_u64 | parse_u64(s: str) -> u64? | u64? | Parses a decimal u64 over the full range; null on failure or out-of-range. |
parse_u128 | parse_u128(s: str) -> u128? | u128? | Parses a decimal u128 over the full range; null on failure or out-of-range. |
parse_usize | parse_usize(s: str) -> usize? | usize? | Parses a decimal usize; null on failure or out-of-range. |
parse_float | parse_float(s: str) -> f64? | f64? | Parses a decimal floating-point value; null on failure. |
Related Modules
Use std.bit for bit rotations, byte swaps, population counts, and zero counts. Use std.mem for AllocError when overflow-sensitive size calculation feeds checked allocation failure handling.
Status
std.arith is the current public explicit-overflow API. The exact helper list is intentionally small; additional arithmetic policies should follow Standard Library Naming Policy. Builtin numeric type calls are the primary public string-to-number API. std.arith.parse remains the lower-level optional parser API for compatibility and does not provide generic numeric construction. For the full exported surface, see Standard Library Inventory.