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

MethodMeaning
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

NeedAPI
Full frame after finishing the walkcomplete stage as_bytes_with_header()
Full frame without consuming stagesinherent 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.