Skip to main content

Learn Rust

8 min read

Requirements

  • Rust: The main source code repository for Rust. It contains the compiler, standard library, and documentation.
  • Cargo: Distributed by default with Rust. If you’ve got rustc installed locally, you probably also have cargo installed locally.

Installation

git clone https://github.com/rjoydip/learn-rust.git
cd learn-rust
npm start

Hello Rust

println!("Hello, rust!");

Comments

// single line comment
/*
    Multiline comments
*/

Variables

// print a variable
let x = 5;
println!("{}", x);

// variable mutant
let mut x = 5;
x = 9;
println!("{}", x);

// print multiple variable
let (x, y): (i32, i16) = (15, 16);
println!("{}", x);
println!("{}", y);
println!("Multiple value print {}, {}", x, y);

// print variable
let x = 18;
println!("Binary: {:b}, Hexadecimal: {:x}, Octal: {:o}", x, x, x);

// display decimal value
println!("Display output 1: {:.2}", 1.234);

Primitive Data Types

  • bool: The boolean type.
  • char: A character type.
  • i8: The 8-bit signed integer type.
  • i16: The 16-bit signed integer type.
  • i32: The 32-bit signed integer type.
  • i64: The 64-bit signed integer type.
  • isize: The pointer-sized signed integer type.
  • u8: The 8-bit unsigned integer type.
  • u16: The 16-bit unsigned integer type.
  • u32: The 32-bit unsigned integer type.
  • u64: The 64-bit unsigned integer type.
  • usize: The pointer-sized unsigned integer type.
  • f32: The 32-bit floating point type.
  • f64: The 64-bit floating point type.
  • array: A fixed-size array, denoted [T; N], for the element type, T, and the no-negative compile-time constant size, N.
  • slice: A dynamically-sized view into a contiguous sequence, [T].
  • str: String slices.
  • tuple: A finite heterogeneous sequence, (T, U, ..).

Conditional Flow

If else

let x = 10;
if x == 1 {
    println!("Inside if");
} else if x > 5 {
    println!("Inside else if");
} else {
    println!("Inside else");
}

If else with let

let condition = false;
let number = if condition {
    5
} else {
    6
};
println!("The value of number is: {}", number);

Casting

let f = 24.4321_f32;
let i = f as u8;
println!("{}, {}", f, i);

Array

An array is fixed-size, collection of same-type elements.

let array: [i32; 5] = [0, 1, 2, 3, 4];
println!("The first element of the array is: {}", array[0]);

// slice
let slice = &array[0..3];

Tuple

Tuples are finite, heterogeneous, sequences. Let’s unpack that quickly. First of all they are finite; this is fairly self-explanatory. They have a size, a fixed number of elements. They are heterogeneous. They can contain multiple different types. This is in contrast to an array, which can only contain elements of the same type. And lastly they are sequences, meaning they have an order, and most importantly they can be accessed by index (although in a different manner than arrays).

let tuple = ("hello", 42, "world", [3,6,9]);
println!("First element is {}", tuple.0);
println!("Second element is {}", tuple.1);
println!("Third element is {}", tuple.2);
let mut counter = 0;
for x in &tuple.3 {
    println!("Element {} of the fourth element is {}", counter, x);
    counter += 1;
}

Loop

Infinite Loop

loop {
    println!("Infinite");
}

While Loop

let mut count = 5;
while count > 1 {
    count -= 1;
    println!("Count is {}", count);
}

For Loop

let arr = [10, 20, 30, 40, 50];
for ele in arr.iter() {
    println!("Element of array is {}", ele);
}

Reverse Loop

for number in (1..4).rev() {
    println!("{}", number);
}

Enumerate

for (i, x) in (1..10).enumerate() {
    println!("Index is: {} and value is: {}", i, x);
}

Break Statement

let mut x = 0;

// break with loop
loop {
    println!("Value of i is {}", x);
    if x >= 10 {
        break;
    }
    x += 1;
}

// break with while
while x < 100 {
    println!("Value of i is {}", x);
    if x >= 10 {
        break;
    }
    x += 1;
}

// break with for
for ele in 1..100 {
    println!("Value of i is {}", x);
    if x >= 10 {
        break;
    }
    x += 1;
}

Match

Match with number

let x = 4;
match x {
    0 => { println!("Value is 0"); }
    1 => { println!("Value is 1"); }
    2 => { println!("Value is 2"); }
    3 => { println!("Value is 3"); }
    4 => { println!("Value is 4"); }
    5 => { println!("Value is 5"); }
    _ => { println!("Any value match"); }
}

Match with string

let x = '6';
match x {
    '0' => { println!("Value is 0"); }
    '1' => { println!("Value is 1"); }
    '2' => { println!("Value is 2"); }
    '3' => { println!("Value is 3"); }
    '4' => { println!("Value is 4"); }
    '5' => { println!("Value is 5"); }
    _ => { println!("Any value match"); }
}

Match with OR

let x = 3;
match x {
    3 | 2 => { println!("Value is {}", x); }
    3 => { println!("Value is three"); }
    _ => { println!("Any value match"); }
}

Match with condition

let x = 1;
let y = 3;
match (x, y) {
    (x, y) if x > y => { println!("Decreasing"); }
    (x, y) if x < y => { println!("Increasing"); }
    (x, y) => { println!("Equal"); }
}

Match with range

let a = 8;
match a {
    4..=7 => println!("In between 4...7: {}", a),
    _ => println!("Not in between 4...7"),
}

Pair/Tuple Match

let pair = (1, 0);
match pair {
    (0, y) => println!("Y: {}", y),
    (x, 0) => println!("X: {}", x),
    _ => println!("Not match"),
}

Pattern Match

let p = 1;
match p {
    1..=10 => println!("Between 1 to 10: {}", p),
    11..=20 => println!("Between 11 to 20: {}", p),
    _ => println!("Other"),
}

Implement

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

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

    fn new(width: u32, height: u32) -> Object {
        Object {
            width: width,
            height: height,
        }
    }

    fn show(&self) {
        println!("{} x {} with area: {}", self.width, self.height, self.area());
    }
}

let o = Object {
    width: 3,
    height: 2,
};
let obj = Object::new(o.width, o.height);
obj.show();

Enum

enum Shape {
    Rectangle { height: u32, width: u32 },
    Square(u32),
    Circle(f64),
}

impl Shape {
    fn area(&self) -> f64 {
        match self {
            Shape::Rectangle { height, width } => (height * width) as f64,
            Shape::Square(ref s) => (s * s) as f64,
            Shape::Circle(ref r) => 3.14 * (r * r),
        }
    }
}

let r = Shape::Rectangle { height: 10, width: 10 };
let s = Shape::Square(10);
let c = Shape::Circle(4.5);

println!("Area of rectangle: {}", r.area());
println!("Area of square: {}", s.area());
println!("Area of circle: {}", c.area());
// Struct
struct Object {
    width: u32,
    height: u32,
}

// Methods
impl Object {
    fn area(&self) -> u32 {
        return self.height * self.width;
    }

    fn show(&self) {
        println!("{}x{} with area: {}", self.width, self.height, self.area());
    }
}

// Related Function
impl Object {
    fn new(width: u32, height: u32) -> Object {
        Object {
            width: width,
            height: height,
        }
    }
}

let o = Object {
    width: 3,
    height: 2
};
let obj = Object::new(o.width, o.height);
obj.show();

Vector enum

#[derive(Debug)]
enum Example {
    Float(f64),
    Int(i32),
    Test(String),
}

let r = vec![
    Example::Int(111),
    Example::Float(11.1),
    Example::Test(String::from("Hello world")),
];
println!("{:?}", r);

Char match

let s = Some('c');

// Example 1
match s {
    Some(i) => println!("Value inside match: {}", i),
    None => {},
}

// Example 2
if let Some(i) = s {
    println!("Value inside if: {}", i);
} else {
    println!("Inside else");
}

Trait

trait Shape {
    fn area(&self) -> u32;
}

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

struct Circle {
    radius: f64,
}

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

impl Shape for Circle {
    fn area(&self) -> u32 {
        (3.14 * self.radius * self.radius) as u32
    }
}

let c = Circle { radius: 100.2 };
let r = Rectangle { height: 2, width: 3 };
println!("Circle area: {}, Rectangle area: {}", c.area(), r.area());

Ownership and Borrowing

Example 1

let x = 1; // x is owner of one and it stores in stack
let s = String::from("hi");

// x and s move reference ownership to y
// NOTE: one reference can own one piece of data
// println!("{}", s);

Example 2

// example is called ownership moving
fn take(x: Vec<i32>) {
    println!("We took v: {} and {}", x[0], x[1]);
}

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

take(v); // transferring v ownership from main -> take

println!("{:?}", v); // This will cause an error

Example 3

// example is called ownership copy
fn copy(a: i32, b: i32) {
    println!("Value of a and b is (inside copy fn): {}, {}", a, b);
}

let a = 1;
let b = 2;

// Unallocated from main because a,b just copied to another fn
println!("Value of a and b is (inside main fn): {}, {}", a, b);

Example 4

// more complicated
// borrowing (rather reference of v)
fn borrow1(v: &Vec<i32>) {
    println!("Value of v in borrow1 fn is: {}", v[0]);
}

fn borrow2(v: &Vec<i32>) {
    println!("Value of v in borrow2 fn is: {}", v[3]);
}

fn borrow3(v: &Vec<i32>) {
    println!("Value of v in borrow3 fn is: {}", v[4]);
}

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

borrow1(&v);
borrow2(&v);
borrow3(&v);

println!("Value of v (inside main fn): {:?}", v);

Example 5

// complex ownership and borrowing
fn count(v: &Vec<i32>, val: i32) -> usize {
    v.iter().filter(|&&x| x == val).count()
}

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

for &i in &v {
    let c = count(&v, i); // x and v are borrowed
    println!("{} is repeated {} times", i, c);
}

Concepts

  1. What is Ownership?

Examples

All example area here. rjoydip-zz/learn-rust

Tags

ProgrammingRustLearning To CodeBasics