给定包含元素集合的结构Foo:
#[derive(Debug)]
struct Foo {
bar: Vec<i8>,
}我编写了一个可变视图对象,用于封装Foo的一部分。
#[derive(Debug)]
struct View<'a> {
foo: &'a mut Foo,
}
impl<'a> View<'a> {
fn iter(&'a self) -> std::slice::Iter<'a, i8> {
self.foo.bar.iter()
}
fn iter_mut(&'a mut self) -> std::slice::IterMut<'a, i8> {
self.foo.bar.iter_mut()
}
fn mutate(&'a mut self) {
let mut vector: Vec<i8> = vec![];
for value in self.iter().take(1).cloned() {
vector.push(value);
}
for value in self.iter_mut() {
*value = 0;
}
}
}上面的View结构按预期工作,下面的代码打印Foo { bar: [0, 0, 0] }。
fn main() {
let mut foo = Foo { bar: vec![0, 1, 2] };
let mut view = View { foo: &mut foo };
view.mutate();
println!("{:?}", foo);
}然而,不同类型的视图应该是可能的--如果Foo是一个矩阵,视图可以是行、列,甚至子矩阵。因此,我将View重写为一个由结构实现的特性,并为mutate提供了一个默认实现:
trait AbstractView<'a> {
type Iterator: Iterator<Item = &'a i8>;
type IteratorMut: Iterator<Item = &'a mut i8>;
fn iter(&'a self) -> Self::Iterator;
fn iter_mut(&'a mut self) -> Self::IteratorMut;
fn mutate(&'a mut self) {
let mut vector: Vec<i8> = vec![];
for value in self.iter().take(1).cloned() {
vector.push(value);
}
for value in self.iter_mut() {
*value = vector[0];
}
}
}
#[derive(Debug)]
struct View<'a> {
foo: &'a mut Foo,
}
impl<'a> AbstractView<'a> for View<'a> {
type Iterator = std::slice::Iter<'a, i8>;
type IteratorMut = std::slice::IterMut<'a, i8>;
fn iter(&'a self) -> Self::Iterator {
self.foo.bar.iter()
}
fn iter_mut(&'a mut self) -> Self::IteratorMut {
self.foo.bar.iter_mut()
}
}这段代码没有成功编译,rustc抱怨在mutate中调用mutate
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:18:22
|
6 | trait AbstractView<'a> {
| -- lifetime `'a` defined here
...
15 | for value in self.iter().take(1).cloned() {
| -----------
| |
| immutable borrow occurs here
| argument requires that `*self` is borrowed for `'a`
...
18 | for value in self.iter_mut() {
| ^^^^^^^^^^^^^^^ mutable borrow occurs here为什么将mutate作为特征上的默认方法来实现,会导致与借入检查器不同的行为?我怎样才能让这种特质发挥作用?
使用rustc版本1.43.1。
发布于 2020-05-10 04:05:51
很容易看出为什么基于特征的版本不起作用,但更难说出原版的工作原理。
一切都在有生之年。对于基于特性的版本,到处都只有一个生命周期'a。当我们叫self.iter()或self.iter_mut()时,借用就会持续相同的一生。这意味着我们不能同时调用两者:如果同时调用两者,不可变的和可变的借款具有相同的生命周期,因此它们同时存在。
这就提出了为什么非特质版本有效的问题。它不也是一样的吗?答案在于方差类型的std::slice::Iter<'a, T>和std::slice::IterMut<'a, T>。泛型类型T<'a>的方差是T<'a>与'a和'b相关时是否和如何被胁迫到T<'b>。
对于许多类型,这种关系是协变量的:如果'a比'b (书面'a: 'b)长,那么T<'a>类型的值可以强制为T<'b>类型的值。对于其他类型,这种关系是相反的:如果是'a: 'b,那么T<'b>可以强制到T<'a> (这方面的一个例子是Fn(&'a T))。最后,有些类型是不变的,因此不会发生强制。
std::slice::Iter<'a, T>在生命周期'a中是协变的。如果'a比'b长,我们可以强迫寿命更短。这正是你的代码中所发生的事情。当我们调用self.iter().take(1).cloned()时,self.iter()实际上被强迫使用较短的std::slice::Iter<'b, i8>,以便以后可以发生可变的借用。
fn mutate(&'a mut self) {
let mut vector: Vec<i8> = vec![];
// let iter = self.iter(); // works
let mut iter: std::slice::Iter<'a, i8> = self.iter(); // doesn't work!
for value in iter.take(1).cloned() {
vector.push(value);
}
for value in self.iter_mut() {
*value = vector[0];
}
}使用以上代码,我们得到一个类似于基于特征的代码的错误。
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:27:22
|
11 | impl<'a> View<'a> {
| -- lifetime `'a` defined here
...
23 | let iter: std::slice::Iter<'a, i8> = self.iter(); // doesn't work!
| ------------------------ ---- immutable borrow occurs here
| |
| type annotation requires that `*self` is borrowed for `'a`
...
27 | for value in self.iter_mut() {
| ^^^^^^^^^^^^^^^ mutable borrow occurs here顺便说一句,std::slice::IterMut<'a, T>在其生命周期内是不变的。这是因为在一般情况下,可变的引用必须是不变的,才能成为声音。这意味着,如果您交换了可变和不可变借款的顺序,那么即使在非特征版本中也会出现错误。
fn mutate(&'a mut self) {
let mut vector: Vec<i8> = vec![];
for value in self.iter_mut() {
// This would panic if it compiled, of course
*value = vector[0];
}
for value in self.iter().take(1).cloned() {
vector.push(value);
}
}因此,基于特性的版本不起作用,因为self.iter()要求借入时间太长,而且不能强迫它使用较短的借款。事实上,根据文章的写法,更短的借入甚至可能是没有意义的。Self::Iter可能只为该特定生命期定义。
那么,写这个的理想方法是什么呢?一种方法是将mutate的实现放在AbstractView的每个实现中。当使用具体类型Iter和IterMut时,编译器知道我们可以使用协方差来缩短生命周期。
一个更有原则的解决方案是使Self::Iter和Self::IterMut在其生命周期内具有通用性,以便可以根据需要缩短借款时间。像这样的通用关联类型还不可能。
在夜间编译器上,可以这样做,尽管正如编译器正确警告的那样,泛型关联类型尚未完成,可能会导致编译器崩溃或错误。
#![feature(generic_associated_types)]
#[derive(Debug)]
struct Foo {
bar: Vec<i8>,
}
trait AbstractView {
type Iterator<'b>: Iterator<Item = &'b i8>;
type IteratorMut<'b>: Iterator<Item = &'b mut i8>;
// Eventually, these lifetimes should be elided
// But it doesn't seem that that's implemented yet
fn iter<'a>(&'a self) -> Self::Iterator<'a>;
fn iter_mut<'a>(&'a mut self) -> Self::IteratorMut<'a>;
fn mutate(&mut self) {
let mut vector: Vec<i8> = vec![];
for value in self.iter().take(1).cloned() {
vector.push(value);
}
for value in self.iter_mut() {
*value = vector[0];
}
}
}
#[derive(Debug)]
struct View<'a> {
foo: &'a mut Foo,
}
impl<'a> AbstractView for View<'a> {
type Iterator<'b> = std::slice::Iter<'b, i8>;
type IteratorMut<'b> = std::slice::IterMut<'b, i8>;
fn iter<'b>(&'b self) -> Self::Iterator<'b> {
self.foo.bar.iter()
}
fn iter_mut<'b>(&'b mut self) -> Self::IteratorMut<'b> {
self.foo.bar.iter_mut()
}
}
fn main() {
let mut foo = Foo { bar: vec![0, 1, 2] };
let mut view = View { foo: &mut foo };
view.mutate();
println!("{:?}", foo);
}发布于 2020-05-10 14:49:22
感谢SCappella的回答,我理解并解决了我的问题。由于我更希望有一个干净的代码库,而不是高效的用例,所以我用向量替换了迭代器:
trait AbstractView {
fn refs(&self) -> Vec<&i8>;
fn refs_mut(&mut self) -> Vec<&mut i8>;
fn mutate(&mut self) {
let mut vector: Vec<i8> = vec![];
for value in self.refs().iter().take(1) {
vector.push(**value);
}
for value in self.refs_mut() {
*value = vector[0];
}
}
}impl AbstractView for View<'_>
{
fn refs(&self) -> Vec<&i8> {
self.foo.bar.iter().collect()
}
fn refs_mut(&mut self) -> Vec<&mut i8> {
self.foo.bar.iter_mut().collect()
}
}这允许我不复制mutate方法。
https://stackoverflow.com/questions/61705663
复制相似问题