首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >锈蚀函数间的共享值

锈蚀函数间的共享值
EN

Stack Overflow用户
提问于 2022-11-24 22:31:33
回答 1查看 52关注 0票数 0

我试着去学习锈病,但是碰到了一堵墙,我想要做一些相对简单的事情。我试图为ESP32c3单片机编写一个简单的眨眼示例。我得到了一个基本的示例,但当试图扩展/概括该示例时,我开始遇到编译错误。

我的项目由两个板条箱组成的货物工作区- entrypointblink

我能够使以下基本版本在没有问题的情况下工作:

代码语言:javascript
复制
// entrypoint/src/main.rs

use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use blink::blink;

fn main() {
    // Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
    // or else some patches to the runtime implemented by esp-idf-sys might not link properly.
    esp_idf_sys::link_patches();

    println!("Hello, world!");    
    blink();
}
代码语言:javascript
复制
// blink/src/lib.rs

use std::thread;
use std::time::Duration;

use esp_idf_hal::prelude::Peripherals;
use esp_idf_hal::gpio;

pub fn blink() {
    let peripherals = Peripherals::take().unwrap();
    let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).unwrap();
    for _ in 0..20 {
        led.set_high().unwrap();
        thread::sleep(Duration::from_secs(1));
        led.set_low().unwrap();
        thread::sleep(Duration::from_secs(1));
    }
}

然后,我希望改进错误处理(停止在blink机箱中使用blink),并使blink()函数可重用(如果不止一次执行Peripherals::take()调用panics )。

为了改进错误处理,我做了以下修改。这个版本也很好,我只是为了得到反馈,我的方法是多么的习以为常/你会怎么做呢?我猜,最好是使用自定义错误类型,或者即使在生产代码中,也可以将字符串切片作为错误返回到可接受的/常见的位置吗?

代码语言:javascript
复制
pub fn blink(count: i32) -> Result<(), &'static str> {
    let peripherals = Peripherals::take().ok_or("Failed to take peripherals")?; 
    let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
    for _ in 0..count {
        led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
        thread::sleep(Duration::from_secs(1));
        led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
        thread::sleep(Duration::from_secs(1));
    }
    Ok(())
}

接下来,我试图使blink()函数可重用,方法是将Peripherals::take()调用从blink()函数的其余部分分离出来,这样在引导时只能调用一次。我知道我可以在我的入口点进行调用,并将外围设备作为参数传递给blink(),但是我想让blink机箱负责进行Peripherals::take()调用。这就是我开始遇到麻烦的地方。

尝试nr.1: --我的第一种方法是尝试使用全局Peripherals变量。我很快发现这是行不通的,除非我用thread_local宏包装全局变量,或者将全局变量上的操作包装到我想要避免的unsafe块中。我尝试了许多事情,但是在使用thread_local时无法让我的代码编译。

无论是否使用RefCell (我发现有文章建议使用RefCell,但是在尝试了它并阅读了文档之后,我发现没有一个很好的理由将它用于我的用例),thread_local似乎将我的全局变量包装成了一个LocalKey。除了LocalKey函数之外,我不知道如何使用with() --如果可能的话,我想避免使用with(),因为我需要将代码移动到闭包中,这样就很难阅读。我也不知道如何将for循环保持在闭包之外,并且只从闭包内部初始化led变量--通常我会将变量声明从闭包中移出,初始化为null,但据我所知,null似乎不是一个存在于Rust中的概念。

代码语言:javascript
复制
thread_local! {
    static PERIPHERALS: Option<Peripherals> = Peripherals::take();
}

pub fn blink(count: i32) -> Result<(), &'static str> {
    PERIPHERALS.with(| p | {
        let peripherals = match p {
            Some(peripherals) => peripherals,
            None => return Err("Failed to take peripherals")
        };
        let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
        for _ in 0..count {
            led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
            thread::sleep(Duration::from_secs(1));
            led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
            thread::sleep(Duration::from_secs(1));
        }
        Ok(())
    })
}

以上代码导致以下编译器错误:

代码语言:javascript
复制
error[E0507]: cannot move out of `peripherals.pins.gpio8` which is behind a shared reference
  --> blink/src/lib.rs:19:47
   |
19 |         let mut led = gpio::PinDriver::output(peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
   |                                               ^^^^^^^^^^^^^^^^^^^^^^ move occurs because `peripherals.pins.gpio8` has type `Gpio8`, which does not implement the `Copy` trait

For more information about this error, try `rustc --explain E0507`.
error: could not compile `blink` due to previous error

如果我首先尝试取消引用peripherals变量,也会发生相同的错误:

代码语言:javascript
复制
...
let mut led = gpio::PinDriver::output((*peripherals).pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
...

尝试nr.2:作为我的下一种方法,我尝试编写一个带有几个函数的结构,这些函数将充当类。不幸的是,我遇到了完全相同的编译器错误。

代码语言:javascript
复制
// blink/src/lib.rs

use std::thread;
use std::time::Duration;

use anyhow::Result;
use esp_idf_hal::prelude::Peripherals;
use esp_idf_hal::gpio;
use esp_idf_sys::EspError;

pub struct Blink {
    peripherals: Peripherals,
}

impl Blink {
    pub fn new() -> Result<Blink, &'static str> {
        match Peripherals::take() {
            Some(peripherals) => Ok(Blink{ peripherals }),
            None => return Err("Failed to take peripherals")
        }
    }

    pub fn blink(&self, count: i32) -> Result<(), &'static str> {
        let mut led = gpio::PinDriver::output(self.peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
        for _ in 0..count {
            led.set_high().map_err(|_: EspError| "Failed to set pin high")?;
            thread::sleep(Duration::from_secs(1));
            led.set_low().map_err(|_: EspError| "Failed to set pin low")?;
            thread::sleep(Duration::from_secs(1));
        }
        Ok(())
    }
}
代码语言:javascript
复制
// entrypoint/src/main.rs

use std::thread;
use std::time::Duration;

use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
use blink::Blink;

fn main() {
    // Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
    // or else some patches to the runtime implemented by esp-idf-sys might not link properly.
    esp_idf_sys::link_patches();

    println!("Hello, world!");
    let blink = Blink::new()?;
    loop {
        blink.blink(2).unwrap();
        thread::sleep(Duration::from_secs(5));
    }
}
代码语言:javascript
复制
error[E0507]: cannot move out of `self.peripherals.pins.gpio8` which is behind a shared reference
  --> blink/src/lib.rs:23:47
   |
23 |         let mut led = gpio::PinDriver::output(self.peripherals.pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^ move occurs because `self.peripherals.pins.gpio8` has type `Gpio8`, which does not implement the `Copy` trait

For more information about this error, try `rustc --explain E0507`.
error: could not compile `blink` due to previous error

对于借用、引用和/或变量移动/复制在Rust中是如何工作的,我还没有很好的理解,只是还不能解决这个问题。它似乎与我熟悉的其他(更传统的)语言(C、C++、Java、JS/TS、Python )截然不同。

再一次,如果您在上面的代码中发现任何不寻常的地方,我也非常感谢您提供的任何最佳实践建议/更正。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-11-25 07:35:16

错误的基本依据可以用一个简单的例子再现:

代码语言:javascript
复制
struct Foo {
  bar: String
}

impl Foo {
  fn baz(&self) {
    let s = self.bar;
  }
}

正在发生的事情是:

  • self&Foo类型,因为参数是用&self声明的,&selfself: &Self的缩写。
  • self.bar具有String类型,因为bar被声明为String
  • 这导致了一个问题,我们试图*将sself中移出,但我们只能引用self,而不是拥有对self的访问权。

您将需要找到一种方法,使之在没有拥有访问的情况下工作。免责声明,我以前没有使用过这个板条箱(安装说明有点疯狂),但是下面是我可以从文档 for PinDriver::output中拼凑出来的东西

  • PinDriver::output采用impl Peripheral,即实现特征Peripheral的任何类型
  • 查看Peripheral,我们可以看到实现它的结构列表。
  • Gpio8确实实现了它,但是这是不好的,因为我们再次遇到了“无法移出共享引用”的问题。
  • 在大多数锈蚀代码中,您可能想要.clone() gpio引脚,但是,没有克隆方法(因为它代表了物理资源的唯一句柄,除非您已经有了一些可以生成新引脚的板:p)
  • 然而,在最底层,有这样的推动力:
代码语言:javascript
复制
impl<T: DerefMut> Peripheral for T
where
    T::Target: Peripheral { ... }
  • 即实现DerefMut<Target = T>的任何类型都实现Periperal,只要T也实现Peripheral
  • 这意味着您可以使用&mut Gpio8。这是有道理的,因为你正在改变光的状态,所以你需要一个可变的手柄。如果您将blink()更改为接受&mut self,则应该能够编写PinDriver::output(&mut self.peripherals.pins.gpio8)

FWIW,embedded通常使用聪明的类型系统技巧在编译时验证更多的行为(要么是为了节省宝贵的CPU周期,要么是为了更好的可靠性)。如果您刚接触过Rust,它可能会非常混乱,因为它可以说比大多数CLI应用程序更“高级”,例如

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74566785

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档