rust-embedded / embedded-hal

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

InputPin with mutable self #546

Closed usbalbin closed 9 months ago

usbalbin commented 9 months ago

Sorry if this has been asked earlier, I could not find an answer.

I was trying to implement a new type FilteredInputPin. Which wraps an ordinary generic InputPin but with some basic filtering added.

My plan was to implement InputPin for FilteredInputPin, every call to is_low/is_high would update its history and return the filters result. However that does not work since is_low and is_high both take shared references. Thus there is no way for me to update the filters history data.

Is this a use case that embedded-hal does not intend to support or is this an oversight? :)

Semi pseudo code:

struct FilteredInputPin<P> {
    pin: P,
    history: History,
}

impl<P: InputPin> InputPin for FilteredInputPin {
    type Error = P::Error;
    fn is_high(&self) -> Result<bool, Self::Error> {
        let new_value = self.pin.is_high()?;
        let result = self.history.update(new_value); // <--- Needs mutable access
        Ok(result)
    }
    ...
}
usbalbin commented 9 months ago

@GrantM11235 's comment

Yes I think a Cell would probably be completely fine. Have actually never used gotten around to using a Cell, but that does indeed seem to be a sufficient solution here since my History type would probably just be something like a

#[derive(Copy, Clone)]
struct History {
    bits: u32
}

impl History {
    fn update(self, new_value: bool) -> (Self, bool) {
        let new_history = (self.bits << 1 | u32::from(new_value));
        let result = foo(new_history.count_ones());
        (new_history, result)
    }
}

so updating the example above:

struct FilteredInputPin<P> {
    pin: P,
    history: Cell<History>,
}

impl<P: InputPin> InputPin for FilteredInputPin {
    type Error = P::Error;
    fn is_high(&self) -> Result<bool, Self::Error> {
        let new_value = self.pin.is_high()?;
        let history = self.history.get(); // <-- Cell::get makes a copy of the history, assuming History: Copy
        let (new_history, result) = history.update(new_value); // <--- creates a new History object plus the result
        self.history.set(new_history); // <-- Overwrite cells contents with new history
        Ok(result)
    }
    ...
}