dsp*_*pyz 3 return rust borrow-checker
我正在尝试编写一个函数,只有当元素大于向量中已有的最后一个元素时,才会将元素推送到有序向量的末尾,否则返回带有ref的错误给最大元素.据我所知,这似乎并没有违反任何借用规则,但借用检查员不喜欢它.我不明白为什么.
struct MyArray<K, V>(Vec<(K, V)>);
impl<K: Ord, V> MyArray<K, V> {
pub fn insert_largest(&mut self, k: K, v: V) -> Result<(), &K> {
{
match self.0.iter().next_back() {
None => (),
Some(&(ref lk, _)) => {
if lk > &k {
return Err(lk);
}
}
};
}
self.0.push((k, v));
Ok(())
}
}
Run Code Online (Sandbox Code Playgroud)
error[E0502]: cannot borrow `self.0` as mutable because it is also borrowed as immutable
--> src/main.rs:15:9
|
6 | match self.0.iter().next_back() {
| ------ immutable borrow occurs here
...
15 | self.0.push((k, v));
| ^^^^^^ mutable borrow occurs here
16 | Ok(())
17 | }
| - immutable borrow ends here
Run Code Online (Sandbox Code Playgroud)
为什么这不起作用?
我们可以将带有return语句的任何函数转换为没有return语句的函数,如下所示:
fn my_func() -> &MyType {
'inner: {
// Do some stuff
return &x;
}
// And some more stuff
}
Run Code Online (Sandbox Code Playgroud)
成
fn my_func() -> &MyType {
let res;
'outer: {
'inner: {
// Do some stuff
res = &x;
break 'outer;
}
// And some more stuff
}
res
}
Run Code Online (Sandbox Code Playgroud)
由此可见,借款超出了范围'inner.
是否有任何问题而是使用以下重写进行借用检查?
fn my_func() -> &MyType {
'outer: {
'inner: {
// Do some stuff
break 'outer;
}
// And some more stuff
}
panic!()
}
Run Code Online (Sandbox Code Playgroud)
考虑到返回声明可以排除事后发生的任何事情,否则可能会违反借用规则.
如果我们明确地命名生命期,那么签名insert_largest就变成了fn insert_largest<'a>(&'a mut self, k: K, v: V) -> Result<(), &'a K>.因此,当您创建返回类型时&K,其生命周期将与&mut self.
而且,事实上,你正在lk从内部进行回归self.编译器看到引用lk转义匹配的范围(因为它被分配给函数的返回值,因此它必须比函数本身更长)并且当匹配结束时它不能让借用结束.
我想你是说编译器应该更聪明,并且意识到self.0.push只有lk在没有返回时才能达到.但事实并非如此.我甚至不确定教它那种分析是多么困难,因为它比我理解借用检查器的理由要复杂得多.
今天,编译器看到了一个引用,并且基本上试图回答一个问题("这个问题有多长?").当它看到你的返回值是lk,它会lk从fn的签名('a我们在上面给出的显式名称)中为返回值指定它所期望的生命周期,并将其称为一天.
简而言之:
暂时我建议只返回克隆而不是引用,如果可能的话.我假设返回a Err不是典型的情况,所以性能不应该特别担心,但我不确定K:Clone绑定可能如何与您正在使用的类型一起使用.
impl <K, V> MyArray<K, V> where K:Clone + Ord { // 1. now K is also Clone
pub fn insert_largest(&mut self, k: K, v: V) ->
Result<(), K> { // 2. returning K (not &K)
match self.0.iter().next_back() {
None => (),
Some(&(ref lk, _)) => {
if lk > &k {
return Err(lk.clone()); // 3. returning a clone
}
}
};
self.0.push((k, v));
Ok(())
}
}
Run Code Online (Sandbox Code Playgroud)