You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

返回InputOutputPin trait对象时遇编译错误:编译时类型大小未知

问题描述

我正在使用DHT11库,基于ESP32实现,Dht11::new方法的gpio参数必须同时实现InputPin和OutputPin trait。

于是我定义了一个包含这两个trait的超级trait:

trait InputOutputPin: InputPin<Error = EspError> + OutputPin<Error = EspError> {}
impl<T: InputPin<Error = EspError> + OutputPin<Error = EspError>> InputOutputPin for T {}

接着写了根据引脚号返回该trait对象引用的函数:

fn get_gpio(pin: &u8) -> Result<&dyn InputOutputPin, &'static str>{
    match pin {
        33 => Ok(&Peripherals::take().unwrap().pins.gpio33.into_input_output().unwrap()),
        32 => Ok(&Peripherals::take().unwrap().pins.gpio32.into_input_output().unwrap()),
        27 => Ok(&Peripherals::take().unwrap().pins.gpio27.into_input_output().unwrap()),
        26 => Ok(&Peripherals::take().unwrap().pins.gpio26.into_input_output().unwrap()),
        25 => Ok(&Peripherals::take().unwrap().pins.gpio25.into_input_output().unwrap()),
        _ => Err("Pin not configurable for dht")
    }
}

但调用Dht11::new时:

let gpio = Self::get_gpio(pin).unwrap();
let dht = Dht11::new(*gpio);

报错:doesn't have a size known at compile-time。

由于ESP32的GPIO每个都是独立结构体,我想根据引脚号动态创建DHT11实例,求问题原因和优化方案。


问题原因
  1. 动态大小类型限制:dyn InputOutputPin属于动态大小类型(DST),解引用&dyn InputOutputPin得到的是DST实例,而Dht11::new需要的是编译期大小固定的具体类型——因为DHT11库的new方法会直接持有GPIO实例,DST无法在栈上分配或直接作为参数传递。
  2. 悬垂引用风险:get_gpio中每次调用Peripherals::take()都会获取外设所有权,into_input_output()也会转移引脚所有权,函数结束后临时对象会被销毁,返回的引用会变成悬垂引用,属于未定义行为。
  3. 重复调用Peripherals::take():该函数只能被调用一次,原代码每个match分支都调用会直接触发panic。

优化实现方案

方案1:枚举封装支持的GPIO类型(推荐嵌入式场景)

既然支持的引脚数量有限,用枚举包裹所有可能的GPIO类型,为枚举实现InputPin和OutputPin trait,这样既能保持静态类型的性能,又能实现动态选择引脚:

use esp_idf_hal::gpio::{InputPin, OutputPin, EspError, InputOutput};
use dht11::Dht11;
use esp_idf_sys::Peripherals;

// 枚举所有支持的DHT引脚类型
enum DhtPin {
    Gpio25(esp_idf_hal::gpio::Gpio25<InputOutput>),
    Gpio26(esp_idf_hal::gpio::Gpio26<InputOutput>),
    Gpio27(esp_idf_hal::gpio::Gpio27<InputOutput>),
    Gpio32(esp_idf_hal::gpio::Gpio32<InputOutput>),
    Gpio33(esp_idf_hal::gpio::Gpio33<InputOutput>),
}

// 为枚举实现InputPin trait
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 trait
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(),
        }
    }
}

// 改写get_gpio函数,返回枚举实例
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")
    }
}

// 使用示例
fn main() -> Result<(), EspError> {
    let target_pin = 33;
    let gpio = get_gpio(target_pin).unwrap();
    let mut dht = Dht11::new(gpio);
    // 后续调用dht.read()等方法
    Ok(())
}

方案2:Box包装trait对象(适合允许动态分发的场景)

如果Dht11::new支持接受Box<dyn InputOutputPin>类型参数,可以用Box包装引脚对象,绕过静态类型限制:

use esp_idf_hal::gpio::{InputPin, OutputPin, EspError};
use dht11::Dht11;
use esp_idf_sys::Peripherals;

// 为超级trait添加Send/Sync约束(嵌入式环境通常需要)
trait InputOutputPin: InputPin<Error = EspError> + OutputPin<Error = EspError> + Send + Sync {}
impl<T: InputPin<Error = EspError> + OutputPin<Error = EspError> + Send + Sync> InputOutputPin for T {}

// 返回Box包装的trait对象
fn get_gpio(pin: u8) -> Result<Box<dyn InputOutputPin>, &'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")
    }
}

// 使用示例
fn main() -> Result<(), EspError> {
    let target_pin = 33;
    let gpio = get_gpio(target_pin).unwrap();
    let mut dht = Dht11::new(gpio);
    // 后续调用dht.read()等方法
    Ok(())
}

注意:此方案要求Dht11的new方法支持动态分发参数,如果原库仅接受具体类型,方案1更可靠。


内容的提问来源于stack exchange,提问作者elhe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 01:30:47