Rustkid

Rust for Javascript and Golang Developers

1. Introduction

2. Hello World

hello.rs
rust
        fn main() {
    let k = "World!";
    println!("Hello, {}", k);
    println!("Hello, {k}");
}
/* Output
Hello, World!
Hello, World!
*/
        
    
hello.js
javascript
        let k = "World!";
console.log("Hello", k);
console.log(`Hello ${k}`);

/* Output
Hello World!
Hello World!
*/
        
    
hello.go
golang
        package main

import (
	"fmt"
)

func main() {
	k := "World!"
	println("Hello", k)
	fmt.Printf("Hello %s\n", k)
}

/* Output
Hello World!
Hello World!
*/

        
    

println!

If you want to know, how does the converted function look like? expand this line. hello_expand.rs
rust
        #![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
    let k = "World!";
    {
        ::std::io::_print(::core::fmt::Arguments::new_v1(&["Hello, ", "\n"],
                &[::core::fmt::ArgumentV1::new_display(&k)]));
    };
    {
        ::std::io::_print(::core::fmt::Arguments::new_v1(&["Hello, ", "\n"],
                &[::core::fmt::ArgumentV1::new_display(&k)]));
    };
}

/*

# To see above code, you need to install rust nightly

rustup toolchain install nightly
rustup default nightly

# In Makefile
expand:
    cargo rustc --profile=check -- -Zunpretty=expanded

# Later in terminal
make expand

# then you should see the above rust code.

*/
        
    

3. Simple logging with dbg!

dbg.rs
rust
        fn main() {
    let k = "Hello World!";
    dbg!(k);
    dbg!(k);
}
/* Output
[src/main.rs:3] k = "Hello World!"
[src/main.rs:4] k = "Hello World!"
*/
        
    
dbg.js
javascript
        let k = "Hello World";
debugger;
console.log(k);

// debugger sets the breakpoint, 
// variable values can be seen live in code editors
// as js is a dynamic language!
        
    
dbg.go
golang
        package main

import (
	"log"
	"os"
)

var logger = log.New(os.Stdout, "app: ", log.Ldate+log.Ltime+log.Lshortfile)

func main() {
	k := "Helllo World"
	logger.Println(k)
	logger.Println(k)
}

/* Output
app: 2022/05/11 00:28:19 dbg.go:12: Helllo World
app: 2022/05/11 00:28:19 dbg.go:13: Helllo World
*/

        
    

4. Memory

To understand rust we must understand what is memory? and what is memory location, what is a pointer? and what is reference?

4.1. Memory Regions

5. Data types based on how assignment operator (=) works

assignment.rs
rust
        fn main() {
    // copy-type
    let a: [i32; 5] = [1, 2, 3, 4, 5];
    let b: [i32; 5] = a; // Implicitly copied data from a to b

    let c: [i32; 5] = a.clone(); // Explicitly copied data from a to c
    dbg!(a, b, c); // a is still in scope as we only copied data

    // As the data can be copied implicitly using assingment operator
    // these variables are called copy-type

    // move-type
    let v1: Vec<i32> = vec![1, 2, 3, 4, 5];
    let v2: Vec<i32> = v1; // Implicitly moved ownership from v1 to v2 and v1 is dropped
                           // dbg!(v1); // throws error as v1 is no more.
    dbg!(v2);

    // Todo the same explicitly
    let v1: Vec<i32> = vec![1, 2, 3, 4, 5];
    let v2: Vec<i32> = v1.clone();
    drop(v1);

    // borrow-type
    let s: &'static str = "Hello World"; // Implicitly borrowed data from Static Memory
    dbg!(s);

    // Compiler embeds string literals into executable in a location called static memory
    // and gives a reference to it during compilation

    // we can't borrow data from stack memory and heap memory implicitly using assignment operator(=).
    // we have to always use reference (&) to borrow from stack and heap memories.
    // static memory lives for the entire program duraion unlike heap memory which gets cleared once owner goes out of scope.

    // to convert borrow-type to own type
    let s1: String = s.to_owned();
    let s2: &String = &s1; // explicitly borrowed data from s1
    dbg!(s2);
}
/* Output
[src/main.rs:7] a = [1,2,3,4,5]
[src/main.rs:7] b = [1,2,3,4,5]
[src/main.rs:7] c = [1,2,3,4,5]
[src/main.rs:16] v2 = [1,2,3,4,5]
[src/main.rs:25] s = "Hello World"
[src/main.rs:37] s2 = "Hello World"
*/

        
    

Golang and Javascript

5.1. Copy-type

copy.rs
rust
        fn main() {
    // primitives - always uses stack memory
    // as their size is known at compile time.
    
    // scalar types - copy types
    let signed_int: i32 = -100;
    let unsigned_int: u32 = 100;
    let float: f64 = 10.0;
    let is_playing: bool = true;
    let special: char = '🍋';
    dbg!(signed_int, unsigned_int, float, is_playing, special);
    // These scalar types are still in scope
    // even after passing to dbg! as they are copy-type.
    dbg!(signed_int, unsigned_int, float, is_playing, special);

    // composite types with scalar values
    let tupple: (i32, bool, char) = (100, true, '🍋');
    dbg!(tupple);
    dbg!(tupple.1);

    // fixed-size array
    let array:[i32; 5] = [1,2,3,4,5];
    dbg!(array);

    // Now contents of array are copied into a new variable myarray
    let myarray = array; // = operator is used as implicit copy
    // contents of array are available even after assignment as it is copy-type
    dbg!(array);

    // if you want to copy contents of array explicitily use clone method
    let iarray = myarray.clone();
    dbg!(iarray);
}
/* Output
[src/main.rs:11] signed_int = -100
[src/main.rs:11] unsigned_int = 100
[src/main.rs:11] float = 10.0
[src/main.rs:11] is_playing = true
[src/main.rs:11] special = '🍋'
[src/main.rs:14] signed_int = -100
[src/main.rs:14] unsigned_int = 100
[src/main.rs:14] float = 10.0
[src/main.rs:14] is_playing = true
[src/main.rs:14] special = '🍋'
[src/main.rs:18] tupple = (100,true,'🍋')
[src/main.rs:19] tupple.1 = true
[src/main.rs:23] array = [1,2,3,4,5]
[src/main.rs:28] array = [1,2,3,4,5]
[src/main.rs:32] iarray = [1,2,3,4,5]
*/
        
    
copy.js
javascript
        function main() {
    // Javascript has only two types of data based on assignment operator
    // 1. Copy-type
    // 2. Borrow-type i.e. reference type
    
    let signed_int = -100;
    let unsigned_int = 100;
    let float = 10.0;
    let is_playing = true;
    let special = '🍋';
    console.log(signed_int, unsigned_int, float, float, is_playing, special);

    console.log(signed_int, unsigned_int, float, float, is_playing, special);

    let tuple = [100, true, '🍋'];
    console.log(tuple);
    
    // javascript has only dynamic arrays and they are borrow-type
    let array = [1,2,3,4,5];
    console.log(array);
    let myarray = array; // Assignment operator borrows data implicitly. 
    
    // change in myarray will effect array as well
    myarray[0] = 222;
    console.log(array);
    console.log(myarray);

    // if you want to copy contents of array use spread operator
    let iarray = [...myarray];
    iarray[0] = 333;
    console.log(iarray);
    console.log(myarray);
}

main()

/* Output
-100 100 10 10 true 🍋
-100 100 10 10 true 🍋
[ 100, true, '🍋' ]
[ 1, 2, 3, 4, 5 ]
[ 222, 2, 3, 4, 5 ]
[ 222, 2, 3, 4, 5 ]
[ 333, 2, 3, 4, 5 ]
[ 222, 2, 3, 4, 5 ]
*/
        
    
copy.go
golang
        package main

import (
	"log"
	"os"
)

var logger = log.New(os.Stdout, "app: ", log.Ldate+log.Ltime+log.Lshortfile)

func main() {
	var signed_int int = -100
	var unsigned_int uint = 100
	var float float32 = 10.0
	var is_playing bool = true
	var special string = "🍋"
	logger.Println(signed_int, unsigned_int, float, is_playing, special)
	logger.Println(signed_int, unsigned_int, float, is_playing, special)

	myint, mybool, mystring := 100, true, '🍋'
	logger.Println(myint, mybool, mystring)

	// fixed-size array - copy type
	var array = [5]int{1, 2, 3, 4, 5}

	// Now contents of array are copied into a new variable myarray
	var myarray = array // = operator is used as implicit copy
	// change in myarray will not effect array
	myarray[0] = 222
	logger.Println(myarray)
	logger.Println(array)

	// if you want to copy contents of array explicitily, same like implicit
	var iarray = myarray
	iarray[0] = 333
	logger.Println(iarray)
	logger.Println(myarray)

}

/* Output
app: 2022/06/02 17:02:50 copy.go:16: -100 100 10 true 🍋
app: 2022/06/02 17:02:50 copy.go:17: -100 100 10 true 🍋
app: 2022/06/02 17:02:50 copy.go:20: 100 true 127819
app: 2022/06/02 17:02:50 copy.go:29: [222 2 3 4 5]
app: 2022/06/02 17:02:50 copy.go:30: [1 2 3 4 5]
app: 2022/06/02 17:02:50 copy.go:35: [333 2 3 4 5]
app: 2022/06/02 17:02:50 copy.go:36: [222 2 3 4 5]
*/

        
    

5.2. Move-type

Vector

move.rs
rust
        fn main(){
    // dynamic array
    let vector1: Vec<i32> = vec![1,2,3]; // move-type

    // ownership moved from vector1 to vector2. And vector1 is dropped here.
    let vector2 = vector1;
    dbg!(vector2);
    
    dbg!(vector1); // throws an error
}

fn main(){
    let vector1: Vec<i32> = vec![1,2,3]; // move-type
    let vector2 = &vector1; // reference to vector1
    dbg!(vector2); // vector2 is a borrower
    dbg!(vector1); // vector1 is a owner
}

/* Output
[src/main.rs:4] vector2 = [1,2,3]
[src/main.rs:5] vector1 = [1,2,3]
*/
        
    
move.js
javascript
        function main(){
    // Javascript has only dynamic arrays

    let vector1 = [1,2,3]; //borrow-type

    let vector2 = vector1; // borrows data from vector1 to vector2 implicitly

    vector2[0] = 100; // // it effects vector1 also
    console.log(vector1);
    console.log(vector2);
}
main();
/* Output
[100, 2, 3]
[100, 2, 3]
*/
        
    
move.go
golang
        package main

import (
	"log"
	"os"
)

var logger = log.New(os.Stdout, "app: ", log.Ldate+log.Ltime+log.Lshortfile)

func main() {
	// This is slice of array as we are not specifying length of array
	// This is called dynamic-array
	// This is borrow-type data
	vector1 := []int{1, 2, 3}

	vector2 := vector1 // borrows data from vector1 to vector2 implicitly
	vector2[0] = 100   // it effects vector1 also
	logger.Println(vector1)
	logger.Println(vector2)
}

/* output
app: 2022/06/01 16:28:04 move.go:16: [100 2 3]
app: 2022/06/01 16:28:04 move.go:17: [100 2 3]
*/

        
    

String

5.3. Borrow-type

6. String vs. &str

7. dbg! Vs. println!

dbg_print.rs
rust
        fn main() {
    // type annotaion
    let k: &str = "World!";
    let kid: String = format!("Hello {k}");
    println!("{}", kid);
    println!("{}", kid);
}
/* Output
Hello World!
Hello World!
*/
        
    
dbg_dbg.rs
rust
        fn main() {
    let k: &str = "World!";
    let kid: String = format!("Hello {k}");
    dbg!(kid);
    dbg!(kid);
}
/* Output
error[E0382]: use of moved value: `kid`
 --> src/main.rs:5:10
  |
3 |     let kid = format!("Hello {k}");
  |         --- move occurs because `kid` has type `String`, which does not implement the `Copy` trait
4 |     dbg!(kid);
  |     --------- value moved here
5 |     dbg!(kid);
  |          ^^^ value used here after move
*/
        
    

Why println! is compiling? and why dbg! is not compiling?

8. Lifetime of a variable

8.1. Function scope and Block scope

fn_scope.rs
rust
        fn scope(){
    let fn_scope = "I have function scope";
    {
        // This can't be accessed outside this block
        let block_scope = "I have block scope";
        dbg!(block_scope);
        dbg!(block_scope);
        // this is dropped at the end of this block
    }
    dbg!(fn_scope); // works
    dbg!(fn_scope); // works
    //fn_scope is dropped here
}

        
    
fn_scope.js
javascript
        function scope(){
    let fn_scope = "I have function scope";
    {
        // This can't be accessed outside this block
        let block_scope = "I have block scope";
        console.log(block_scope);
        console.log(block_scope);
        // this is dropped at the end of this block
    }
    console.log(fn_scope); // works
    console.log(fn_scope); // works
    //fn_scope is dropped here
}

        
    
fn_scope.go
golang
        package main

func main() {
	fn_scope := "I have function scope"
	{
		// This can't be accessed outside this block
		block_scope := "I have block scope"
		println(block_scope)
		println(block_scope)
		// this is dropped at the end of this block
	}
	println(fn_scope) // works
	println(fn_scope) // works
	//fn_scope is dropped here
}

        
    

8.2. Variable Shadowing

shadowing.rs
rust
        fn main(){
    let hello = "Hello";
    let hello = "Hello World"; // works
    dbg!(hello);
}

/* Output
[src/main.rs:4] hello = "Hello World"
*/
        
    
shadowing.js
javascript
        function main(){
    let hello = "Hello";
    let hello = "Hello World"; // error
    console.log(hello)
}
main()

/* Output
Uncaught SyntaxError: Identifier 'hello' has already been declared
*/
        
    
shadowing.go
golang
        package main

func main() {
	hello := "Hello"
	hello := "Hello World" // error
	println(hello)
}

/* Output
snippets/shadowing.go:5:8: no new variables on left side of :=
*/

        
    

8.3. Ownership

8.4. Non-Lexical Lifetime

9. Type Annotations

type_annotations.rs
rust
        fn main(){
    let a: f64 = 20.5; // specified explicitly as f64
    let b: f32 = 20.5; // specified explicitly as f32
    let c = a + b as f64; // b casted as f64
    dbg!(c); // 41.0

    infer_types_as_f64();
    infer_types_as_i32();
    infer_types_as_i64();
    mixed_types();
    text();
    tuples();
    array();
    vectors();
}

// default type for floating-point types is f64
fn infer_types_as_f64(){
    let a = 20.5;
    let b = 20.5;
    let c = a + b;
    dbg!(c); // 41.0
}

// default type for integers is i32
fn infer_types_as_i32(){
    let a = 10;
    let b = -10;
    let c = a + b;
    dbg!(c); // 0
}

// If you specify type for one variable in the equation, all other variables in the equation will also get same type.
fn infer_types_as_i64(){
    let a = 10;
    let b: i64 = 10; // type specified as i64
    let c = a + b;
    dbg!(c); // 20
}

fn mixed_types() {
    let a = 10.9;
    let b = 10;
    let c = a as i32 + b; // here compiler asks us to specify type. As a and b are different types.
    dbg!(c); // prints 20 . Ignores 0.9
    dbg!(a); // a = 10.9
}

fn text(){
    let a: &str = "hello";
    let b: String = "world".to_string();
    let c: String = format!("{} {}", a, b);
    dbg!(c); // "hello world"
    let d: char = 'A';
    let e: char = 'B';
    let f:String = format!("{}{}", d, e);
    dbg!(f); // "AB"
}

fn tuples(){
    let t = (100, 300, "Hai"); // tupples can have different types
    let t: (i32, i32, &str) = (100, 300, "Hai"); // with annotations
    dbg!(t.1); // 300
}


fn array() {
    let a = [0,1,2,3,4]; // array can have only same type
    let a: [i32;5] = [0,1,2,3,4]; // with annotation
    let b = ['h', 'e', 'l', 'l', 'o'];
    let b: [char; 5] = ['h', 'e', 'l', 'l', 'o']; // with annotation
    let c = [3;5]; // [3, 3, 3, 3, 3]
    let c: [i32; 5] = [3; 5]; // with annotation
    let d = ["hai", "hello"];
    let d: [&str; 2] = ["hai", "hello"]; // with annotation
    dbg!(d.get(1)); // Some("hello")
    dbg!(d[1]); // "hello"
}

fn vectors(){
    let v = vec![1,2,3]; //vectors can have only same type
    let v: Vec<i32> = vec![1,2,3]; //with annotation
    dbg!(v[1]); // 2
    dbg!(v.get(1)); // Some(2)
}

        
    

10. Creating new types and Type aliases

10.1. Creating new types

struct.rs
rust
        fn main(){
    let stock: Stock = Stock{
        qty: 10,
        rate: 100
    };
    dbg!(stock);

    // this will throw an error if copy attribute is not there at struct definition
    dbg!(stock);

    let w = Weekend::Saturday;
    dbg!(w);

    // this will throw an error if copy attribute is not there at enum definition
    dbg!(w);
}

#[derive(Debug, Clone, Copy)]
struct Stock {
    qty: i32,
    rate: i32,
}

#[derive(Debug, Clone, Copy)]
enum Weekend {
    Saturday,
    Sunday,
}
/*
[src/main.rs:6] stock = Stock {
    qty: 10,
    rate: 100,
}
[src/main.rs:9] stock = Stock {
    qty: 10,
    rate: 100,
}
[src/main.rs:12] w = Saturday
[src/main.rs:13] w = Saturday
*/
        
    

10.2. Type aliases

11. Mutable vs Immutable

Book Analogy

mut.rs
rust
        fn main() {
    // values
    let v1 = 100; // immutable value

    // v1 = 50; throws error

    dbg!(v1);

    let mut v2 = 100; // mutable value
    v2 = 200; // works
    dbg!(v2);

    // references
    let v1 = 100; // immutable variable
    let r1 = &v1; // immutable reference
    dbg!(r1);
    dbg!(r1);

    let mut v2 = 100; // mutable variable
    let r2 = &v2; // immutable reference
    dbg!(r2);
    dbg!(r2);
    let r3 = &mut v2; // mutable reference
    dbg!(r3); // works

    // dbg!(r3); throws an error as mutable reference is move-type

    // dbg!(r2); throws error - Non-lexical lifetime. r2 can't be used after mutable reference

    let r2 = &v2; // create a reference again as v2 is still in scope
    dbg!(r2);
    dbg!(r2);
}

fn main2() {
    let mut girl = "hi";
    let friend1 = &girl;
    let friend2 = &girl;
    let husband = &mut girl; // all friends are dropped here.
    dbg!(friend1); // throws error. friend1 already dropped.
}

fn main3() {
    let mut girl = "hi";
    let husband1 = &mut girl;
    let husband2 = &mut girl; // husband1 dropped here.
    dbg!(husband1); // throws error. husband1 already dropped.
}

        
    

12. Functions and Closures

12.1 Functions

func.rs
rust
        fn main() {
    let k = add(2, 3);
    dbg!(k);
}

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

        
    
func.js
javascript
        function main() {
    let k = add(2, 3);
    console.log(k);
}

function add(a, b) {
    return a + b;
}
        
    
func.go
golang
        package main

import "fmt"

func main(){
	k := add(2, 3)
	fmt.Println(k)
}

func add(a,b int) int {
	return a + b
}
        
    

12.2 Function Pointer

func_pointer.rs
rust
        fn add(a: i32, b: i32) -> i32 {
    a + b
}

// function pointer aliased to a type
type Binop = fn(i32, i32) -> i32;

fn main() {
    let k = add(1, 2);
    dbg!(k);

    // function pointer
    let kadd: fn(i32, i32) -> i32 = add;
    let k = kadd(1, 2);
    dbg!(k);

    // function pointer with type alias
    let bo: Binop = add;
    let k = bo(1, 2);
    dbg!(k);

    // passing function as argument to another function with function pointer
    let k = add_two(add);
    dbg!(k);

    // closure - no need to specify types. compiler can infer.
    let cadd = |a, b| a + b;
    let k = cadd(1, 2);
    dbg!(k);
}
// passing function as argument to another function with function pointer
fn add_two(f: Binop) -> i32 {
    f(2, 3) + 2
}

/*
[src/main.rs:10] k = 3
[src/main.rs:15] k = 3
[src/main.rs:20] k = 3
[src/main.rs:24] k = 7
[src/main.rs:29] k = 3
 */

        
    

12.3 Closures

func_closure.rs
rust
        fn main() {
    let add = |a, b| a + b;
    let k = add(1, 2);
    dbg!(k);
    let k = add(2.5, 3.5); // throws error. Already infered as integers.
    dbg!(k);
}

        
    

13. Methods and Associated Functions

methods.rs
rust
        #[derive(Debug)]
struct Circle {
    radious: f64,
}

impl Circle {
    fn area(self: &Self) -> f64 {
        std::f64::consts::PI * self.radious * self.radious
    }

    // &self is short hand notaion for self: &Self
    fn circumference(&self) -> f64 {
        2.0 * std::f64::consts::PI * self.radious
    }

    fn update_radious(&mut self, radious: f64) -> &Self {
        self.radious = radious;
        self
    }

    // Static Method. It doesn't take self as first argument
    fn new(radious: f64) -> Self {
        Circle { radious: radious }
    }
}

fn main() {
    let mut c = Circle { radious: 5.0 };
    dbg!(&c);
    dbg!(c.area());
    dbg!(c.circumference());
    dbg!(c.update_radious(10.0));
    dbg!(c.area());
    let c = Circle::new(3.0);
    dbg!(&c);
    dbg!(c.area());
}

/*
[src/main.rs:29] &c = Circle {
    radious: 5.0,
}
[src/main.rs:30] c.area() = 78.53981633974483
[src/main.rs:31] c.circumference() = 31.41592653589793
[src/main.rs:32] c.update_radious(10.0) = Circle {
    radious: 10.0,
}
[src/main.rs:33] c.area() = 314.1592653589793
[src/main.rs:35] &c = Circle {
    radious: 3.0,
}
[src/main.rs:36] c.area() = 28.274333882308138
 */

        
    

14. Generics

generics.rs
rust
        // A function that takes generic type
fn hai<T>(a: T) -> T {
    a
}

// A struct that takes generic type
#[derive(Debug)]
struct Point<T> {
    x: T,
    y: T,
}

// A struct that takes generic tupple
#[derive(Debug)]
struct Circle<T>(T);

// A function that takes generic Circle
fn hello<T>(c: Circle<T>) -> Circle<T> {
    c
}

fn main() {
    dbg!(hai(123));
    dbg!(hai(Point { x: 1, y: 1 }));
    dbg!(hai(Circle(1)));
    dbg!(hello(Circle(100)));
}

/*
[src/main.rs:23] hai(123) = 123
[src/main.rs:24] hai(Point { x: 1, y: 1 }) = Point {
    x: 1,
    y: 1,
}
[src/main.rs:25] hai(Circle(1)) = Circle(
    1,
)
[src/main.rs:26] hello(Circle(100)) = Circle(
    100,
)
 */

        
    
generic_methods.rs
rust
        #[derive(Debug)]
struct Square<T> {
    side: T,
}
// Generic Type with Trait Bounds
impl<T: std::ops::Mul<Output = T> + Copy + Into<f64>> Square<T> {
    fn area(&self) -> T {
        self.side * self.side
    }
    fn circumference(&self) -> f64 {
        4.0 * self.side.into()
    }
}

fn main() {
    let s = Square { side: 2 };
    dbg!(&s);
    dbg!(s.area());
    dbg!(s.circumference());

    let s = Square { side: 3.0 };
    dbg!(&s);
    dbg!(s.area());
    dbg!(s.circumference());
}
/*
[src/main.rs:17] &s = Square {
    side: 2,
}
[src/main.rs:18] s.area() = 4
[src/main.rs:19] s.circumference() = 8.0
[src/main.rs:22] &s = Square {
    side: 3.0,
}
[src/main.rs:23] s.area() = 9.0
[src/main.rs:24] s.circumference() = 12.0
 */

        
    

15. Traits

traits.rs
rust
        use std::fmt::Debug;

trait Shape {
    fn area(&self) -> f64;
    fn circumference(&self) -> f64;

    // Default implementation for whoami method
    fn whoami(&self) -> String {
        "I am default implementation for Shape Trait whoami method".to_owned()
    }
}

#[derive(Debug)]
struct Circle {
    radious: f64,
}

impl Shape for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * self.radious * self.radious
    }
    fn circumference(&self) -> f64 {
        2.0 * std::f64::consts::PI * self.radious
    }
}
#[derive(Debug)]
struct Square {
    side: f64,
}

impl Shape for Square {
    fn area(&self) -> f64 {
        self.side * self.side
    }
    fn circumference(&self) -> f64 {
        4.0 * self.side
    }

    fn whoami(&self) -> String {
        "I am Square".to_owned()
    }
}

// This function accepts any type that implements Shape Triat
fn print(s: impl Shape + Debug) {
    dbg!(&s);
    dbg!(s.area());
    dbg!(s.circumference());
    dbg!(s.whoami());
}

// Above function can also be written using generic notation
fn kprint<T: Shape + Debug>(s: T) {
    dbg!(&s);
    dbg!(s.area());
    dbg!(s.circumference());
    dbg!(s.whoami());
}

fn main() {
    print(Circle { radious: 10.0 });
    kprint(Square { side: 5.0 });
}

/*
[src/main.rs:46] &s = Circle {
    radious: 10.0,
}
[src/main.rs:47] s.area() = 314.1592653589793
[src/main.rs:48] s.circumference() = 62.83185307179586
[src/main.rs:49] s.whoami() = "I am default implementation for Shape Trait"
[src/main.rs:54] &s = Square {
    side: 5.0,
}
[src/main.rs:55] s.area() = 25.0
[src/main.rs:56] s.circumference() = 20.0
[src/main.rs:57] s.whoami() = "I am Square"
 */

        
    

16. Generic Lifetime Annotations

lifetime.rs
rust
        
        
    

17. Dangling Pointer

dangling.rs
rust
        
        
    

18. Closures in Detail

closures.rs
rust
        

        
    

19. Iterators

iterators.rs
rust
        
        
    

20. Smart Pointers

smart.rs
rust
        
        
    

21. Concurrency

concurrency.rs
rust