How to model computed and conditional fields

Include a field conditionally

rustruct.when()(pred, then=…) includes a field only when its predicate is true:

from rustruct import Struct, U8, U16, described, when


class Message(Struct):
    has_extra: U8 = described(0)
    extra: object = when(pred="has_extra", then=U16, default=None)


assert Message(has_extra=0).pack() == b"\x00"
assert Message(has_extra=1, extra=0xABCD).pack() == b"\x01\xab\xcd"

The predicate can be a field name or an expression lambda:

class ExecutableHeader(Struct):
    optional_size: U16
    optional: object = when(
        pred=lambda f: f.optional_size > 0,
        then=U16,
        default=None,
    )

Predicates can read fields already decoded in the same or an enclosing scope. When false, the field consumes zero bytes.

Derive lengths and counts

Lengths are relationships declared on the consuming field:

from rustruct import U32, digest, slice


class Payload(Struct):
    size: U16
    data: bytes = slice(len="size")

Pack derives size from data; unpack reads size first and bounds data. The same rule applies to rustruct.string()(len=…), rustruct.array()(count=…), and rustruct.sized()(size=…).

Expressions support arithmetic, bitwise operations, comparisons, and boolean logic:

options: bytes = slice(len=lambda f: (f.ihl - 5) * 4)

The compiler only accepts backward-resolvable references and validates expressions before runtime.