Search code examples
genericsstructrusti2c

Struct that has a value of a struct with a generic type


Suppose I have a struct that uses a generic type (BNO055 is a sensor):

pub struct BNO055<T: I2CDevice + Sized> {
    pub i2cdev: T,
    pub mode: BNO055OperationMode,
}

I also have another struct that has the BNO055 as a field:

pub struct IMU {
    device: bno055::BNO055
}

Attempting to compile this code results in an error, with the compiler saying:

error[E0107]: wrong number of type arguments: expected 1, found 0
   --> src/modules/sensors/imu.rs:553:13
    |
553 |     device: bno055::BNO055
    |             ^^^^^^^^^^^^^^ expected 1 type argument

If I add a generic type to the struct, like so:

pub struct IMU<D: I2CDevice + Sized>{
    device: bno055::BNO055<D>
}

And then impl a function:

impl<D: I2CDevice + Sized> IMU<D> {
    pub fn init(imu_addr: u16) -> Self {
        let mut i2c_dev = LinuxI2CDevice::new("/dev/i2c-1", imu_addr).unwrap();
        let mut imu_dev = bno055::BNO055::new(i2c_dev).unwrap();

        IMU {
            device: imu_dev
        }
    }
}

I get this error:

error[E0308]: mismatched types
   --> src/modules/sensors/imu.rs:567:21
    |
567 |             device: imu_dev
    |                     ^^^^^^^ expected type parameter, found struct `modules::sensors::imu::bno055::i2cdev::linux::LinuxI2CDevice`
    |

imu_dev is of the type BNO055, so I assume that it should properly fit the generic's constraints. How do I resolve this error?


Solution

  • This init() function is supposed to create an IMU<D> instance for any D specified by the caller (provided D satisfies the I2CDevice + Sized bounds):

    impl<D: I2CDevice + Sized> IMU<D> {
        pub fn init(imu_addr: u16) -> Self {
    

    ...but it always tries to return an IMU<LinuxI2CDevice>.

    (edit) In my testing, the error[E0308]: mismatched types is explained in a note that follows it, which should look like this:

    note: expected type `......::BNO055<D>`
             found type `......::BNO055<LinuxI2CDevice>`
    

    (/edit)

    To fix this, you could make init() non-generic, e.g. by changing the impl to impl IMU<LinuxI2CDevice>.

    P.S. You call BNO055 a "type" and are trying to use it as a type... I'm not sure about terminology, but note that after you define a struct as being generic over some type parameter D, the struct name alone doesn't represent a concrete type, but rather a family of related - but incompatible - types. You have to provide some type in place of D: either another type parameter BNO055<T> or a specific type BNO055<LinuxI2CDevice> - to get a type.

    Keep in mind the alternatives to using generics: making the I2CDevice a enum or keeping it a trait, but using it as a trait object, e.g. pub i2cdev: Box<dyn I2CDevice>.