Description
I am writing code that is generic over registers, and I'm getting a bit frustrated by the awkward bounds that need to be applied.
The ideal case would look something like this:
fn read_bit<U: Copy, Register: Readable>(reg: &Reg<U, Register>) -> U {
// ...
}
Unfortunately, the Readable
and Writable
traits are impl'd on the Reg
struct typedef itself, not on the inner type. Also unfortunately, the traits don't provide any functionality directly, so I can't rely completely on the traits and forget about the Reg
struct. This means the bounds need to be written in the much more awkward form:
fn read_bit<U: Copy, Register>(reg: &Reg<U, Register>) -> U
where
Reg<U, Register>: Readable,
{
// ...
}
In my case, I'd like to apply the bounds to associated types in a trait. Though I can add the relevant where bounds on Reg<...>
to the trait definition, they don't "propagate" to uses of that trait, meaning I need to repeat them on any block that uses the trait.
Ideally it would look something like:
trait Port {
type U: Sized + Copy + BitAnd + BitOr + ...;
type DDR: Readable + Writable;
type Data: Readable + Writable;
fn ddr() -> Reg<Self::U, Self::DDR>;
fn data() -> Reg<Self::U, Self::Data>;
}
impl<P: Port> Component<P> {
fn configure() {
P::ddr().modify(|r, w| {
// ...
});
}
fn do_something() {
P::data().modify(|r, w| {
// ...
});
}
}
But, given the above constraints, I actually need to write:
trait Port { // I could put a where clause here but it really wouldn't help
type U: Sized + Copy + BitAnd + BitOr + ...;
type DDR;
type Data;
fn ddr() -> Reg<Self::U, Self::DDR>;
fn data() -> Reg<Self::U, Self::Data>;
}
impl<P: Port> Component<P>
where
Reg<P::U, P::DDR>: Readable + Writable
{
fn configure() {
P::ddr().modify(|r, w| {
// ...
});
}
}
impl<P: Port> Component<P>
where
Reg<P::U, P::Data>: Readable + Writable
{
fn do_something() {
P::data().modify(|r, w| {
// ...
});
}
}
I believe that this situation could be resolved by generating the impl Readable
/impl Writable
for the hidden inner register type rather than directly on the outer register type alias. For documentation purposes, there could be a blanket impl on Reg
, like:
impl<U, REG: Readable> Readable for Reg<U, REG> {}
impl<U, REG: Writable> Writable for Reg<U, REG> {}
Which I believe would be allowed due to coherence.
If you are open to these changes, I would be willing to work on a PR implementing them. Or if you have suggestions for another strategy to make this usage more ergonomic, I'm all ears.
I spent a while looking around for related discussion and found none. Feel free to point me to any existing documentation or prior discussion on this topic.
Thanks for all your hard work on this!