Rust에서 변경 가능한 반복자를 어떻게 생성합니까? [복제]

Aug 16 2020

안전한 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()하면 컴파일러가 대신 반복자 특성을 만족하지 못했다고 알려줍니다. 안전한 녹으로 해결할 수 있습니까?

다음은 오류입니다.

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 Aug 16 2020 at 15:06

차용 검사기는 후속 호출 next()이 동일한 데이터에 액세스하지 않는다는 것을 증명할 수 없습니다 . 이것이 문제가되는 이유는 차용의 수명이 반복기의 수명 동안이기 때문에 동일한 데이터에 대한 두 개의 가변 참조가 동시에 존재하지 않는다는 것을 증명할 수 없기 때문입니다.

안전하지 않은 코드 또는 데이터 구조 변경 없이는이 문제를 해결할 수있는 방법이 없습니다. 방정식 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
        }
    }
}