简体   繁体   English

Rust:在递归 Function 中使用可变引用

[英]Rust: Use of Mutable References in Recursive Function

Am trying to print unique contiguous sub-arrays of a Vec using a recursive function like so:我正在尝试使用递归 function 打印Vec的唯一连续子数组,如下所示:

use std::collections::HashSet;

fn recurse<'a>(nums: &'a [i32], already_printed: &'a mut HashSet<&'a [i32]>) {
    if !already_printed.contains(nums) {
        println!("{:#?}", nums);
    }

    already_printed.insert(nums);

    if nums.len() >= 2 {
        recurse(&nums[0..nums.len() - 1], already_printed);
        recurse(&nums[1..nums.len()], already_printed);
    }
}

pub fn main() {
    let k = vec![1, 2, 3, 4, 5];
    let mut already_printed: HashSet<&[i32]> = HashSet::new();
    recurse(&k[0..], &mut already_printed);
}

This of course, as experienced Rustaceans may have guessed already, fails to compile with the following error:当然,正如经验丰富的 Rustaceans 可能已经猜到的那样,编译失败并出现以下错误:

error[E0499]: cannot borrow `*already_printed` as mutable more than once at a time
  --> src/main.rs:12:39
   |
3  | fn recurse<'a>(nums: &'a [i32], already_printed: &'a mut HashSet<&'a [i32]>) {
   |            -- lifetime `'a` defined here
...
11 |         recurse(&nums[0..nums.len() - 1], already_printed);
   |         --------------------------------------------------
   |         |                                 |
   |         |                                 first mutable borrow occurs here
   |         argument requires that `*already_printed` is borrowed for `'a`
12 |         recurse(&nums[1..nums.len()], already_printed);
   |                                       ^^^^^^^^^^^^^^^ second mutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0499`.

I understand from the very helpful error why the compiler refuses to compile this.我从非常有用的错误中理解了为什么编译器拒绝编译它。 However, what would be a workaround, generally speaking, to implement recursive functions that take mutable references as shown in the above code?但是,一般来说,实现上述代码中采用可变引用的递归函数的解决方法是什么?

A possible workaround method I could think of was using the interior mutability pattern à la RefCell :我能想到的一种可能的解决方法是使用内部可变性模式à la RefCell

use std::cell::RefCell;
use std::collections::HashSet;

fn recurse<'a>(nums: &'a [i32], already_printed: &'a RefCell<HashSet<&'a [i32]>>) {
    if !already_printed.borrow().contains(nums) {
        println!("{:#?}", nums);
    }

    already_printed.borrow_mut().insert(nums);

    if nums.len() >= 2 {
        recurse(&nums[0..nums.len() - 1], already_printed);
        recurse(&nums[1..nums.len()], already_printed);
    }
}

pub fn main() {
    let k = vec![1, 2, 3, 4, 5];
    let already_printed: HashSet<&[i32]> = HashSet::new();
    let ref_cell: RefCell<HashSet<&[i32]>> = RefCell::new(already_printed);
    recurse(&k[0..], &ref_cell);
}

While this works, this seems to be discarding the safety rails that the compile time borrow checker provides.虽然这可行,但这似乎放弃了编译时借用检查器提供的安全轨。 Is there a different canonical way to make recursive function calls like the above while still having compile time borrow checker pass?是否有不同的规范方法来进行递归 function 调用,同时仍然通过编译时间借用检查器?

The magical solution is to change the declaration of the function to神奇的解决方案是将 function 的声明更改为

fn recurse<'a, 'b>(nums: &'a [i32], already_printed: &'b mut HashSet<&'a [i32]>) {
// I just changed this lifetime -----------------------^

There was no deep reason for your algorithm not to work, except that you added too many constraints when there was no reason.您的算法无法正常工作并没有深层原因,只是您在没有理由的情况下添加了太多约束。

When you call recurse from inside recurse , it's totally fine, as it frees ownership on end of call.当您从内部调用recurse时,它完全recurse问题,因为它在通话结束时释放了所有权。 So it must work.所以它必须工作。

But you required that all lifetimes be the same while you could just let the compiler determine the real constraints.但是您要求所有生命周期都相同,而您可以让编译器确定真正的约束。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM