Rust编译错误E0499:返回值变更为何引发self多次可变借用冲突
DS18B20 Rust驱动中Result返回值引发的可变借用冲突问题
问题现象
开发DS18B20的Rust驱动时,将onewire_reset方法的返回值从bool改为Result<(), &str>后,出现如下编译错误:
error[E0499]: cannot borrow `*self` as mutable more than once at a time --> src/component/ds18b20.rs:60:9 | 37 | pub fn temperature_read(&mut self) -> Result<f32, &str> { | - let's call the lifetime of this reference `'1` 38 | self.onewire_reset()?; | --------------------- | | | first mutable borrow occurs here | returning this value requires that `*self` is borrowed for `'1` 39 | 40 | self.onewire_byte_write(CMD::Skip as u8); | ^^^^ second mutable borrow occurs here
注释掉temperature_read中第一个self.onewire_reset()?;后,第二个self.onewire_reset()?;下方的self.onewire_byte_write仍会触发相同错误。
相关核心代码如下:
use esp_idf_svc::hal::delay::*; use esp_idf_svc::hal::gpio::*; use esp_idf_svc::hal::peripheral::*; enum CMD { Skip = 0xcc, Write = 0x4e, Temp = 0x44, Read = 0xbe, } pub struct Ds18b20<'a, OnewirePin> where OnewirePin: Peripheral<P = OnewirePin> + InputPin + OutputPin, { pin: PinDriver<'a, OnewirePin, InputOutput>, delay: Delay, } impl<'a, OnewirePin> Ds18b20<'a, OnewirePin> where OnewirePin: Peripheral<P = OnewirePin> + InputPin + OutputPin, { pub fn temperature_read(&mut self) -> Result<f32, &str> { self.onewire_reset()?; self.onewire_byte_write(CMD::Skip as u8); self.onewire_byte_write(CMD::Temp as u8); self.onewire_reset()?; self.onewire_byte_write(CMD::Skip as u8); self.onewire_byte_write(CMD::Read as u8); let temp_l = self.onewire_byte_read(); let temp_h = self.onewire_byte_read(); Ok((((temp_h as i16) << 8 | (temp_l as i16)) as f32) * 0.0625) } fn onewire_reset(&mut self) -> Result<(), &str> { self.pin.set_low().unwrap(); self.delay.delay_us(600); self.pin.set_high().unwrap(); self.delay.delay_us(80); if self.pin.is_low() { self.delay.delay_us(300); if self.pin.is_high() { Ok(()) } else { Err("reset err") } } else { Err("reset err") } } fn onewire_byte_write(&mut self, mut byte: u8) { for _ in 0..8 { let bit = byte & 0x01 != 0; byte >>= 1; self.pin.set_low().unwrap(); self.delay.delay_us(if bit { 5 } else { 60 }); self.pin.set_high().unwrap(); self.delay.delay_us(if bit { 60 } else { 5 }); } } }
问题原因
这是Rust生命周期推导的典型问题:
- 当
onewire_reset返回Result<(), &str>时,编译器会默认推导错误字符串的生命周期与&mut self的生命周期'1绑定(编译器无法自动识别错误字面量是'static的)。 - 使用
?运算符时,这个绑定会导致self的可变借用被延长到整个函数周期,后续调用self.onewire_byte_write时就会触发二次可变借用的冲突。 - 之前返回
bool时没有这个问题,因为bool不涉及生命周期绑定,借用会在语句结束后立即释放。
解决方案
有两种简单的修复方式:
方法一:明确错误字符串的生命周期为'static
修改onewire_reset和temperature_read的返回类型,标注错误字符串的生命周期为'static,让编译器知道错误信息不依赖self的生命周期:
pub fn temperature_read(&mut self) -> Result<f32, &'static str> { // 函数内容不变 } fn onewire_reset(&mut self) -> Result<(), &'static str> { self.pin.set_low().unwrap(); self.delay.delay_us(600); self.pin.set_high().unwrap(); self.delay.delay_us(80); if self.pin.is_low() { self.delay.delay_us(300); if self.pin.is_high() { Ok(()) } else { Err("reset err") // 字面量默认是'static生命周期 } } else { Err("reset err") } }
方法二:将错误信息改为String类型(适用于动态错误场景)
如果后续需要动态生成错误信息,可以改用拥有所有权的String,完全避免生命周期绑定问题:
pub fn temperature_read(&mut self) -> Result<f32, String> { // 函数内容不变 } fn onewire_reset(&mut self) -> Result<(), String> { self.pin.set_low().unwrap(); self.delay.delay_us(600); self.pin.set_high().unwrap(); self.delay.delay_us(80); if self.pin.is_low() { self.delay.delay_us(300); if self.pin.is_high() { Ok(()) } else { Err("reset err".to_string()) } } else { Err("reset err".to_string()) } }
原理说明
- 方法一利用了Rust字符串字面量默认拥有
'static生命周期的特性,明确标注后,编译器不会将错误信息与self的生命周期绑定,self的可变借用会在onewire_reset调用结束后立即释放,后续操作不再触发借用冲突。 - 方法二通过使用
String,摆脱了对引用生命周期的依赖,代价是少量内存分配,对于嵌入式场景来说这种开销几乎可以忽略。
内容的提问来源于stack exchange,提问作者xiaguangbo
相关产品推荐
相关产品推荐

