Praveen's Blog

An Eternal Quest for Incremental Improvement

FizzBuzz in Rust

Overview

I just came across Rust, what seems to be a promising candidate for the next big systems programming language. Eventhough "Hello, world!" is a good first program, FizzBuzz is the first non-trivial program that I attempt in any new language. Let me quickly present the Rust development workflow and a few concepts while showcasing my solution.

Cargo

Cargo is the tool to manage Rust projects. It makes managing (external) dependencies, building, testing, and running Rust code easier. An empty project can be create as follows:

$ cargo new fizzbuzz --bin

Upon running this, Cargo should setup a project directory, fizzbuzz, and create a stub main.rs. A couple of interesting observations that I had are:

  • By default, Cargo sets up the project directory as a git repository. Even git is my most favorite version control tool, there should be an option for disabling this for people who use other tools.
  • The configuration file uses yet another markup language called toml. I'm sure that there are justifications for selecting this markup language. But, this adds to plethora of options that we already have for markup.

Now, you can run the newly created project by changing to the project directory.

$ cd fizzbuzz
$ cargo run
   Compiling fizzbuzz v0.1.0 (file:///Users/praveen/Developer/Rust/Learn/fizzbuzz)
     Running `target/debug/fizzbuzz`
Hello, world!

Logic

Rust supports Haskell style enumerations, and pattern matching that are used in the following solution. Below follows the listing of src/lib.rs:

#[derive(Debug, PartialEq)]
pub enum FizzBuzz {
    Value(u64),
    Fizz,
    Buzz,
    FizzBuzz,
}

/// This function converts the given unsigned integer to FizzBuzz enum.
///
/// # Examples
///
/// ```rust
/// use fizzbuzz::to_fizz_buzz;
/// use fizzbuzz::FizzBuzz;
///
/// assert_eq!(FizzBuzz::Value(13), to_fizz_buzz(13));
/// assert_eq!(FizzBuzz::Fizz, to_fizz_buzz(9));
/// assert_eq!(FizzBuzz::Buzz, to_fizz_buzz(20));
/// assert_eq!(FizzBuzz::FizzBuzz, to_fizz_buzz(30));
/// ```
pub fn to_fizz_buzz(value: u64) -> FizzBuzz {
    match (value % 5, value % 3) {
        (0, 0) => { FizzBuzz::FizzBuzz }
        (0, _) => { FizzBuzz::Buzz }
        (_, 0) => { FizzBuzz::Fizz }
        _ => { FizzBuzz::Value(value) }
    }
}

Some of the key take aways here:

  • Rust enumerations are similar to the Haskell one, where the concept of type constructors comes in.
  • Compiler can provide basic implementation for some traits via the #[derive] attribute.
  • Unit test can be written as part of the documentation example. That way, your documentation is always upto date. These tests can be run using Cargo.
  • Isn't pattern matching beautiful? Any modern language should support pattern matching.

Now tests can be run as follows:

$ cargo test
   Compiling fizzbuzz v0.1.0 (file:///Users/praveek6/Developer/Rust/Learn/fizzbuzz)
src/main.rs:1:1: 3:2 warning: function is never used: `main`, #[warn(dead_code)] on by default
src/main.rs:1 fn main() {
src/main.rs:2     println!("Hello, world!");
src/main.rs:3 }
     Running target/debug/fizzbuzz-63bd5327039a004c

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

     Running target/debug/fizzbuzz-78f5b2ad74d4a952

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured

   Doc-tests fizzbuzz

running 1 test
test to_fizz_buzz_0 ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

Printing

Now, the main.rs could be modified to make use of the to_fizz_buzz function that I implemented earlier. Here is the listing of src/main.rs:

extern crate fizzbuzz;

#[allow(dead_code)]
fn main() {
    for i in (1..101).map(fizzbuzz::to_fizz_buzz) {
        println!("{:?}", i);
    }
}

#[allow(dead_code)] attribute was added to silence the warnin about unused main when running the tests.

Here is the output of the program.

$ cargo run
   Compiling fizzbuzz v0.1.0 (file:///Users/praveek6/Developer/Rust/Learn/fizzbuzz)
     Running `target/debug/fizzbuzz`
Value(1)
Value(2)
Fizz
Value(4)
Buzz
Fizz
Value(7)
Value(8)
Fizz
Buzz
Value(11)
Fizz
Value(13)
Value(14)
FizzBuzz
Value(16)
Value(17)
Fizz
Value(19)
Buzz
Fizz
...
Value(92)
Fizz
Value(94)
Buzz
Fizz
Value(97)
Value(98)
Fizz
Buzz

Comments