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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 20:25:54