Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to store ADC handles in a struct #493

Closed
sebastianohl opened this issue Oct 20, 2024 · 3 comments
Closed

Unable to store ADC handles in a struct #493

sebastianohl opened this issue Oct 20, 2024 · 3 comments

Comments

@sebastianohl
Copy link

sebastianohl commented Oct 20, 2024

Hello,

I would like to create a driver that should read an ADC channel. The idea is to create a struct storing the handles in a new function and then use it afterwards. With the legacy ADC driver this was possible due to the fact that there was no direct link between ADCDriver and ADCChannelDriver. However, the new ADCChannelDriver requires a reference to the ADCDriver. If storing this to a struct, the compiler complains that the reference is a local variable.

Do I understand something wrong? Is there a more obvious solution to achieve something similar?

pub struct WindDirection<'a> {
    adc_driver: AdcDriver<'a, esp_idf_hal::adc::ADC2>,
    adc_channel:
        AdcChannelDriver<'a, esp_idf_hal::gpio::Gpio12, &AdcDriver<'a, esp_idf_hal::adc::ADC2>>,
}

#[allow(dead_code)]
impl<'a> WindDirection<'a>
{
    pub fn new(/*pin: P, adc: impl Peripheral<P = P::Adc> + 'a*/) -> Result<Self, EspError> {
        let config = AdcChannelConfig {
            attenuation: DB_11,
            calibration: true,
            ..Default::default()
        };

        let peripherals = Peripherals::take()?;
        let adc_driver = AdcDriver::<'a>::new(peripherals.adc2)?;
        let adc_channel =
            AdcChannelDriver::<'a>::new(&adc_driver, peripherals.pins.gpio12, &config)?;

        Ok(WindDirection { <---- here the compiler complains
            adc_driver,
            adc_channel,
        })
    }

    pub fn measure(&mut self) -> Result<(), EspError> {
        self.adc_driver.read(&mut self.adc_channel)?;
        Ok(())
    }
}

The compiler error:

error[E0515]: cannot return value referencing local variable `adc_driver`
  --> src/windirection.rs:51:9
   |
49 |               AdcChannelDriver::<'a>::new(&adc_driver, peripherals.pins.gpio12, &config)?;
   |                                           ----------- `adc_driver` is borrowed here
50 |
51 | /         Ok(WindDirection {
52 | |             adc_driver,
53 | |             adc_channel,
54 | |         })
   | |__________^ returns a value referencing data owned by the current function
@ivmarkov
Copy link
Collaborator

ivmarkov commented Oct 20, 2024

You are fighting a basic Rust problem, not something related to esp_idf_hal in that you are trying to create a self-referential struct, which cannot be expressed in safe Rust today. Rather than using a simple & borrow of the driver, you can instead wrap it in an Arc and then store the arced driver, as well as pass the arced driver to the channel driver.

@sebastianohl
Copy link
Author

thanks a lot. It worked even that I don't like to wrap everything in arc or refcells... but it seams that's the way rust handle these things.

@github-project-automation github-project-automation bot moved this from Todo to Done in esp-rs Oct 23, 2024
@ivmarkov
Copy link
Collaborator

but it seams that's the way rust handle these things.

Not necessarily. There are other ways, in that you can store in your struct a & reference to AdcDriver together with an owned intance of AdcChannelDriver but then you have to introduce a (covariant) lifetime in the struct and deal with that everywhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

No branches or pull requests

2 participants