Warnung: Der Iterator rechts unten dargestellt ist, weil es eine unsafe
mehrere Aliase auf ein einzelnes veränderliches Element zu erhalten, ermöglicht; Weiter zum zweiten Teil für die korrigierte Version.(Es wäre in Ordnung, wenn der Rückgabetyp unveränderliche Referenzen enthalten würde).
Wenn Sie bereit sind, Ihren eigenen Fenster-Iterator zu schreiben, wird es ziemlich einfach.
Zuerst der Iterator in all seinen blutigen Details:
use std::marker::PhantomData;
struct WindowIterMut<'a, T>
where T: 'a
{
begin: *mut T,
len: usize,
index: usize,
_marker: PhantomData<&'a mut [T]>,
}
impl<'a, T> WindowIterMut<'a, T>
where T: 'a
{
pub fn new(slice: &'a mut [T]) -> WindowIterMut<'a, T> {
WindowIterMut {
begin: slice.as_mut_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for WindowIterMut<'a, T>
where T: 'a
{
type Item = (&'a mut [T], &'a mut [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index > self.len { return None; }
let slice: &'a mut [T] = unsafe {
std::slice::from_raw_parts_mut(self.begin, self.len)
};
let result = slice.split_at_mut(self.index);
self.index += 1;
Some(result)
}
}
auf [1, 2, 3]
Ausgeführt es (&[], &[1, 2, 3])
dann (&[1], &[2, 3])
zurück, ... bis (&[1, 2, 3], &[])
. Kurz gesagt, es iteriert über alle möglichen Partitionen der Scheibe (ohne Mischen).
Welche sicher zu verwenden ist als:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
for (head, tail) in WindowIterMut::new(&mut data) {
if let Some(element) = head.last_mut() {
if *element % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| *i % 3 == 0).next() {
*element += *n3;
}
}
}
}
println!("{:?}", data);
}
Leider kann es auch verwendet werden, wie:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
let mut it = WindowIterMut::new(&mut data);
let first_0 = { it.next(); &mut it.next().unwrap().0[0] };
let second_0 = &mut it.next().unwrap().0[0];
println!("{:?} {:?}", first_0 as *const _, second_0 as *const _);
}
, die bei Auflage: 0x7f73a8435000 0x7f73a8435000
, Show-Gehäuse, dass beide wandelbar Referenzen alias der gleichen Element.
Da wir Aliasing nicht loswerden können, müssen wir Mutabilität loswerden; oder zumindest auf Interior Wandelbarkeit (Cell
hier seit u8
ist Copy
).
Zum Glück hat Cell
keine Laufzeitkosten, aber es kostet ein bisschen in Ergonomie (all diese .get()
und .set()
).
Ich nutze die Gelegenheit, um den Iterator auch etwas generischer zu machen, und benenne ihn um, da Window
bereits ein benutzter Name für ein anderes Konzept ist.
struct FingerIter<'a, T>
where T: 'a
{
begin: *const T,
len: usize,
index: usize,
_marker: PhantomData<&'a [T]>,
}
impl<'a, T> FingerIter<'a, T>
where T: 'a
{
pub fn new(slice: &'a [T]) -> FingerIter<'a, T> {
FingerIter {
begin: slice.as_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for FingerIter<'a, T>
where T: 'a
{
type Item = (&'a [T], &'a T, &'a [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.len { return None; }
let slice: &'a [T] = unsafe {
std::slice::from_raw_parts(self.begin, self.len)
};
self.index += 1;
let result = slice.split_at(self.index);
Some((&result.0[0..self.index-1], result.0.last().unwrap(), result.1))
}
}
Wir verwenden es als Baustein:
fn main() {
let data: Vec<Cell<u8>> = (1..100).map(|i| Cell::new(i)).collect();
for (_, element, tail) in FingerIter::new(&data) {
if element.get() % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| i.get() % 3 == 0).next() {
element.set(element.get() + n3.get());
}
}
}
let data: Vec<u8> = data.iter().map(|cell| cell.get()).collect();
println!("{:?}", data);
}
On the playpen diese Drucke: [1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]
, die richtige zu sein scheint.
Wollen Sie das? https://play.rust-lang.org/?gist=e42f4ff5833f68a56fd4dfe5540992c0&version=stable&backtrace=0 – Adrian
So etwas würde funktionieren, ja, danke. Ich würde gerne so idiomatisch wie möglich sein, obwohl ich das Item bei Index1 ändere und möglicherweise eine Iteration wie diese durch die ganze Liste/Vec usw. erlaubt. Wenn möglich ohne direkten Zugang wäre es gut. Dies würde für die Situation, die ich sehe, funktionieren, einfach, einen weiteren direkten Zugriff hinzuzufügen und die Index1-Position zu ändern. – dirvine
Ich habe etwas funktioniert ohne viel Indexierung [auf dem Laufstall] (https://play.rust-lang.org/?gist=e42f4ff5833f68a56fd4dfe5540992c0&version=stable&backtrace=0), aber es ist immer noch ziemlich ausführlich. Ich denke, dass ein Iterator, der auf "split_at_mut" basiert, ein interessanterer Building Brick wäre (ein Iterator, der '(& mut [T], & mut [T])' mit der Grenzverschiebung bei jeder Iteration ergibt). –