Rust is a systems programming language that runs fast, and guarantees thread safety. This is a basic introduction to Rust for advanced software developers not for beginners. If you are not familiar with any computer language you better learn first Python.

Language Syntax

Rust syntax is curly braced like C++ and Java. This means every block of code is enclosed in curly braces “{ … }”. In my opinion this is not an advantage since the curly braces are best used for a data set or a dictionary collections. Second thing about syntax is the end of statement is terminated by “;” like in C. Rust use abbreviations not full English keywords.  This again is a disadvantage since you have to learn new keywords and meanings for writing Rust code.

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

In this example fn = function. println!()  is a “macro” not a method or function. “!” is used to signal that is a macro we call.

Every Rust program has at least one function: the main() function. This is the first executed when program starts.

Comments

Single line Comments start with two slashes //. Block comments is enclosed using two symbols: /*… */ exactly like in C++. We know this cause issues with nested comments that I think are not resolved in Rust. There are other conventions for example “/** … */ ” is recognized as documentation.

Variables

A variable is actually a name that can start with “_” or any alphabetic character but not a special character or a number. So this is almost like any other language. Except … in Rust all variables are actually not mutable so they are constants. To make a variable mutable we use keyword “mut”.

The variable declaration in Rust is done using keyword “let” and symbol “=”. Once a variable is initialized we call this “binding”. So a variable is bound to it’s value. A variable that is not bound can’t be used. To assign a type to a variable we use symbol “:” after the variable name.

Examples:

let x: i32 = 5;
let mut y: i32 = 10;

y = 15; //now we can use assignment
x = 15; //this will not work since x is immutable

Assignment

The assignment operator “=” works only with variables that are mutable and bound to initial value. In this case we can change the value of the variable. So you see there is a difference between “let” that initialize a variable and “=” that assign a value to a variable.

Variable Scope

In English the “scope” has two meanings:

  • “the extent of the area or subject matter that something deals with or to which it is relevant”
  • “the opportunity or possibility to do or deal with something.”

This may confuse some for the meaning of “Variable Scope”. We use the first meaning: The “scope” of a variable is the area or the block of code where the variable is visible and relevant.

We have this concept of “code block” that is a compact region of code separated by squiggly brackets: {….}. In other languages we start a block using “begin … end” keywords or indentation. In Rust the indentation is not relevant. What is relevant is that we can have nested blocks of code.

Each block of code define a local scope. All variables defined in outer scope are visible in the inner scope. We can hide a variable by rebinding the name to a new value using “let”. In this case the external variable will be hidden or “shadowed”. Variable shadowing can change the type and the value of the variable name.

Examples:

{ //outer scope
  let x: i32 = 5;
  { // inner scope
     let x: i64 = 65536; 
     println!(x); // this is now 65536
  }
  println!(x); // this remain 5
}

Function

The function is a common way to create reusable code and split a larger problem into smaller parts. A function is a named block of code that can receive parameters and can return a result. We define a function using “fn” keyword and name of the function. The result type can be declared using symbol “->”. In other languages we may use “=>”. In the function body we can use “return” statement to return the function result. In Rust result is optional so the symbol “->” and “return” keyword are optional.

Example:

fn main() {
    print_sum(5, 6);
}

fn print_sum(x: i32, y: i32) {
    println!("sum is: {}", x + y);
}

Primitive Types

In Rust we have two kind of data: primitive and composite data type. The composite type is a group of some sort based on primitive types. We have like in any other languages numbers and characters. Julia is a 64 bit computer language and is using Unicode characters.

Number types: { i8, i16, i32, i64, u8, u16, u32, u64, isize, usize, f32, f64 }

In Rust “i” numbers are integers. “u” numbers are unsigned. “f” numbers are floating point numbers. Variable size numbers: isize and usize depends on machine architecture 32 or 64 bits.

Boolean types: true and false

In Rust the Boolean values are true and false like in Python. The operators for Boolean expressions are like in C: { &&, ||,  !}

Character types: Unicode

In Rust characters are Unicode not ASCII and occupy 4 bytes not 1 byte. A character is enclosed in single quotes like in C.

Composite Types

The composite types are: { Strings, Arrays, Vectors, Tuples, Structs, Enums }. Some composite types have all members of the same type and function more like a data set. The structs and enums can have members of different data types and are more known as “records” in other languages.

Strings

The strings are enclosed in double quotes like this: “abc”. Strings are encoded UTF8 and not ASCII. That means one character occupy a variable number of bytes. Strings are not null terminated like in C and can contains several null characters inside.

Strings are of two kind: immutable &str references and mutable strings. The default notation “abc” will create an imitable “str”. We can convert a non mutable str into a mutable “String” using function to_string(). We can convert a “String” into a &str using operator “&” in front of the String.

Strings are mutable. So we can concatenate a &str to it using “+” operator. Conversion of a String to &str is cheep but conversion of &str to String allocate memory so is not cheep. Therefore avoid it if possible.

Example:

let hello = "Hello ".to_string(); // create a String
let world = "world!";  // create a &str
let hello_world = hello + world; // CONCATENATE 

Large strings

Strings can be created on multiple lines using continuation character “\”. If we do not then the new line and the indentation is included in the string and we may wish to avoid it. “\” will eliminate both end of lines and the spaces and will reduce them to one single space.

let welcome = "Welcome \
               to rust \
               the fastest language \
               in the world.";
println!(welcome); // will print: "Welcome to rust the fastest language in the world"

New way

A new way to define strings in Rust is this notation from this example:

let s = String::from("initial content");

Array

Collections of fixed size elements enclosed in square brackets [1,2,3] and separated by comma. First element can be found like in C using zero based subscript a[0] . The notation [T;N] is used to initialize an array with T using N members.

Example:

let a = [1,2,3]; // All elements are integers i64
let b = [0;10];  // This create b=[0,0,0,0,0,0,0,0,0,0]

Vector

A vector is a smart array that can grow at run-time. It is implemented as a “generic” in the standard library. Vector is not as fast as Array but has more functionality. In other languages like Python this is known as a “List” and is implemented in the language core.

Example:

let v = vec![1,2,3,4]; // vector of integers v:Vec<i32>
let x = v[0]; // first element
let y = v[3]; // last element

Note: Subscript index starts from 0 and is of type “usize”. Using other type will generate an error.

Tuple

Tuple literal is enclosed in round brackets () like in Python. (1, 2, 3). A tuple can have characters and numbers mingled like: (1,’a’,”bcd”). The tuples are immutable collections. Interesting about tuples is the way a member is found using dot notation instead of subscript with brackets.

Example:

//define a tuple of 3 char elements
let tuple = ('a','b','c');

//first method to extract elements
let a = tuple.0;  //extract first element
let b = tuple.1;  //extract second element
let c = tuple.2;  //extract third element

// second method: something exotic found only in Rust
// we decompose a tuple and create 3 new variables x,y,z
let (x, y, z) = tuple;

Struct

This is a composite data where every element has a name and a type. So it is like a record into a database table. Unlike tuple every element can be accessed by it’s name using dot notation not indexes. In the next example we show how to declare a complex type Point. The user defined types in Rust start with Capital letters by convention.

struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let origin = Point { x: 0, y: 0 }; // origin: Point
    println!("The origin is at ({}, {})", origin.x, origin.y);
}

Methods

Methods are like functions defined with “fn” keyword. Methods are bound to a struct type and can be call using dot notation. That is the difference. A method receive automatically a parameter that is called “self” that is a reference to the variable that call the method.

We use “impl” keyword to create methods related to user define struct. This is the way to create object like structures in Rust. So rust has a kind of object oriented behavior.

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle { width: 30, height: 50 };

    println!(
        "The area of the rectangle is {} square pixels.",
        rect1.area()
    );
}

Enumeration

The enumeration is created using enum keyword. It is very similar to a struct except that struct is data oriented while enum is based on symbols. The enumeration is created to somehow extend the language with new keywords. The enumeration elements can be used with symbol :: or can be imported in the current name space using the “use” keyword.

Example:

enum Color {
  Green,
  Yellow,
  Blue,
}

// we can import the elements of Color in current namespace
use Color::{Green, Yellow};

// now we can use the new elements like keywords
fn main() {
   let my_favorite_color = Green;
   let your_favorite_color = Yellow;

   // the Blue color is not available without Color:: prefix
   if your_favorite_color == Color::Blue {
      println!("Your are a sad person!");
   } 
}

There is more about enumerations. Every element can contain data asociated with (). Data can be anything: string, number, anonymous structure even a data type. The enumeration is used to create an esential data type for Rust: Nulable data type: Option<T>, that is a generic data type. For more info pleas look at the manual.

The module

I can’t conclude this introduction without mention an important part of Rust. The module is created using “mod” keyword. This is a way to componentize a larger program into smaller parts. A Rust source file has extension *.rs This file can contain one or more modules. Each module can also contain other modules inside. We use “::” to find members of modules using a “path”.

We can “use” the modules mark as “pub” in the main program. We use “pub” keyword to indicate that a module member can be used outside of module scope. We can use “*” to import all “pub” members defined into a module or we can specify each member to be imported.

After import we can use the members as if is part of current scope. This is the key to organize a large program and avoid name collisions. We call this in architecture: “componentization” or “separation of concerns” when we “divide and conquer” a large problem into small parts.

Example of modules (in a single file).

pub mod first_module {
  pub mod sub_module {
     pub fn test() {
       println!("You found me again");
     }
  } //end sub_module

  pub fn test() {
     println!("You found me");   
  }
} // end first_module

// we import in current namespace all elements of first_module
use first_module::*;

fn main() {
  test(); // You found me
  sub_module::test(); // You found me again
}

Punctuation

SymbolDescription
+addition
subtraction
/division
*multiplication
%modulo (reminder of division)
=assign value (the variable must be mutable otherwise will generate compilation error)
:define type
;statement separator, define array element type
,enumeration
.dot operator
\string continuation or escape code
->define result type
>relation operator: greater then
<relation operator: less then
==relation operator: is equal
!=relation operator: not equal (disjunctive)
>=relation operator: greater then or equal to
<=relation operator: less then or equal to
&&logical operator “and” sometimes called Boolean operator AND.
||logical operator “or” sometimes called Boolean operator OR.
!(bang) is logical operator  “not” when is prefix to something. Can be use as suffix to signal a macro.
//single line comment (can be used at start of line or end of line after “;”
/*…*/block comment. Other variations: /** …. */ or /*! …. */ each with slightly different purpose.
&borrow or bit-wise operator
*multiplication or raw pointer
<<left bit shift
>>right bit shift
..range of numbers or update operator for struct literals
->define result type of a function
@pattern bindings
|bitwise “or”  and pattern alternative
^bitwise exclusive “or”
_ignored
?error propagation symbol
=>used in match patterns
inclusive range pattern
[…]array literal, vector literal
{…}block of code, struct literal
(…)tuple, expression, parameters
<…>used to declare generic data types
#meta (compiler directive)
$macro substitution
::path related syntax
m!macro notation for example: print!(). It is also used as logical operator. Do not confuse the two.
‘…’character literal enclosure
“…”string literal

Update and assign

Most math operators and bitwise operators have an “update” version.

SymbolDescription
+=addition
-=subtraction
*=multiplication
/=division

Keywords

In Rust when you define a new item you must avoid using the reserved keywords. These are used only to describe language elements and not as names for variables, functions or types.  You do not have to memorize them just rad them once to understand better what you will need to learn in the future.

KeywordDescription
as
box
break exit from a loop
const
continue used in a loop to restart from beginning
crate
else part of decision statement
enum used to create an enumeration type
extern
false Boolean value
fn used to define a function or method
for a kind of loop
if decision statement
impl used to create methods bound to a struct type
in part of for loop to specify a range (1..n) or a collection of items
letdeclare immutable variable
loopcreate a loop
match define a match block
mod declare a new module
move
mutdeclare a variable mutable
pub declare a member of module public
ref
returncreate an exit point from a function
selfcurrent value. used to declare methods
Self
static
structcreate a composite type
super
trait
trueBoolean value
type
unsafe used to declare a block of code unsafe
use use a module in another module
where
while a kind of loop

See also: Rust Manual.