Zero-Cost Asynchronous Programming in Rust

Reading Time: 4 minutes

This article helps you out with the concept of Asynchronous Programming in Rust. Writing Asynchronous code can greatly improve performance but the downside is they are complex.

In this post, I will explain you how to use futures(Rust Library) and its methods using simple examples and will assume that readers having an idea about Rust and for those who are unfamiliar with Rust, Rust is a programming language which comes with tons of features like Fearless concurrency, Box pointers, Lifetimes. I’ll suggest you to first go through with Rust before further reading.

What is futures?

futures are used for asynchronous programming in Rust. Asynchronous programming means writing non-blocking code and future is a concept for objects which is a proxy for another value that may not be ready yet.

The simplest example could be HelloWorld, we can use this example to understand the structure of a Future trait.

use futures::{Future,Async,Poll};
use std::result::Result::Ok;

struct HelloWorld;

impl Future for HelloWorld{
  type Item = String;
  type Error = ();

    fn poll(&mut Self) -> Poll<Self::Item,Self::Error> {
        Ok(Async::Ready("hello world".to_string()))
    }
}

In the above example, we have implemented Future for the Hello World, to be more specific futures is a library and Future is trait defined in the futures.

The Future trait has two values Item and Error, the value of Item type is yielded by the stream which is of String type and there is no such Error.

As you know we are working with future, there must be way to continuously check for our future value, I mean how could we know our computation has been done or not, that’s exactly what poll method does, it continuously check whether the task has completed or not, if Error than obviously returning specific error kind and if it returns Ok the method will further check whether of future is ready or not if ready then return Asynchronous Ready with value wrapped with it and if not ready than will return Asynchronous Not Ready and check it will check again after some interval.

Most of the implementation of Future shows some similar pattern and to reduce boilerplate code, Future crate provides a list of Combinators.
Like in the above example we could use ok(“Hello World”), Instead of implementing the whole Future trait.

There are several Combinators defined in the Future trait but here we are going to discuss some of the common Combinators which are used in our program:

  • map
  • and_then
  • map_err
  • select
  • join

map:

Map this future’s result to a different type, returning a new future of the resulting type.
This is useful to chain along with a computation once a future has been resolved.
Let’s understand with the help of an example:

fn main() {
    let number: i32 = 5;
    let result = square(number).map(|num| future::lazy(move   || {
        thread::sleep(time::Duration::from_secs(1));
        ok(num * 2)
    }));
    println!("{}", result.wait().ok().unwrap());
}

fn square(number: i32) -> impl Future<Item=i32, Error=()> {
    future::lazy(move || {
        thread::sleep(time::Duration::from_secs(2));
        future::ok::<i32, ()>(number * number)
    })
}

and_then:

Execute another future after current future has resolved successfully.
This Combinator allows sequencing of two asynchronous operations-basically you want to use this when there is the sequence of operations.
Let’s understand this with the help of an example:

fn main() {
    let name: &str = "Your name";
    let greetings = message(name).and_then(|name_value|           

    ok(println!("Hello {}, Hope you are enjoying this  
      blog",name_value))).wait();
   }

fn message(name: &str) -> impl Future<Item = &str, Error = ()> {
    ok(name)
}

map_err:

This Combinator is used to map the future’s error to a different error, returning a new future and the closure provided will only be called if this future is resolved with an error.
So, here is the example for this:

 let num: u32 = 3;
    ok(num).and_then(|num| {
        if num < 5 {
            err::<u32, u32>(num)
        } else {
            ok(num)
        }
    }).map_err(|num| panic!("{}","The number is less than     
                            five")).wait();   

select:

It waits for either one of the Future to complete and then wait for the other Future for this both the Future must have the same Item and Error type.
Example of this:

fn main() {
    let future1 = future::lazy(|| {
        thread::sleep(time::Duration::from_secs(5));
        future::ok::<&str, ()>("This is first")
    });

    let future2 = future::lazy(|| {
        thread::sleep(time::Duration::from_secs(3));
        future::ok::<&str, ()>("This is second")
    });
    let (firstvalue, lastvalue, ) = future1.select(future2).wait().ok().unwrap();
    println!("{}", firstvalue);
}

join:

It waits for both the Future to complete and will return values of both the future.
Let’s understand the concept of the join with this example:

use futures::future::ok;
use futures::future::Future;

fn main() {
 sum(1).join(sum(11))
     .map(|(x,y)|println!("{}",x+y)).wait().unwrap()
}

/**
  *This function will give sum of next ten natural numbers of a given number
**/
fn sum(mut num: i32) -> impl Future<Item = i32, Error = ()> {
    let mut sum: i32 = 0;

    if num > 0 {
        for i in 1..11 {
            sum = sum + num;
            num = num + 1;
            println!("{}",i);
        }

This is all about futures in Rust Programming as far as my understanding. Hope you all will get acquainted with the concept of futures(a library) and Future(a trait) in Rust.
Thanks for Reading!!!


Knoldus-blog-footer-image

Written by 

Pawan Singh Bisht is the Trainee Software Consultant at Knoldus Software LLP. He has good knowledge of languages C++ and Java, and currently working on Rust. He is always ready to accept new challenges. As a fresher, he always tries to explore the different type of software and tools.

Leave a Reply

Knoldus Pune Careers - Hiring Freshers

Get a head start on your career at Knoldus. Join us!