Rustで、可変イテレータを作成するにはどうすればよいですか?[複製]
安全なRustで可変イテレータを作成しようとすると、ライフタイムに問題が発生します。
これが私の問題を次のように減らしたものです:
struct DataStruct<T> {
inner: Box<[T]>,
}
pub struct IterMut<'a, T> {
obj: &'a mut DataStruct<T>,
cursor: usize,
}
impl<T> DataStruct<T> {
fn iter_mut(&mut self) -> IterMut<T> {
IterMut { obj: self, cursor: 0 }
}
}
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
let i = f(self.cursor);
self.cursor += 1;
self.obj.inner.get_mut(i)
}
}
fn f(i: usize) -> usize {
// some permutation of i
}
私の構造DataStruct
は決して変わることはありませんが、中に保存されている要素の内容を変更できる必要があります。例えば、
let mut ds = DataStruct{ inner: vec![1,2,3].into_boxed_slice() };
for x in ds {
*x += 1;
}
コンパイラーは、返そうとしている参照のライフタイムの競合についてエラーを出します。私が期待していないことがわかった寿命は、next(&mut self)
関数の範囲です。
で有効期間に注釈を付けようとするとnext()
、代わりに、コンパイラーはIterator特性を満たしていないことを通知します。これは安全な錆で解決できますか?
エラーは次のとおりです。
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/iter_mut.rs:25:24
|
25 | self.obj.inner.get_mut(i)
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 22:5...
--> src/iter_mut.rs:22:5
|
22 | / fn next(&mut self) -> Option<Self::Item> {
23 | | let i = self.cursor;
24 | | self.cursor += 1;
25 | | self.obj.inner.get_mut(i)
26 | | }
| |_____^
note: ...so that reference does not outlive borrowed content
--> src/iter_mut.rs:25:9
|
25 | self.obj.inner.get_mut(i)
| ^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 19:6...
--> src/iter_mut.rs:19:6
|
19 | impl<'a, T> Iterator for IterMut<'a, T> {
| ^^
note: ...so that the types are compatible
--> src/iter_mut.rs:22:46
|
22 | fn next(&mut self) -> Option<Self::Item> {
| ______________________________________________^
23 | | let i = self.cursor;
24 | | self.cursor += 1;
25 | | self.obj.inner.get_mut(i)
26 | | }
| |_____^
= note: expected `std::iter::Iterator`
found `std::iter::Iterator`
編集:
- の実装を変更し
next()
て、反復順序が元のシーケンスの順列になるようにしました。
回答
3 PeterHall
借用チェッカーは、以降の呼び出しnext()
が同じデータにアクセスしないことを証明できません。これが問題になる理由は、借用の存続期間がイテレーターの存続期間であるため、同じデータへの2つの可変参照が同時に存在しないことを証明できないためです。
安全でないコードなしで、またはデータ構造を変更せずにこれを解決する方法は実際にはありません。と同等のことを行うこともできslice::split_at_mut
ますが、元のデータを変更できないことを考えると、とにかく安全でないコードでそれを実装する必要があります。安全でない実装は次のようになります。
impl<'a, T> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
let i = self.cursor;
self.cursor += 1;
if i < self.obj.inner.len() {
let ptr = self.obj.inner.as_mut_ptr();
unsafe {
Some(&mut *ptr.add(i))
}
} else {
None
}
}
}