Rust Introduction

by "Blag" - Senior Developer Evangelist

Return to Geeky Thursday

What is Rust?


Rust is a general-purpose, multi-paradigm, compiled programming language.

It's designed to be a safe, concurrent and practical programming language, supporting functional and imperative-procedural paradigms.

The very first compiler was written in OCaml.

Open Source and available on Linux.

How to install Rust?


If you're using Linux or Mac


curl -sSf https://static.rust-lang.org/rustup.sh | sh

Or you can download an installer...where Windows is included...

Who uses Rust?


  • Mozilla
  • Coursera
  • Dropbox
  • npm
  • Tessel
  • Chef
  • MaidSafe

Starting out...


There's no official Rust repl...although some can be get from Github...

Rust must be compiled and then run the executable

To do that, we must use the rustc compiler command

To test out the basic Rust commands, we're going to create a sandbox.rs file...

Basic Concepts

Comments are done by using // and /// for documentation comments that supports Markdown.

The semicolon ";" is used to finish a command line...

Precedence must be enforced by parenthesis

				
fn main(){


    println!("{}", 2*3+5);

//11
	
	
    println!("{}", 2*(3+5));


//16

}
				

Basic Concepts


Printing on the screen is easy...

				
fn main(){


    let text:&str = "Welcome to Rust!";


    println!("{}", text);


//Welcome to Rust


}
				

If you're wondering about the & that's because the string needs a pointer to it...

Variables can have multi-assignments...


				
fn main(){


    let (a, b, c) = (1, 2, 3);


    println!("{}, {}, {}", a, b, c);


}

//1, 2, 3
				

If you need to specify the variable type you need to do it like this...

let (a, b, c) = (1i64, 2i64, 3i64);

Where i64 (lowercase) is the type...

Data Structures


Variables are immutable by default...so we need to make them mutable...

				
fn main(){


    let test:i64 = 10;


    println!("{}", test);


    test = 11;


//error: re-assignment of immutable 

//variables `test` [E0384]


}
				

				
fn main(){


    let mut test:i64 = 10;


    println!("{}", test);


//10


    test = 11;


    println!("{}", test);


//11


}
				

Arrays and Hashes

				
fn main(){


    let a = [1, 2, 3];


    for i in &a{


        println!("{}", i);


    }


//1


//2


//3


}
				

We can also create nested arrays

				
fn main(){


    let a = [1, 2, 3];


    let b = [4, 5, 6];


    let c = [a, b];


    
    println!("{}", c[1][1]);


//5


}
				

or hashes/dictionaries

				
use std::collections::HashMap;


fn main(){


    let mut dict:HashMap<&str, &str> = HashMap::new();


    dict.insert("0", "Hello");


    dict.insert("1", "Bye");


    match dict.get("0"){


        Some(result) => println!("{}", result),


        None => println!("Not found")


    }


}
				

That example was a little bit complicate...let's explain it...

use std::collections::HashMap; is a library that allows us to use Hash Maps...

let mut dict:HashMap<&str, &str> = HashMap::new(); we need to Hash Map to be mutable...otherwise we can't add anything...

dict.insert("0", "Hello"); inserts a new element on the Hash Map

match dict.get("0"){ match is like a switch and we need it because we might or not get an answer

Some(result) => means that we found something...

None => nothing found...that element is not present...

Operations on Arrays

Arrays are fixed in size...so we need to use Vectors in order to apply operations

								
fn main(){


    let mut programming = vec!["Forth","Perl","Rust"];


    programming.push("Visual Basic");


    println!("{}",programming.len());


    //4


    programming.remove(3);


    println!("{}",programming.len());
    
    
    //3
    
}
				

Functions

Functions are very interesting...and a lot can be learned...

				
fn greet(name:String, age:i64) -> String{

    let mut result:String = "".to_string();


    result = result + "Hello " + &name + ", you're " + 
    
             &age.to_string() + " years old.";

    result
    
}



fn main(){

    let result:String = greet("Blag".to_string(), 39);

    println!("{}", result);

}
				


  • Functions return values simply by passing the variable as the last command

  • Strings are handled a little bit different. By default a string is considered immutable.

  • "Blag" is considered an &str that's why we convert it using to_string()

  • Inside functions we must declare the parameters type

  • Note that inside the function we used &name instead of name...because we need a variable pointer...

Returning multiple values

				
fn operations(num:i64) -> (i64, i64, i64){

    let a:i64 = &num * 2;

    let b:i64 = &num * 3;

    let c:i64 = &num * 4;

    (a, b, c)

}


fn main(){

    let (a, b, c) = operations(2);

    println!("{}, {}, {}", a, b, c);


//4, 6, 8


}
				

Ownership

This is a weird concept but very important in Rust, and it has a lot to do with security

				
fn main(){


	let v = vec![1, 2, 3];
	

	let v2 = v;
	

	println!("{}", v[0]);


}


//error: use of moved value: `v` [E0382]
				

				
fn main(){


	let v = vec![1, 2, 3];
	

	let v2 = &v;
	

	println!("{}", v[0]);


}


//1					
				

Basically, Rust allocates memory on the stack for "v", and that memory can't be reassigned unless we use a reference...

The good thing about this is that it prevents value changes that are not explicitily called, making our apps more secure...

Ownership, Borrowing and Lifetimes are not so easy to digest key concepts of Rust...so please go ahead and read the documentation -:)

Fibonacci List


Finally...we're going to make our first application...


So grab your favorite text editor and get ready...


Name your file "fibonacci.rs"


				
use std::io;


fn fib(num: i64, a: i64, b:i64) -> String{

    let mut result: String = "".to_string();

    let sum: i64 = a + b;

    let sum_str: &str = &sum.to_string();

    let a_str: &str = &a.to_string();

    let b_str: &str = &b.to_string();

    if a > 0 && num > 1 {

        result = result + sum_str + " " + 
        
                 &fib((num - 1), (a + b), a);
				
				
    }else if a == 0{

        result = "".to_string() + a_str + " " + 
        
                 b_str + " " +  sum_str + " " + 
                 
                 &fib((num - 1), (a + b), b); 

    }

    result

}
				
				
fn main(){

    println!("Enter a number : ");

    let mut input_num = String::new();

    io::stdin().read_line(&mut input_num)

               .expect("failed to read");



    let trimmed = input_num.trim();

    match trimmed.parse::<i64>() {

        Ok(i) => { let result: String = fib(i, 0, 1); 
			
                   print!("{}\n", result);}

        Err(..) => println!("Please enter an interger, 
        
                   not {}", trimmed)

    };

}
				

Open the Terminal and go to your source code folder...

rustc "Name_of_File.rs"


When we run it we're going to see...

Making an LED Number App


This is one of my favorite codes of all time...


Name your file "LED_Numbers.rs"


				
use std::io;

use std::collections::HashMap;


fn main(){

    let mut leds:HashMap<&str, &str> = HashMap::new();


    leds.insert("0", " _  ,| | ,|_| ");

    leds.insert("1", "  ,| ,| ");

    leds.insert("2", " _  , _| ,|_  ");

    leds.insert("3", "_  ,_| ,_| ");

    leds.insert("4", "    ,|_| ,  | ");	

    leds.insert("5", " _  ,|_  , _| ");

    leds.insert("6", " _  ,|_  ,|_| ");

    leds.insert("7", "_   , |  , |  ");
				
				
    leds.insert("8", " _  ,|_| ,|_| ");
    
    leds.insert("9", " _  ,|_| , _| ");

    
    
    println!("Enter a number : ");
    
    let mut input_text = String::new();
    
    io::stdin().read_line(&mut input_text)
    
               .expect("failed to read");



    let split = input_text.split("");

    let vec: Vec<&str> = split.collect();

    let count = &vec.len() - 2;
				
				
    for i in 0..3{

        for j in 0..count{

            match leds.get(&vec[j]){

                Some(led_line) => { 

                    let line = led_line.split(",");

                    let vec_line: Vec<&str> = 
                    
                    line.collect();

                    print!("{}",&vec_line[i]);

                    },

                None => println!("")

            }

        }

        print!("");

    }

    println!("");

}
				

When we run it we're going to see...


Random Names


This App will generate 100,000 random names using two 16 elements arrays


We will measure the runtime


Name your file "Random_Names.rs"


Cargo


Sorry to interrupt...but this for this app we need to do something a little bit different...


Cargo is Rust's build system and package manager


Libraries on Rust are managed by Cargo


Create a folder called "random_names", then inside a folder called "scr" and a file called "main.rs"

Now, on our "random_names" directory create a "Cargo.toml" file (First letter uppercase and the rest lowercase)

Write this inside the Cargo.toml file...

				
[package]


name = "random_names"

version = "1.0.0"

authors = ["Author author's email"]



[dependencies]

rand="0.3.0"

time="*"
				

We can now create our main.rs file inside Random_Numbers/src

				
extern crate rand;

extern crate time;


use rand::Rng;

use time::PreciseTime;
				

"extern crate" is Rust's way of calling an external library...

				
fn main(){
	
	
    let start = PreciseTime::now();
	
    
    let names = vec!["Anne","Gigi","Blag","Juergen",
    
                     "Marek","Ingo","Lars","Julia",
                     
                     "Danielle","Rocky","Julien",
                     
                     "Uwe","Myles","Mike","Steven", 
    
                     "Fanny"];
    
    let last_names = vec!["Hardy","Read","Tejada",
    
                          "Schmerder","Kowalkiewicz",
                          
                          "Sauerzapf","Karg","Satsuta",
                          
                          "Keene","Ongkowidjojo", 
    
                          "Vayssiere","Kylau","Fenlon",
                          
                          "Flynn","Taylor","Tan"];
			
				
    let mut name: String = String::new();

    let mut full_names = Vec::new();

    let mut x:i64 = 0;
	

    loop{

        x = x + 1;

        name = names[rand::thread_rng().
        
               gen_range(0, 16)].
        
               to_string() + " " + 
               
               last_names[rand::thread_rng().
               
               gen_range(0, 16)];

        full_names.push(name);

        if x == 1000000 {break;}

    }
			
				
    let end = PreciseTime::now();
    
    println!("Time: {}", start.to(end));
    
    println!("{} names generated", full_names.len());

}
			

In order to compile and run this app we need to do the following


* Inside your "random_names" app run Cargo build

* The run cargo run


Cargo will download any library defined in your cargo.toml file


When we run it we're going to see...

How this behaves in Python?

And in Julia?

Decimal to Romans


This App will create a Roman Numeral based on a decimal number


This will include some nice commands...


Name your file "decimal_to_romans.rs"


				
use std::collections::HashMap;

use std::io;


fn main(){

    let mut roman_table:HashMap<&str, &str> = 
    
                        HashMap::new();


    roman_table.insert("1000", "M");

    roman_table.insert("900", "CM");

    roman_table.insert("500", "D");

    roman_table.insert("400", "CD");

    roman_table.insert("100", "C");

    roman_table.insert("90", "XC");

    roman_table.insert("50", "L");

    roman_table.insert("40", "XC");
			
				
    roman_table.insert("10", "X");

    roman_table.insert("9", "IX");

    roman_table.insert("5", "V");

    roman_table.insert("4", "IV");

    roman_table.insert("1", "I");
    
    
    let mut vec = Vec::new();
	
    for key in roman_table.keys(){
    
        match key.parse::<i64>(){
    
            Ok(i) => {vec.push(i);},
    
            Err(..) => println!("Sorry...can't 
            
                                 make it")
    
        }
    
    }    
			
				
    vec.sort_by(|a, b| b.cmp(a));

    let mut num:i64 = 0;

    let mut result: String = "".to_string();


    println!("Enter a number : ");

    let mut input_num = String::new();

    io::stdin().read_line(&mut input_num)

               .expect("failed to read");


    let trimmed = input_num.trim();

    match trimmed.parse::<i64>() {

        Ok(i) => { num = i;}

        Err(..) => println!("Please enter an interger, 
        
                             not {}", trimmed)

    };
				
				
    while num > 0{

        for i in &vec{

            if &num >= i{

                match roman_table.get(&i.to_string().
                
                                  as_str()){

                    Some(value) => {result = result + 
						
						            &value; num = num - i; },

                    None => println!("")

                } break

            }

        }

    }

    println!("{}", result);
    
}
				

When we run it we're going to see...


Count Letters


In this example we're going to read a file and count how many time a letter appears...


Call your file "countletters.rs" (all in lowercase).


Create a file called "readme.txt" with the following text...


"This is a text file that we're going to read it using Rust"


				
use std::fs::File;

use std::io::BufReader;

use std::io::BufRead;

use std::collections::BTreeMap;
			
				
fn letters<'a>(line:&'a str,

               mut leds:BTreeMap<String, isize>){

    let letters = line.split("");

    let v: Vec<&str> = letters.collect();

    for i in v{

        *(leds.entry(i.to_string()).or_insert(0)) +=1;

    }

    for (key, value) in leds.iter() {

        println!("{} {}", key, value);

    }

}
			
				
fn main(){

    let leds: BTreeMap<String,isize>=BTreeMap::new();

    let f = File::open("readme.txt").unwrap();

    let f = BufReader::new(f);

    let mut lines = String::new();

    for line in f.lines(){

        lines = line.unwrap();

    }

    letters(&mut lines, leds);

}
			

When we run it we're going to see...


That's it for now


Rust is a fairly new language...


The ownership, borrowing, lifetime system is pretty complex and sometimes frustrating...


but...learning Rust will bend your mind and make you a better developer...


so go ahead and learn more Rust...


Contact Information


Blag --> blag@blagarts.com

@Blag on Twitter

Go back home...