Search code examples
genericsrust

Type doesn't propagate from impl to a struct in Rust


So as I'm learning rust so I wanted to write a wrapper for uart in stm32l0xx-hal as understand it, the error I keep getting is the USART template in impl is different to struct declaration

the code:

pub struct Uart<USART, TX, RX> {
    _tx: serial::Tx<USART>,
    _rx: serial::Rx<USART>,
    _tx_pin: TX,
    _rx_pin: RX
}

impl<USART: serial::Serial2Ext<TX,RX>, TX, RX> Uart<USART, TX,RX> {
    pub fn new
        (usart: USART,  mut rcc: rcc::Rcc, tx_pin: TX, rx_pin: RX) -> Self {

        let serial = usart
            .usart(tx_pin, rx_pin, serial::Config::default(), &mut rcc)
            .unwrap();

        let (tx, rx) = serial.split();

        Uart{
            _tx : tx, // <- error
            _rx : rx, // <- error
            _tx_pin : tx_pin,
            _rx_pin : rx_pin
        }
    }
}

the error:

error[E0308]: mismatched types
  --> src/uart.rs:28:19
   |
17 | impl<USART: serial::Serial2Ext<TX,RX>, TX, RX> Uart<USART, TX,RX> {
   |      ----- expected this type parameter
...
28 |             _tx : tx,
   |                   ^^ expected `Tx<USART>`, found `Tx<USART2>`
   |
   = note: expected struct `stm32l0xx_hal::serial::Tx<USART>`
              found struct `stm32l0xx_hal::serial::Tx<stm32l0xx_hal::serial::USART2>`

stm32l0xx_hal::serial::USART2 - is the type of usart that I pass to the new function


Solution

  • The problem is that you are lying to the compiler: The new-function as written in it's impl-block says that it will return any Uart<USART, TX, RX> that the caller chose (as long as USART: serial::Serial2Ext<TX,RX>). But then, instead of assigning to _tx a value of the caller-chose generic type UART, it tries to assign the specific type UART2. That would work if UART was UART2, but the impl-block doesn't say that.

    It would be possible to keep the generics around if Serial2Ext had defined an associated type, so one would have been be able to constraint the impl ("this type here is not yet defined, but it is whatever Serial2Ext says will be returned from uart() for the given (TX, RS)). Since it does not, one can't do that. So it's not possible - without adding even more code - to have UART be a generic type and use a specific type at the same time.

    The simplest way would be to remove the generic parameter UART and replace it with the specific type UART2, since this is what Serial2Ext::uart will return in the Ok-case.