Consuming Decode Stages
Groups and var-data are consumed in schema order. finish() hands the next
named stage back to you:
#![allow(unused)] fn main() { pub fn demo_car_decode_stages(wire: &[u8]) -> Result<(), Box<dyn std::error::Error>> { let car = CarDecoder::try_decode(wire, 0)?; assert_eq!(car.serial_number(), 1234); assert_eq!(car.model_year(), 2013); // Domain conversion: BooleanType → bool when configured. let available: bool = car.try_available()?; assert!(available); assert_eq!(car.code(), Model::A); assert_eq!(car.discounted_model(), Model::C); // constant field assert_eq!(car.engine().capacity(), 2000); // Consuming stages enforce fuelFigures → performanceFigures → strings. let mut fuel = car.into_fuel_figures()?; let mut speeds = Vec::new(); for entry in &mut fuel { speeds.push(entry?.speed()); } assert_eq!(speeds, vec![30, 60]); let decoder = fuel.finish()?; let mut decoder = decoder.into_performance_figures()?; let mut octanes = Vec::new(); for entry in &mut decoder { let e = entry?; octanes.push(e.octane_rating()); let mut acc = e.into_acceleration()?; let mut mphs = Vec::new(); for a in &mut acc { mphs.push(a.mph()); } assert_eq!(mphs, vec![30, 60]); let _ = acc.finish()?; } assert_eq!(octanes, vec![95]); let decoder = decoder.finish()?; let (mfr, decoder) = decoder.into_manufacturer_as_str()?; let (model, decoder) = decoder.into_model_as_str()?; let (code, _decoder) = decoder.into_activation_code_as_str()?; // All three &str coexist — each borrows 'a from the original wire buffer. assert_eq!((mfr, model, code), ("Honda", "Civic VTi", "abcdef")); Ok(()) } }
Each into_*_as_str() returns (&'a str, NextStage<'a>) — the &str borrows
from the original wire buffer, not from the consumed stage. All three strings
remain valid simultaneously while the stage chain advances.
(This code comes from the sbe-feature-tour sample crate.)
#[must_use] on stages
Consuming stages (CarDecoderAfterFuelFigures, …AfterManufacturer,
CarDecoderComplete, …) are #[must_use]. Dropping a stage without
into_* / finish / skip_remaining silently skips remaining wire
tails (groups and var-data). That is easy to miss when a function returns
early — prefer advancing until Complete or an explicit skip.
finish vs skip_remaining
| Method | Meaning |
|---|---|
finish() | Advance past any remaining entries of the current group and hand back the next named stage (or complete). |
skip_remaining() | Explicit sequential spelling of the same idea — “I am done with this group; jump to the next tail.” |
Use skip_remaining when you want the intent obvious in review; both move the
tail cursor in wire order.
Full-frame bytes mid-walk
| Need | API |
|---|---|
| Full frame after finishing the walk | complete stage as_bytes_with_header() |
| Full frame without consuming stages | inherent dec.as_bytes_with_header()? (rescans tails) |
| Fixed block only (not a full frame) | dec.get_metadata().as_fixed_region_with_header()? |
See Generated code for the
metadata limit vs full-frame table.