Rust 如何找到或插入生锈的Vec

Rust 如何找到或插入生锈的Vec,rust,borrow-checker,Rust,Borrow Checker,我正在尝试编写一个函数,该函数find返回对Vec中现有元素的可变引用,或者如果该元素不存在,则插入它并返回对新元素的可变引用 我试过好几次了,但借书人不相信。我已经将我试图编写的代码简化为下面的示例,这会产生相同的错误 fn mut\u find\u或插入(vec:&mut vec,val:T)->&mut T T{ 如果让某些(u)=向量iter_mut().find(|u |**u==val){ U }否则{ 矢量推力(val); vec.last_mut().unwrap() } }

我正在尝试编写一个函数,该函数find返回对Vec中现有元素的可变引用,或者如果该元素不存在,则插入它并返回对新元素的可变引用

我试过好几次了,但借书人不相信。我已经将我试图编写的代码简化为下面的示例,这会产生相同的错误

fn mut\u find\u或插入(vec:&mut vec,val:T)->&mut T T{
如果让某些(u)=向量iter_mut().find(|u |**u==val){
U
}否则{
矢量推力(val);
vec.last_mut().unwrap()
}
}
游乐场连接:

Rust向我提供了以下编译器错误(通过游乐场链接的完整消息):


这似乎可以在rust中实现,但我不清楚如何重新实现它以避免借用检查器错误。

Vec
是一种无序的、不太结构化的类型。它无法查找项目在其中的确切位置;默认函数最接近的是
contains()
,它只告诉您是否包含该项

此外,由于
Vec
不是
集合
,因此“查找项目或追加并返回”的行为未定义-“查找项目”,如果存在重复项,则需要进一步定义

要解决此问题而不更改为正确的类型(这是您真正想要的类型。请注意的存在,这正是您所追求的。为工作使用适当的结构是值得的,而不是试图使所有内容都适合
Vec
),我们必须自己构建它。根据您的签名,它看起来是这样的():

trait向量seeKorAppend:大小{
fn获取或插入(&mut self,项目:T)->&mut T T;
}
impl Vec为Vec添加
其中T:PartialEq+Clone{
fn获取或插入(&mut self,项目:T)->&mut T T{
if!self.contains(&item){
self.push(item.clone());
}
因为我在self.iter_mut(){
如果i==&mut项{
返回i;
}
}
遥不可及!();
}
}
初始版本无法工作的原因是返回的生存期要求;从
Vec
返回引用的所有方法都要求在使用期间具有终身有效性。通过返回这样一个
&mut
引用,如果您试图一次完成,那么
Vec
的变异将在它已经存在可变借用时发生

将循环一分为二,然后执行插入(不保留引用)以找到引用,这样我们就可以避开这个问题。执行此操作的另一种方法是通过可序列化或可散列标识符(确切的方式是
HashMap
HashSet
工作)存储项,以便天生提供此间接层


作品中有一个锈迹特征可以减轻一些痛苦(),但是,正如你从github问题中看到的,它不是在不久的将来出现的。

这不起作用的原因是当前借阅检查器的限制。这非常类似于,其中编译器对整个
match
语句过度借用,而借用仅在其中一个分支中使用。使用实验性的“Polonius”借用检查器(可在夜间编译器上使用
-Z Polonius
标志),您的代码可以按原样接受

在稳定编译器中工作时,按照建议重新设计数据结构可能是个好主意,但如果需要使用
Vec
,可以通过迭代索引而不是项来解决此问题:

fn mut_find_or_insert<T: PartialEq>(vec: &mut Vec<T>, val: T) -> &mut T {
    if let Some(i) = (0..vec.len()).find(|&i| vec[i] == val) {
        &mut vec[i]
    } else {
        vec.push(val);
        vec.last_mut().unwrap()
    }
}
fn mut\u find\u或插入(vec:&mut vec,val:T)->&mut T T{
如果让一些(i)=(0..vec.len()).find(|&i|vec[i]==val){
&mut-vec[i]
}否则{
矢量推力(val);
vec.last_mut().unwrap()
}
}
这是因为调用
find
的结果不是一个引用,因此
vec
的借用在
if let
期间不会保持

这类似于以下问题,这些问题使用循环的提前返回来找到相同的限制:


谢谢您的建议。我完全同意HashSet是一个更好的选择,我可能不应该固执地尝试使用Vec。然而,要想对答案感到满意,我真的很想更多地解释为什么rust不允许我做我想做的事情。正如您所强调的,检查
contains
会增加复杂性,我想了解如何避免将来借用检查器出现此类问题。@chrispeace
HashSet
内部包含一个
HashMap
,因此,
get\u或\u insert()
对检索键进行操作,不是在整个集合上。这就是关键的区别,也是我的(工作)片段所说明的——由于生命周期的工作方式,你不得不将你的步骤一分为二,不管它是在不同的结构中,还是像我一样,使用不同的路径。首先,你编辑,然后返回。实际上,这与“生锈”或“借阅检查”几乎没有关系。这取决于代码结构和合理性,我只是查看了HashMap,我担心使用
get\u或\u insert
不会返回我可以从函数返回的可变引用,因为不允许修改HashMap中的条目,从而使哈希无效。这是否只让我可以选择使用
Vec
将插入和返回分开?@ChrisPearce此时,我真的需要问一下您试图做什么。您有一个拥有的T,您将其用作默认值;整个事情听起来你真的应该把它一分为二。我将重写答案中的代码片段,以考虑muta
trait VecSeekOrAppend<T:PartialEq>:Sized {
    fn get_or_insert(&mut self, item: T) -> &mut T;
}

impl<T> VecSeekOrAppend<T> for Vec<T> 
    where T: PartialEq + Clone {

    fn get_or_insert(&mut self, item: T) -> &mut T {
        if !self.contains(&item) {
            self.push(item.clone());
        }
        for i in self.iter_mut() {
            if i == &mut item {
                return i;
            }
        }
        unreachable!();
    }
}
fn mut_find_or_insert<T: PartialEq>(vec: &mut Vec<T>, val: T) -> &mut T {
    if let Some(i) = (0..vec.len()).find(|&i| vec[i] == val) {
        &mut vec[i]
    } else {
        vec.push(val);
        vec.last_mut().unwrap()
    }
}