Tuesday, May 28, 2019

Rust Closure - PART2

Rust Closure - PART2.html

Closure Rust - Cont…

FnMut Trait

This is a continuation of the previous post ( https://naveendavisv.blogspot.com/2019/05/what-is-closures-how-can-it-be-used-in.html) on closures in rust.
Last post we defined the closures as Fn(i32) -> i32 when we want to pass the closure to a function.
Fn trait borrows values from the environment immutably.
But now we will see what is FnMut? .
FnMut can change the environment because it mutably borrows values.
fn double(mut db1:T)
   where T:FnMut() -> i32 {
     println!("{:?}",db1());
  }

fn main() {

  let mut x = 10;
  let  db = || { x*=2; x};
  double(db);
  println!("{:?}",x);
}
The above example, the ‘x’ value will recalculated when we call the closure inside the function ( double)
If we want to change a value in the environment that closure is enclosing, the FnMut trait can be used to define the closure.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=5102dff068bc256a59bc9c8c9bf3f92f

Cacher struct with closure.

We will try to build a Cacher Structure as explained in the rust book - https://doc.rust-lang.org/book/ch13-01-closures.html
The need for Cacher structure is explained in the book.
Final part of Cacher structure chapter , the author ask us to create the struct with HaspMap to store the calculated value
I will try to explain the Code wrote in Rust here.
How nice would it be if we can cache the computation intensive function and store it's calculated values in a struct !!
A data structure with a 'Closure' and 'Hashmap' will serve the purpose very nicely.

How do we define Cacher Structure with HashMap ?

We can call data structure as Cacher as it cache/store the result values.
Normally we don’t define the parameter type or return type of a Closure in Rust. But it is must to explicitly declare the field types of a Struct , so we need to explicitly define the 'T' where T is type of the Closure .Rust Compiler needs to understand all the field types of structures in order to allocate memory
resultMap field is Box (A pointer type for heap allocation - https://doc.rust-lang.org/std/boxed/struct.Box.html) reference to Hashmap.
If you don’t know how much memory you are going to use, one option would be “Box” type. Here we defined the field resultMao as Box reference to a HaspMap .
struct Cacher
  where T:Fn(u32) -> u32
  {
    square:T,
    resultMap:Box>,
  }

How do we implement the Cacher Struct ?

We defined the Cacher Struct , the next step would be we need to implement the Struct with methods.
one method that we would require is “new” . This method creates Objects of the Struct. Another one would be “value” - to get the value from the resultMap. As resultMap is reference to a HashMap , we can use “insert” method to add key and value pairs to the hashmap.
Also we can use “get” method to get a value for a key from the haspmap.
impl Cacher
  where T:Fn(u32) -> u32{
  fn new(square:T,mut resultMap: HashMap) -> Cacher{
    Cacher{
              square,
              resultMap:Box::new(resultMap),
           }
  }

  fn value(&mut self,x:u32) -> u32 {


     match self.resultMap.get(&x){
       Some(v) => *v,

       None    => {
                   let v = (self.square)(x);
                      self.resultMap.insert(x,v);
                   v
                  },
    }
  }
}

Rust Playground - https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d131716d3620b60521a2af2067a32dcd

Tuesday, May 21, 2019

What is a Closure ? How can it be used in Rust programming ? - PART1

Closures in Rust

Closure is record storing a function along with it environment variables. You can read about closures in wiki

Difference between Closure and a function.

function definition:
Note: we have to explicitly pass the type and variable.
fn add (x:u32) -> u32 { x +1 }

Closure definition in Rust:
Note: We can follow any one of the following syntax to define a Closure
//explicitly defining types
let add = |x:u32| -> u32 { x +1 }
or
//the compiler dynamically type the Closure variables
let add = |x| -> {x+1}
or
//ignoring curly braces if you have only one statement
let add = |x| -> x+1

We will go through some example for Closure and discuss its features

Example#1 - passing Closure to a function

Below example shows the closure is defined with parameter ’ x ’ passed from environment where it is called. Closure is enclosed in a variable called square_x here.
We can see that value of the variable x is 2 . In the main program, first we define the Closure and then it is called from square_x(x) in println! statement.
square_x is the Closure.
When Closure looks for it's required variable's 'x' value for calculation, Closure its self will be enclosed with its required variables and understand that 'x' is parameter and it need to get its value from lexical environment which is 2 . And it perform the calculation: x*x - square of 2 which is 4.

The second print statement execute inside the function square . When we call the square(square_x) function, Closure invoked and looks for its lexical environment and find the value for parameter ‘x’ variable.The ‘x’ variable value is 3 over there, so the Closure calculate the square of 3 which is 9 .

fn square<T>(sq:T)
 where T:Fn(i32) -> i32
  {
    let  x = 3;
    println!("square called from insidefn value = {}",sq(x));
  }

fn main() {

    let mut x = 2;
    let square_x  =  |x| x*x;
    println!("square of x = {}",square_x(x));
   

    square(square_x);

}

From what we learned , Closure doesn’t need to specify it’s Parameters type or Return type. But in the (square) function definition, we explicitly defined ‘T’ as Fn(i32) -> i32.
Because for a function, it is necessary to define it's parameters type.

run in Rust Playground -
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c7dafb2aa3b7f7f83fc7490be6f30925

Example 2 # What will happen if we are not passing variable

This example we remove the passing parameter, to make the Closure as no-parameters.

let sq = || x*x;

But Closure has the property to get all its variables from its lexical environment . When you pass it to a function, it actually pass the Closure and along with variables enclosed.

fn square<T>(sq:T)
 where T:Fn() -> i32
  {
    let  x = 3;
    println!("square called from insidefn value = {}",sq());
  }

fn main() {
    let x = 2;    
    let square_x  =  || x*x;
    println!("square of x = {}",square_x());
    square(square_x);
 
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=adc2ce2cb7fc2f73ddee5e1cbfe10a9d

Example 3- What will happen if you pass different variable to Closure in the above example

fn square<T>(sq:T)
 where T:Fn(i32) -> i32
  {
    let  x = 3;
    println!("square called from insidefn value = {}",sq(x));
  }

fn main() {

    let x = 2;
    let square_x  =  |y| x*x;

    square(square_x);
    println!("square of x = {}",square_x(x));
}

Can you guess the output ?Hint : Closure is record which stores function along with its environment variables.
run the example above and check out the output.
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7763c76893f87426f975db3eafab5e0f

Reference https://doc.rust-lang.org/rust-by-example/fn/closures.html

https://doc.rust-lang.org/book/ch13-00-functional-features.html