David
David

Reputation: 1876

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.

Upvotes: 1

Views: 1429

Answers (1)

Kevin Reid
Kevin Reid

Reputation: 43753

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.

Upvotes: 1

Related Questions