« Back to home

Notes on Rust

Getting started

#install
curl https://sh.rustup.rs -aSf | sh
source $HOME/.cargo/env
export PATH="$HOME/.cargo/bin:$PATH"
cargo -V
rustc -V

#hello world
cargo new --bin hello_world
cd hello_world/
tree
cargo run
>     	Finished dev [unoptimized + debuginfo] target(s) in 0.01s
>   	Running `target/debug/hello_world`
>	Hello, world!

cat src/main.rs 
>		fn main() {
>		    println!("Hello, world!"); # println!: a macro, not a function
>		}

cargo build

variables and types

fn main() {
    let name = "world";
    let name2: &str = "planet";

    let mut age = 42; //variables are immutable, hence add mut
    age += 1;

    println!("Hello, {} {} {}!", name, name2, age);
}

built in data types

Integers, floats, Booleans, characters

//integers
u8 => 0, 255
i16 => -32768, 32767

usize, isize

default type i32

//floats
f32, f64

//bool

true, false

//char

'!'

Control flow

conditional

fn compare() {

        let number1 = 24;
        let number2 = 42;

        if number1 > number2 {
                println!("{} > {}", number1, number2);
        } else {
                println!("{} <= {}", number1, number2);
        }


        let minimum =
                if number1 > number2 {
                        number1
                } else {
                        number2
                }; // semicolon needed

        println!("min = {}", minimum)


}

looping (while)

fn divisor(){
        let mut a = 15;
        let mut b = 40;

        println!("Divisor of {} and {}", a, b);

        while  b != 0 {

                let temp = b;
                b = a % b;
                a = temp;
        }

        println!(" is {}", a);

}

function with parameters

//parameters explicitly type
fn max(a: i32, b: i32) -> i32 { // omit {-> i32} if no return
        if a > b {
          a
        } else {
          b
        }
} //return keyword only needed if ret early

structures

#[derive(Debug)]
struct Point {
        x: i32,
        y: i32,
}

let point = Point {
        x: 24,
        y: 42,
    };
    println!("{}, {}", point.x, point.y);
//    println!("{}", point) // compiler wont accept this 
    println!("{:#?}", point) // unless uses derive and format + pprint

reference

values are moved by default instead of being copied, with exceptions. Use`&` to sen by reference instead, applicable to function parameters too.

   let p1 = Point {x: 1, y: 2};
   //let p2 = p1; //invalid, values moved to p2, can't be used via p1 (below)
   let p2 = &p1;
   println!("{}", p1.x);
   print_point(&p1); //usable as function parameter

fn print_point(point: &Point) {
        println!("x: {}, y: {}", point.x, point.y)
}

clone types


#[derive(Clone, Debug)]
struct Point {
        x: i32,
        y: i32,
}

let p2 = p1.clone();
   print_point_clone(p1.clone());
   println!("{}", p2.x);
   println!("{}", p1.x);

fn print_point_clone(point: Point) {
        println!("x: {}, y: {}", point.x, point.y)
}

copy types

special basic types aren't moved, so can be copied without issues, implement Copy in a structure to avoid moving values. Copy requires Clone. Copy can only be derived for a type containing values that implement Copy.

let num1 = 42; //basic types like integer aren't moved
let num2 = num1; //so can be copied
println!("{}", num1);

#[derive(Clone, Copy, Debug)]
struct Point {
        x: i32,
        y: i32,
}

let p2 = p1 // will work as expected!

mutable references

all is by default immutable, to change values pass by reference and make mutable

fn inc_x(point: &mut Point){ 
        point.x +=1;
}

let mut p1 = Point {x: 1, y: 2};
inc_x(&mut p1);

methods

Add to custom types

impl Point {  //impl Type construct
        fn dist_from_origin(&self) -> f64{ //special parameter &self, instance called on
                let sum_of_squares = self.x.pow(2) + self.y.pow(2); //calling methods on simple types!
                (sum_of_squares as f64).sqrt() //cast the values using keyword as
        }

        fn translate(&mut self, dx: i32, dy: i32){ //here self is mutable
                self.x += dx;
                self.y += dy;
        }
}

constructors

new: not a constructor, common idiom, static method = associated function, doesn't take self, Self is the type of the self value (or use the custom type name, e.g., Point)

impl Point{       
	fn new(x: i32, y:i32) -> Self { 
                Self { x: x, y: y }
        }

        }

        fn start(x: i32, y:i32) -> Self {
                Self { x, y } //shorthand if value and field have same name
        }

        fn origin() -> Self {   //multiple constructors
                Point {x: 0, y: 0}
        }

}

   let new_point = Point::new(11, 22); // allocates values on the stack

Tuples

Similar to structures, but fields are unnamed.

   let tuple = (24, 42);
   println!("{}, {}", tuple.0, tuple.1); //access via index, from 0

   let (hello, world) = "hello world".split_at(5); //use tuples to return multiple values from a fn
   println!("{} {}", hello, world);

Enumerations

Choose one value from diff types

enum Expr{
        Null,
        Add(i32, i32),
        Sub(i32, i32),
        Mul(i32, i32),
        Div {dividend: i32, divisor: i32},
        Val(i32),
}

let quotient = Expr::Div {dividend: 10, divisor: 2}; //this variant has 2 values associated, similar to struct

let sum = Expr::Add(40, 2);