.. _tutorial-advanced-conditionals: Conditional Fields ================== *Conditional fields* allow you to define fields in a struct that are included or excluded based on certain conditions. This feature is especially useful when working with versioned formats or optional fields that depend on runtime conditions. You can easily achieve this using context-based lambdas, which are built into the library. How it works ------------ By using the `with` keyword in combination with conditional expressions, you can bind certain fields to a specific condition. This allows you to include or exclude fields dynamically, depending on the value of other fields or context. Here's an example demonstrating how to use conditional fields for versioned structs: .. code-block:: python :caption: Conditional fields (e.g. for versioned structs) @struct class Format: version: uint32 # all following fields will be bound to the condition with this.version == 1: header: uint8 Key Concepts ------------ 1. **`with` and Conditionals**: The :code:`with` keyword is used to define a block of fields that should only be included if the condition evaluates to :code:`True`. In the example above, the fields inside :code:`with this.version == 1` are included only when the :code:`version` field has a value of :code:`1`. 2. **`ElseIf` for Multiple Conditions**: For multiple conditions, use :code:`ElseIf` rather than :code:`Else`. The :code:`ElseIf` construct ensures that the next condition is checked only if the previous one was false. This is safer and more predictable than using a generic :code:`Else` clause, which could introduce unintended side effects by executing under unanticipated conditions. Example: Versioned Struct ^^^^^^^^^^^^^^^^^^^^^^^^^ Conditional fields are particularly useful when dealing with versioned structs, where the structure of the data may change based on the version number or other factors. For example: .. code-block:: python :caption: Conditional fields (e.g. for versioned structs) @struct class Format: version: uint32 # all following fields will be bound to the condition with this.version == 1: length: uint8 extra: uint8 data: Bytes(this.length) # Use else-if over 'Else' alone with ElseIf(this.version == 2): name: CString(16) data: Prefixed(uint8) Best Practices --------------- - **Avoid Using `Else`**: It is **strongly recommended** to **avoid** using :code:`Else` for conditional field inclusion, as it can introduce unintended behavior if not properly managed. Instead, always use :code:`ElseIf` with an inverted condition to ensure more predictable and controlled struct parsing. .. note:: When using conditional fields, it's essential to remember that the struct's layout can change dynamically depending on the conditions. This flexibility makes it possible to define complex, version-dependent data structures.