Thanks for taking the time, this seems like it should be simple but I must be miss understanding how something works.
I have two methods, one taking the concrete type and the other taking two generics:
struct DCMotor {
//...
fn high_speed<I: SliceId, C: ChannelId>(
&mut self,
enable : &mut Channel<Slice<I, FreeRunning>, C>
) { enable.set_duty_cycle(HIGH_SPEED) . unwrap(); }
fn low_speed(
&mut self,
enable : &mut Channel<Slice<Pwm1, FreeRunning>, B>
) { enable.set_duty_cycle(LOW_SPEED) . unwrap(); }
}
low_speed
with concrete type compiles, but high_speed
with generics doesn't.
I thought the inclusion of <I: SliceId, C: ChannelId>
should restrict the potential types used in any calling code:
motor.high_speed::<Pwm1, A>(channel_a);
motor.low_speed(channel_b);
Specifying types (Pwm1../A|B) that (seem to) implement the traits SliceId, and ChannelId:
impl SliceId for Pwm1
impl ChannelId for A
I think SetDutyCycle is implemented for Channel:
impl<S: AnySlice> SetDutyCycle for Channel<S, A>
But compilation still complains the trait bounds are not satisfied:
error[E0599]: the method `set_duty_cycle` exists for mutable reference `&mut Channel<Slice<I, FreeRunning>, C>`, but its trait bounds were not satisfied
--> src/main.rs:93:14
|
93 | ) { enable.set_duty_cycle(HIGH_SPEED) . unwrap(); }
| ^^^^^^^^^^^^^^ method cannot be called on `&mut Channel<Slice<I, FreeRunning>, C>` due to unsatisfied trait bounds
|
::: /home/c/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rp2040-hal-0.10.2/src/pwm/mod.rs:606:1
|
606 | pub struct Channel<S: AnySlice, C: ChannelId> {
| --------------------------------------------- doesn't satisfy `_: SetDutyCycle`
|
= note: the following trait bounds were not satisfied:
`rp_pico::rp2040_hal::pwm::Channel<Slice<I, rp_pico::rp2040_hal::pwm::FreeRunning>, C>: SetDutyCycle`
which is required by `&mut rp_pico::rp2040_hal::pwm::Channel<Slice<I, rp_pico::rp2040_hal::pwm::FreeRunning>, C>: SetDutyCycle`
What am I missing, how could I satisfy the compiler while still retaining the slight abstraction?
In attempts to flesh out the solution for anyone looking, as alluded to the generic S is ok but the channel is the concrete A|B.
In the rust book there is a section on the where
clause and its ability to express more abstract types directly.
Where clauses - Rust By Example
// Because we would otherwise have to express this as `T: Debug` or
// use another method of indirect approach, this requires a `where` clause:
impl<T> PrintInOption for T where
Option<T>: Debug {
// We want `Option<T>: Debug` as our bound because that is what's
// being printed. Doing otherwise would be using the wrong bound.
fn print_in_option(self) {
println!("{:?}", Some(self));
}
}
Applying the same logic here:
fn high_speed<I, C> (
&mut self,
enable : &mut Channel<Slice<I, FreeRunning>, C>
)
where I: SliceId,
C: ChannelId,
Channel<Slice<I, FreeRunning>, C> : SetDutyCycle {
enable.set_duty_cycle(HIGH_SPEED) . unwrap();
}
We can call the function as initially intended and keep the generic abstraction:
motor.high_speed::<Pwm1, A>(channel_a);
motor.high_speed::<Pwm1, B>(channel_b);