Glossary of directives in binrw attributes (`#[br]`, `#[bw]`, `#[brw]`).
# List of directives
| r/w | Directive | Supports[*] | Description
|-----|-----------|----------|------------
| rw | [`align_after`](#padding-and-alignment) | field | Aligns the readerwriter to the Nth byte after a field.
| rw | [`align_before`](#padding-and-alignment) | field | Aligns the readerwriter to the Nth byte before a field.
| rw | [`args`](#arguments) | field | Passes arguments to another binrw object.
| rw | [`args_raw`](#arguments) | field | Like `args`, but specifies a single variable containing the arguments.
| rw | [`assert`](#assert) | struct, field, non-unit enum, data variant | Asserts that a condition is true. Can be used multiple times.
| rw | [`big`](#byte-order) | all except unit variant | Sets the byte order to big-endian.
| rw | [`calc`](#calculations) | field | Computes the value of a field instead of reading datausing a field.
| r | [`count`](#count) | field | Sets the length of a vector.
| r | [`dbg`](#debug) | field | Prints the value and offset of a field to `stderr`.
| r | [`default`](#ignore) | field | An alias for `ignore`.
| r | [`err_context`](#backtrace) | field | Adds additional context to errors.
| rw | [`if`](#conditional-values) | field | Reads or writesReadsWrites data only if a condition is true.
| rw | [`ignore`](#ignore) | field | For `BinRead`, uses the [`default`](core::default::Default) value for a field instead of reading data. For `BinWrite`, skips writing the field.Uses the [`default`](core::default::Default) value for a field instead of reading data.Skips writing the field.
| rw | [`import`](#arguments) | struct, non-unit enum, unit-like enum | Defines extra arguments for a struct or enum.
| rw | [`import_raw`](#arguments) | struct, non-unit enum, unit-like enum | Like `import`, but receives the arguments as a single variable.
| rw | [`is_big`](#byte-order) | field | Conditionally sets the byte order to big-endian.
| rw | [`is_little`](#byte-order) | field | Conditionally set the byte order to little-endian.
| rw | [`little`](#byte-order) | all except unit variant | Sets the byte order to little-endian.
| rw | [`magic`](#magic) | all | MatchesWrites a magic number.
| rw | [`map`](#map) | all except unit variant | Maps an object or value to a new value.
| rw | [`map_stream`](#stream-access-and-manipulation) | all except unit variant | Maps the readwrite stream to a new stream.
| r | [`offset`](#offset) | field | Modifies the offset used by a [`FilePtr`](crate::FilePtr) while parsing.
| rw | [`pad_after`](#padding-and-alignment) | field | Skips N bytes after readingwriting a field.
| rw | [`pad_before`](#padding-and-alignment) | field | Skips N bytes before readingwriting a field.
| rw | [`pad_size_to`](#padding-and-alignment) | field | Ensures the readerwriter is always advanced at least N bytes.
| r | [`parse_with`](#custom-parserswriters) | field | Specifies a custom function for reading a field.
| r | [`pre_assert`](#pre-assert) | struct, non-unit enum, unit variant | Like `assert`, but checks the condition before parsing.
| rw | [`repr`](#repr) | unit-like enum | Specifies the underlying type for a unit-like (C-style) enum.
| rw | [`restore_position`](#restore-position) | field | Restores the reader’swriter’s position after readingwriting a field.
| r | [`return_all_errors`](#enum-errors) | non-unit enum | Returns a [`Vec`] containing the error which occurred on each variant of an enum on failure. This is the default.
| r | [`return_unexpected_error`](#enum-errors) | non-unit enum | Returns a single generic error on failure.
| rw | [`seek_before`](#padding-and-alignment) | field | Moves the readerwriter to a specific position before readingwriting data.
| rw | [`stream`](#stream-access-and-manipulation) | struct, non-unit enum, unit-like enum | Exposes the underlying readwrite stream.
| r | [`temp`](#temp) | field | Uses a field as a temporary variable. Only usable with the [`binread`](macro@crate::binread) attribute macro.
| r | [`try`](#try) | field | Tries to parse and stores the [`default`](core::default::Default) value for the type if parsing fails instead of returning an error.
| rw | [`try_calc`](#calculations) | field | Like `calc`, but returns a [`Result`].
| rw | [`try_map`](#map) | all except unit variant | Like `map`, but returns a [`Result`].
| w | [`write_with`](#custom-parserswriters) | field | Specifies a custom function for writing a field.
[*]: #terminology
# Terminology
Each binrw attribute contains a comma-separated list of directives. The
following terms are used when describing where particular directives are
supported:
```text
#[brw(…)] // ← struct
struct NamedStruct {
#[brw(…)] // ← field
field: Type
}
#[brw(…)] // ← struct
struct TupleStruct(
#[brw(…)] // ← field
Type,
);
#[brw(…)] // ← struct
struct UnitStruct;
#[brw(…)] // ← non-unit enum
enum NonUnitEnum {
#[brw(…)] // ← data variant
StructDataVariant {
#[brw(…)] // ← field
field: Type
},
#[brw(…)] // ← data variant
TupleDataVariant(
#[brw(…)] // ← field
Type
),
#[brw(…)] // ← unit variant
UnitVariant
}
#[brw(…)] // ← unit-like enum
enum UnitLikeEnum {
#[brw(…)] // ← unit variant
UnitVariantA,
…
#[brw(…)] // ← unit variant
UnitVariantN
}
```
# Arguments
Arguments provide extra data necessary for
readingwriting an
object.
The `import` and `args` directives define the type of
[`BinRead::Args`](crate::BinRead::Args)[`BinWrite::Args`](crate::BinWrite::Args)
and the values passed in the `args`
argument of a
[`BinRead::read_options`](crate::BinRead::read_options)[`BinWrite::write_options`](crate::BinWrite::write_options)
call, respectively.
Any (earlier only, when reading)earlier
field or [import](#arguments) can be referenced in `args`.
## Ways to pass and receive arguments
There are 3 ways arguments can be passed and received:
* Tuple-style arguments (or “ordered arguments”): arguments passed as a tuple
* Named arguments: arguments passed as an object
* Raw arguments: arguments passed as a type of your choice
### Tuple-style arguments
Tuple-style arguments (or “ordered arguments”) are passed via `args()` and
received via `import()`:
[Field init shorthand](https://doc.rust-lang.org/book/ch05-01-defining-structs.html#using-the-field-init-shorthand)
and optional arguments are both supported.
Named arguments are particularly useful for container objects like [`Vec`],
but they can be used by any parserserialiser
that would benefit from labelled, optional, or unordered arguments:
Named arguments can also be used with types that manually implement
`BinRead``BinWrite` by creating
a separate type for its arguments that implements [`binrw::NamedArgs`].
The [`count`](#count) and [`offset`](#offset) directives are sugar for setting
the `count` and `offset` arguments on any named arguments object; see those
directives’ documentation for more information.
### Raw arguments
Raw arguments allow the
[`BinRead::Args`](crate::BinRead::Args)[`BinWrite::Args`](crate::BinWrite::Args)
type to be specified explicitly and to receive all arguments into a single
variable:
```text
#[br(import_raw($binding:ident : $ty:ty))]
#[br(args_raw($value:expr))] or #[br(args_raw = $value:expr)]
```
```text
#[bw(import_raw($binding:ident : $ty:ty))]
#[bw(args_raw($value:expr))] or #[bw(args_raw = $value:expr)]
```
# Assert
The `assert` directive validates objects and fields
after they are read or before they are written,after they are read,before they are written,
returning an error if the assertion condition evaluates to `false`:
Multiple assertion directives can be used; they will be combined and
executed in order.
Assertions added to the top of an enum will be checked against every variant
in the enum.
Any (earlier only, when reading)earlier
field or [import](#arguments) can be referenced by expressions
in the directive. When reading, anAn
`assert` directive on a struct, non-unit enum, or data variant can access the
constructed object using the `self` keyword.
## Examples
### Formatted error
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug)]
#[br(assert(some_val > some_smaller_val, "oops! {} <= {}", some_val, some_smaller_val))]
struct Test {
some_val: u32,
some_smaller_val: u32
}
let error = Cursor::new(b"\0\0\0\x01\0\0\0\xFF").read_be::();
assert!(error.is_err());
let error = error.unwrap_err();
let expected = "oops! 1 <= 255".to_string();
assert!(matches!(error, binrw::Error::AssertFail { message: expected, .. }));
```
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinWrite)]
# #[derive(Debug)]
#[bw(assert(some_val > some_smaller_val, "oops! {} <= {}", some_val, some_smaller_val))]
struct Test {
some_val: u32,
some_smaller_val: u32
}
let object = Test { some_val: 1, some_smaller_val: 255 };
let error = object.write_le(&mut Cursor::new(vec![]));
assert!(error.is_err());
let error = error.unwrap_err();
let expected = "oops! 1 <= 255".to_string();
assert!(matches!(error, binrw::Error::AssertFail { message: expected, .. }));
```
```
# use binrw::{prelude::*, io::Cursor};
#[derive(Debug, PartialEq)]
struct NotSmallerError(u32, u32);
impl core::fmt::Display for NotSmallerError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{} <= {}", self.0, self.1)
}
}
#[derive(BinWrite, Debug)]
#[bw(assert(some_val > some_smaller_val, NotSmallerError(*some_val, *some_smaller_val)))]
struct Test {
some_val: u32,
some_smaller_val: u32
}
let object = Test { some_val: 1, some_smaller_val: 255 };
let error = object.write_le(&mut Cursor::new(vec![]));
assert!(error.is_err());
let error = error.unwrap_err();
assert_eq!(error.custom_err(), Some(&NotSmallerError(0x1, 0xFF)));
```
### In combination with `map` or `try_map`
```
# use binrw::{prelude::*, io::Cursor};
use modular_bitfield::prelude::*;
#[bitfield]
#[derive(BinRead)]
#[br(assert(self.is_fast()), map = Self::from_bytes)]
pub struct PackedData {
status: B4,
is_fast: bool,
is_static: bool,
is_alive: bool,
is_good: bool,
}
let data = Cursor::new(b"\x53").read_le::().unwrap();
```
## Errors
If the assertion fails and there is no second argument, or a string literal
is given as the second argument, an [`AssertFail`](crate::Error::AssertFail)
error is returned.
If the assertion fails and an expression is given as the second argument,
a [`Custom`](crate::Error::Custom) error containing the result of the
expression is returned.
Arguments other than the condition are not evaluated unless the assertion
fails, so it is safe for them to contain expensive operations without
impacting performance.
In all cases, the readerwriter’s
position is reset to where it was before
parsingserialisation
started.
# Backtrace
When an error is raised during parsing, `BinRead` forms a backtrace, bubbling the
error upwards and attaching additional information (surrounding code, line numbers,
messages, etc.) in order to aid in debugging.
The `#[br(err_context(...))]` attribute can work in one of two ways:
1. If the first (or only) item is a string literal, it will be a message format string,
with any other arguments being used as arguments. This uses the same formatting as `format!`,
`println!`, and other standard library formatters.
2. Otherwise, only a single argument is allowed, which will then be attached as a context
type. This type must implement [`Display`](std::fmt::Display), [`Debug`], [`Send`], and [`Sync`].
## Example
```
# use binrw::{io::Cursor, BinRead, BinReaderExt};
#[derive(BinRead)]
struct InnerMostStruct {
#[br(little)]
len: u32,
#[br(count = len, err_context("len = {}", len))]
items: Vec,
}
#[derive(BinRead)]
struct MiddleStruct {
#[br(little)]
#[br(err_context("While parsing the innerest most struct"))]
inner: InnerMostStruct,
}
#[derive(Debug, Clone)] // Display implementation omitted
struct Oops(u32);
#[derive(BinRead)]
struct OutermostStruct {
#[br(little, err_context(Oops(3 + 1)))]
middle: MiddleStruct,
}
# let mut x = Cursor::new(b"\0\0\0\x06");
# let err = x.read_be::().map(|_| ()).unwrap_err();
# impl core::fmt::Display for Oops {
# fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
# write!(f, "Oops({})", self.0)
# }
# }
```
# Byte order
The `big` and `little` directives specify the [byte order](https://en.wikipedia.org/wiki/Endianness)
of data in a struct, enum, variant, or field:
```text
#[br(big)]
#[br(little)]
```
```text
#[bw(big)]
#[bw(little)]
```
The `is_big` and `is_little` directives conditionally set the byte order of
a struct field:
```text
#[br(is_little = $cond:expr)] or #[br(is_little($cond:expr))]
#[br(is_big = $cond:expr)] or #[br(is_big($cond:expr))]
```
```text
#[bw(is_little = $cond:expr)] or #[bw(is_little($cond:expr))]
#[bw(is_big = $cond:expr)] or #[bw(is_big($cond:expr))]
```
The `is_big` and `is_little` directives are primarily useful when byte order
is defined in the data itself. Any
(earlier only, when reading)earlier
field or [import](#arguments) can
be referenced in the condition. Conditional byte order directives can only
be used on struct fields.
The order of precedence (from highest to lowest) for determining byte order
within an object is:
1. A directive on a field
2. A directive on an enum variant
3. A directive on the struct or enum
4. The `endian` parameter of the
[`BinRead::read_options`](crate::BinRead::read_options)
[`BinWrite::write_options`](crate::BinWrite::write_options) call
However, if a byte order directive is added to a struct or enum, that byte
order will *always* be used, even if the object is embedded in another
object or explicitly called with a different byte order:
```
# use binrw::{Endian, prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
#[br(little)] // ← this *forces* the struct to be little-endian
struct Child(u32);
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
struct Parent {
#[br(big)] // ← this will be ignored
child: Child,
};
let endian = Endian::Big; /* ← this will be ignored */
# assert_eq!(
Parent::read_options(&mut Cursor::new(b"\x01\0\0\0"), endian, ())
# .unwrap(), Parent { child: Child(1) });
```
```
# use binrw::{Endian, prelude::*, io::Cursor};
#[derive(BinWrite)]
# #[derive(Debug, PartialEq)]
#[bw(little)] // ← this *forces* the struct to be little-endian
struct Child(u32);
#[derive(BinWrite)]
# #[derive(Debug, PartialEq)]
struct Parent {
#[bw(big)] // ← this will be ignored
child: Child,
};
let object = Parent { child: Child(1) };
let endian = Endian::Big; /* ← this will be ignored */
let mut output = Cursor::new(vec![]);
object.write_options(&mut output, endian, ())
# .unwrap();
# assert_eq!(output.into_inner(), b"\x01\0\0\0");
```
When manually implementing
[`BinRead::read_options`](crate::BinRead::read_options)[`BinWrite::write_options`](crate::BinWrite::write_options) or a
[custom parserwriter function](#custom-parserswriters),
the byte order is accessible from the `endian` parameter.
## Examples
### Mixed endianness in one object
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
#[br(little)]
struct MyType (
#[br(big)] u32, // ← will be big-endian
u32, // ← will be little-endian
);
# assert_eq!(MyType::read(&mut Cursor::new(b"\0\0\0\x01\x01\0\0\0")).unwrap(), MyType(1, 1));
```
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinWrite)]
#[bw(little)]
struct MyType (
#[bw(big)] u32, // ← will be big-endian
u32, // ← will be little-endian
);
# let object = MyType(1, 1);
# let mut output = Cursor::new(vec![]);
# object.write(&mut output).unwrap();
# assert_eq!(output.into_inner(), b"\0\0\0\x01\x01\0\0\0");
```
**These directives can only be used with [`binwrite`](macro@crate::binwrite).
They will not work with `#[derive(BinWrite)]`.**
**When using these directives, the `#[binwrite]` attribute must be placed
*before* other attributes like `#[derive(Debug)]`. Otherwise, the other
attributes will generate code that references non-existent fields, and
compilation will fail.**
The `calc` and `try_calc` directives compute the value of a field instead of
reading data from the readerwriting from
a struct field:
```text
#[br(calc = $value:expr)] or #[br(calc($value:expr))]
#[br(try_calc = $value:expr)] or #[br(try_calc($value:expr))]
```
```text
#[bw(calc = $value:expr)] or #[bw(calc($value:expr))]
#[bw(try_calc = $value:expr)] or #[bw(try_calc($value:expr))]
```
Any (earlier only, when reading)earlier
field or [import](#arguments) can be referenced by the expression in the
directive.
When using `try_calc`, the produced value must be a [`Result`](Result).
Since the field is treated as a temporary variable instead of an actual
field, when deriving `BinRead`, the field should also be annotated with
`#[br(temp)]`.
```
# use binrw::{prelude::*, io::Cursor};
#[binwrite] // ← must be before other attributes that use struct fields
#[bw(big)]
struct MyType {
var: u32,
#[bw(calc = var - 3)]
var_minus_3: u32,
}
let object = MyType { var: 4 };
let mut output = Cursor::new(vec![]);
object.write(&mut output).unwrap();
assert_eq!(output.into_inner(), b"\0\0\0\x04\0\0\0\x01");
```
```
# use binrw::{prelude::*, io::Cursor};
#[binwrite] // ← must be before other attributes that use struct fields
#[bw(big)]
struct MyType {
var: u32,
#[bw(try_calc = u16::try_from(var - 3))]
var_minus_3: u16,
}
let object = MyType { var: 4 };
let mut output = Cursor::new(vec![]);
object.write(&mut output).unwrap();
assert_eq!(output.into_inner(), b"\0\0\0\x04\0\x01");
```
# Conditional values
The `if` directive allows conditional
parsingserialisation of a field,
readingwriting data if the
condition is true and optionally using a computed value if the condition is
false:
If an alternate is provided, that value will be used when the condition is
false; otherwise,
for reads, the [`default`](core::default::Default) value for
the type will be used, and for writes, no data will be written.the [`default`](core::default::Default) value for the type
will be used.no data will be written.
The alternate expression is not evaluated unless the condition is false, so
it is safe for it to contain expensive operations without impacting
performance.
Any (earlier only, when reading)earlier
field or [import](#arguments) can be referenced by the
expression in the directive.
The [`map`](#map) directive can also be used to conditionally
write a field by returning an [`Option`], where a [`None`] value skips
writing.
## Examples
### Reading an [`Option`] field with no alternate
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
struct MyType {
var: u32,
#[br(if(var == 1))]
original_byte: Option,
#[br(if(var != 1))]
other_byte: Option,
}
# assert_eq!(Cursor::new(b"\0\0\0\x01\x03").read_be::().unwrap().original_byte, Some(3));
# assert_eq!(Cursor::new(b"\0\0\0\x01\x03").read_be::().unwrap().other_byte, None);
```
### Reading a scalar field with an explicit alternate
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
struct MyType {
var: u32,
#[br(if(var == 1, 0))]
original_byte: u8,
#[br(if(var != 1, 42))]
other_byte: u8,
}
# assert_eq!(Cursor::new(b"\0\0\0\x01\x03").read_be::().unwrap().original_byte, 3);
# assert_eq!(Cursor::new(b"\0\0\0\x01\x03").read_be::().unwrap().other_byte, 42);
```
### Skipping a scalar field with no alternate
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinWrite)]
struct Test {
x: u8,
#[bw(if(*x > 1))]
y: u8,
}
let mut output = Cursor::new(vec![]);
output.write_be(&Test { x: 1, y: 3 }).unwrap();
assert_eq!(output.into_inner(), b"\x01");
let mut output = Cursor::new(vec![]);
output.write_be(&Test { x: 2, y: 3 }).unwrap();
assert_eq!(output.into_inner(), b"\x02\x03");
```
### Writing a scalar field with an explicit alternate
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinWrite)]
struct Test {
x: u8,
#[bw(if(*x > 1, 0))]
y: u8,
}
let mut output = Cursor::new(vec![]);
output.write_be(&Test { x: 1, y: 3 }).unwrap();
assert_eq!(output.into_inner(), b"\x01\0");
let mut output = Cursor::new(vec![]);
output.write_be(&Test { x: 2, y: 3 }).unwrap();
assert_eq!(output.into_inner(), b"\x02\x03");
```
# Count
The `count` directive is a shorthand for passing a `count` argument to a
type that accepts it as a named argument:
```text
#[br(count = $count:expr) or #[br(count($count:expr))]
```
It desugars to:
```text
#[br(args { count: $count as usize })]
```
This directive is most commonly used with [`Vec`], which accepts `count`
and `inner` arguments through its
[associated `VecArgs` type](crate::VecArgs).
When manually implementing
[`BinRead::read_options`](crate::BinRead::read_options) or a
[custom parser function](#custom-parserswriters), the `count` value is accessible
from a named argument named `count`.
Any earlier field or [import](#arguments) can be referenced by the
expression in the directive.
## Examples
### Using `count` with [`Vec`]
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
struct Collection {
size: u32,
#[br(count = size)]
data: Vec,
}
# assert_eq!(
# Cursor::new(b"\0\0\0\x04\x01\x02\x03\x04").read_be::().unwrap().data,
# &[1u8, 2, 3, 4]
# );
```
### Using `count` with a [`Vec`] with arguments for the inner type
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
#[br(import(version: i16))]
struct Inner(#[br(if(version > 0))] u8);
#[derive(BinRead)]
struct Collection {
version: i16,
size: u32,
#[br(count = size, args { inner: (version,) })]
data: Vec,
}
# assert_eq!(
# Cursor::new(b"\0\x01\0\0\0\x04\x01\x02\x03\x04").read_be::().unwrap().data,
# &[Inner(1), Inner(2), Inner(3), Inner(4)]
# );
```
# Custom parserswriters
The `parse_with` directive specifies a custom parsing function which can be
used to override the default [`BinRead`](crate::BinRead) implementation for
a type, or to parse types which have no `BinRead` implementation at all:
```text
#[br(parse_with = $parse_fn:expr)] or #[br(parse_with($parse_fn:expr))]
```
Use the [`#[parser]`](crate::parser) attribute macro to create compatible
functions.
Any earlier field or [import](#arguments) can be referenced by the
expression in the directive (for example, to construct a parser function at
runtime by calling a function generator).
The `write_with` directive specifies a custom serialisation function which
can be used to override the default [`BinWrite`](crate::BinWrite)
implementation for a type, or to serialise types which have no `BinWrite`
implementation at all:
```text
#[bw(write_with = $write_fn:expr)] or #[bw(write_with($write_fn:expr))]
```
Use the [`#[writer]`](crate::writer) attribute macro to create compatible
functions.
Any field or [import](#arguments) can be referenced by the expression in the
directive (for example, to construct a serialisation function at runtime by
calling a function generator).
## Examples
### Using a custom parser to generate a [`HashMap`](std::collections::HashMap)
```
# use binrw::{prelude::*, io::{prelude::*, Cursor}, Endian};
# use std::collections::HashMap;
#[binrw::parser(reader, endian)]
fn custom_parser() -> BinResult> {
let mut map = HashMap::new();
map.insert(
<_>::read_options(reader, endian, ())?,
<_>::read_options(reader, endian, ())?,
);
Ok(map)
}
#[derive(BinRead)]
#[br(big)]
struct MyType {
#[br(parse_with = custom_parser)]
offsets: HashMap
}
# assert_eq!(Cursor::new(b"\0\0\0\x01").read_be::().unwrap().offsets.get(&0), Some(&1));
```
### Using a custom serialiser to write a [`BTreeMap`](std::collections::BTreeMap)
```
# use binrw::{prelude::*, io::{prelude::*, Cursor}, Endian};
# use std::collections::BTreeMap;
#[binrw::writer(writer, endian)]
fn custom_writer(
map: &BTreeMap,
) -> BinResult<()> {
for (key, val) in map.iter() {
key.write_options(writer, endian, ())?;
val.write_options(writer, endian, ())?;
}
Ok(())
}
#[derive(BinWrite)]
#[bw(big)]
struct MyType {
#[bw(write_with = custom_writer)]
offsets: BTreeMap
}
let object = MyType {
offsets: BTreeMap::from([(0, 1), (2, 3)]),
};
let mut output = Cursor::new(vec![]);
object.write(&mut output).unwrap();
assert_eq!(output.into_inner(), b"\0\0\0\x01\0\x02\0\x03");
```
### Using `FilePtr::parse` to read a `NullString` without storing a `FilePtr`
```
# use binrw::{prelude::*, io::Cursor, FilePtr32, NullString};
#[derive(BinRead)]
struct MyType {
#[br(parse_with = FilePtr32::parse)]
some_string: NullString,
}
# let val: MyType = Cursor::new(b"\0\0\0\x04Test\0").read_be().unwrap();
# assert_eq!(val.some_string.to_string(), "Test");
```
# Debug
The `dbg` directive prints the offset and value of a field to
[`stderr`](std::io::stderr) for quick and dirty debugging:
```text
#[br(dbg)]
```
The type of the field being inspected must implement [`Debug`](std::fmt::Debug).
Non-nightly Rust versions without support for
[`proc_macro_span`](https://github.com/rust-lang/rust/issues/54725) will emit
line numbers pointing to the binrw attribute on the parent struct or enum rather
than the field.
## Examples
```
# #[cfg(not(feature = "std"))] fn main() {}
# #[cfg(feature = "std")]
# fn main() {
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead, Debug)]
# #[derive(PartialEq)]
#[br(little)]
struct Inner {
#[br(dbg)]
a: u32,
#[br(dbg)]
b: u32,
}
#[derive(BinRead, Debug)]
# #[derive(PartialEq)]
#[br(little)]
struct Test {
first: u16,
#[br(dbg)]
inner: Inner,
}
// prints:
//
// [file.rs:5 | offset 0x2] a = 0x10
// [file.rs:7 | offset 0x6] b = 0x40302010
// [file.rs:15 | offset 0x2] inner = Inner {
// a: 0x10,
// b: 0x40302010,
// }
# assert_eq!(
Test::read(&mut Cursor::new(b"\x01\0\x10\0\0\0\x10\x20\x30\x40")).unwrap(),
# Test { first: 1, inner: Inner { a: 0x10, b: 0x40302010 } }
# );
# }
```
# Enum errors
The `return_all_errors` (default) and `return_unexpected_error` directives
define how to handle errors when parsing an enum:
```text
#[br(return_all_errors)]
#[br(return_unexpected_error)]
```
`return_all_errors` collects the errors that occur when enum variants fail
to parse and returns them in [`binrw::Error::EnumErrors`] when no variants
parse successfully:
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug)]
#[br(return_all_errors)]
enum Test {
#[br(magic(0u8))]
A { a: u8 },
B { b: u32 },
C { #[br(assert(c != 1))] c: u8 },
}
let error = Test::read_le(&mut Cursor::new(b"\x01")).unwrap_err();
if let binrw::Error::EnumErrors { pos, variant_errors } = error {
assert_eq!(pos, 0);
assert!(matches!(variant_errors[0], ("A", binrw::Error::BadMagic { .. })));
assert!(matches!(
(variant_errors[1].0, variant_errors[1].1.root_cause()),
("B", binrw::Error::Io(..))
));
assert!(matches!(variant_errors[2], ("C", binrw::Error::AssertFail { .. })));
}
# else {
# panic!("wrong error type");
# }
```
`return_unexpected_error` discards the errors and instead returns a generic
[`binrw::Error::NoVariantMatch`] if all variants fail to parse. This avoids
extra memory allocations required to collect errors, but only provides the
position when parsing fails:
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug)]
#[br(return_unexpected_error)]
enum Test {
#[br(magic(0u8))]
A { a: u8 },
B { b: u32 },
C { #[br(assert(c != 1))] c: u8 },
}
let error = Test::read_le(&mut Cursor::new(b"\x01")).unwrap_err();
if let binrw::Error::NoVariantMatch { pos } = error {
assert_eq!(pos, 0);
}
# else {
# panic!("wrong error type");
# }
```
# Ignore
For [`BinRead`](crate::BinRead), the `ignore` directive, and its alias
`default`, sets the value of the field to its
[`Default`] instead of reading data from the reader:
```text
#[br(default)] or #[br(ignore)]
```
For [`BinWrite`](crate::BinWrite), the `ignore` directive skips writing the
field to the writer:
```text
#[bw(ignore)]
```
## Examples
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
struct Test {
#[br(ignore)]
path: Option,
}
assert_eq!(
Test::read_le(&mut Cursor::new(b"")).unwrap(),
Test { path: None }
);
```
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinWrite)]
struct Test {
a: u8,
#[bw(ignore)]
b: u8,
c: u8,
}
let object = Test { a: 1, b: 2, c: 3 };
let mut output = Cursor::new(vec![]);
object.write_le(&mut output).unwrap();
assert_eq!(
output.into_inner(),
b"\x01\x03"
);
```
# Magic
The `magic` directive matches [magic numbers](https://en.wikipedia.org/wiki/Magic_number_(programming))
in data:
```text
#[br(magic = $magic:literal)] or #[br(magic($magic:literal))]
```
```text
#[bw(magic = $magic:literal)] or #[bw(magic($magic:literal))]
```
The magic number can be a byte literal, byte string, float, or integer. When
a magic number is matched, parsing begins with the first byte after the
magic number in the data. When a magic number is not matched, an error is
returned.
## Examples
### Using byte strings
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
#[br(magic = b"TEST")]
struct Test {
val: u32
}
# assert_eq!(
Test::read_le(&mut Cursor::new(b"TEST\0\0\0\0"))
# .unwrap(), Test { val: 0 });
```
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinWrite)]
# #[derive(Debug, PartialEq)]
#[bw(magic = b"TEST")]
struct Test {
val: u32
}
let object = Test { val: 0 };
let mut output = Cursor::new(vec![]);
object.write_le(&mut output)
# .unwrap();
# assert_eq!(output.into_inner(), b"TEST\0\0\0\0");
```
## Errors
If the specified magic number does not match the data, a
[`BadMagic`](crate::Error::BadMagic) error is returned and the reader’s
position is reset to where it was before parsing started.
# Map
The `map` and `try_map` directives allow data to be read using one type and
stored as another:
```text
#[br(map = $map_fn:expr)] or #[br(map($map_fn:expr)))]
#[br(try_map = $map_fn:expr)] or #[br(try_map($map_fn:expr)))]
```
```text
#[bw(map = $map_fn:expr)] or #[bw(map($map_fn:expr)))]
#[bw(try_map = $map_fn:expr)] or #[bw(try_map($map_fn:expr)))]
```
When using `#[br(map)]` on a field, the map function must
explicitly declare the type of the data to be read in its first parameter
and return a value which matches the type of the field.
When using `#[bw(map)]` on a field, the map function will receive
an immutable reference to the field value and must return a type which
implements [`BinWrite`](crate::BinWrite).When using `map` on a field, the map function must
explicitly declare the type of the data to be read in its first parameter
and return a value which matches the type of the field.When using `map` on a field, the map function will receive
an immutable reference to the field value and must return a type which
implements [`BinWrite`](crate::BinWrite).
The map function can be a plain function, closure, or call expression which
returns a plain function or closure.
When using `try_map`, the same rules apply, except that the function must
return a [`Result`](Result) instead.
When using `map` or `try_map` on a struct or enum, the map function
must return `Self` (`map`) or `Result` (`try_map`)
for `BinRead`. For `BinWrite`, it will receive an immutable reference to the
entire object and must return a type that implements [`BinWrite`](crate::BinWrite).must return `Self` (`map`) or `Result` (`try_map`).will receive an immutable reference to the entire object
and must return a type that implements [`BinWrite`](crate::BinWrite).
Any (earlier only, when reading)earlier
field or [import](#arguments) can be
referenced by the expression in the directive.
## Examples
### Using `map` on a field
### Using `map` on a struct to create a bit field
The [`modular-bitfield`](https://docs.rs/modular-bitfield) crate can be used
along with `map` to create a struct out of raw bits.
```
# use binrw::{prelude::*, io::Cursor};
use modular_bitfield::prelude::*;
// This reads a single byte from the reader
#[bitfield]
#[derive(BinRead)]
#[br(map = Self::from_bytes)]
pub struct PackedData {
status: B4,
is_fast: bool,
is_static: bool,
is_alive: bool,
is_good: bool,
}
// example byte: 0x53
// [good] [alive] [static] [fast] [status]
// 0 1 0 1 0011
// false true false true 3
let data = Cursor::new(b"\x53").read_le::().unwrap();
assert_eq!(data.is_good(), false);
assert_eq!(data.is_alive(), true);
assert_eq!(data.is_static(), false);
assert_eq!(data.is_fast(), true);
assert_eq!(data.status(), 3);
```
```
# use binrw::{prelude::*, io::Cursor};
use modular_bitfield::prelude::*;
// The cursor dumps a single byte
#[bitfield]
#[derive(BinWrite, Clone, Copy)]
#[bw(map = |&x| Self::into_bytes(x))]
pub struct PackedData {
status: B4,
is_fast: bool,
is_static: bool,
is_alive: bool,
is_good: bool,
}
let object = PackedData::new()
.with_is_alive(true)
.with_is_fast(true)
.with_status(3);
let mut output = Cursor::new(vec![]);
output.write_le(&object).unwrap();
assert_eq!(output.into_inner(), b"\x53");
```
## Errors
If the `try_map` function returns a [`binrw::io::Error`](crate::io::Error)
or [`std::io::Error`], an [`Io`](crate::Error::Io) error is returned. For
any other error type, a [`Custom`](crate::Error::Custom) error is returned.
In all cases, the
reader’swriter’s position is
reset to where it was before parsing started.
# Offset
The `offset` directive is shorthand for passing `offset` to a parser that
operates like [`FilePtr`](crate::FilePtr):
```text
#[br(offset = $offset:expr)] or #[br(offset($offset:expr))]
```
When manually implementing
[`BinRead::read_options`](crate::BinRead::read_options) or a
[custom parser function](#custom-parserswriters), the offset is accessible
from a named argument named `offset`.
Any (earlier only, when reading)earlier
field or [import](#arguments) can be
referenced by the expression in the directive.
## Examples
```
# use binrw::{prelude::*, io::Cursor, FilePtr};
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
#[br(little)]
struct OffsetTest {
#[br(offset = 4)]
test: FilePtr
}
# assert_eq!(
# *OffsetTest::read(&mut Cursor::new(b"\0\xFF\xFF\xFF\x02\0")).unwrap().test,
# 2u16
# );
```
## Errors
If seeking to or reading from the offset fails, an [`Io`](crate::Error::Io)
error is returned and the reader’s position is reset to where it was before
parsing started.
# Padding and alignment
binrw includes directives for common forms of
[data structure alignment](https://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding).
The `pad_before` and `pad_after` directives skip a specific number of bytes
either before or after
readingwriting a field,
respectively:
```text
#[br(pad_after = $skip_bytes:expr)] or #[br(pad_after($skip_bytes:expr))]
#[br(pad_before = $skip_bytes:expr)] or #[br(pad_before($skip_bytes:expr))]
```
```text
#[bw(pad_after = $skip_bytes:expr)] or #[bw(pad_after($skip_bytes:expr))]
#[bw(pad_before = $skip_bytes:expr)] or #[bw(pad_before($skip_bytes:expr))]
```
This is equivalent to:
```
# let mut pos = 0;
# let padding = 0;
pos += padding;
```
---
The `align_before` and `align_after` directives align the next
readwrite to the
given byte alignment either before or after
readingwriting a field,
respectively:
```text
#[br(align_after = $align_to:expr)] or #[br(align_after($align_to:expr))]
#[br(align_before = $align_to:expr)] or #[br(align_before($align_to:expr))]
```
```text
#[bw(align_after = $align_to:expr)] or #[bw(align_after($align_to:expr))]
#[bw(align_before = $align_to:expr)] or #[bw(align_before($align_to:expr))]
```
This is equivalent to:
```
# let mut pos = 0;
# let align = 1;
if pos % align != 0 {
pos += align - (pos % align);
}
```
---
The `seek_before` directive accepts a [`SeekFrom`](crate::io::SeekFrom)
object and seeks the
readerwriter to an arbitrary
position before
readingwriting a field:
```text
#[br(seek_before = $seek_from:expr)] or #[br(seek_before($seek_from:expr))]
```
```text
#[bw(seek_before = $seek_from:expr)] or #[bw(seek_before($seek_from:expr))]
```
This is equivalent to:
```
# use binrw::io::Seek;
# let mut stream = binrw::io::Cursor::new(vec![]);
# let seek_from = binrw::io::SeekFrom::Start(0);
stream.seek(seek_from)?;
# Ok::<(), binrw::io::Error>(())
```
The position of the
readerwriter will not be
restored after the seek; use the
[`restore_position`](#restore-position) directive to seek, then
readwrite, then restore
position.
---
The `pad_size_to` directive will ensure that the
readerwriter has advanced at
least the number of bytes given after the field has been
readwritten:
```text
#[br(pad_size_to = $size:expr)] or #[br(pad_size_to($size:expr))]
```
```text
#[bw(pad_size_to = $size:expr)] or #[bw(pad_size_to($size:expr))]
```
For example, if a format uses a null-terminated string, but always reserves
at least 256 bytes for that string, [`NullString`](crate::NullString) will
read the string and `pad_size_to(256)` will ensure the reader skips whatever
padding, if any, remains. If the string is longer than 256 bytes, no padding
will be skipped.
Any (earlier only, when reading)earlier
field or [import](#arguments) can be
referenced by the expressions in any of these directives.
## Examples
## Errors
If seeking fails, an [`Io`](crate::Error::Io) error is returned and the
reader’swriter’s position is
reset to where it was before
parsingserialisation
started.
# Pre-assert
`pre_assert` works like [`assert`](#assert), but checks the condition before
data is read instead of after:
```text
#[br(pre_assert($cond:expr $(,)?))]
#[br(pre_assert($cond:expr, $msg:literal $(,)?)]
#[br(pre_assert($cond:expr, $fmt:literal, $($arg:expr),* $(,)?))]
#[br(pre_assert($cond:expr, $err:expr $(,)?)]
```
This is most useful when validating arguments or selecting an enum variant.
## Examples
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
#[br(import { ty: u8 })]
enum Command {
#[br(pre_assert(ty == 0))] Variant0(u16, u16),
#[br(pre_assert(ty == 1))] Variant1(u32)
}
#[derive(BinRead)]
# #[derive(Debug, PartialEq)]
struct Message {
ty: u8,
len: u8,
#[br(args { ty })]
data: Command
}
let msg = Cursor::new(b"\x01\x04\0\0\0\xFF").read_be::();
assert!(msg.is_ok());
let msg = msg.unwrap();
assert_eq!(msg, Message { ty: 1, len: 4, data: Command::Variant1(0xFF) });
```
# Repr
The `repr` directive is used on a unit-like (C-style) enum to specify the
underlying type to use when
readingwriting the
field and matching variants:
```text
#[br(repr = $ty:ty)] or #[br(repr($ty:ty))]
```
```text
#[bw(repr = $ty:ty)] or #[bw(repr($ty:ty))]
```
## Errors
If a readwrite fails, an
[`Io`](crate::Error::Io) error is returned. If no variant
matches, a [`NoVariantMatch`](crate::Error::NoVariantMatch) error is
returned.
In all cases, the
reader’swriter’s position is
reset to where it was before
parsingserialisation
started.
# Restore position
The `restore_position` directive restores the position of the
readerwriter after a field
is readwritten:
```text
#[br(restore_position)]
```
```text
#[bw(restore_position)]
```
To seek to an arbitrary position, use [`seek_before`](#padding-and-alignment)
instead.
## Examples
## Errors
If querying or restoring the
readerwriter position fails,
an [`Io`](crate::Error::Io) error is returned and the
reader’swriter’s
position is reset to where it was before
parsingserialisation
started.
# Stream access and manipulation
The `stream` directive allows direct access to the underlying
readwrite stream on a struct or
enum:
```text
#[br(stream = $ident:ident)] or #[br(stream($ident:ident))]
```
```text
#[bw(stream = $ident:ident)] or #[bw(stream($ident:ident))]
```
The `map_stream` directive allows the readwrite
stream to be replaced with another stream when readingwriting
an object or field:
```text
#[br(map_stream = $map_fn:expr)] or #[br(map_stream($map_fn:expr))]
```
```text
#[bw(map_stream = $map_fn:expr)] or #[bw(map_stream($map_fn:expr))]
```
The map function can be a plain function, closure, or call expression which
returns a plain function or closure. The returned object must
implement [`Read`](crate::io::Read) + [`Seek`](crate::io::Seek) (for reading)
or [`Write`](crate::io::Write) + [`Seek`](crate::io::Seek) (for writing).
The mapped stream is used to readwrite
the *contents* of the field or object it is applied to:
```
# use binrw::{prelude::*, io::{TakeSeek, TakeSeekExt}};
fn make_stream(s: S) -> /* … */
/* … */
# S {
# s
}
#[derive(BinRead)]
// `magic` does not use the mapped stream
#[br(magic = b"foo", map_stream = make_stream)]
struct Foo {
// everything inside the struct uses the mapped stream
a: u32,
/* … */
}
#[derive(BinRead)]
struct Bar {
// `pad_before` and `pad_after` do not use the mapped stream
#[br(pad_before(4), pad_after(4), map_stream = make_stream)]
b: u64 // reading `u64` uses the mapped stream
}
```
```
# use binrw::{prelude::*, io::{TakeSeek, TakeSeekExt}};
fn make_stream(s: S) -> /* … */
/* … */
# S {
# s
}
#[derive(BinWrite)]
// `magic` does not use the mapped stream
#[bw(magic = b"foo", map_stream = make_stream)]
struct Foo {
// everything inside the struct uses the mapped stream
a: u32,
/* … */
}
#[derive(BinWrite)]
struct Bar {
// `pad_before` and `pad_after` do not use the mapped stream
#[bw(pad_before(4), pad_after(4), map_stream = make_stream)]
b: u64 // writing `u64` uses the mapped stream
}
```
# Temp
**This directive can only be used with [`binread`](macro@crate::binread). It
will not work with `#[derive(BinRead)]`.**
**When using `#[br(temp)]`, the `#[binread]` attribute must be placed *before*
other attributes like `#[derive(Debug)]`. Otherwise, the other attributes will
generate code that references non-existent fields, and compilation will fail.**
The `temp` directive causes a field to be treated as a temporary variable
instead of an actual field. The field will be removed from the struct
definition generated by [`binread`](macro@crate::binread):
```text
#[br(temp)]
```
This allows data to be read which is necessary for parsing an object but
which doesn’t need to be stored in the final object. To skip data entirely,
use an [alignment directive](#padding-and-alignment) instead.
## Examples
```
# use binrw::{BinRead, io::Cursor, binread};
#[binread] // ← must be before other attributes that use struct fields
# #[derive(Debug, PartialEq)]
#[br(big)]
struct Test {
// Since `Vec` stores its own length, this field is redundant
#[br(temp)]
len: u32,
#[br(count = len)]
data: Vec
}
assert_eq!(
Test::read(&mut Cursor::new(b"\0\0\0\x05ABCDE")).unwrap(),
Test { data: b"ABCDE".to_vec() }
);
```
# Try
The `try` directive allows parsing of a field to fail instead
of returning an error:
```text
#[br(try)]
```
If the field cannot be parsed, the position of the reader will be restored
and the value of the field will be set to the [`default`](core::default::Default) value for the type.
## Examples
```
# use binrw::{prelude::*, io::Cursor};
#[derive(BinRead)]
struct MyType {
#[br(try)]
maybe_u32: Option
}
assert_eq!(Cursor::new(b"").read_be::().unwrap().maybe_u32, None);
```