Fields and declarations¶
Signatures and docstrings only – for a table view of the same vocabulary alongside the value it produces and its wire extent, see Declarative schema language. For selection and composition examples, see the how-to guides on How to choose field types and choosing among field types, How to model collections and nesting and modeling collections, and How to model computed and conditional fields and other computed fields.
Scalars¶
- class rustruct.I8¶
- class rustruct.I16¶
- class rustruct.I32¶
- class rustruct.I64¶
Signed integers of 1, 2, 4, and 8 bytes.
- class rustruct.U8¶
- class rustruct.U16¶
- class rustruct.U32¶
- class rustruct.U64¶
Unsigned integers of 1, 2, 4, and 8 bytes.
- class rustruct.F32¶
- class rustruct.F64¶
Four- and eight-byte floating-point values.
- class rustruct.Bool¶
A one-byte boolean.
Descriptors¶
- rustruct.described(default=MISSING, *, default_factory=MISSING, help=None)[source]¶
A plain field: wire kind comes from the annotation, this only carries a construction default and/or documentation.
class Header(Struct): version: U8 = described(1, help="wire format version") assert Header().pack() == bytes.fromhex("01")
- rustruct.raw(length, *, const=None, default=MISSING, help=None)[source]¶
A fixed-length bytes value: exactly length bytes, every time, no length prefix and no expression. const fixes the value (checked on unpack, written on pack) for signatures and magic numbers.
class Magic(Struct): signature: bytes = raw(4, const=b"RSTC") assert Magic().pack() == b"RSTC"
- rustruct.slice(len=None, *, max=None, default=MISSING, help=None)[source]¶
A runtime-sized bytes value. len is an integer constant, “*” for the rest of the current region, a sibling field name, or an expression lambda; referencing a sibling by name derives that sibling from the actual data on pack. max bounds the allocation regardless of what len evaluates to.
- rustruct.string(len=None, *, max=None, encoding='utf-8', errors='strict', default=MISSING, help=None)[source]¶
A runtime-sized str value: the same length rules as slice(), but the wire bytes are decoded to text on unpack and encoded on pack. len and max count encoded bytes, not Unicode code points.
- rustruct.cstring(*, max=None, encoding='utf-8', errors='strict', default=MISSING, help=None)[source]¶
A NUL-terminated str value: no length field at all, the extent is a x00 byte on unpack, and pack writes one after the encoded text. Always give max when reading untrusted input – it’s the only thing stopping a missing terminator from consuming the rest of the buffer.
- rustruct.convert(base, *, decode, encode, default=MISSING, default_factory=MISSING, help=None)[source]¶
Wrap base (a scalar type, or another field spec like raw(4)) with a value-level transcoder, e.g. u32 <-> ipaddress.IPv4Address.
import ipaddress class Endpoint(Struct): address: object = convert(U32, decode=ipaddress.IPv4Address, encode=int) addr = ipaddress.IPv4Address("192.0.2.1") assert Endpoint(address=addr).pack() == bytes.fromhex("c0000201")
- rustruct.bits(width, *, signed=False, const=None, default=MISSING, help=None)[source]¶
One field inside a run of consecutive bit fields, packed most-significant-bit-first into whole bytes. Each run (however many bits() fields appear back to back) must add up to a whole number of bytes. signed=True reads the field as two’s-complement; const= fixes the value the same way it does for raw().
- rustruct.array(elem, *, count=None, until_eof=False, default_factory=list, help=None)[source]¶
A repeated elem (a scalar type, another field descriptor, or a Struct subclass). count is an integer constant, a sibling field name, or an expression lambda; referencing a sibling by name derives that sibling from len(…) on pack. until_eof=True instead repeats until the current region ends, with a partial trailing element treated as invalid rather than dropped.
- rustruct.sized(struct_cls, size, *, default_factory=MISSING, help=None)[source]¶
A nested struct framed as a window: only size bytes are visible to it, and it must consume exactly that many. Use this instead of a bare Struct-subclass annotation whenever the outer schema needs TLV-style framing (a length field bounding the body).
- rustruct.when(pred, then, *, default=None, help=None)[source]¶
A conditionally-present field: then (any type_spec – a scalar type, another field descriptor, a Struct subclass, …) is decoded/encoded only when pred (evaluated against already-decoded sibling fields) is truthy. When it isn’t, the field is entirely absent from the wire – zero bytes, not a null – and absent from the decoded mapping; default (None unless given) is its Python-side value in that case.
- rustruct.switch(on, cases, *, default=None, help=None)[source]¶
cases: a dict {tag: type_spec}, a Registry, or an iterable of (tag, type_spec) pairs. default, if given, is a type_spec applied to unmatched tags; a common choice is slice(len=”*”) for a raw, round-trippable fallback.
- rustruct.digest(algo, over, *, verify=True, poly=None, init=None, xorout=None, refin=None, refout=None, help=None)[source]¶
A checksum/hash computed over over (“*” for the whole enclosing scope, self-zeroing its own bytes, or a tuple of sibling field names) and written on pack; verified on unpack unless verify=False. The caller never supplies this field – pack() always recomputes it and ignores whatever’s given, the same as a derived length.