将一个变量传递给另一个函数内的函数

huangapple go评论59阅读模式
英文:

Passing a variable to a function within another function

问题

我正在学习Rust。我正在进行一个嵌入式Rust项目,用于与I2C LED驱动器进行接口交互。

我已经定义了一个公共枚举LedRegister,用于定义用于控制每个LED的所有I2C寄存器。这些寄存器定义始终为一个字节,它们由芯片的数据表设置,永远不会更改。但是,我喜欢将它们放在一个枚举中,因为这允许我创建一个只接受LedRegisters作为输入的函数。

pub enum LedRegister {
    // 0-255 PWM value. 255 = max LED brightness.
    CpuStatusR    = 0x03,
    CpuStatusG    = 0x02,
    CpuStatusB    = 0x01,
    Ch1Cor        = 0x04,
    Ch1Ptt        = 0x06,
    Ch1SpareR     = 0x05,
    Ch1StatusR    = 0x0C,
    Ch1StatusG    = 0x0B,
    Ch1StatusB    = 0x0A,
    Ch2Cor        = 0x07,
    Ch2Ptt        = 0x09,
    Ch2SpareR     = 0x08,
    Ch2StatusR    = 0x0E,
    Ch2StatusG    = 0x0D,
    Ch2StatusB    = 0x0F,
    SpareLedR     = 0x12,
    SpareLedG     = 0x11,
    SpareLedB     = 0x10,
}

在同一作用域中,我有一个公共函数enable_led,用于切换每个LED的开关状态:

pub fn enable_led(i2c: &mut I2c, led: LedRegister, state: bool) -> Result<(), Box<dyn Error>> {
    if state {
        i2c.write(&[led as u8, 0xFF])?;
    } else {
        i2c.write(&[led as u8, 0x00])?;
    }

    i2c.write(&[ControlRegister::Update as u8, 0x00])?;

    Ok(())
}

从主函数中,我可以调用这个函数并看到我的LED亮起:

led_driver::enable_led(&mut i2c, led_driver::LedRegister::SpareLedG, true);

我想写另一个函数,允许我调用enable_led多次,并在它们之间添加延迟来让LED闪烁:

pub fn blink_led(i2c: &mut I2c, led: LedRegister, duration_ms: u64) -> Result<(), Box<dyn Error>> {
    enable_led(i2c, led, true);
    // 添加延迟
    enable_led(i2c, led, false);
    // 添加延迟

    Ok(())
}

在这里,Rust会报错,因为'led'的值已经在enable_led的第一个调用中被移动了。我理解只能有一个值的所有者 - 所以我理解为什么Rust会报错。

我已经尝试“借用”LedRegister的值:

pub fn enable_led(i2c: &mut I2c, led: &LedRegister, state: bool) -> Result<(), Box<dyn Error>> {
    if state {
        i2c.write(&[led as u8, 0xFF])?;
    } else {
        i2c.write(&[led as u8, 0x00])?;
    }

    i2c.write(&[ControlRegister::Update as u8, 0x00])?;

    Ok(())
}

led_driver::enable_led(&mut i2c, &led_driver::LedRegister::SpareLedG, true);

这会导致E0606错误,并建议通过原始指针和unsafe块实现强制转换,这在这种情况下似乎过于复杂。

我还考虑过在LedRegister上实现Copy,以便在调用led_enable()之前对初始引用进行复制。这可以编译,但似乎比应该的更复杂。我认为实际上不需要在RAM中多次复制寄存器定义字节。这真的有必要吗?

我感觉我没有使用Rust的正确功能来完成这个任务。我并不强烈要求在这里使用枚举,如果它是错误的工具。但是,我确实想限制enable_led和blink_led的输入,以便它们只接受有效的LED。

在Rust中实现我的目标的最佳方法是什么?

英文:

I am learning Rust. I am working on an embedded Rust project to interface with an I2C LED driver.

I have defined a pub enum LedRegister that defines all of the I2C registers used to control each LED. These register definitions are always one byte, they set by the chip's datasheet, and they will never change. However, I like having them in an enum, because it allows me to create a function that will only accept LedRegisters as inputs.

pub enum LedRegister {
    // 0-255 PWM value. 255 = max LED brightness.
    CpuStatusR    = 0x03,
    CpuStatusG    = 0x02,
    CpuStatusB    = 0x01,
    Ch1Cor        = 0x04,
    Ch1Ptt        = 0x06,
    Ch1SpareR     = 0x05,
    Ch1StatusR    = 0x0C,
    Ch1StatusG    = 0x0B,
    Ch1StatusB    = 0x0A,
    Ch2Cor        = 0x07,
    Ch2Ptt        = 0x09,
    Ch2SpareR     = 0x08,
    Ch2StatusR    = 0x0E,
    Ch2StatusG    = 0x0D,
    Ch2StatusB    = 0x0F,
    SpareLedR     = 0x12,
    SpareLedG     = 0x11,
    SpareLedB     = 0x10,
}

In the same scope, I have a pub fn enable_led to toggle each LED on or off:

pub fn enable_led(i2c: &amp;mut I2c, led: LedRegister, state: bool) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
    if state {
        i2c.write(&amp;[led as u8, 0xFF])?;
    } else {
        i2c.write(&amp;[led as u8, 0x00])?;
    }

    i2c.write(&amp;[ControlRegister::Update as u8, 0x00])?;

    Ok(())
}

From main, I can call this function and see that my LED turns on:

led_driver::enable_led(&amp;mut i2c, led_driver::LedRegister::SpareLedG, true);

I would like to write another function that allows me to blink the LED by calling enable_led multiple times with a delay in between:

pub fn blink_led(i2c: &amp;mut I2c, led: LedRegister, duration_ms: u64) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
    enable_led(i2c, led, true);
    // add a delay
    enable_led(i2c, led, false);
    // add a delay

    Ok(())
}

Here, Rust complains because the value of 'led' has moved into the first call of enable_led. I understand that there can only be one owner of the value - so I get why Rust is complaining.

I have attempted to 'borrow' the value of LedRegister:

pub fn enable_led(i2c: &amp;mut I2c, led: &amp;LedRegister, state: bool) -&gt; Result&lt;(), Box&lt;dyn Error&gt;&gt; {
    if state {
        i2c.write(&amp;[led as u8, 0xFF])?;
    } else {
        i2c.write(&amp;[led as u8, 0x00])?;
    }

    i2c.write(&amp;[ControlRegister::Update as u8, 0x00])?;

    Ok(())
}

led_driver::enable_led(&amp;mut i2c, &amp;led_driver::LedRegister::SpareLedG, true);

This gives me E0606 and a suggestion to implement casting through a raw pointer and unsafe blocks, which seems needlessly complicated for this use case.

I have also considered implementing copy on my LedRegister, so I can make copies of the initial reference before calling led_enable() with the same LED. This compiles, but it also seems more difficult than it should be. I don't think I actually need multiple copies of my register definition byte in RAM. Is this actually necessary?

I get the feeling that I'm not using the right features of Rust for this task. I am not strongly tied to using an enum here if that is the wrong tool for the job. However, I do want to limit the inputs to enable_led and blink_led so they only accept valid LEDs.

What is the best approach to accomplish my objective here in Rust?

答案1

得分: 1

将引用转换为整数就像将指针转换为整数一样,它会给你地址而不是转换后的值,只是不允许这样做,你需要明确使用原始指针。这不需要不安全操作(led as *const LedRegister as u8),但这也不会实现你想要的效果。

正确的解决方案是为你的枚举类型实现 Copy。对于简单的枚举类型,除非有强烈的理由不这样做,否则应该总是实现 Copy。将值的引用传递给函数并没有比复制它更好的方式:它会更慢,需要解引用并将其存储在堆栈中,而不是可能存储在寄存器中,而且没有任何理由使源代码变得更复杂。

英文:

Casting a reference to an integer is like casting a pointer to an integer, which gives you the address and not the value inside casted, except it is not allowed and you need to go through a raw pointer explicitly. This doesn't require unsafe (led as *const LedRegister as u8), but this also doesn't do what you want.

The correct solution is to implement Copy for your enum. You should always implement Copy for simple enums, unless there is a strong reason not to. Taking references to the value is not in any way better than copying it: it is slower, requiring dereferences and storing in the stack instead of potentially in registers, and it complicates the source code for no reason.

huangapple
  • 本文由 发表于 2023年6月2日 02:00:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76384547.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定