Rust 如何找到或插入生锈的Vec
我正在尝试编写一个函数,该函数find返回对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() } }
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
期间不会保持
这类似于以下问题,这些问题使用循环的提前返回来找到相同的限制:
contains
会增加复杂性,我想了解如何避免将来借用检查器出现此类问题。@chrispeaceHashSet
内部包含一个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()
}
}