It's common one writes code like
pub fn foo_rng<R: RngCore>(&mut self, .., mut rng: R) { .... }
#[cfg(getrandom)]
pub fn foo(&mut self, ..) { self.foo_rng(.., rand::thread_rng() }
Alone this works fine, but we could've related behavior which require sensible defaults, so the convenience methods could multiply. What tricks make this more ergonomic?
At the extreme, we'd want a builder pattern for foo invocations
pun struct Foo<A,B,..,R> {
a: A,
b: B,
...
rng: R,
}
...
impl<A,B,..,R: RngCore> Foo<A,B,..,()> {
pub fn add_rng(self, rng: R) -> Foo<A,B,..,R> {
let Foo { a, b, ..., rng: () } = self;
Foo { a, b, ..., rng }
}
}
We'd ideally do this via a procmacro like
#[builder]
pub fn foo_rng<R: RngCore>(
&mut self,
..,
#[builder(() => { rand::thread_rng() })] mut rng: R,
) { .... }
I've no idea if such a builder procmacro exists or what constraints it'd impose, but what about cases that lie somewhere in between? Has anyone really explored a type-level option analog that provides some? It looks rather tricky.