Skip to main content

ConstWriteXDR

Derive Macro ConstWriteXDR 

Source
#[derive(ConstWriteXDR)]
{
    // Attributes available to this derive:
    #[xdr]
    #[xdrs]
}
Available on crate feature derive only.
Expand description

Derives const-compatible XDR encoding methods for compile-time use.

Generates four inherent const fn methods on the type:

  • const_write_cxs<const N: usize>(&self, w: &mut cxs::ConstWriter<N>)
  • const_write_txs<const N: usize>(&self, w: &mut txs::ConstWriter<N>)
  • const_cxs_size(&self) -> usize
  • const_txs_size(&self) -> usize

These methods can be used in const blocks to encode values at compile time. The size methods allow the buffer size to be derived automatically:

ⓘ
static ENCODED: [u8; MY_VAL.const_txs_size()] = {
    let mut w = xdrs::txs::ConstWriter::<{ MY_VAL.const_txs_size() }>::new();
    MY_VAL.const_write_txs(&mut w);
    w.into_bytes()
};

For worst-case size bounds over all values of a type (buffer sizing before a value is known), see #[derive(ConstXDRSize)].

§Limitations

  • Does not support #[xdr(into_type)] or #[xdr(via)] attributes on the derive input, struct fields, or enum variant fields. The via/into_type conversions require Into::into() which is not const fn.
  • Supports lifetime and const generics, but not type parameters
  • Field types may use recursive concrete shapes inline, including Option<T>, Result<T, E>, &[T], [T; N], Box<T>, VecM<T, MAX>, BytesM<MAX>, StringM<MAX>, and tuples, as long as leaf types are concrete and const-encodable
  • With the alloc feature, non-empty VecM/BytesM/StringM values cannot be constructed in const contexts (const heap allocation is not possible), so const encoding of non-empty values of these types is only available in no-alloc builds, where they wrap &'static [T]
  • Likewise Box<T> values can only be constructed in const contexts in no-alloc builds, where Box::new is const and the value is inline; with alloc the generated methods still compile and run at runtime

§Generated code hygiene

The generated const fn methods are syntax-directed: type recognition uses raw identifiers rather than Rust’s resolved types, and generated code emits unqualified names into the call site scope. This means:

  • Use bare primitive names. u32 is recognized; core::primitive::u32 is not and will be treated as a named leaf type.
  • Do not shadow built-in types. A local type u8 = MyType; will cause the opaque byte fast-path to misfire. Similarly, shadowing str will confuse string detection.
  • Length guards use ::core::assert!() to resist call-site macro shadowing.
  • Do not use __N-prefixed names in discriminant expressions. The generated methods introduce a buffer-size const generic with an __N-prefixed name; discriminant expressions referencing a same-named constant will resolve to the wrong binding.
  • Avoid naming const generics i, arr, slice, or total. The generated array/slice iteration uses these as local bindings, and Rust does not allow a let binding to shadow an in-scope const parameter.
  • Avoid naming modules cxs or txs in discriminant paths. The generated const block imports use xdrs::cxs; use xdrs::txs;, which can capture discriminant expressions whose paths start with those segments.

§Standard library type recognition

The macro recognizes Option, Result, Box, PhantomData, Infallible, and the xdr_common container types VecM, BytesM, and StringM by their last path segment (e.g. both Option<T> and std::option::Option<T> match). This is a syntactic check — type aliases are not resolved. A field declared as type MyOpt = Option<Foo>; .. my_field: MyOpt will not be recognized as an Option and will instead be treated as a named leaf whose type must provide its own const_cxs_size / const_write_cxs / etc. inherent methods (i.e. it must itself derive ConstWriteXDR or provide them manually). The same applies to PhantomData — if you need a type-alias, wrap it in a newtype and derive ConstWriteXDR on the newtype instead.