目標,要實現三種遍歷的方式。
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
全部代碼:
#[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!");
}
還有一種方式在迭代器中用臨時的變量引用記錄迭代的值,但是我並沒有實驗成功,還是爆生命週期問題。