Implement a Custom Iterator in Rust
This challenge focuses on understanding and implementing the Iterator trait in Rust. You will create a custom iterator that generates a sequence of numbers based on a simple rule, allowing you to practice the core concepts of iteration in Rust. Mastering iterators is fundamental for efficient and idiomatic Rust programming.
Problem Description
Your task is to implement a custom iterator for a struct called FibonacciSequence. This iterator should generate numbers following the Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, ...
The FibonacciSequence struct will store the current and next two numbers in the sequence needed to generate the subsequent term.
Requirements:
- Define a
FibonacciSequencestruct: This struct should hold the state necessary to generate the Fibonacci sequence. At a minimum, it needs to store the two most recent numbers to calculate the next one. - Implement the
Iteratortrait forFibonacciSequence:- The
Itemtype associated with the iterator should beu64(unsigned 64-bit integer). - The
next()method should returnOption<Self::Item>.
- The
- The
next()method logic:- The first call to
next()should returnSome(0). - The second call should return
Some(1). - Subsequent calls should return the sum of the two preceding numbers in the sequence.
- The iterator should be able to produce a potentially large number of Fibonacci numbers. Consider the capacity of
u64.
- The first call to
Expected Behavior:
When you create a FibonacciSequence and call next() repeatedly, you should observe the standard Fibonacci sequence.
Edge Cases:
- Overflow: The Fibonacci sequence grows quickly. Consider how your implementation will handle reaching the maximum value of
u64. For this challenge, you can choose to stop the iterator or panic, but a graceful stopping is preferred.
Examples
Example 1:
Input:
let mut fib = FibonacciSequence::new();
println!("{:?}", fib.next());
println!("{:?}", fib.next());
println!("{:?}", fib.next());
println!("{:?}", fib.next());
println!("{:?}", fib.next());
Output:
Some(0)
Some(1)
Some(1)
Some(2)
Some(3)
Explanation: This demonstrates the initial generation of Fibonacci numbers, starting from 0 and 1.
Example 2:
Input:
let mut fib = FibonacciSequence::new();
for _ in 0..10 {
if let Some(num) = fib.next() {
println!("{}", num);
}
}
Output:
0
1
1
2
3
5
8
13
21
34
Explanation: This shows generating the first 10 numbers of the Fibonacci sequence.
Example 3: Overflow consideration
Input:
// This example might run for a while depending on how overflow is handled.
// If the iterator stops gracefully on overflow, it will terminate.
// If it panics, the program will crash.
let mut fib = FibonacciSequence::new();
let mut count = 0;
while let Some(_) = fib.next() {
count += 1;
}
println!("Generated {} Fibonacci numbers before overflow/stopping.", count);
Output (example, actual count may vary slightly based on exact overflow detection):
Generated 93 Fibonacci numbers before overflow/stopping.
Explanation:
This illustrates that the iterator should eventually stop producing numbers as it approaches the limit of u64. The exact number of generated items before overflow depends on the specific u64 limit.
Constraints
- The Fibonacci numbers should be of type
u64. - The
next()method should returnOption<u64>. - The implementation should avoid unnecessary recalculations.
- The iterator should ideally stop gracefully when the next Fibonacci number would overflow
u64.
Notes
- Consider how to store the state within the
FibonacciSequencestruct. You'll need at least two variables to keep track of the previous two numbers. - The
Iteratortrait is defined instd::iter. - Think about how to handle the initial two numbers (0 and 1) correctly within your
next()method. - For overflow detection, Rust's checked arithmetic methods (like
checked_add) can be very useful.