Saturday, November 14, 2020

Callbacks in Rust and Python



Callbacks

Following is the definition of callbacks from wiki https://en.wikipedia.org/wiki/Callback_(computer_programming)

callback in Rust

Whenever I read any web code, callbacks are important part of the code.
So tried to build a basic callback in rust to understand what is it .


I believe , callbacks are basically storing functions/closures in array and calling them whenever required. Actually it is a little difficult to build in Rust because of the syntax and type checks.
But it’s work , type of the functions will be verified in compile time.
For example , if I give x+2 , instead of x +=2 in the below code, rust comiplier gives compilation error.
below code


Rust callback


//create a callbacks list and make some basic operations like
//register a function and calling it etc.

//callbacks struct to store the vector( list of functions)
struct CallbackV1{
    callbacks:Vec<Box<FnMut(i32)>>
}

//implemenation of the CallbackV1
impl CallbackV1{

    fn new() ->Self{

        CallbackV1 {
            callbacks : Vec::new()
        }
    }
	
	//method to register functions in the callbacks vector
    fn register(&mut self,f:Box<FnMut(i32)>){
        self.callbacks.push(f)
    }

	//method to call the functions in the callbacks
    fn call(&mut self,p:i32){

		//iterate over the callbacks vector and take a mutable reference
        for callback in self.callbacks.iter_mut(){
			
			//dereference the callback function and call with passed argument.
            (&mut *callback)(p);
        }
    }
}

fn add(x:i32){

    println!("{}",x+1);
}

fn main() {
//   let mut x = 2;
   let mut  mycallbacks = CallbackV1::new();
   let f1 = |mut x:i32| { 
                            x += 2
                            // println!("{}",x) ;
            };
   let f2 = add;

   mycallbacks.register(Box::new(f1));
   mycallbacks.register(Box::new(f2));

   mycallbacks.call(4);


}


Callback in Python

Built the same code with Python as well. Its quite easy to build the same in python. But the type checks are not there . Its simply call the functions registered. I believe this can lead to vulnerabilities .

Python Callback code


#create a callbacks list and make some basic operations like
#register a function and calling it etc.

class CallbacksV1:
    
    def __init__(self):
        self.callbacks = []
    
    def register(self,f):
        self.callbacks.append(f)
    
    def call(self,value):
        
        callback =  self.callbacks.pop()
            
        callback(value)

    
def add(x):
    print(x+1)

def add_two(x):
    print(x+2)
    return (x+2) 

c = CallbacksV1()
c.register(add)
c.register(add_two)

c.call(3)
c.call(5)

Saturday, July 25, 2020

Why tokio tasks are run-time dependent ?


Tokio and async-std 


I was going through two major crates for async programming in Rust. I believe Tokio and async-std are predominant crates now available for async programming. 

But when I tried to run tokio tasks with async-std run-time, but default it won't allow. I need to change run-time as tokio using #[tokio::main] that's not nice.

But when  I tried to run async-std tasks with tokio run-time, I am able to run.  Looks like async-std is more compatible with other Rust eco-systems.



error:
thread 'main' panicked at 'must be called from the context of Tokio runtime configured with either `basic_scheduler` or `threaded_scheduler


There would be libraries/crates to make it happen, but I think by default this should be available.


Friday, July 24, 2020

Where is the infinite loop in the below Rust Program ?

Have you ever wonder how the below  TcpListener Code run infinitely looking for new connections/clients.

Code:
When you run this below program with a Rust compiler or Cargo run, you will see that the program is not ending. How ?


As you noticed , the for loop doesn't have a explicit condition to exit. As far as the Iterator can return values, the "for loop" construct  execute the  statements inside the body. The Iterators can behave like  an infinite loop.But can return "None"(which can be used to exit from "for") , or can return Some values from next method .

Read more to understand how "for loop"  decompose in Rust - https://doc.rust-lang.org/reference/expressions/loop-expr.html

Ok..we understood that there is something called Iterator which can yield for values infinite.

But where is the Iterator here ?

When you look at the TcpListener , it is just a struct. But it has one particular method which implements Iterator trait. 
The connections can be accepted from TcpListener through a  struct called  std::net::Incoming  which is an Iterator.It is hidden(abstracted) behind the method incoming  in this case. As I said earlier  Iterators can yield and ask for values infinite times.





Iterator Trait is implemented on the struct "Incoming"



As you noticed the next method on the Iterator is not going to end. It just keep iterate as far as we are calling incoming method.




Incoming struct accepts connections of TcpListener:



Incoming implementing Iterator trait: