无法返回双Trait组成的Trait对象,ESP-IDF GPIO函数报错求助
解决ESP-IDF GPIO返回合并Trait对象的编译错误
问题场景
编写了一个函数,意图根据传入引脚号返回不同的ESP-IDF GPIO引脚,用Box<dyn InputOutputPin>作为返回类型。其中InputOutputPin是自定义的组合Trait,整合了ESP-IDF库的InputPin和OutputPin。但编译时报错Cannot return a trait object of two traits,且由于GPIO是宏生成的具体类型,无法统一返回GpioX<InputOutput>这类特定类型。
函数代码
fn get_gpio(pin: &u8) -> Result<Box<dyn InputOutputPin>, &'static str>{ match pin { 33 => Ok(Box::new(Peripherals::take().unwrap().pins.gpio33.into_input_output().unwrap())), 32 => Ok(Box::new(Peripherals::take().unwrap().pins.gpio32.into_input_output().unwrap())), 27 => Ok(Box::new(Peripherals::take().unwrap().pins.gpio27.into_input_output().unwrap())), 26 => Ok(Box::new(Peripherals::take().unwrap().pins.gpio26.into_input_output().unwrap())), 25 => Ok(Box::new(Peripherals::take().unwrap().pins.gpio25.into_input_output().unwrap())), _ => Err("Pin not configurable for dht") } }
自定义Trait定义
trait InputOutputPin: InputPin<Error = EspError> + OutputPin<Error = EspError>{}
解决方案
方法1:为自定义Trait添加对象安全的转发方法
Rust的Trait对象要求Trait具备对象安全性,直接继承两个Trait的组合Trait默认不满足该要求。可以给InputOutputPin显式定义所需方法,转发到父Trait的实现:
use esp_idf_hal::gpio::{InputPin, OutputPin, EspError}; trait InputOutputPin { fn set_high(&mut self) -> Result<(), EspError>; fn set_low(&mut self) -> Result<(), EspError>; fn is_high(&self) -> Result<bool, EspError>; fn is_low(&self) -> Result<bool, EspError>; } // 为所有同时实现InputPin和OutputPin的类型自动实现InputOutputPin impl<T: InputPin<Error = EspError> + OutputPin<Error = EspError>> InputOutputPin for T { fn set_high(&mut self) -> Result<(), EspError> { OutputPin::set_high(self) } fn set_low(&mut self) -> Result<(), EspError> { OutputPin::set_low(self) } fn is_high(&self) -> Result<bool, EspError> { InputPin::is_high(self) } fn is_low(&self) -> Result<bool, EspError> { InputPin::is_low(self) } }
修改后InputOutputPin成为独立的对象安全Trait,可正常返回Box<dyn InputOutputPin>。
方法2:用枚举封装所有支持的GPIO类型
由于支持的引脚数量有限,可定义枚举包含所有可能的GPIO类型,再为枚举实现InputPin和OutputPin:
use esp_idf_hal::gpio::{Gpio25, Gpio26, Gpio27, Gpio32, Gpio33, InputOutput, InputPin, OutputPin, EspError}; use esp_idf_hal::peripherals::Peripherals; enum DhtPin { Gpio25(Gpio25<InputOutput>), Gpio26(Gpio26<InputOutput>), Gpio27(Gpio27<InputOutput>), Gpio32(Gpio32<InputOutput>), Gpio33(Gpio33<InputOutput>), } // 实现InputPin impl InputPin for DhtPin { type Error = EspError; fn is_high(&self) -> Result<bool, Self::Error> { match self { DhtPin::Gpio25(pin) => pin.is_high(), DhtPin::Gpio26(pin) => pin.is_high(), DhtPin::Gpio27(pin) => pin.is_high(), DhtPin::Gpio32(pin) => pin.is_high(), DhtPin::Gpio33(pin) => pin.is_high(), } } fn is_low(&self) -> Result<bool, Self::Error> { match self { DhtPin::Gpio25(pin) => pin.is_low(), DhtPin::Gpio26(pin) => pin.is_low(), DhtPin::Gpio27(pin) => pin.is_low(), DhtPin::Gpio32(pin) => pin.is_low(), DhtPin::Gpio33(pin) => pin.is_low(), } } } // 实现OutputPin impl OutputPin for DhtPin { type Error = EspError; fn set_high(&mut self) -> Result<(), Self::Error> { match self { DhtPin::Gpio25(pin) => pin.set_high(), DhtPin::Gpio26(pin) => pin.set_high(), DhtPin::Gpio27(pin) => pin.set_high(), DhtPin::Gpio32(pin) => pin.set_high(), DhtPin::Gpio33(pin) => pin.set_high(), } } fn set_low(&mut self) -> Result<(), Self::Error> { match self { DhtPin::Gpio25(pin) => pin.set_low(), DhtPin::Gpio26(pin) => pin.set_low(), DhtPin::Gpio27(pin) => pin.set_low(), DhtPin::Gpio32(pin) => pin.set_low(), DhtPin::Gpio33(pin) => pin.set_low(), } } } // 修改函数返回类型 fn get_gpio(pin: &u8) -> Result<DhtPin, &'static str>{ let peripherals = Peripherals::take().unwrap(); match pin { 33 => Ok(DhtPin::Gpio33(peripherals.pins.gpio33.into_input_output().unwrap())), 32 => Ok(DhtPin::Gpio32(peripherals.pins.gpio32.into_input_output().unwrap())), 27 => Ok(DhtPin::Gpio27(peripherals.pins.gpio27.into_input_output().unwrap())), 26 => Ok(DhtPin::Gpio26(peripherals.pins.gpio26.into_input_output().unwrap())), 25 => Ok(DhtPin::Gpio25(peripherals.pins.gpio25.into_input_output().unwrap())), _ => Err("Pin not configurable for dht") } }
此方法避免了Trait对象的使用,性能更优,同时将Peripherals::take()移到match外,避免多次调用导致panic。
方法3:直接返回dyn InputPin + OutputPin(Rust 1.90+)
若使用Rust 1.90及以上版本,可直接返回组合Trait对象,无需自定义组合Trait:
fn get_gpio(pin: &u8) -> Result<Box<dyn InputPin<Error = EspError> + OutputPin<Error = EspError>>, &'static str>{ let peripherals = Peripherals::take().unwrap(); match pin { 33 => Ok(Box::new(peripherals.pins.gpio33.into_input_output().unwrap())), 32 => Ok(Box::new(peripherals.pins.gpio32.into_input_output().unwrap())), 27 => Ok(Box::new(peripherals.pins.gpio27.into_input_output().unwrap())), 26 => Ok(Box::new(peripherals.pins.gpio26.into_input_output().unwrap())), 25 => Ok(Box::new(peripherals.pins.gpio25.into_input_output().unwrap())), _ => Err("Pin not configurable for dht") } }
该方法最简洁,但依赖较新版本的Rust。
内容的提问来源于stack exchange,提问作者elhe
相关产品推荐
相关产品推荐

