Search code examples
rusttraitsavrhal

Trait bound not satisfied in function argument


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.


Solution

  • 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.