[Beginner] Trait Dependency Cycle Error E0391

THIS IS A GENERAL ERRORS QUESTION, WHICH SHOULDN'T BE AFFECTED BY THE FACT THAT ITS AN EMBEDDED RUST PROJECT.

Hi im a beginner rustacean, I just finished THE BOOK, and am doing some embedded rust on the pico2. I was modifying the tmc2209-uart crate on crates.io to use the rp235x-hal uart functions/methods, when bam out of nowhere I get hit with this trait cyclical dependency error, that doesn't even make sense. I can't understand what the compiler is trying to tell me, and I don't find these links:- "note: see Overview of the compiler - Rust Compiler Development Guide and Queries: demand-driven compilation - Rust Compiler Development Guide for more information" very helpful.

the traits bit that is causing the error is this bit:

pub struct Tmc2209<D, P>
where
    D: uart::UartDevice,
    P: uart::ValidUartPinout<D>,
{
    /// UART peripheral.
    uart: uart::UartPeripheral<uart::Enabled, D, P>,
}

I'll post the full code in a separate post below :slight_smile:

I am downright confused I would be grateful if the community could enlighten me.

the output of cargo check has been given below :slight_smile:

Thank You

error[E0391]: cycle detected when computing the variances for items in this crate
    |
note: ...which requires computing function signature of `driver::<impl at src/driver.rs:125:1: 128:33>::read_register`...
   --> src/driver.rs:141:5
    |
141 |     pub fn read_register<R: ReadableRegister>(&mut self) -> Result<R, Error<_>> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires type-checking `driver::<impl at src/driver.rs:125:1: 128:33>::read_register`...
   --> src/driver.rs:141:5
    |
141 |     pub fn read_register<R: ReadableRegister>(&mut self) -> Result<R, Error<_>> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires computing function signature of `driver::<impl at src/driver.rs:125:1: 128:33>::read_echo`...
   --> src/driver.rs:222:5
    |
222 |     fn read_echo(&mut self, buf: &mut [u8]) -> Result<(), Error<_>> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires type-checking `driver::<impl at src/driver.rs:125:1: 128:33>::read_echo`...
   --> src/driver.rs:222:5
    |
222 |     fn read_echo(&mut self, buf: &mut [u8]) -> Result<(), Error<_>> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...which requires computing the variances of `error::Error::Echo`...
   --> src/error.rs:33:5
    |
 33 |     Echo(E),
    |     ^^^^
    = note: ...which again requires computing the variances for items in this crate, completing the cycle
note: cycle used when computing the variances of `driver::Tmc2209`
   --> src/driver.rs:42:1
    |
 42 | pub struct Tmc2209<D, P>
    | ^^^^^^^^^^^^^^^^^^^^^^^^
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information

For more information about this error, try `rustc --explain E0391`.
error: could not compile `tmc2209-pi` (lib) due to 1 previous error

the code in driver.rs

use crate::datagram::{ReadRequest, ReadResponse, ResponseReader, WriteRequest};
use crate::error::Error;
use crate::registers::{
    Chopconf, Coolconf,

 DrvStatus, Gconf, Gstat, Ifcnt, IholdIrun, Ioin, MicrostepResolution,
    Mscnt, Pwmconf, ReadableRegister, SgResult, Sgthrs, Tcoolthrs, Tpwmthrs, Tstep, Vactual,
    WritableRegister,
};

use rp235x_hal::uart;

pub struct Tmc2209<D, P>
where
    D: uart::UartDevice,
    P: uart::ValidUartPinout<D>,
{
    /// UART peripheral.
    uart: uart::UartPeripheral<uart::Enabled, D, P>,
    /// Slave address (0-3).
    slave_addr: u8,
    /// Response reader for parsing incoming data.
    reader: ResponseReader,
}


#[cfg(feature = "blocking")]
impl<D, P> Tmc2209<D, P>
where
    D: uart::UartDevice,
    P: uart::ValidUartPinout<D>,
{
    /// Read a register (blocking).
    ///
    /// Sends a read request and waits for the response.
    ///
    /// # Type Parameters
    ///
    /// * `R` - Register type to read (must implement `ReadableRegister`)
    ///
    /// # Returns
    ///
    /// The register value, or an error if communication fails.
    pub fn read_register<R: ReadableRegister>(&mut self) -> Result<R, Error<_>> {
        let request = self.read_request::<R>();

        // Send the read request
        self.uart.write_full_blocking(request.as_bytes());

        // Read the response
        // TMC2209 echoes back the request, then sends the response
        // We need to skip the echo (4 bytes) and read the response (8 bytes)
        let mut echo_buf = [0u8; 8];
        self.read_echo(&mut echo_buf)?;

        let response = self.read_response()?;

        // Verify the register address matches
        let expected_addr = R::ADDRESS as u8;
        if response.reg_addr() != expected_addr {
            return Err(Error::AddressMismatch {
                expected: expected_addr,
                actual: response.reg_addr(),
            });
        }

        Ok(R::from(response.data()))
    }

    fn read_echo(&mut self, buf: &mut [u8]) -> Result<(), Error<_>> {
        let mut total_read = 0;
        while total_read < buf.len() {
            if self.uart.uart_is_readable() {
                let n = self
                    .uart
                    .read_raw(&mut buf[total_read..])
                    .map_err(Error::Echo)?;
                total_read += n;
            } else {
                if total_read < (buf.len() / 2) {
                    return Err(Error::NoEcho(total_read as u8));
                }
                break;
            }
        }
        Ok(())
    }

below the code in error.rs

//! Error types for TMC2209 driver.

use core::fmt;

/// Errors that can occur during TMC2209 communication.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error<E> {
    /// UART communication error (read or write failed).
    Uart(E),
    /// CRC checksum mismatch in received response.
    CrcMismatch,
    /// Invalid sync byte in response (expected 0x05).
    InvalidSync,
    /// Invalid master address in response (expected 0xFF).
    InvalidMasterAddress,
    /// Register address in response doesn't match request.
    AddressMismatch {
        /// The expected register address.
        expected: u8,
        /// The actual register address received.
        actual: u8,
    },
    /// Unknown register address received.
    UnknownAddress(u8),
    /// Invalid slave address (must be 0-3).
    InvalidSlaveAddress(u8),
    /// Response buffer too small.
    BufferTooSmall,
    /// No response received (timeout or no data).
    NoResponse,
    NoEcho(u8),
    Echo(E),
}

impl<E> Error<E> {
    /// Map the UART error type to a different type.
    pub fn map_uart<F, E2>(self, f: F) -> Error<E2>
    where
        F: FnOnce(E) -> E2,
    {
        match self {
            Error::Uart(e) => Error::Uart(f(e)),
            Error::CrcMismatch => Error::CrcMismatch,
            Error::InvalidSync => Error::InvalidSync,
            Error::InvalidMasterAddress => Error::InvalidMasterAddress,
            Error::AddressMismatch { expected, actual } => {
                Error::AddressMismatch { expected, actual }
            }
            Error::UnknownAddress(addr) => Error::UnknownAddress(addr),
            Error::InvalidSlaveAddress(addr) => Error::InvalidSlaveAddress(addr),
            Error::BufferTooSmall => Error::BufferTooSmall,
            Error::NoResponse => Error::NoResponse,
            Error::NoEcho(len) => Error::NoEcho(len),
            Error::Echo(e) => Error::Echo(f(e)),
        }
    }
}

impl<E: fmt::Debug> fmt::Display for Error<E> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Uart(e) => write!(f, "UART error: {:?}", e),
            Error::CrcMismatch => write!(f, "CRC checksum mismatch"),
            Error::InvalidSync => write!(f, "Invalid sync byte (expected 0x05)"),
            Error::InvalidMasterAddress => write!(f, "Invalid master address (expected 0xFF)"),
            Error::AddressMismatch { expected, actual } => {
                write!(
                    f,
                    "Register address mismatch: expected 0x{:02X}, got 0x{:02X}",
                    expected, actual
                )
            }
            Error::UnknownAddress(addr) => write!(f, "Unknown register address: 0x{:02X}", addr),
            Error::InvalidSlaveAddress(addr) => {
                write!(f, "Invalid slave address: {} (must be 0-3)", addr)
            }
            Error::BufferTooSmall => write!(f, "Response buffer too small"),
            Error::NoResponse => write!(f, "No response received"),
            Error::NoEcho(len) => write!(f, "Only recieved few/none echoes length: {:?}", len),
            Error::Echo(e) => write!(f, "Echo error {:?}", e),
        }
    }
}

In pub fn read_register<R: ReadableRegister>(&mut self) -> Result<R, Error<_>>, what are you trying to accomplish by specifying an underscore for the generic parameter to Error? I don't think that works. If you mean that the E in Error<E> can be anything when returned from the method, you should declare it as a generic parameter for the method:

pub fn read_register<R: ReadableRegister, E>(&mut self) -> Result<R, Error<E>>

Thanks for the suggestion but it doesn't seem to fix the cycle dependency error. also you were right I was trying to get the error to be anything, so the bit worked out.

Thanks

Nothing about the error changed i.e the exact same compiler output, no difference.

so I guess changing _ into E didn't do much

Can you please post a self-contained example of the failure, that doesn’t refer to other modules and crates?

Creating such an example can be a fair bit of work (because you need to disentangle the code from its dependencies), but it’s a good strategy whenever you have a “is this a compiler bug???” kind of situation, and the work of minimizing it can help you discover the problem yourself. And once you have it to share, we can look at it, try it out on the Rust Playground, and probably explain what’s going on.

I can’t say for certain without a minimal example, but this feels like an issue with the compiler itself. If I’m reading this right, Tmc2209<D, P> should have the same variances as UartPeripheral, and Echo should be covariant over E.

I always like sharing this link for tips on reproducing compiler errors, or diagnostics in this case

Thanks for all your answers. I think the first step I should take to resolve this error is to actually understand how the error get mapped etc.

I only recently completed The Book, so I still don't fully grasp generics, traits and their implementations in something this complex, I would appreciate it if y'all could explain what all the traits and generics actually do, as Im thinking of replacing the U and E generics with maybe a rp235x-hal uart error, but I still want to keep the E generic in the error implementation, is this even possible or am I just even more confused.

Thanks

I was thinking of replacing it with something like the following:-

type TmcUart = rp235x_hal::uart::UartPeripheral<
    rp235x_hal::uart::Enabled,
    rp235x_hal::pac::UART1,
    (
        rp235x_hal::gpio::Pin<
            rp235x_hal::gpio::bank0::Gpio8,
            rp235x_hal::gpio::FunctionUart,
            rp235x_hal::gpio::PullDown,
        >,
        rp235x_hal::gpio::Pin<
            rp235x_hal::gpio::bank0::Gpio9,
            rp235x_hal::gpio::FunctionUart,
            rp235x_hal::gpio::PullDown,
        >,
    ),
>;
pub struct myTmc2209 {
    uart: TmcUart,
}

impl myTmc2209 {
    pub fn new(uart: TmcUart) -> Self {
        Self { uart }
    }
}

impl myTmc2209 {
    pub fn read_register(
        &mut self,
        buf: &mut [u8],
    ) -> Result<(), Error<rp235x_hal::uart::ReadError>> {
        self.uart.read_full_blocking(buf).map_err(Error::Uart)?;
        Ok(())
    }
}