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はiter_mut
inの呼び出しについて文句を言います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を使用します。
それは、形質ベースのバージョンが作業をしない理由を伝えるのは簡単だが、元はなぜ難しいと言ってい仕事を。
それはすべて一生に一度です。トレイトベースのバージョンの場合、'a
どこにでも1つのライフタイムしかありません。self.iter()
またはを呼び出すself.iter_mut()
と、借用は同じ存続期間続きます。つまり、両方を呼び出すことはできません。両方を呼び出すと、不変と可変の借用の有効期間が同じになるため、同時に存在します。
これは、なぜ非特性バージョンが機能するのかという疑問を提起します。まったく同じことをしませんか?答えは、タイプとの分散にあります。ジェネリック型の分散があれば、どのようにあるに強制変換することができたときと関連しています。std::slice::Iter<'a, T>
std::slice::IterMut<'a, T>
T<'a>
T<'a>
T<'b>
'a
'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
その特定のライフタイムに対してのみ定義される場合があります。
それで、これを書くための理想的な方法は何ですか?1つの方法は、の実装をの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);
}
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加