假设我们想在Rust中创建一个slice或vector的迭代器,迭代器从指定索引k处开始,而不是从起始索引0开始。这对于遍历环形缓冲区非常有用。
你能实现下面的iter_from()函数,使程序精确地打印出5,6,7,8,9,0,1,2,3,4吗?
fn iter_from<T>(slice: &[T], k: usize) -> impl Iterator<Item=&T> {
// todo
}
fn main() {
let array = [0,1,2,3,4,5,6,7,8,9];
for i in iter_from(&array, 5) {
print!("{},", i);
} // 5,6,7,8,9,0,1,2,3,4,
}
我的解决方案是使用Iterator::chain方法:
fn iter_from<T>(slice: &[T], k: usize) -> impl Iterator<Item=&T> {
slice[k..].iter().chain(slice[..k].iter())
}
现在,这是今天真正的挑战——你能实现函数的可变变体吗?
fn iter_mut_from<T>(slice: &mut [T], k: usize) -> impl Iterator<Item=&mut T> {
// todo
}
有人可能会认为我们可以简单地将&替换为&mut,将iter替换为iter_mut,就像这样:
fn iter_mut_from<T>(slice: &mut [T], k: usize) -> impl Iterator<Item=&mut T> {
slice[k..].iter_mut().chain(slice[..k].iter_mut())
}
然而,这行不通。编译器会报错:对同一切片有多个可变借用。
error[E0499]: cannot borrow `*slice` as mutable more than once at a time
--> src/main.rs:6:33
|
5 | fn iter_mut_from<T>(slice: &mut [T], k: usize) -> impl Iterator<Item=&mut T> {
| - let's call the lifetime of this reference `'1`
6 | slice[k..].iter_mut().chain(slice[..k].iter_mut())
| ----------------------------^^^^^-----------------
| | |
| | second mutable borrow occurs here
| first mutable borrow occurs here
| returning this value requires that `*slice` is borrowed for `'1`
For more information about this error, try `rustc --explain E0499`.
这是因为我们对同一个slice请求了两次可变借用,一次是slice[k..],另一次是slice[..k]。我们知道这两者是互斥的,但是编译器还不够聪明。
为了显式地告诉编译器我们在索引k处将切片拆分为两个互斥的子切片,我们需要使用内置方法split_at_mut(),它拆分切片并为每一侧返回可变借用:
pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T])
现在,我们可以实现iter_mut_from()方法:
fn iter_mut_from<T>(slice: &mut [T], k: usize) -> impl Iterator<Item=&mut T> {
let (s1, s2) = slice.split_at_mut(k);
s2.iter_mut().chain(s1.iter_mut())
}
我们已经学习了如何在Rust中创建自定义迭代器,它从给定的索引开始并循环回到开头。我们使用了chain()和split_at_mut()方法来实现这个功能。迭代器是在Rust中编写优雅高效代码的强大工具。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8