在这里插入图片描述

对于 Rust 新手来说,“迭代器” (Iterator) 是我们最常打交道的工具之一。它让我们能用 for 循环优雅地遍历集合(比如 Vec、数组等)。

DoubleEndedIterator(双端迭代器)就是 Iterator 的一个“超级强化版”!

💡 核心概念:

  • Iterator:只能从头到尾一个一个地取元素。
  • DoubleEndedIterator:不仅能从头到尾取,还能从尾到头取!

就像排队买票,Iterator 只能从队首开始办业务;而 DoubleEndedIterator 允许你同时从队首和队尾开始办业务。

1. 基础回顾:什么是 Iterator? (排队只能从前面叫号)

在我们深入“双端”之前,我们必须先牢牢掌握“单端”的 Iterator

Iterator 是一个 trait(特性),它只要求你实现一个核心方法:next()

  • next():从迭代器中取出下一个元素。

  • 它返回一个 Option<Self::Item>

    • Some(T):表示拿到了一个元素 T
      *一个元素 T

      • None:表示迭代器空了,所有元素都拿完了。

示例:

let numbers = vec![10, 20, 30];

// .iter() 会创建一个实现了 Iterator 的迭代器
let mut iter = numbers.iter(); 

// 手动调用 next()
assert_eq!(iter.next(), Some(&10)); // 拿第一个
assert_eq!(iter.next(), Some(&20)); // 拿第二个
assert_eq!(iter.next(), Some(&30)); // 拿第三个
assert_eq!(iter.next(), None);      // 拿完了,返回 None
assert_eq!(iter.next(), None);      // 之后再拿,还是 None

我们平时写的 for 循环,其实就是 Rust 编译器帮我们自动调用 next() 并处理 None 的“语法糖”:

// 这段代码...
for num in numbers.iter() {
    println!("{}", num);
}
// ...在底层大致等同于上面的手动调用

2. 核心登场:DoubleEndedIterator!(队首队尾都能叫号)

DoubleEndedIterator 也是一个 trait,它继承Iterator

这意味着,如果你想实现 DoubleEndedIterator,你****首先实现 Iterator(也就是必须有 next() 方法)。

DoubleEndedIteratorIterator 的基础上,只增加了一个新方法:next_back()

  • next_back():从迭代器中取出倒数第一个元素。

  • 它同样返回 Option<Self::Item>

    • Some(T):表示拿到了一个元素 T(从后面拿的)。
    • None:表示迭代器空了。

它是如何工作的?

你可以把 DoubleEndedIterator 想象成一个两端都可以“缩短”的序列。

  • 调用 next() 会消耗掉最前面的元素。
  • 调用 next_back() 会消耗掉最后面的元素。

next()next_back() “相遇”时,迭代器就空了。

示例:Vec 的迭代器就实现了 DoubleEndedIterator

let numbers = vec![1, 2, 3, 4, 5];

// .iter() 创建的迭代器同时实现了 Iterator 和 DoubleEndedIterator
let mut iter = numbers.iter();

// 1. 从前面拿一个
assert_eq!(iter.next(), Some(&1));       // 剩下 [2, 3, 4, 5]

// 2. 从后面拿一个
assert_eq!(iter.next_back(), Some(&5));  // 剩下 [2, 3, 4]

// 3. 再从后面拿一个
assert_eq!(iter.next_back(), Some(&4));  // 剩下 [2, 3]

// 4. 从前面拿一个
assert_eq!(iter.next(), Some(&2));       // 剩下 [3]

// 5. 从前面拿一个
assert_eq!(iter.next(), Some(&3));       // 剩下 [] (空了)

// 6. 此时,无论从哪边拿,都是 None
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);

看!是不是很直观?我们就像从两头吃一根香肠一样,直到中间吃完为止。😊

3. DoubleEndedIterator 的杀手级应用:rev() 🔄

你可能在想:我平时也很少手动调用 next_back() 啊,那这个 trait 有什么用呢?

**答案rev() 方法!**

你一定用过 .rev() 来反转一个迭代器吧?

let numbers = vec![1, 2, 3];

// 倒序打印
for num in numbers.iter().rev() {
    println!("{}", num);
}
// 输出:
// 3
// 2
// 1

rev() 方法的秘密:

rev() 方法只有DoubleEndedIterator 上才能调用!

为什么呢?

因为 rev() 并不是真的在内存中创建了一个新的、反转过来的 Vec(那样效率太低了)。

rev() 只是创建了一个“包装器”(Rev 结构体),这个包装器会**“调换” next()next_back() 的行为**:

  • 当你调用 rev() 后的迭代器的 next() 时,它实际上在底层调用的是原始迭代器的 next_back()
  • 当你调用 rev() 后的迭代器的 `nextback()时,它**实际上**在底层调用的是**原始**迭代器的next()`。

💡 一句话总结 rev()
.rev() 的存在,就是 DoubleEndedIterator 最大的价值之一。它允许 Rust 在分配新内存的情况下,实现高效的、惰性(lazy)的反向迭代。

4. 哪些类型是 DoubleEndedIterator

Rust 标准库中很多常见的集合都提供了双端迭代器:

  • Vec / 数组 / 切片 (Slice).iter()、`.iter_t().into_iter()` 都是双端。
  • VecDeque (双端队列):天生就是双端的,操作效率极高。
  • LinkedList (链表):也是双端的。
  • Range (范围):比如 `(0…1),你既可以从 0 开始 next(),也可以从 9 开始 next_back()`。
  • **BTreeMap / BTreeSet:它们的 .keys().values().range() 迭代器也是双端的。

5. 进阶:自己实现一个 `DoublendedIterator` 🔧

(这部分稍微有点难度,但对理解原理非常有帮助!加油!💪)

假设我们想创建一个自定义的倒计时器 Counter,它从 start 倒数到 end

struct Counter {
    start: u32,
    end: u32,
}

第一步:实现 Iterator (必须的!)

我们先让它能从 startend 迭代。

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.start <= self.end {
            // 拿到当前值
            let current = self.start;
            // 指针向后移动
            self.start += 1; 
            // 返回当前值
            Some(current)
        } else {
            // 超过 end 了,迭代结束
            None
        }
    }
}

**第二步:实现DoubleEndedIterator`**

现在我们来添加 next_back(),让它能从 endstart 迭代。

impl DoubleEndedIterator for Counter {
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.start <= self.end {
            // 拿到当前值 (从后面拿)
            let current = self.end;
            // 指针向前移动
            // 注意:要处理 0 的边界情况,防止 u32 下溢
            if self.end == 0 {
                // 如果 end 已经是 0,说明拿完 0 迭代就该结束了
                // 我们把 start 设为 1 (大于 end),这样下次 next() 或 next_back() 都会返回 None
                self.start = 1; // 确保 start > end
                self.end = 0;   // 保持 end 不变
            } else {
                 self.end -= 1;
            }
           
            // 返回当前值
            Some(current)
        } else {
            // start 已经大于 end 了,迭代结束
            None
        }
    }
}

// 修正:上面的 next_back 有点复杂,我们换个思路,让 Counter 结构体持有 (start, count)
// 抱歉,我们用 (start, end) 并且让 end 变成 “不包含” (exclusive) 会更简单!

// --- 让我们用更简单的方式重写 ---

struct SimpleCounter {
    start: u32, // 当前的头
    end: u32,   // 当前的尾 (包含)
    finished: bool, // 标记是否已耗尽
}

// 辅助函数,用于创建计数器
fn new_counter(start: u32, end: u32) -> SimpleCounter {
    SimpleCounter { start, end, finished: start > end }
}

// 1. 实现 Iterator (从 start 到 end)
impl Iterator for SimpleCounter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.finished {
            return None;
        }

        // 检查头尾是否相遇
        if self.start > self.end {
            self.finished = true;
            return None;
        }

        let current = self.start;
        self.start += 1;
        Some(current)
    }
}

// 2. 实现 DoubleEndedIterator (从 end 到 start)
impl DoubleEndedIterator for SimpleCounter {
    fn next_back(&mut self) -> Option<Self::Item> {
        if self.finished {
            return None;
        }

        // 检查头尾是否相遇
        if self.start > self.end {
            self.finished = true;
            return None;
        }

        let current = self.end;
        // 防止 u32 下溢
        if self.end == 0 {
            // 如果 end 是 0,拿完 0 之后,下一次 start(>=0) > end(-1的概念) 就会触发
            // 我们直接标记为 finished
            self.finished = true; 
            // 并且把 end 设为 0,start 设为 1,确保 start > end
            self.start = 1;
            self.end = 0; 
        } else {
            self.end -= 1;
        }
        
        Some(current)
    }
}

// --- 测试一下!---
fn main() {
    let mut counter = new_counter(1, 5); // 迭代 1, 2, 3, 4, 5

    println!("next(): {:?}", counter.next());       // Some(1)
    println!("next_back(): {:?}", counter.next_back()); // Some(5)
    println!("next(): {:?}", counter.next());       // Some(2)
    println!("next_back(): {:?}", counter.next_back()); // Some(4)
    println!("next(): {:?}", counter.next());       // Some(3)
    
    // 此时 start = 4, end = 3 (因为 4 被减 1 了)。start > end
    
    println!("next(): {:?}", counter.next());       // None
    println!("next_back(): {:?}", counter.next_back()); // None
}

(注意:在实现 DoubleEndedIterator 时,next()next_back() 必须正确地处理边界条件,确保它们在 “相遇” 时能正确地返回 None,这是最容易出错的地方!)

6. 总结 🎉

恭喜你!你已经学完了 DoubleEndedIterator 的全部核心知识!

让我们来回顾一下:

  1. Iterator:是 Rust 迭代的基础,提供了 next() 方法(从头取)。
  2. **`DoubleedIterator**:是 Iterator的超集,额外提供了next_back()` 方法(从尾取)。
  3. **核心价值:它允许你从集合的两端高效地消耗元素。
  4. 杀手级应用rev() 方法!只有实现了 `DoublendedIterator的迭代器才能调用.rev()` 来实现高效的、零开销的反向迭代。
  5. 实现:自己实现它时,关键在于正确管理两端的“指针”,并在它们相遇时停止。
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐