This page serves as a quick reference constructions in Rust programming language. Knowledge of the language is assumed so the code explained minimally. However, for a skilled programmer this page may serve as a syntax reference.

Main and prints

fn main() {
    // todo here
}
fn function(par1: Type, par2: Type) -> ReturnType {
    expressions;
    let val = function2(par1, par2, par3);
    returned_value // or return returned_value;
}
  • format!, print! (println!), and eprint! for string formatting and printing
  • format!("{} {:b} {:.5} {1:>20} {1:0>width}","first","one",width=3); prints in binary, decimals, alligned, and 0-alligned
  • traits fmt::Debug is used by {:?}, and fmt::Display is used by {} (see later); json style print {:#?}
  • use std::fmt; imports fmt
  • write!(buf, "val: {}", val) writes formatted data into a buffer.
  • struct Structure(i32, String);
  • return keyword exists, but we can just leave out ; after the last command to return its result

Implement display for a structure to be able to format/print it with {}.

impl fmt::Display for Structure {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}
  • #[derive(Debug)] autoimplements fmt::Debug trait
  • ? after a function which returns Result makes it so that if error occurs the error is returned immediately
  • vec![1, 2, 3] macro to create vector instance with given entries

Types and operators

  • signed integers: i8, i16, i32, i64, i128 and isize (pointer size) as 7 or 7i32 (or 10x, 10011b, etc.)
  • unsigned integers: u8, u16, u32, u64, u128 and usuze (pointer size) as 7u32
  • floating point: f32, f64 as 7.1 or 7.1f64
  • unicode characters: char as 'a' or '☺'
  • boolean: bool as true or false
  • static string: str as "abc"
  • arrays: i32[] as [1,2,3]
  • tuples: (i32,f64,bool,(str,i32)) as (1,2,true,("inner tuple",3))
  • unit type: () as ()

Types can be explicit with : or inferred, but not dynamic. We can cast one type to another with as keyword as var as i32.

let logical: bool = true;
let a_float: f64 = 1.0;  // Regular annotation
let an_integer   = 5i32; // Suffix annotation
let default_float   = 3.0; // default `f64`
let default_integer = 7;   // default `i32`
let mut inferred_type = 3; // Type i64 is inferred from the next line
inferred_type = 4294967296i64;
let inferred_type = true; // shadowing
  • typical operators + - && || ! & | ^ << >>
  • numbers can be interleaved with _ for readibility as 1_000_000u32
  • creating tuples let t = (1,2,3,var);
  • destructuring tuples let (a,b,c,d) = t;
  • type of an array is [type; length] as [i32; 5] for [1,2,3,4,5] or for a fixed value [1; 5]
  • slice is a pointer to a subarray created as &arr[1 .. 4]

Custom types struct and enum (const and static). There are three types of structures.

  • classic structs
    • Instantiate with Struct {3, 5} or Struct {x: 3, y: 5} but also with just Struct {x, y} when the fields and variable names coincide.
    • One can construct an object based on another (struct update) as let struct = Struct {x: 3, ..other_struct};.
    • Destructure by let Struct { x: left, y: right } = point;
  • tuple structs / named tuples
    • Instantiate with let pair = Pair(1, 2);
    • Access with pair.0 and pair.1
    • Deconstruct with let Pair(a, b) = pair;
  • unit structs, which are empty but named

Desconstruction can be nested, e.g. let Rectangle{top_left:Point{x:a,y:b},bottom_right:Point{x:c,y:d}} = r;

enum contains exactly one of its defined values.

enum Event {
    PageLoad,
    KeyPress(char),
    Click { x: i64, y: i64 },
}

These are by default numbered from 0. If we set Val = 3, in the enum, then that value is used instead.

fn inspect(event: Event) {
    match event {
        Event::PageLoad => println!("page loaded"),
        Event::KeyPress(c) => println!("pressed '{}'.", c),
        Event::Click { x, y } => { println!("clicked at x={}, y={}.", x, y); },
    }
}
  • Type aliases type Op = LongOperationsStructName or type IoResult<T> = Result<T, IoError>.
  • In struct methods, one can refer to the object with default alias Self
  • use keyword defaults values to a given namespace as follows
use crate::Status::{Poor, Rich}; // list where we do not have to use Status::Rich but just Rich
use crate::Work::*; // include all

When using match, one can use Some(ref n) to avoid moving n. This is quite different from Some(&ref) as ref is not matched against (otherwise a reference is expected).

There are two types of constants: const and static.

Values which may be null are enclosed in Option<T> and may contain either Null or Some<T> which can be recognized with match.

Retype value with as keyword as x as i32; note that it does not overflow but saturates. To for an overflow we have to use unsafe together with e.g. 300.to_int_unchecked::<u8>() function. We can measure size of values by std::mem::size_of_val(&val).

Covert with From, Into, and failable TryFrom and TryInto. Convert with string using fmt::Display and FromStr.

use std::convert::From;
#[derive(Debug)]
struct Number { value: i32, }

impl From<i32> for Number {
    fn from(item: i32) -> Self {
        Number { value: item }
    }
}
...
let val = 30;
let num = Number::from(val);
let num: Number = val.into();

Basic flow of control examples:

if cond { // basic if/else
} else if cond {
} else {
}
loop { // basically while true
    // use continue & break
}
// continue & break outer loops with labels
// and return values from cycles
let res = 'go: loop {
    loop {
        break 'go 2;
    }
}; // assignment needs to be ended with ;
while cond { // usual while loop
    cond = false;
};
for n in 1..101 { ... }
for n in 1..=100 { ... } // inclusive range

Iterator can be created with

  • into_iter which consumes (moves) the collection
  • iter which borrows each element
  • iter_mut which mutably borrows each element
match num {
    1 => ... , // matches 1
    2 | 3 => ... , // this matches 2 or 3
    4..9 => ... , // matching range
    n @ 5..=13 => ... , // binding (useful for matching functions)
    _ => ... , // anything else
}
match triple {
    (0, y, z) => ... ,
    (1, ..) => ... ,
    _ => ... ,
}
match structure {
    St { x: (1, b), y } => ... ,
    St { x: 2, y: i } => ... ,
    St { x, y } if x==y => ... , // so-called guard
    St { x, .. } => ... ,
}
match enumvar {
    Enm::Var1 => ... ,
    Enm::Var2(x, y) => ... ,
    // no need for default if all cases are covered
}

Reference can be dereferenced with *. Destructuring uses &, ref, and ref mut.

let varref = &4;
let ref varref = 3;
match varref {
    &val => ... ,
}
match *varref {
    val => ... ,
}
let var = 3;
match var {
    ref val => ... ,
}
match var {
    ref mut val => ... ,
}
if let Some(i) = number { ... }
if let Foo::Bar = a { ... }
while let Some(i) = number { ... }

Functions

fn func1(var: i32, var2: i32) -> bool {
    if rhs == 0 { return false; } // return from middle
    lhs % rhs == 0 // last expression returned when ; is omitted
}
fn func2(var: i32) { ... } // same as -> ()

Methods, closures

fn consume(self) -> Self { ... } // Consuming `self`
fn borrow(&self) -> &i32 { ... } // Borrowing `self`.
fn borrow_mut(&mut self) -> &mut i32 { ... } // Borrowing `self` mutably.

// closure captures variable x by first possible of: &,  &mut,  value
//                   respective traits for these are Fn, FnMut, FnOnce
let closure = |i: i32| -> i32 { i + x }; // i32 can be inferred both parameter & return
// the value of x is now borrowed as reference upto the last use of the closure
let res = closure();
let forcedmove = move |j| -> { j + y }; // take ownership of y
// use closures as parameters using generics
fn apply<F>(f: F) where F: Fn() { f(); }
fn call_me<F: Fn()>(f: F) { f(); }
// function may also be supplied in place of closure
fn function() { ... }
call_me(function);
// return closures as 'some' type which implements a given trait
fn create_fn() -> impl Fn() { ... }

Higher order fuctions are used like this:

let sum_of_squared_odd_numbers: u32 =
    (0..).map(|n| n * n)                             // All natural numbers squared
         .take_while(|&n_squared| n_squared < upper) // Below upper limit
         .filter(|&n_squared| is_odd(n_squared))     // That are odd
         .fold(0, |acc, n_squared| acc + n_squared); // Sum them

A function may not return control to its called when it purpusefuly panic!s. This is represented by type marked as !.

let x: ! = panic!("oh no!");

A function which can end in error should return Result<okType,errType>. Return type has a built-in feature: typing ? after function which returns Result returns error immediately if error occurs, or gives the value if Ok was the result.

fn fun(val: i32) -> Result<i32,String> {
    if ... {
        Ok(val)
    }else{
        Err("bad value")
    }
}
fn main() -> Result<_,String>{
    let res: Result<i32,String> = fun(2);
    match res { // result contains enum
        Ok(int) => ... ,
        Err(err) => ... ,
    }
    let val: i32 = fun(3)?; // Err case propagates up
}

Todo: Option

Generics

fn fun<T>(list: &[T]) -> T where T: Trait {
    ...
}
struct Point<T> {
    atr: T,
}
impl<T> Point<T> {
    fn generic_method(&self, val: T) -> &T { ... }
}
// implemented just for T which has the Trait
impl<T: Trait> Point<T> {
    fn conditional_impl(&self) { ... }
}

Traits

pub trait Summary {
    fn summarize(&self) -> String;
}