Rust 自定義迭代器探索,關鍵詞:IntoIterator, Iterator, IterMut

目標,要實現三種遍歷的方式。
for v in obj { ... } // 消耗對象
for v in &obj { ... } // 引用
for v in &mut obj { ... } // 可寫引用

碰到的問題,在可寫引用的時候會爆生命週期問題,據說是 GAT 還不支持。
問題:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fe3d017d93d25f4d851b4894d7ddd9c9

Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/main.rs:28:23
   |
28 |             0 => Some(&mut self.pixel.red),
   |                       ^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
  --> src/main.rs:26:13

經過各位大神的提示和討論,通過 unsafe 實現了繞過這個問題。
UB 問題:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=15e254aac603f450463d2a1a95f5a272

解決後:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5bdd781692d28ce7260529962d8e75f9

全部代碼:

#[derive(Debug)]
struct Pixel {
    red: i8,
    green: i8,
    blue: i8
}

struct PixelIntoIter {
    pixel: Pixel,
    current: i8,
}
struct PixelIter<'a> {
    pixel: &'a Pixel,
    current: i8,
}
struct PixelIterMut<'a> {
    pixel: &'a mut Pixel,
    current: i8,
}

impl IntoIterator for Pixel {
    type Item = i8;
    type IntoIter = PixelIntoIter;

    fn into_iter(self) -> Self::IntoIter {
        PixelIntoIter {
            pixel: self,
            current: 0,
        }
    }
}

impl<'a> IntoIterator for &'a Pixel {
    type Item = &'a i8;
    type IntoIter = PixelIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        PixelIter {
            pixel: self,
            current: 0,
        }
    }
}

impl<'a> IntoIterator for &'a mut Pixel {
    type Item = &'a mut i8;
    type IntoIter = PixelIterMut<'a>;

    fn into_iter(self) -> Self::IntoIter {
        PixelIterMut {
            pixel: self,
            current: 0,
        }
    }
}

impl Iterator for PixelIntoIter {
    type Item = i8;
    fn next(&mut self) -> Option<Self::Item> {
        let r = match self.current {
            0 => Some(self.pixel.red),
            1 => Some(self.pixel.green),
            2 => Some(self.pixel.blue),
            _ => None,
        };
        self.current += 1;
        r
    }
}

impl<'a> Iterator for PixelIter<'a> {
    type Item = &'a i8;
    fn next(&mut self) -> Option<Self::Item> {
        let r = match self.current {
            0 => Some(&self.pixel.red),
            1 => Some(&self.pixel.green),
            2 => Some(&self.pixel.blue),
            _ => None,
        };
        self.current += 1;
        r
    }
}

// 目前還不支持, 等 GAT 來解決這個問題。
// impl<'a> Iterator for PixelIterMut<'a> {
//     type Item = &'a mut i8;
//     fn next(&mut self) -> Option<Self::Item> {
//         let r = match self.current {
//             0 => Some(&mut self.pixel.red),
//             1 => Some(&mut self.pixel.green),
//             2 => Some(&mut self.pixel.blue),
//             _ => None,
//         };
//         self.current += 1;
//         r
//     }
// }

impl<'a> Iterator for PixelIterMut<'a> {
    type Item = &'a mut i8;
    fn next(self: &'_ mut PixelIterMut<'a>) -> Option<Self::Item> {
        let r = match self.current {
            0 => Some(&mut self.pixel.red),
            1 => Some(&mut self.pixel.green),
            2 => Some(&mut self.pixel.blue),
            _ => None,
        };
        self.current += 1;

        // 通過,並且不報錯。
        match r {
            Some(mut t) => unsafe {
                // 強制轉換
                Some(&mut *(t as *mut i8))
            },
            None => None
        }

        // 這裏編譯會通過,但是會報 UB, *const -> *mut
        // match r {
        //     Some(t) => unsafe {
        //         Some(&mut *(t as *const i8 as *mut i8))
        //     },
        //     None => None
        // }

        // 編譯不通過, 報 UB
        // match r {
        //     Some(t) => unsafe {
        //         Some(unsafe { std::mem::transmute::<&i8, &mut i8>(t) })
        //     },
        //     None => None
        // }
    }
}

fn main() {
    let mut p = Pixel {red: 1, green: 2, blue: 3};
    for v in &mut p {
        *v += 1;
    }
    dbg!(p);
    println!("Hello, world!");
}

還有一種方式在迭代器中用臨時的變量引用記錄迭代的值,但是我並沒有實驗成功,還是爆生命週期問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章