首页 > 解决方案 > 在 Rust 中返回一个可变引用

问题描述

我正在学习 Rust,如果这是一个微不足道的问题,我深表歉意。我用谷歌搜索了一个小时无济于事。

我有一个枚举值数组。我希望在该数组中找到与特定模式匹配的随机位置并返回对其的可变引用,以修改该位置中的元素。

enum Tile {
    Empty,
    ...  // Other enum values
}

fn random_empty_tile(arr: &mut [Tile]) -> &mut Tile {
    loop {
        let i = rand::thread_rng().gen_range(0, arr.len());
        let tile = &mut arr[i];
        if let Tile::Empty = tile {
            return tile;
        }
    }
}

借用检查器在这里抱怨两件具体的事情。首先是arr.len()通话。这是不允许的,因为它需要对 进行不可变引用arr,并且我们已经arr通过参数获得了对 的可变引用。因此,不能进行其他引用,并且不允许调用。

第二个是return tile。这会失败,因为借用检查器无法证明此引用的生命周期与arr自身的生命周期相同,因此返回是不安全的。

我认为以上对错误的描述是正确的;我想我明白出了什么问题。不幸的是,我不知道如何解决这些问题。如果有人可以提供一个惯用的解决方案来实现这种行为,将不胜感激。

最终,我希望做到以下几点:

let mut arr = [whatever];
let empty_element = random_empty_tile(&mut arr);
*empty_element = Tile::SomeOtherValue;

从而改变数组以替换空值。

标签: rust

解决方案


回答问题

fn random_empty_tile(arr: &mut [Tile]) -> &mut Tile {
    let len = arr.len();
    let mut the_chosen_i = 0;
    loop {
        let i = rand::thread_rng().gen_range(0, len);
        let tile = &mut arr[i];
        if let Tile::Empty = tile {
            the_chosen_i = i;
            break;
        }
    }
    &mut arr[the_chosen_i]
}

将工作。从借用检查器的角度来看,您可以在循环中使用可变借用,但不要滥用它。您实际上正在做的事情是反复地反复借用一个数组。与往常一样,编译器非常有用,如果您知道如何使用它。

为了找到问题的根源,让我们看看我们循环的前两次迭代:

fn random_empty_tile_2<'arr>(arr: &'arr mut [Tile]) -> &'arr mut Tile {
   let len = arr.len();

   // First loop iteration
   {
       let i = thread_rng().gen_range(0, len);
       let tile = &mut arr[i]; // Lifetime: 'arr
       if let Tile::Empty = tile {
           return tile;
       }
   } 

   // Second loop iteration
   {
       let i = thread_rng().gen_range(0, len);
       let tile = &mut arr[i]; // Lifetime: 'arr
        if let Tile::Empty = tile {
           return tile;
       }
   }

   unreachable!();

}

编译器告诉我们: 的借用arr,被调用tile必须具有与数组本身相同的生命周期,被调用'arr,因为它被返回。在下一次循环迭代中,我们再次借用arrfor 'arr。这违反了borrowcheckers 规则。

一些评论

所有这些可变性并不是在帮自己一个忙。这可能会在稍后在 main 中抱怨的 borrowchecker 中表现出来,当您尝试同时持有对 inarr和 use的值的可变引用arr时,因为这是(当然,如果您考虑一下!)是不允许的。

此外,您选择随机空图块的算法具有危险的投机性。如果一个大数组中只有一个空图块怎么办?您的实施将永远存在。考虑首先过滤所有指向空图块的索引,然后从该集合中选择一个随机索引,然后返回该索引指向的条目。我不会为此提供代码,你自己得到这个:)


推荐阅读