如何解决Rust - 在递归函数中收集 Vec 的切片
我目前正在尝试构建一个霍夫曼编码程序,并且在遍历生成的霍夫曼树以创建查找表时遇到了一个问题。我决定用递归函数实现上述遍历。在实际实现中,我使用 bitvec crate 来保存位序列,但为了简单起见,我将在这篇文章中使用 Vec<bool>
。
我的想法是在 Vec
codewords
中保存所有代码字的集合,然后只保存该向量中的一部分用于实际查找表,为此我使用了 {{ 1}}。
问题是我将如何解决为左右遍历添加 0 或 1 的问题。我的想法是保存当前序列切片的克隆,将 0 附加到 HashMap
,然后在向左遍历后将该克隆附加到 codewords
的末尾,以便我可以推1 并向右移动。我想出的函数是这样的:
codewords
显然,生命周期和那段代码中的借用存在问题,我有点明白问题是什么。据我了解,当我在递归调用中给出 use std::collections::HashMap;
// ignore everything being public,I use getters in the real code
pub struct HufTreeNode {
pub val: u8,pub freq: usize,pub left: i16,pub right: i16,}
fn traverse_tree<'a>(
cur_index: usize,height: i16,codewords: &'a mut Vec<bool>,lookup_table: &mut HashMap<u8,&'a [bool]>,huffman_tree: &[HufTreeNode],) {
let cur_node = &huffman_tree[cur_index];
// if the left child is -1,we reached a leaf
if cur_node.left == -1 {
// the last `height` bits in codewords
let cur_sequence = &codewords[(codewords.len() - 1 - height as usize)..];
lookup_table.insert(cur_node.val,cur_sequence);
return;
}
// save the current sequence so we can traverse to the right afterwards
let mut cur_sequence = codewords[(codewords.len() - 1 - height as usize)..].to_vec();
codewords.push(false);
traverse_tree(
cur_node.left as usize,height + 1,codewords,// mutable borrow - argument requires that `*codewords` is borrowed for `'a`
lookup_table,huffman_tree,);
// append the prevIoUsly saved current sequence
codewords.append(&mut cur_sequence); // second mutable borrow occurs here
codewords.push(true); // third mutable borrow occurs here
traverse_tree(
cur_node.right as usize,// fourth mutable borrow occurs here
lookup_table,);
}
fn main() {
// ...
}
作为参数时,只要我将切片保存在 codewords
中,它就必须借用向量,这显然是不可能的,从而导致错误.我该如何解决这个问题?
这就是 lookup_table
给我的:
cargo check
我在这里错过了什么?向量 API 中是否有一些我遗漏的神奇功能,为什么这首先会造成生命周期问题?据我所知,我所有的一生都是正确的,因为 error[E0499]: cannot borrow `*codewords` as mutable more than once at a time
--> untitled.rs:43:5
|
14 | fn traverse_tree<'a>(
| -- lifetime `'a` defined here
...
34 | / traverse_tree(
35 | | cur_node.left as usize,36 | | height + 1,37 | | codewords,// mutable borrow - argument requires that `*codewords` is borrowed for `'a`
| | --------- first mutable borrow occurs here
38 | | lookup_table,39 | | huffman_tree,40 | | );
| |_____- argument requires that `*codewords` is borrowed for `'a`
...
43 | codewords.append(&mut cur_sequence); // second mutable borrow occurs here
| ^^^^^^^^^ second mutable borrow occurs here
error[E0499]: cannot borrow `*codewords` as mutable more than once at a time
--> untitled.rs:44:5
|
14 | fn traverse_tree<'a>(
| -- lifetime `'a` defined here
...
34 | / traverse_tree(
35 | | cur_node.left as usize,40 | | );
| |_____- argument requires that `*codewords` is borrowed for `'a`
...
44 | codewords.push(true); // third mutable borrow occurs here
| ^^^^^^^^^ second mutable borrow occurs here
error[E0499]: cannot borrow `*codewords` as mutable more than once at a time
--> untitled.rs:48:9
|
14 | fn traverse_tree<'a>(
| -- lifetime `'a` defined here
...
34 | / traverse_tree(
35 | | cur_node.left as usize,40 | | );
| |_____- argument requires that `*codewords` is borrowed for `'a`
...
48 | codewords,// fourth mutable borrow occurs here
| ^^^^^^^^^ second mutable borrow occurs here
总是活得足够长,codewords
可以保存所有这些切片,而且我从来没有同时两次借东西。如果我的生命周期有问题,编译器会在 lookup_table
块内抱怨,而我在它之后取的 if cur_node.left == -1
是一个拥有的 cur_sequence
,所以不能有任何借用问题。所以问题真的在于核心思想有一个以可变引用作为参数的递归函数。
有什么办法可以解决这个问题吗?我尝试让 Vec
拥有并返回它,但是编译器无法确保我在 codewords
中保存的位序列存在足够长的时间。我仍然拥有的唯一想法是将拥有的向量保存在 lookup_table
中,但此时 lookup_table
向量首先已过时,我可以通过使用 codewords
向量来简单地实现这一点作为我在每次调用中克隆的参数,但我选择了我的方法以在实际编码过程中获得更好的缓存性能,然后我会丢失。
解决方法
问题在于,当您像在 cur_sequence
中那样从 codewords
创建切片 let cur_sequence = &codewords[(codewords.len() - 1 - height as usize)..];
时,编译器会将 codewords
引用的生命周期延长至至少与 cur_sequence
相同(原因:编译器希望确保切片 cur_sequence
始终有效,但是如果您更改 codewords
(例如,清除它),那么 {{1 }} 无效。通过保持对 cur_sequence
的不可变引用,借用规则将禁止在切片仍然存在时修改 codewords
)。不幸的是,您将 codewords
保存在 cur_sequence
中,从而在整个函数中保持对 lookup_table
的引用,因此您不能再可变地借用 codewords
。
解决办法是自己维护切片的索引:创建一个struct:
codewords
然后用它代替切片:
struct Range {
start: usize,end: usize
}
impl Range {
fn new(start: usize,end: usize) -> Self {
Range{ start,end}
}
}
这样,保持范围有效的责任就是您的责任。
完整代码:
let cur_range = Range::new(
codewords.len() - 1 - height as usize,codewords.len() - 1
);
lookup_table.insert(cur_node.val,cur_range);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。