Yesterday I was working with a friend on an embedded crate.
The crate has to handle some pins on unknown hardware. The only thing we know is that the pins implement
The crate itself uses these pins to abstract outputs on a shift register as OutputPins. The shift register is connected via the pins on the unknown hardware that also implement OuputPin.
I receive errors of type <hardwarepin as OutputPin>::Error and have to reraise them as <shiftregisterpin as OutputPin>::Error. Currently we just do this with map_error(|_e| ())? because we cannot know what kind of error the concrete hardware pins may raise.(
Probably none, because I don't know of any hardware where the pin could fail to set, but that's besides the point.)
My favourite solution would have been to just "reraise" the error happening on the pin. But I don't really see how to do this in a sensible manner. If OutputPin::Error had a trait bound of std::error::Error I could at least raise a Box<dyn std::error::Error> but that's not the case. (Besides the fact, that microchips usually work without an allocator, so no Box at all)
So now I have two questions
Is there another way to just "reraise" errors of unknown type? Beware that the different pins may raise different errors.
Do you often use std::error::Error? I haven't seen it in the wild yet but I also haven't looked particularily hard yet.
Instead of using Box<dyn Error>, why not make your error type generic?
enum ShiftRegisterPinError<E> {
/// An error raised when writing to the underlying hardware pin fails.
Pin(E),
...
}
impl<P: OutputPin> ShiftRegister<P> {
fn do_stuff(&mut self) -> Result<(), ShiftRegisterPinError<P::Error>> {
self.pin.set_high().map_err(ShiftRegisterPinError::Pin)?;
...
}
}
Otherwise if you want to retain all the context without infecting the rest of your code with the hardware pin's error type, E, you'll need to use some form of type erasure like Box<dyn ...> (e.g. Box<dyn Display> if you don't have access to std::error::Error).
Yeah that's basically the same as I proposed above. The reason why I included multiple pins is because all three pins are used to set a single Pin on the Shiftregister.
Using Display instead of std::error::Error might be interesting though. But doesn't that still have the problem that I need to restrict Pinx::Error: Display but not all OutputPin::Error necesarily implement that? And people using this crate usually won't have controll over that.