我试着去学习锈病,但是碰到了一堵墙,我想要做一些相对简单的事情。我试图为ESP32c3单片机编写一个简单的眨眼示例。我得到了一个基本的示例,但当试图扩展/概括该示例时,我开始遇到编译错误。
我的项目由两个板条箱组成的货物工作区- entrypoint和blink。
我能够使以下基本版本在没有问题的情况下工作:
// 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();
}// 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 )。
为了改进错误处理,我做了以下修改。这个版本也很好,我只是为了得到反馈,我的方法是多么的习以为常/你会怎么做呢?我猜,最好是使用自定义错误类型,或者即使在生产代码中,也可以将字符串切片作为错误返回到可接受的/常见的位置吗?
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中的概念。
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(())
})
}以上代码导致以下编译器错误:
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变量,也会发生相同的错误:
...
let mut led = gpio::PinDriver::output((*peripherals).pins.gpio8).map_err(|_: EspError| "Failed to set pin to output")?;
...尝试nr.2:作为我的下一种方法,我尝试编写一个带有几个函数的结构,这些函数将充当类。不幸的是,我遇到了完全相同的编译器错误。
// 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(())
}
}// 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));
}
}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 )截然不同。
再一次,如果您在上面的代码中发现任何不寻常的地方,我也非常感谢您提供的任何最佳实践建议/更正。
发布于 2022-11-25 07:35:16
错误的基本依据可以用一个简单的例子再现:
struct Foo {
bar: String
}
impl Foo {
fn baz(&self) {
let s = self.bar;
}
}正在发生的事情是:
self有&Foo类型,因为参数是用&self声明的,&self是self: &Self的缩写。self.bar具有String类型,因为bar被声明为Strings从self中移出,但我们只能引用self,而不是拥有对self的访问权。您将需要找到一种方法,使之在没有拥有访问的情况下工作。免责声明,我以前没有使用过这个板条箱(安装说明有点疯狂),但是下面是我可以从文档 for PinDriver::output中拼凑出来的东西
PinDriver::output采用impl Peripheral,即实现特征Peripheral的任何类型Peripheral,我们可以看到实现它的结构列表。Gpio8确实实现了它,但是这是不好的,因为我们再次遇到了“无法移出共享引用”的问题。.clone() gpio引脚,但是,没有克隆方法(因为它代表了物理资源的唯一句柄,除非您已经有了一些可以生成新引脚的板:p)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应用程序更“高级”,例如
https://stackoverflow.com/questions/74566785
复制相似问题