Sage-Code Laboratory
index<--

Rust Types

Rust is a strongly typed programming language. That means after a variable is declared its data type remain the same during program execution. This restriction enable compiler to optimize the executable code and improve application efficiency. It is also useful to avoid runtime errors and improve developer experience.

Primitive Types

In Rust we have 3 primitive types. Signed, unsigned and floating precision numbers. Each type specify the number of bits, so it is much more easy to remember the type notation than in C language.

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 data types: 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 and Ruby. The operators for Boolean expressions are like: {&&,||,!}.

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: {'0','1'....'a','b',.... 'π','α','β',... 'ω'}

Variables

In Rust you must declare data type to create a variable using symbol ":"> and keyword let Variables are immutable in Rust unless you specify otherwise. Global variables can be defined using keyword static or const.

Example:

/* demo to declare a static variable */
static TEST: i32 = 10; // global variable

fn main() {
  let x = TEST + 1;        // use TEST in expression
  println!("TEST = {}",x); // printing the result
}

Notes:

Expressions

Rust is using infix notation to create expressions. An expression is created using variables, constants and literals separated by operators. In previous example we use expression: TEST+1 to calculate initial value for x. The result of expressions can be captured into a new variable using assign symbol "=".

You can create larger expressions from small expressions using parenthesis and operators. The order of operations is important. Operators { *, / } have higher precedence than { +, – }.

Example:

(x + y) / 2 < (x + y)

Notes:

Numeric Operators

Symbol Description
/ division
* multiplication
% modulo (reminder of division)
+ addition
subtraction

Update Operators

Most math operators and bitwise operators have an "update" version. The variable you use in left side of these operators must be mutable otherwise will generate compilation error:

Symbol Description
= assign value
+= addition
-= subtraction
*= multiplication
/= division

Relation Operators

Symbol Description
> 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

Logic Operators

Symbol Description
! logical operator "not" when is used as prefix
&& logical operator "and" sometimes called Boolean operator AND.
|| logical operator "or" sometimes called Boolean operator OR.

Bitwise Operators

Symbol Description
~ but-wise operator "not"
& bit-wise operator "and"
| bitwise "or" and pattern alternative
^ bitwise exclusive "or"
<< left shift
>> right shift

Composite Types

The composite types are: { Arrays, Vectors, Slices, Strings, 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.

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.

Slice

A slice is a view for a range of addresses in an array. You can create slice using "borrow" operator: "&" and range operator ".."

Example:

/* demo for array slicing */
fn main() {
    let ints = [1, 2, 3, 4, 5];
    let slice1 = &ints[0..2]; // first 2 elements
    let slice2 = &ints[1..];  // open range!

    /* debug printing */
    println!("ints {:?}", ints);
    println!("slice1 {:?}", slice1);
    println!("slice2 {:?}", slice2);
}

Notes:

Homework: run this example and investigate the result: slicing

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 and mutable. The default notation "abc" will create an immutable string. You can convert a non mutable string into a mutable string using function to_string(). You can convert a mutable string into immutable string using operator "&" in front of the string variable.

Example:

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

Warning: Previous example and some of the next examples are code fragments. To run such code you need to wrap it in a function main(). Rust do not run statements outside functions. Also declarations using let are not supported outside functions. That will be a global variable that must be "static" otherwise is not supported.

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"

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

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

Notes:

A literal for example "hello" is not of type String. It is of type "&str" (that is a string slice). Therefore to make a proper string out of a literal you can use above notation: String::from("") that is better than using "".to_string() method.

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

// 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 its 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);
}

You can open this example live and run it: struct

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:

#[derive(PartialEq)]
enum Color {
  Green,
  Yellow,
  Blue,
}

//use the new elements like symbols
fn main() {
  // unused variables have prefix "_"
  let _unused_green  = Color::Green; 
  let _unused_yellow = Color::Yellow;

  // import the elements of Color
  use Color::{Blue};

  //Blue color is not available without prefix
  let your_favorite_color = Blue;

  if your_favorite_color == Blue {
    println!("Your are a sad person!");
  } 
}

This example is available on-line here: enumeration

Note:

Homework: Try this example and remove first line: #[derive(PartialEq)]


Read next: Decision