I'm running into an issue with trait bounds and can't understand what I'm doing wrong. I'm working with the arduino-uno crate from avr-hal and I have a function that reads the ADC, implemented as follows:
fn read_signal<T: avr_hal_generic::hal::adc::Channel<board::adc::Adc, ID = u8>>(
adc: &mut board::adc::Adc,
pinA0: &mut T,
) {
let x = adc.read(&mut pinA0);
}
However, I receive the following error:
the trait bound `&mut T: avr_hal_generic::embedded_hal::adc::Channel<arduino_uno::adc::Adc>` is not satisfied
required because of the requirements on the impl of `arduino_uno::prelude::_embedded_hal_adc_OneShot<arduino_uno::adc::Adc, _, &mut T>` for `arduino_uno::adc::Adc` rustc(E0277)
I have tried using where
instead, but that doesn't help. How can I fix this?
Also, though I show this specific example, I would really appreciate an explanation or pointers to better documentation on this subject/error as I've encountered it a few times and struggle to understand what is wrong.
The compiler error says:
the trait bound
&mut T: avr_hal_generic::embedded_hal::adc::Channel<arduino_uno::adc::Adc>
is not satisfied
Notice that the error is asking for &mut T
to implement Channel
, &mut T: Channel<...>
, whereas your T
has the bound T: Channel<...>
— applying to T
itself rather than &mut T
. That's why the bound you've already written isn't helping.
Now, what's the right fix? If I look at the docs you linked, I can find the type of <Adc as OneShot>::read
. (Note: I copied the text from the docs to construct this snippet; I didn't read the source code.)
impl<WORD, PIN> OneShot<Adc, WORD, PIN> for Adc
where
WORD: From<u16>,
PIN: Channel<Adc, ID = MUX_A>, // <---- look here
{
pub fn read(
&mut self,
_pin: &mut PIN // <---- look here
) -> Result<WORD, ...>
}
So, read
should be given an &mut PIN
and the type variable PIN
should implement Channel
. That all sounds reasonable, but in your code the compiler thinks we want &mut T
to implement Channel
. An extra &mut
has appeared. Where did it come from? You wrote:
fn read_signal<T: avr_hal_generic::hal::adc::Channel<board::adc::Adc, ID = u8>>(
adc: &mut board::adc::Adc,
pinA0: &mut T,
) {
let x = adc.read(&mut pinA0);
}
pinA0
is of type &mut T
, but you then wrote adc.read(&mut pinA0)
, which means the parameter to read()
is of type &mut &mut T
. Since read
wants &mut
of something implementing Channel
, this results in the error you saw, asking for &mut T: Channel
.
The fix, then, is to not take &mut
of &mut
:
let x = adc.read(pinA0);
Under some circumstances, Rust provides implicit coercions which will, in particular, turn a reference to a reference (or a reference to a 'smart pointer' type implementing Deref
) into a reference. This is why you might have had extraneous &
or &mut
work in the past. However, these coercions are not applied when there isn't a single concrete type that's expected, such as in this case where the function parameter's type contains the type variable T
.