I have the following code:
/// Variable settings for [`Bandpass`] block
///
/// These settings may be passed as `BandpassParams` struct to
/// [`Bandpass::new`] or the field names can be used with the [`bandpass!`]
/// macro.
#[derive(Clone, Debug)]
pub struct BandpassParams {
/// Lower end of passband in hertz (must have positive sign)
pub low_freq: f64,
/// Upper end of passband in hertz (must have positive sign)
pub high_freq: f64,
/// Select whether lower, upper, or both side bands should pass
pub band: Band,
/// Blur frequency response up to ±`blur * sample_rate / chunk_len` hertz (first null)
pub blur: f64,
/// Parameter τ for RC lowpass (deemphasis)
pub deemphasis: f64,
}
impl Default for BandpassParams {
/// Default settings
/// (all frequencies pass, `blur` set to `2.0`, deemphasis disabled)
fn default() -> Self {
Self {
low_freq: 0.0,
high_freq: f64::INFINITY,
band: Band::Dual,
blur: 2.0,
deemphasis: 0.0,
}
}
}
pub struct Bandpass {
receiver: Receiver<Chunk<Complex<f32>>>,
sender: Sender<Chunk<Complex<f32>>>,
params: watch::Sender<BandpassParams>,
}
#[macro_export]
macro_rules! bandpass {
($chunk_len:expr, $sample_rate:expr $(,)?) => {
$crate::blocks::filters::Bandpass::new($chunk_len, $sample_rate, Default::default())
};
($chunk_len:expr, $sample_rate:expr, $( $name:ident: $value:expr ),+ $(,)?) => {
$crate::blocks::filters::Bandpass::new(
$chunk_len,
$sample_rate,
$crate::blocks::filters::BandpassParams {
$( $name: $value ),+,
..Default::default()
}
)
};
}
impl Bandpass {
pub fn new(chunk_len: usize, sample_rate: f64, params: BandpassParams) -> Self {
/* … */
}
/* … */
}
I considered adding #[non_exhaustive]
to the BandpassParams
struct, but the reference says:
Non-exhaustive types cannot be constructed outside of the defining crate:
- Non-exhaustive variants (
struct
orenum
variant) cannot be constructed with a StructExpression (including with functional update syntax).
See also this thread.
I.e. in another crate which uses my library, I can no longer write:
let params = BandpassParams { low_freq = 300.0, .. Default::default() };
And even my macro would fail and would need to be written in a different way.
What's the best practice for non-exhaustive structs where all fields are guaranteed (by convention/documentation) to have a default?
- Use
#[non_exhaustive]
and disallow the.. Default::default()
syntax? - Simply live with a documentation comment to warn users that fields may be added any time?