Rust中如何在循环内引用外部定义的对象?
Rust循环内外对象引用问题解决方案
问题代码
fn main() -> ! { // ..... // 初始化代码 let slow_bdrate = <Rate<u32, 1, 1>>::Hz(120000); let slow_uart = UartConfig::new( slow_bdrate, uart::DataBits::Eight, None, uart::StopBits::One, ); let data: u8 = 0; let uart_peripheral = UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS); loop { uart_peripheral.enable(slow_uart, clocks.peripheral_clock.freq()) .unwrap(); uart_peripheral.write_full_blocking(&data); delay.delay_ms(100); } }
问题描述
作为Rust新手,我想在循环内引用外部定义的slow_uart,但该类型未实现Copy trait,编译器报错。除了Copy之外还有什么方法能在循环内访问外部对象?能不能用类似C++指针的方式?相关最佳实践是什么?我试过把uart_peripheral移入循环,但必须同时移动所有参数,不想这么做。
编译错误
error[E0382]: use of moved value: `uart_peripheral` --> blinky.rs:131:9 | 112 | let mut uart_peripheral = UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS); | -------- move occurs because `uart_peripheral` has type `UartPeripheral<rp2040_hal::uart::Disabled, UART0, (rp2040_hal::gpio::Pin<Gpio0, FunctionUart, PullDown>, rp2040_hal::gpio::Pin<Gpio1, FunctionUart, PullDown>)>`, which does not implement the `Copy` trait ... 126 | loop { | ---- inside of this loop ... 131 | uart_peripheral.enable(slow_uart, clocks.peripheral_clock.freq()) | ^^^^ value moved here, in previous iteration of loop error[E0382]: use of moved value: `slow_uart` --> blinky.rs:131:21 | 98 | let slow_uart = UartConfig::new( | --------- move occurs because `slow_uart` has type `UartConfig`, which does not implement the `Copy` trait ... 126 | loop { | ---- inside of this loop ... 131 | uart_peripheral.enable(slow_uart, clocks.peripheral_clock.freq()) | ^^^^^^^^^ value moved here, in previous iteration of loop
解决方案与最佳实践
核心问题分析
编译错误的本质是:enable方法会**转移(move)**传入的uart_peripheral和slow_uart的所有权,第一次循环执行后,这两个变量的所有权已被方法拿走,第二次循环就无法再使用。更关键的是,UART外设只需要启用一次,不需要每次循环都调用enable,这是代码逻辑的错误。
1. 修正逻辑:将enable移出循环(最优解)
这是最符合嵌入式外设使用逻辑的做法,把启用操作放到循环外,只执行一次:
fn main() -> ! { // ..... // 保留原有初始化代码 let slow_bdrate = <Rate<u32, 1, 1>>::Hz(120000); let slow_uart = UartConfig::new( slow_bdrate, uart::DataBits::Eight, None, uart::StopBits::One, ); let data: u8 = 0; let mut uart_peripheral = UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS); // 外设启用只做一次,得到启用后的UART对象 let mut uart = uart_peripheral.enable(slow_uart, clocks.peripheral_clock.freq()) .unwrap(); loop { // 直接用启用后的uart对象操作 uart.write_full_blocking(&data); delay.delay_ms(100); } }
解释:rp2040-hal的UartPeripheral采用状态机设计,Disabled状态的外设调用enable后,会转换成Enabled状态的Uart对象,后续直接用这个对象进行读写即可,无需重复启用。
2. 通用场景:用引用/借用访问外部对象
如果确实需要在循环内多次访问外部对象(比如某些需要反复传入配置的场景),可以用不可变引用(&),前提是方法支持接收引用参数:
- 查看
enable方法的签名,如果它能接收&UartConfig,就把调用改成uart_peripheral.enable(&slow_uart, ...),这样就不会转移slow_uart的所有权。 - 对于可变对象,需要先声明为
mut,再用可变引用&mut传递,但这个例子里enable是消耗对象的,所以不适用。
3. 类似C++指针的替代方案:智能指针
如果必须在多处持有对象所有权,可以用Arc(原子引用计数指针),但这多用于多线程场景,单线程嵌入式环境完全没必要,反而会增加不必要的性能开销,不推荐。
最佳实践总结
- 遵循HAL库的状态设计:嵌入式HAL库的外设大多用状态机区分未初始化/已启用等状态,不要违背API设计反复调用初始化/启用方法。
- 避免不必要的所有权转移:先看方法签名判断是否会消耗对象(参数是值传递还是引用),值传递的话要么只调用一次,要么用引用/克隆(如果类型实现
Clone)。 - 先修正逻辑再调语法:很多编译错误是因为逻辑不符合硬件或API的使用规则,比如这个例子里反复启用外设本身就不合理,修正逻辑比折腾引用更高效。
内容的提问来源于stack exchange,提问作者Gu-Cho
相关产品推荐
相关产品推荐

