1
votes

I am using the I2CDevice trait which is defined in an external crate like this:

pub trait I2CDevice {
    type Error: Error;
    // members
}

Neither it nor any of its members contain any generic arguments.

I am creating a decorator for any I2CDevice implementation which delegates to an inner/concrete I2CDevice, adding console printing for diagnostic purposes:

struct debugDeviceDecorator<'a, T: I2CDevice<Error = LinuxI2CError> + Sized + 'a> {
    device: &'a mut T,
}

impl I2CDevice__A__ for debugDeviceDecorator__B__ {
    type Error = LinuxI2CError;

    fn read(&mut self, data: &mut [u8]) -> Result<(), Self::Error> {
        println!("read: data: {:?}", data);
        self.device.read(data)
    }

    // etc.
}

I can't figure out what to put in place of __A__ and __B__.

When I use no generic arguments whatsoever, I get compiler errors: Expected 1 lifetime parameter and Expected 1 type argument.

My best guess then is to leave __A__ blank because the definition of I2CDevice requires no generic arguments, and then to mirror the type argument used for the debugDeviceDecorator struct itself in place of __B__ like:

impl I2CDevice for debugDeviceDecorator<'a, T: I2CDevice<Error=LinuxI2CError> + Sized + 'a> { }

I get the compilation error: error: expected one of !, (, +, ,, ::, <, or >, found :, which occurs on the colon (:) that begins the type constraint on the generic argument T.

Perhaps the compiler thinks that I'm trying to constrain T in a manner which diverges from the constraint in the struct definition itself, so instead I try:

impl I2CDevice for debugDeviceDecorator<'a, T> {}

Which results in the errors undeclared lifetime for the lifetime parameter, and undefined or not in scope for the type argument itself, at which point I cannot figure out how to proceed. I would assume that the lifetime and the type argument are defined by the future, as yet nonexistent code that will some day initialize a debugDeviceDecorator struct, values which cannot yet be known and seem to me as though should not prevent this existing code from compiling.

I can see the compiler needing some information about the type of self.device used within the functions of the impl, but I feel like my first guess in which I mirrored the struct's expanded type definition in my impl declaration should have provided that.

1
@ardave FYI, you can put something between impl and I2CDevice.kennytm
which occurs on the colon (:) that begins the type constraintwhy would you go out of your way to retype in English what would have been more clearly expressed by simply copy-and-pasting the error?Shepmaster

1 Answers

5
votes

You should re-read The Rust Programming Language, specifically the section on how to implement traits for generic structs. A lot of hard work has gone into that documentation so that people can get started in Rust easier.

The book will show you the proper syntax for how to define a trait for a struct:

impl<'a, T> I2CDevice for DebugDeviceDecorator<'a, T>
    where T: I2CDevice<Error = LinuxI2CError> + Sized + 'a
{
    // ...
}

Note:

  1. You have to declare the two generic values ('a and T) before you can use them.

  2. There's nothing special about the fact that the trait has no generics.

  3. Types in Rust use PascalCase, not camelCase, so I've changed the name.

  4. I switched to a where clause because it's too hard to read the bounds when they are crammed into the generic declaration.