Implementing Format
#[derive(Format)]
The easiest way to implement the Format trait for a struct or enum is to use the derive attribute.
#![allow(unused)] fn main() { extern crate defmt; use defmt::Format; #[derive(Format)] struct Header { source: u8, destination: u8, sequence: u16, } #[derive(Format)] struct Descriptor; #[derive(Format)] enum Request { GetDescriptor { descriptor: Descriptor, length: u16 }, SetAddress { address: u8 }, } }
Like built-in derives (e.g. #[derive(Debug)]), #[derive(Format)] will add Format bounds to the generic type parameters of the struct.
⚠️ Do not use the API used by the expansion of the
derive(Format)macro; it is unstable.
Feature-gated #[derive(Format)]
It is also possible to feature-gate the implementation by defining
derive implementation via cfg_attr(feature = ...):
#![allow(unused)] fn main() { #[derive(Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] struct Header { // ... } }
Manual implementation with write!
It is also possible to implement the Format trait manually.
This trait has a single required method: format.
In this method you need to format the value (self) into the given Formatter argument using the defmt::write! macro.
Example below:
#![allow(unused)] fn main() { extern crate defmt; // value read from a MMIO register named "CRCCNF" struct CRCCNF { bits: u32, } impl defmt::Format for CRCCNF { fn format(&self, f: defmt::Formatter) { // format the bitfields of the register as struct fields defmt::write!( f, "CRCCNF {{ LEN: {0=0..2}, SKIPADDR: {0=8..10} }}", self.bits, ) } } }
Newtypes
If you need to implement Format for some "newtype" struct you can delegate the formatting to the inner type.
Example below:
#![allow(unused)] fn main() { extern crate defmt; struct MyU8 { inner: u8 } impl defmt::Format for MyU8 { fn format(&self, f: defmt::Formatter) { self.inner.format(f) } } }
Uncompressed adapters
If you quickly want to get some code running and do not care about it being efficient you can use the two adapter types Display2Format and Debug2Format.
⚠️ These adapters disable compression and use the
core::fmtcode on-device! You should always preferdefmt::FormatoverDebugwhenever possible!
Note that this always uses {:?} to format the contained value, meaning that any provided defmt display hints will be ignored.
When using #[derive(Format)] you may use the #[defmt()] attribute on specific fields to use these adapter types.
Example below:
#![allow(unused)] fn main() { extern crate defmt; use defmt::Format; mod serde_json { #[derive(Debug)] pub enum Error {} } #[derive(Format)] enum Error { Serde(#[defmt(Debug2Format)] serde_json::Error), ResponseTooLarge, } struct Celsius(); impl std::fmt::Display for Celsius { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { Ok(()) } } #[derive(Format)] struct Reading { #[defmt(Display2Format)] temperature: Celsius, } }