Base85迭代器解码遇Rust借用检查器错误(E0716)求助
我正在基于迭代器实现Base85解码,目前为初步版本(算法可能不准确,先聚焦解决借用检查器问题)。编写的代码如下:
pub fn decode(indata: impl IntoIterator<Item=impl Borrow<u8>> + 'static) -> impl Iterator<Item=Result<u8>> { #[inline] fn char85_to_byte(c: u8) -> Result<u8> { match c { b'0'..=b'9' => Ok(c - b'0'), b'A'..=b'Z' => Ok(c - b'A' + 10), b'a'..=b'z' => Ok(c - b'a' + 36), b'!' => Ok(62), b'#' => Ok(63), b'$' => Ok(64), b'%' => Ok(65), b'&' => Ok(66), b'(' => Ok(67), b')' => Ok(68), b'*' => Ok(69), b'+' => Ok(70), b'-' => Ok(71), b';' => Ok(72), b'<' => Ok(73), b'=' => Ok(74), b'>' => Ok(75), b'?' => Ok(76), b'@' => Ok(77), b'^' => Ok(78), b'_' => Ok(79), b'`' => Ok(80), b'{' => Ok(81), b'|' => Ok(82), b'}' => Ok(83), b'~' => Ok(84), v => Err(Error::UnexpectedCharacter(v)), } } indata .into_iter() .map(|v|*v.borrow()) .filter(|v| !(*v == 32 || *v == 10 || *v == 11 || *v == 13)) .chunks(5) .into_iter() .map(|mut v| { let (a,b,c,d,e) = (v.next(), v.next(), v.next(), v.next(), v.next()); let accumulator = u32::from(char85_to_byte(a.unwrap())?) + u32::from(b.map_or(Err(Error::UnexpectedEnd), char85_to_byte)?) * 85u32.pow(1) + u32::from(c.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(2) + u32::from(d.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(3) + u32::from(e.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(4); Ok([ Some((accumulator >> 24) as u8), c.map(|_|(accumulator >> 16) as u8), d.map(|_|(accumulator >> 8) as u8), e.map(|_|accumulator as u8) ]) }) .flatten_ok() .filter_map_ok(|v| v) }
编译时出现如下错误:
error[E0716]: temporary value dropped while borrowed --> src/lib.rs:96:5 | 96 | indata | _____^ | |_____| | || 97 | || .into_iter() 98 | || .map(|v|*v.borrow()) 99 | || .filter(|v| !(*v == 32 || *v == 10 || *v == 11 || *v == 13)) 100 | || .chunks(5) | ||__________________^ creates a temporary which is freed while still in use ... | 114 | | ]) 115 | | }) | |___________- argument requires that borrow lasts for `'static` ... 118 | } | - temporary value is freed at the end of this statement For more information about this error, try `rustc --explain E0716`.
我认为所有内容都按值传递了,想请教仍被借用的是什么?
问题根源
问题出在chunks(5)方法:该方法返回的迭代器会借用上游迭代器,而非获取其所有权。你的函数要求返回的迭代器具有'static生命周期(因输入参数indata标注了'static),但上游迭代器是函数内部创建的临时对象,函数执行完毕返回时,这个临时对象会被销毁,导致返回的迭代器引用了已释放的内存,触发借用检查错误。
你误以为所有操作都是按值传递,但chunks是例外:它不会消耗上游迭代器,而是借用它生成批次,形成了对函数内临时值的引用,无法满足'static的生命周期要求。
解决思路
方案1:移除不必要的'static约束
如果不需要返回的迭代器具有'static生命周期,去掉输入参数的'static标注即可:
pub fn decode(indata: impl IntoIterator<Item=impl Borrow<u8>>) -> impl Iterator<Item=Result<u8>> { // 剩余代码保持不变 }
此时返回的迭代器生命周期会与输入indata绑定,不再要求引用必须存活到'static。
方案2:使用所有权转移的分块方式(保持惰性)
若需保持迭代器惰性且必须使用'static,可借助itertools的batching方法手动实现分块,直接获取上游元素的所有权:
use itertools::Itertools; pub fn decode(indata: impl IntoIterator<Item=impl Borrow<u8>> + 'static) -> impl Iterator<Item=Result<u8>> { #[inline] fn char85_to_byte(c: u8) -> Result<u8> { // 原函数逻辑不变 } indata .into_iter() .map(|v| *v.borrow()) .filter(|v| !(*v == 32 || *v == 10 || *v == 11 || *v == 13)) .batching(|it| { let mut batch = Vec::with_capacity(5); while batch.len() < 5 { match it.next() { Some(c) => batch.push(c), None => break, } } batch.is_empty().then_some(batch) }) .map(|batch| { let (a, b, c, d, e) = match batch.as_slice() { [a] => (Some(*a), None, None, None, None), [a, b] => (Some(*a), Some(*b), None, None, None), [a, b, c] => (Some(*a), Some(*b), Some(*c), None, None), [a, b, c, d] => (Some(*a), Some(*b), Some(*c), Some(*d), None), [a, b, c, d, e] => (Some(*a), Some(*b), Some(*c), Some(*d), Some(*e)), _ => unreachable!(), }; let accumulator = u32::from(char85_to_byte(a.unwrap())?) + u32::from(b.map_or(Err(Error::UnexpectedEnd), char85_to_byte)?) * 85u32.pow(1) + u32::from(c.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(2) + u32::from(d.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(3) + u32::from(e.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(4); Ok([ Some((accumulator >> 24) as u8), c.map(|_|(accumulator >> 16) as u8), d.map(|_|(accumulator >> 8) as u8), e.map(|_|accumulator as u8) ]) }) .flatten_ok() .filter_map_ok(|v| v) }
batching会获取上游迭代器的所有权,生成的批次迭代器不再依赖函数内的临时对象,满足'static要求。
方案3:提前收集所有数据(牺牲惰性)
若可接受一次性加载所有数据到内存,先将过滤后的元素收集到Vec<u8>,再对Vec的迭代器分块:
pub fn decode(indata: impl IntoIterator<Item=impl Borrow<u8>> + 'static) -> impl Iterator<Item=Result<u8>> { #[inline] fn char85_to_byte(c: u8) -> Result<u8> { // 原函数逻辑不变 } let filtered: Vec<u8> = indata .into_iter() .map(|v| *v.borrow()) .filter(|v| !(*v == 32 || *v == 10 || *v == 11 || *v == 13)) .collect(); filtered .chunks(5) .into_iter() .map(|chunk| { let (a, b, c, d, e) = match chunk { [a] => (Some(*a), None, None, None, None), [a, b] => (Some(*a), Some(*b), None, None, None), [a, b, c] => (Some(*a), Some(*b), Some(*c), None, None), [a, b, c, d] => (Some(*a), Some(*b), Some(*c), Some(*d), None), [a, b, c, d, e] => (Some(*a), Some(*b), Some(*c), Some(*d), Some(*e)), _ => unreachable!(), }; let accumulator = u32::from(char85_to_byte(a.unwrap())?) + u32::from(b.map_or(Err(Error::UnexpectedEnd), char85_to_byte)?) * 85u32.pow(1) + u32::from(c.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(2) + u32::from(d.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(3) + u32::from(e.map_or(Ok(0), char85_to_byte)?) * 85u32.pow(4); Ok([ Some((accumulator >> 24) as u8), c.map(|_|(accumulator >> 16) as u8), d.map(|_|(accumulator >> 8) as u8), e.map(|_|accumulator as u8) ]) }) .flatten_ok() .filter_map_ok(|v| v) }
这种方式简单直接,Vec<u8>持有所有数据的所有权,chunks返回的迭代器引用Vec,而Vec被移动到返回的迭代器链中,生命周期满足要求。
内容的提问来源于stack exchange,提问作者spease

