rust real-world series - Fibonacci using Iterator iterator

Posted by co.ador on Fri, 04 Feb 2022 18:11:30 +0100

Why Fibonacci Sequence

The Fibonacci sequence is well suited for the iterator used in real-world rust, and the algorithm is simple and straightforward. This example can be used to learn the use of Iterator and is a good example for practice after learning the iterator chapter of rust.

Code Actual

don't bb, show me the code

struct Fib(usize, usize);

impl Fib {
    fn new() -> Fib {
        Fib(0, 1)
    }
}

impl Iterator for Fib {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        *self = Fib(self.1, self.0 + self.1);
        Some(self.0)
    }
}

fn main() {
    let last = 20;
    println!("fib({}) result: {:?}", last, Fib::new().take(last).collect::<Vec<usize>>());
}

Decomposition Knowledge Points

  1. The code defines a tuple structs named Fib. Since our implementation encapsulates implementation details, it is not necessary to define a named structure.
    There are other structures on the web that give names, which I find redundant. Like this
struct Fibonacci {
    a: u64,
    b: u64,
}
  1. The second is how to implement Iterator. The key is to define the type of association and implement the next method
impl Iterator for Fib {
    // 1. Define the association type as usize
    type Item = usize;
    // 2. Implement the next method, which is also the main logic here
    fn next(&mut self) -> Option<usize> {
        *self = Fib(self.1, self.0 + self.1);
        Some(self.0)
    }
}
  1. The third point is *self = Fib (self.1, self.0 + self.1). Self is defined as a variable reference (&mut), where *self dereferences are of type Fib.
    Another way of writing
self = &mut Fib(self.1, self.0 + self.1);

The above defines a mut Fib assignment to self, prompted directly by the rust compiler

   |
12 |         self = &mut Fib(self.1, self.0 + self.1);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign to immutable argument

error[E0716]: temporary value dropped while borrowed
  --> src\main.rs:12:21
   |
11 |     fn next(&mut self) -> Option<usize> {
   |             - let's call the lifetime of this reference `'1`
12 |         self = &mut Fib(self.1, self.0 + self.1);
   |         ------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
   |         |           |
   |         |           creates a temporary which is freed while still in use
   |         assignment requires that borrow lasts for `'1`

The prompt borrows a temporary variable and the temporary variable is discarded. In fact, &mut Fib is just a variable borrowing and will expire immediately after being lent to self, which points to a dangling pointer, which the rust compiler will not allow (force a wave, welcome a face).
So the right thing to do is just let self take ownership of the newly created Fib. That is *self = Fib(self.1, self.0 + self.1);.

  1. The fourth point is Fib:: new (). Take(last). Collect:: <Vec<usize>(). The results are printed directly here in the println macro, and the compiler cannot infer the type that needs to be collected. Instead, use the collect:: label.
    The compiler can infer this automatically unless it is written in the following way
let result: Vec<usize> = Fib::new().take(last).collect();
println!("fib({}) result: {:?}", last, result);

summary

In order to implement Fibonacci series by rust iterator, we need to master some key points.

  • Tuple Structural Writing
  • How to implement iterator trait
  • collect:: Helps the compiler infer types

Why Fibonacci Sequence

The Fibonacci sequence is well suited for the iterator used in real-world rust, and the algorithm is simple and straightforward. This example can be used to learn the use of Iterator and is a good example for practice after learning the iterator chapter of rust.

Code Actual

don't bb, show me the code

struct Fib(usize, usize);

impl Fib {
    fn new() -> Fib {
        Fib(0, 1)
    }
}

impl Iterator for Fib {
    type Item = usize;
    fn next(&mut self) -> Option<usize> {
        *self = Fib(self.1, self.0 + self.1);
        Some(self.0)
    }
}

fn main() {
    let last = 20;
    println!("fib({}) result: {:?}", last, Fib::new().take(last).collect::<Vec<usize>>());
}

Decomposition Knowledge Points

  1. The code defines a tuple structs named Fib. Since our implementation encapsulates implementation details, it is not necessary to define a named structure.
    There are other structures on the web that give names, which I find redundant. Like this
struct Fibonacci {
    a: u64,
    b: u64,
}
  1. The second is how to implement Iterator. The key is to define the type of association and implement the next method
impl Iterator for Fib {
    // 1. Define the association type as usize
    type Item = usize;
    // 2. Implement the next method, which is also the main logic here
    fn next(&mut self) -> Option<usize> {
        *self = Fib(self.1, self.0 + self.1);
        Some(self.0)
    }
}
  1. The third point is *self = Fib (self.1, self.0 + self.1). Self is defined as a variable reference (&mut), where *self dereferences are of type Fib.
    Another way of writing
self = &mut Fib(self.1, self.0 + self.1);

The above defines a mut Fib assignment to self, prompted directly by the rust compiler

   |
12 |         self = &mut Fib(self.1, self.0 + self.1);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot assign to immutable argument

error[E0716]: temporary value dropped while borrowed
  --> src\main.rs:12:21
   |
11 |     fn next(&mut self) -> Option<usize> {
   |             - let's call the lifetime of this reference `'1`
12 |         self = &mut Fib(self.1, self.0 + self.1);
   |         ------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
   |         |           |
   |         |           creates a temporary which is freed while still in use
   |         assignment requires that borrow lasts for `'1`

The prompt borrows a temporary variable and the temporary variable is discarded. In fact, &mut Fib is just a variable borrowing and will expire immediately after being lent to self, which points to a dangling pointer, which the rust compiler will not allow (force a wave, welcome a face).
So the right thing to do is just let self take ownership of the newly created Fib. That is *self = Fib(self.1, self.0 + self.1);.

  1. The fourth point is Fib:: new (). Take(last). Collect:: <Vec<usize>(). The results are printed directly here in the println macro, and the compiler cannot infer the type that needs to be collected. Instead, use the collect:: label.
    The compiler can infer this automatically unless it is written in the following way
let result: Vec<usize> = Fib::new().take(last).collect();
println!("fib({}) result: {:?}", last, result);

summary

In order to implement Fibonacci series by rust iterator, we need to master some key points.

  • Tuple Structural Writing
  • How to implement iterator trait
  • Collect:: <B>Helps the compiler infer types

Topics: Back-end Rust