rust Vec如何实现BorrowMut?

yc0p9oo0  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(133)

我在标准库文档中运行了下面的示例,遇到了一个难题。
我发现BorrowMut trait的一个实现是Vec,我不明白它是如何工作的。例如,下面的代码表明No.1工作,为什么No.2不工作,泛型T做什么?

use std::borrow::BorrowMut;

fn check<T: BorrowMut<[i32]>>(mut v: T) {
    assert_eq!(&mut [1, 2, 3], v.borrow_mut()); // ! no.1 can call, Why?
}

fn main() {
    let v = vec![1, 2, 3];
    // v.borrow_mut();  // ! no.2 Can't call,Why?

    check(v);
}
xzabzqsa

xzabzqsa1#

rustc显示的完整错误很好地解释了这一点。并强调:

error[E0283]: **type annotations needed**
 --> src/main.rs:9:7
  |
9 |     v.borrow_mut();  //! no.2 Can't call,Why?
  |     --^^^^^^^^^^--
  |     | |
  |     | **cannot infer type for type parameter `Borrowed` declared on the trait `BorrowMut`**
  |     this method call resolves to `&mut Borrowed`
  |     help: use the fully qualified path for the potential candidate: `<Vec<T> as BorrowMut<[T]>>::borrow_mut(v)`
  |
  = note: cannot satisfy `Vec<i32>: BorrowMut<_>`

BorrowMut可以在一个类型上实现多次。例如,BorrowMut<Foo>BorrowMut<Bar>都可以为Vec<i32>实现;在这些示例中,FooBar取代了“*type parameter Borrowed declared on the trait BorrowMut *",如its documentation所示(单击“Show declaration”)。
在“no.1”中,您已指定T: BorrowMut<[i32]>;由于没有提到TBorrowMut的其他实现,因此“type parameter Borrowed declared on the trait BorrowMut”可以明确地推断为[i32]
在“no.2”中,对于您所追求的BorrowMut的实现存在模糊性:即使现在没有其他Vec<i32>BorrowMut实现在作用域中,它们也可能潜伏在编译器不知道的地方(等待通过use语句被带入作用域);即使现在没有其他的库,将来上游的crate(即std库)也可以添加一个,这将破坏您的代码。因此,编译器会要求你明确地告诉它你所追求的是哪个实现,从而消除歧义。它通过报告“type annotations needed"来做到这一点,甚至向您展示了如何做到这一点:“
使用潜在候选人的完全限定路径:<Vec<T> as BorrowMut<[T]>>::borrow_mut(v)
"。我们可以在这里这样做:

<Vec<i32> as BorrowMut<[i32]>>::borrow_mut(v)

但是,这不会起作用,因为另一个原因:Rust只在使用.方法语法调用时执行deref强制-当这样调用时,您必须显式传递&mut v而不是v(我已经提交了an issue关于这个错误的建议);但这在您的情况下仍然不起作用,因为v没有声明为可变的。

编译器还得出结论:

For more information about an error, try `rustc --explain E0283`.

这样做会显示这些额外的信息,这可能也有帮助。
请建议错误消息如何更清楚地解释问题。
实际上,编译器上面的建议比解决这种情况下的二义性所需的更详细。您也可以通过以下两种方式解决它,而不提及v的类型:

<dyn BorrowMut<[i32]>>::borrow_mut(&mut v)
BorrowMut::<[i32]>::borrow_mut(&mut v)

还要注意的是,对于Vec,您可以通过其固有的as_mut_slice方法、索引(通过其std::ops::IndexMut的实现)或显式或隐式调用std::ops::DerefMut::deref_mut(即,使用解引用操作符)。分别为:

v.as_mut_slice()
&mut v[..]
v.deref_mut()  // DerefMut must be in scope, else qualify as above
&mut *v

事实上,由于解引用,&mut Vec也在强制站点(如函数参数和let绑定)将coerces转换为可变切片-因此您通常可以只使用&mut v,Rust将为您完成其余工作。
See all approaches discussed in this answer on the Rust Playground
考虑到它们都编译成完全相同的代码,你使用哪一个实际上只是一个偏好问题。但是,为了保持代码尽可能的清晰(因此也是维护),我建议索引方法&mut v[..]最清楚和简洁地表明您正在对整个向量进行可变切片。

相关问题