post cover

SIMPLE SYNTAX REFERENCE AND DOCUMENTATION FOR RUST

May 07, 2019

First I heard about Rust in the summer 2018. Since then I didn’t have much time to run into learning new stuff. I’m glad this is the right time to look at the Rust. This is the reference for Rust language and I’ll try my best to write parts of the language as clear as possible so everyone could understand.

Disclosure: This reference doesn’t cover: lifetimes, threads, generics and other advanced features of the language. This is just a simple reference of syntax of the Rust language.

List of content:

  1. Types
  2. Conditions

  3. Loops

  4. Variables

  5. Function definition

  6. Enums
  7. Struct

  8. Ownership and Borrowing

  9. Feelings and conclusion

1. Types

// unsigned integers
let unsigned_8: u8 = 1; // // from 0 to 255 -> FORMULA: 0 to (2^n)-1
let unsigned_16: u16 = 1; // from 0 to 65536
let unsigned_32: u32 = 1; // from 0 to (2^32)-1
let unsigned_64: u64 = 1; // from 0 to (2^64)-1
let unsigned_128: u128 = 1; // from 0 to (2^128)-1

// signed integers
let signed_8: i8 = 1; // from -128 to 127 -> FORMULA: -(2^(n-1)) to (2^(n-1))-1
let signed_16: i16 = 1; // from -32 768 to 32 767
let signed_32: i32 = 1; // from -(2^31) to (2^31-1)
let signed_64: i64 = 1; // from -(2^63) to (2^63-1)
let signed_128: i128 = 1; // from -(2^127) to (2^127-1)

// lets get real here
let floated_32bit: f32 = 2.0;
let floated_64bit: f64 = 2.0;

// bools
let b: bool = true;

// static string - compile time strings
let static_string: &str = "hello world";

// dynamic strings
let std_string: String = String::new("Hello world");

// vectors
let num_vec: Vec<i32> = vec![1, 2, 3]; // vec! is macro for creating Vec

2. Conditions

2.1 Simple condition

if age > 17 {
   println!("Okay, now you can drink alcohol");
} else {
   println!("Sorry, alcohol is for people older than 18...");
}

2.2 Ternary condition

let condition = true;
let variable = if condition {
   // 1 is assigned to variable if condition is true
   1
} else {
   // 0 is assigned to variable if condition is false
   0
}

3. Loops

3.1 While

let mut number = 0;
while number < 10 {
   println!("{}!", number);
   number = number + 1;
}

3.2 Forever

loop {
    println!("forever!");
}

3.3 Array iterating

let a = [10, 20, 30, 40, 50];
for element in a.iter() {
    println!("the value is: {}", element);
}

4. Variables

4.1 Declaration

let x = 5; // type - immutable integer variable
let mut y: i8; // mutable integer variable - signed mutable variable

4.2 Change primitive value

// all variables without "mut" are immutable by default
// if variables are declared with "mut" identifier, you can mutate
// these variables
let mut year = 2018;
year = 2019; // OK

// code below can't be mutated
let immutable = 5;
immutable = 10; // ERROR !!!!

5. Function definition

5.1 Static (classic) function

fn sum_two_numbers(a: i32, b: i32) -> i32 {
   return a + b;
}

// OR
// valid returning without return statement
// see there is no semicolon ;

fn sum_two_numbers(a: i32, b: i32) -> i32 {
   a + b
}

5.2 Lambda function

fn main() {
   let increment = 5;
   let increment_by = |x: i32| x + increment;

   println!("increment 5 + 5 is {}", increment_by(5));
   // OUTPUT: increment 5 + 5 is 10
}

6. Enums

enum Color {
   Transparent,
   RGB(u8, u8, u8),
   RGBA(u8, u8, u8, f32),
   Brown,
   Blue,
}

fn main() {
   let kinda_red = Color::RGBA(255, 0, 0, 0.5);

   match kinda_red {
      Color::Transparent => println!("This color is transparent"),
      Color::RGB(r, g, b) => println!("RGB color with {} red, {} green, {} blue", r, g, b),
      Color::RGBA(r, g, b, a) => println!("RGBA colors {},{},{} transparency = {}", r, g, b, a),
      _ => println!("Other cases: Brown or Blue"), // in other languages: Default
   };
   // We must cover all enum cases, because compiler gonna have problem with that.
}

7. Struct

7.1 Struct definition and usage

struct Person {
   first_name: String,
   last_name: String,
   age: u32,
   address: String,
}

fn main() {
   let me = Person{
      first_name: "Martin".to_string(),
      last_name: "Schnurer".to_string(),
      age: 23,
      address: "Some address".to_string(),
   };

   println!(
      "I am {} {}, I am {} years old and I live at {}",
      me.first_name, 
      me.last_name,
      me.age,
      me.address
   );
   //OUTPUT: I am Martin Schnurer, I am 23 years old and I live at Some address
}

7.2 Methods

struct Person {
   first_name: String,
   last_name: String,
   age: u32,
   address: String,
}

impl Person {
   fn find_girlfriend(self: &Self, with_name: String) {
      println!("Hi I am {} {} and I am looking for a girl with name {}", 
               self.first_name,self.last_name, with_name);
   }
}

fn main() {
   let me = Person{
      first_name: "Martin".to_string(),
      last_name: "Schnurer".to_string(),
      age: 23,
      address: "Some address".to_string(),
   };

   me.find_girlfriend("Monica".to_string());
   // OUTPUT: Hi I am Martin Schnurer and I am looking for a girl with name Monica
}

8. Ownership and Borrowing

8.1 Ownership

8.1.1 Move ownership

struct Person{ age: u16 }

fn write_age(person: Person) {
   println!("This person is {} years old", person.age);
}

fn is_adult(person: Person) -> bool {
   person.age > 17
}

fn main() {
   let john = Person{age: 23};
   write_age(john); // OK: This person is 23 years old  (ownership passed to the write_age function)
   // write_age is out of scope and john variable will be deleted there

   let john_is_adult = is_adult(john); // COMPILATION FAIL, variable john was moved to the write_age fn
   // we can't use variable.
} 

After the ownership (variable) is moved into another scope, you can’t use this variable in the scope. See above how we tried to use john variable passing into is_adult function - this will not compile, because we moved ownership of john to the write_age function scope and after write_age is done (out of scope), the content of the john variable is deleted.

8.1.2 Returning ownership back to the scope

struct Person{ age: u16 }

fn write_age(person: Person) -> Person {
   println!("This person is {} years old", person.age);
   return person // Now we move back the ownership from the function
}

fn is_adult(person: Person) -> bool {
   person.age > 17
}

fn main() {
   let john = Person{age: 23};
   let john = write_age(john); // OK: This person is 23 years old  (ownership passed to the write_age function)
   // ownership is moved back from the write_age to the john, we can use john again 

   let john_is_adult = is_adult(john); // OK
   println!("john is adult = {}", john_is_adult); // OK
} 

8.2 Borrowing

8.2.1 Immutable borrow

Passing and returning ownerships is not always best decision to do - code overhead,

struct Person{ age: u16 }

fn write_age(person: &Person) { // Not returning ownership anymore
   println!("This person is {} years old", person.age);
}

fn is_adult(person: &Person) -> bool {
   person.age > 17
}

fn main() {
   let john = Person{age: 23};
   write_age(&john); // OK: passing reference
   
   let john_is_adult = is_adult(&john); // OK, passing reference
   println!("john is adult = {}", john_is_adult);
}

8.2.2 Mutable borrow

When we want to pass a reference to another scope and we want also alter this passed variable - then we need to pass this with keyword &mut - mutable reference.

struct Person{ age: u16 }

fn increment_age(person: &mut Person) {
   person.age += 1;
}

fn main() {
   let mut john = Person{age: 23}; // we need to mark john as mutable as the function will mutate our passed variable
   increment_age(&mut john); // instead of &john we pass &mut john
}

8.3 Borrowing rules

  1. In the given scope, you can borrow as much immutable borrows as you want. (To observe source value)
  2. If you create one mutable borrow, you can’t create immutable borrows in that scope. (Only one variable can change source value)

Feelings and conclusion

The one reason rust convinced me to look deeper at it was the claim that we can write rust in completely safe manner without garbage collection - completely dependant on the out of a scope garbage collection. I haven’t written anything serious in rust - but I had the feeling that the compiler was a real dictator here didn’t allow me easily do whatever I want - but when it does, I had a good feeling about compiled code and large amount of executions of the code was bug free and has run as expected - without panicking.

On the other side, I had serious problem to get used to traits and lifetimes. I didn’t referenced lifetimes above, because I assume them to be more difficult to learn and as I wanted to let this reference as simple as possible - I didn’t describing them here (but it doesn’t mean I won’t describe them in another post later).

In C language, you probably didn’t have a problem to easily implement linked list. Well, good luck with trying that in Rust. Linked list is pretty all about pointers and pointer checking. Rust is pretty good at controlling (Borrow checker) what exactly are you doing with pointers and memory - and if you do something unsafe, it won’t compile.

I found this article to deeply describe linked list implementation in Rust. Have fun.

If you want to read full documentation and rust reference - go here.


Join the Newsletter

Name
E-Mail