rust-embedded / embedded-hal

A Hardware Abstraction Layer (HAL) for embedded systems
Apache License 2.0
2.01k stars 202 forks source link

async/i2c: fix lifetimes on transaction() #363

Closed Dirbaio closed 2 years ago

Dirbaio commented 2 years ago

Trying to implement i2c for a shared i2c bus behind an async mutex yielded lots of cursed lifetime errors, because &'a mut [Operation<'b>] is invariant on 'b, not covariant as one would expect...

To fix this, the GAT future needs two lifetimes. Also counterintuitively, the future must be + 'a, but NOT + 'b. Then AddressMode: 'static is needed because Rust wants annoying where A: 'a bounds otherwise.

The async SPI PR has the same issue, will fix later. #347

With these fixes, implementing i2c on a mutex works nicely now:


struct SharedI2c<T>(tokio::sync::Mutex<T>);

impl<T: ErrorType> ErrorType for SharedI2c<T> {
    type Error = T::Error;
}

impl<A: AddressMode, T: I2c<A>> I2c<A> for SharedI2c<T> {
    type ReadFuture<'a>
    where
        Self: 'a,
    = impl Future<Output = Result<(), Self::Error>> + 'a;

    fn read<'a>(&'a mut self, address: A, read: &'a mut [u8]) -> Self::ReadFuture<'a> {
        async move { self.0.lock().await.read(address, read).await }
    }

    type WriteFuture<'a>
    where
        Self: 'a,
    = impl Future<Output = Result<(), Self::Error>> + 'a;

    fn write<'a>(&'a mut self, address: A, write: &'a [u8]) -> Self::WriteFuture<'a> {
        async move { self.0.lock().await.write(address, write).await }
    }

    type WriteReadFuture<'a>
    where
        Self: 'a,
    = impl Future<Output = Result<(), Self::Error>> + 'a;

    fn write_read<'a>(
        &'a mut self,
        address: A,
        write: &'a [u8],
        read: &'a mut [u8],
    ) -> Self::WriteReadFuture<'a> {
        async move { self.0.lock().await.write_read(address, write, read).await }
    }

    type TransactionFuture<'a, 'b>
    where
        Self: 'a,
        'b: 'a,
    = impl Future<Output = Result<(), Self::Error>> + 'a;

    fn transaction<'a, 'b>(
        &'a mut self,
        address: A,
        operations: &'a mut [Operation<'b>],
    ) -> Self::TransactionFuture<'a, 'b> {
        async move { self.0.lock().await.transaction(address, operations).await }
    }
}

cc @matoushybl

rust-highfive commented 2 years ago

r? @eldruin

(rust-highfive has picked a reviewer for you, use r? to override)

bors[bot] commented 2 years ago

Build succeeded: