Building a Random Friend Dialer Part 1: Getting Started with Rust and Iron

July 13, 2015
Written by

IMG_2939

Few things make me happier than taking a stroll in Brooklyn and crossing paths with someone I haven’t talked to in a long time. But living in a city of eight and half million people this serendipitous moment doesn’t happen nearly enough. Lately I’ve been wondering if I can recreate some of the magic of this experience using technology. In this series I’m going to show you how to build an application that let’s you place a phone call to a random friend whenever you want. It’s not exactly the same as crossing paths in Brooklyn but it will still be a fun way to connect with long lost buddies.

This series is also an excuse for me to learn a new programming language, Rust. Has Rust been on your list of languages to check out? Let’s learn together! In this first part of this series I’ll show you how to get started with Rust and the Iron web framework.

Hello, Rust!

I’ve been super surprised with how much fun I’ve had playing with Rust. Let me walk you through a quick “Hello World!” so you can see why I’m so excited.

Are you a Rust expert? As fun as writing “Hello, World!” is, I’m not offended if you wait for part 2 of this series to come out before you dive in.

The first step is to install Rust. I hope you’re not surprised! It only takes a few minutes. Don’t worry about me, I’ll just be reading fan theories about Agents of S.H.I.E.L.D. while we wait.

Now that you have Rust installed we can start building our first application. We’re going to build our application with the help of Cargo. Cargo is the package manager for Rust. It feels similar to working with npm in Node.JS or Composer in PHP. To create a new Rust project with Cargo jump into terminal, navigate to the folder where you’d like your application directory to live and run the following command:

cargo new randomfriend --bin

We pass cargo new the name of the application we want to build (in this case: randomfriend). We’re also passing —bin to indicate we’re creating a binary application. Now that we’ve run that command we’ll have a randomfriend directory in whatever folder we ran the command from.

Cargo is very nice to us and defaults a pre-written “Hello, World!” application. Open src/main.rs and take a look:

fn main() {
    println!("Hello, world!");
}

Only 3 lines of code but it’s important to recognize that the code that runs when our application starts is enclosed in our main function. Coming from a background of PHP and JavaScript this is a bit new for me.

With our code in place, we can build our application by running cargo build. After we build our application we can run it by running:

target/debug/randomfriend

Look at our beautiful “Hello, world!” output.

Building and running our application is a bit annoying, fortunately we can have cargo do both of these steps for us by running:

cargo run

Oh yeah! Our first Rust app!

dancing animated GIF

I AM IRONMAN

We’ve got Rust installed and we’ve successful run our first “Hello, World!” application. But how do we build our first Rust application for the web? Meet Iron. Iron is an extensible web framework for Rust. If you’ve worked with Express in the Node.JS world or Sinatra in the Ruby world it may feel somewhat similar.

In order to use Iron we need to add it as a dependency for our in our application. Rust makes it really easy for us to manage our dependencies by just modifying our Cargo.toml file. This file not only contains dependencies but also other information about our application (like the name). It’s similar to our package.json in a Node.JS application. Open it up and add the following lines at the end of the file:

dependencies]
iron = "*"

 

We’re using * to indicate that we want to pull the latest release of our iron dependency.

Now we need to replace our main.rs file with the “Hello, World!” on the main page of the Iron homepage:

extern crate iron;

use iron::prelude::*;
use iron::status;

fn main() {
  fn hello_world(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "Hello World!")))
  }

  Iron::new(hello_world).http("localhost:3000").unwrap();
  println!("On 3000");
}

I know everyone loves some copy pasta code but before we run this we should make sure we know what’s happening.

  1. On the first line, we’re linking the iron library. Of course, since we install libraries using Cargo they’re called “crates”.
  2. We import some names from the iron crate into our local scope. This let’s us reference functions with much shorter names which will make our code much cleaner.
  3. We have our familiar main function, this will be the code that runs when we run our application.
  4. Within our main application we define a new function called hello_world. This function just takes any request and responds with an HTTP status of Ok and a response body of “Hello World!”
  5. We setup our new Iron web server to execute this function for every incoming request and run the server on localhost:3000.

Now that we have this code in place let’s run it:

cargo run

Since this is our first time running our application since adding a dependency it’ll first download and install Iron during the build process. Once that finishes up Cargo will let you know that your application is running. Open up a web browser and go to http://localhost:3000. You should see “Hello World!” Beautiful, isn’t it?

Where Are You Going?!?

There’s one small issue with our application right now. Try going to any other page on http://localhost:3000. We’re always returning “Hello World!”. That’s not right! We need to implement a router so that we can take different actions depending on which page a user goes to.

We can fix this problem by using Router. Router is a middleware for the Iron framework that allows us to quickly and easily implement routing. To use Router we need to add it as a dependency in our Cargo.toml:

[dependencies]
iron = "*"
router = "*"

Now we can jump over to our main.rc and link our Router crate so we can use it:

extern crate iron;
extern crate router;

To make things a bit cleaner we’ll also import the Router functions to our local scope:

use iron::prelude::*;
use iron::status;
use router::Router;

Now with just a couple changes to our main() function we can put our router in place:

fn main() {
  let mut router = Router::new();

  router.get("/", hello_world);

  fn hello_world(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "Hello World!")))
  }

  Iron::new(router).http("localhost:3000").unwrap();
  println!("On 3000");
}

We added just three lines but we’re getting introduced to a couple new Rust concepts with the first line of code. Let’s take a closer look at it:

    let mut router = Router::new();

Here we’re seeing two new things that may not be immediately obvious. First, the let keyword. This indicates we’re defining a variable. It’s a bit like using var in JavaScript but with one key difference – by default you can’t modify variables in Rust. A variable in Rust is what we consider in other languages a constant. That leads us to the next keyword: mut. mut changes our variable to be mutable. This means we can now make changes to it. I’m still wrapping my head around mutability in Rust but understanding the basics of let and mut gave me enough knowledge to move forward with this code.

The other two lines we’ve added are a bit easier to digest. We create a new route for ‘/’ and pass it our ‘hello_world’ function. We then tell Iron to use our router when a request comes in instead of using the same function for every request.

Fire up your application with cargo run (if you were still running it from before kill it and start it again so you get a fresh build) and test it out. Going to http://localhost:3000/ will return “Hello World!” but going to another route will get a blank screen.

Now that we’ve added our first route it’s pretty easy to add another route that we’ll eventually use for our dialing:

fn main() {
  let mut router = Router::new();

  router.get("/", hello_world);
  router.post("/randomfriend", randomfriend);

  fn hello_world(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "Hello World!")))
  }

  fn randomfriend(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "Make Random Dial Happen Here!")))
  }

  Iron::new(router).http("localhost:3000").unwrap();
  println!("On 3000");
}

What’s Next?

We’ve now built a web application using Rust and Iron and started our path towards building a random friend dialer. In the next part of this series we’ll integrate a database so we can save and retrieve the contacts we’d like to randomly dial.

In the meantime, enjoy spending some more time playing with Rust. I’m having an absolute blast learning it. Have any questions or want to tell me about your experience with Rust? Find me on twitter (@rickyrobinett) or say “hi” in the comments below.