我尝试编写一个函数,只有当元素大于向量中已有的最后一个元素时,才将元素推送到有序向量的末尾,否则返回带有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(())
}
}
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
为什么这不起作用?
我们可以将带有return语句的任何函数转换为没有return语句的函数,如下所示:
fn my_func() -> &MyType {
'inner: {
// Do some stuff
return &x;
}
// And some more stuff
}
向
fn my_func() -> &MyType {
let res;
'outer: {
'inner: {
// Do some stuff
res = &x;
break 'outer;
}
// And some more stuff
}
res
}
由此可见,借款的范围超过了'inner
。
是否存在使用以下重写进行借用检查的问题?
fn my_func() -> &MyType {
'outer: {
'inner: {
// Do some stuff
break 'outer;
}
// And some more stuff
}
panic!()
}
考虑到返回声明可以排除事后发生的任何事情,否则可能会违反借用规则。
答案 0 :(得分:7)
如果我们明确命名生命时间,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
。但事实并非如此。而且我甚至不确定教它那种分析是多么困难,因为它比我理解借用检查器的理由要复杂得多。
今天,编译器看到了一个引用,并且基本上试图回答一个问题(&#34;它存活了多长时间?&#34;)。当它看到您的返回值为lk
时,它会为fn的签名(lk
的返回值指定'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(())
}
}
答案 1 :(得分:1)
为什么早归还不能完成未偿还的借款?
因为借阅检查器的当前实现过于保守。
启用non-lexical lifetimes后,您的代码将按原样工作,但仅适用于实验性的“ Polonius”实现。 Polonius使有条件的借入跟踪成为可能。
我还简化了您的代码:
#![feature(nll)]
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> {
if let Some((lk, _)) = self.0.iter().next_back() {
if lk > &k {
return Err(lk);
}
}
self.0.push((k, v));
Ok(())
}
}