Rust Cheatsheet

kuniga.me > Docs > Rust Cheatsheet

Rust Cheatsheet

Index

  1. Basic types
    1. Type names
    2. Arrays
      1. Initialize
      2. Iterate
    3. Bool
    4. Enum
    5. Integers
      1. Bit operations
    6. Iterator
    7. Optional
    8. Pair
    9. String
      1. Types
      2. String (variable length)
      3. Operations
    10. Struct
      1. Destructuring
      2. Methods
    11. Tuple
  2. Functions
    1. Closure
      1. Multi-line
  3. Flow Control
    1. Conditional
    2. Loops
  4. Data structures
    1. BTreeMap
    2. BTreeSet
    3. Vector
      1. Initialize
      2. Inserting
      3. Iterating
      4. Mapping
      5. Filtering
      6. Length
      7. Sorting
      8. Destructured assignment
    4. HashMap
    5. HashSet
    6. Queue
  5. Object Oriented
  6. I/O
    1. Read from stdin
    2. Read CLI arguments
    3. Printing to stdout
  7. Math
    1. Exponentiation
    2. Square root
  8. Mutability
  9. Attributes
  10. Modules
    1. Nested
  11. Ownership

Basic types

Type names

Arrays

Arrays are of fixed size. For variable size, see Vec.

Initialize

// Initialize array
let arr: [i32; 3] = [1, 2, 3];
let arr: [i32; 10] = [0; 10];

Iterate

let it:
let v: Option<Self::Item> = it.next()

Bool

Enum

enum Direction {
    N,
    E,
    S,
    W,
}

Integers

Bit operations

! is the Rust version of ~

Iterator

Note that Iterator is not a class but rather a trait that classes implement.

Get the next element:

// returns Split class which implements Iterator
let mut parts = s.split(',');
let e: Optional<&str> = parts.next();

NOTE: next() is not idempotent.

Optional

// Handle option:
match my_option {
    Some(value) => value,
    None => 1, // handle null value
}

Create Some:

Some(1)

Throw if None:

my_option.unwrap()

Test if some:

my_option.is_some()

Test if none:

my_option.is_none()

Pair

Done via tuples. See Tuple.

String

Types

String (variable length)

// Empty string
let mut my_str: String = "".to_owned();

// Other form
let mut my_str = String::from("Hello");

// Length
my_str.len();

// Iterate over its characters
for c in my_str.chars() {
    // do something with c
}

Operations

let concatenated = format!("{}{}", a, b);

From literal strings:

let s: String = "hello".to_owned();

Convert to int:

let i = s.parse::<i32>().expect("Should be numeric");

Convert from int:

let i: i32 = 10;
let s = i.to_string();

Convert to Vec<char>:

let cs: Vec<char> = s.chars().collect();

Split:

let parts: Vec<&str> = s.split(',').collect();

Split into multiple lines. There’s a shortcut for splitting by the character \n:

let parts: Vec<&str> = s.lines().collect();

Substring:

s.contains(p);

Trim:

let trimmed = s.trim();

Struct

keywords: record / object / shape

struct Tree {
    guess: Vec<i32>,
    children: Vec<Tree>,
}

Destructuring

let foo = Foo { x: (1, 2), y: 3 };
let Foo { x: (a, b), y } = foo;

Methods

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

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

Tuple

Type: (T1, T2), e.g. (i32, bool)

Create:

let tup: (i32, String) = (64, "hello")

Access:

tup.0 // 64
tup.1 // "hello"

or via destructuring:

let (n, s) = &tup;

Functions

fn myFun(arg1: i32, arg2: i32) -> i32 {
}

Rust doesn’t support default arguments

Closure

keywords: lambda

    let plus_one = |x: i32| x + 1;

Multi-line

let multi_line = |x: i32| {
    let mut result: i32 = x;
    result += 1;
    result
}

Flow Control

Conditional

if n < 0 {
    print!("{} is negative", n);
} else if n > 0 {
    print!("{} is positive", n);
} else {
    print!("{} is zero", n);
}

Loops

for i in 0..3 {
    println!("{}", i);
}

See also “Iterating” on different data structures.

Data structures

BTreeMap

BTreeMap is another implementation of a efficient key-value (the other being HashMap). One analogy for C++ is that BTreeMap is std::ordered_map and HashMap is std::unordered_map.

use std::collections::BTreeMap;

// Create empty
let mut my_map = btreemap! {};

// Create initialized
let my_map = btreemap! {
    "blue" => 1,
    "green" => 2,
    "red" => 3
};

BTreeSet

There’s an analogy between BTreeSet and HashSet with BTreeMap and HashMap.

// Create empty
let mut my_map = btreeset![];

// Create initialized
let fruits = btreeset![
    "apple",
    "banana"
]

Vector

Initialize

let mut vec = Vec::new();

Fixed size, same value:

let mut vec = vec![0; 100];

Inserting

vec.push(1);

Iterating

for item in vec.iter() {
    ...
}

Mapping

let u = vec![1, 2, 3];
let v: Vec<_> = u.iter().map(f).collect();

Filtering

let u = vec![1, 2, 3];
let v: Vec<_> = u.iter().filter(f).collect();

In-place

let mut u = vec![1, 2, 3];
u.retain(f);

Length

vec.len()

Sorting

vec.sort_by(|a, b| a.cmp(b))

Destructured assignment

Like in Python, we can do destructured assignment by assuming a fixed length of a vector, but we have to handle the other cases:

let [a, b] = vec.as_slice() else {
    panic!();
}

HashMap

Reference: HashMap

use std::collections::HashMap;

// Type definition
HashMap<String, String>;

// Create empty
let mut my_map = HashMap::new()

// Create initialized
let my_map = HashMap::from([
    ("blue", 1),
    ("green", 2),
    ("red", 3),
]);

// Insert
my_map.insert(
    "key_a".to_string(),
    "value_a".to_string(),
);

// Access
match my_map.get("key_a") {
    Some(value) => println!("value={}", value),
    None => println!("not found")
}

// Update
if let Some(value) = my_map.get_mut(key) {
    *curr_value = 1;
}

// All values
for value in my_map.values() {
}

// All keys
for key in my_map.keys() {
}

HashSet

Use:

use std::collections::HashSet;

Create:

let s = HashSet::new();

From vector:

let v = vec![1, 2, 3];
// cloned needed if borrowing v
let s: HashSet<_> = v.iter().cloned().collect();

Set intersection:

let i: HashSet<_> = s1.intersection(&s2).cloned().collect();

Queue

use std::collections::VecDeque;

// Create
let mut queue = VecDeque::new();

// Enqueue
queue.push_back("a");

// Dequeue
if let Some(x) = queue.pop_front() {
    // use x
}

// Is empty?
if !queue.is_empty() {
    // use queue
}

Object Oriented

pub structure MyClass {
    my_field: bool,
}

impl MyClass {
    // Constructor-like
    pub fn new() -> MyClass {
        return MyClass {
        my_field: true,
        }
    }

    // Read Method
    pub fn set(&self, value: bool) {
        self.my_field = value;
    }

    // Write Method
    pub fn set(&mut self, value: bool) {
        self.my_field = value;
    }
}

I/O

Read from stdin

use std::io::{self, Read};

let mut input = String::new();
io::stdin().read_to_string(&mut input)
    .expect("Failed to read input");

Read CLI arguments

use std::env;

// Skip program name
let args: Vec<String> = env::args().skip(1).collect();

Printing to stdout

Vector:

let vec = vec![1, 2, 3, 4, 5];
println!("Vector:\n{:#?}", vec);

Math

A lot of the math operations are methods on the numerical types.

Exponentiation

let b: i32 = 10;
let p10: i32 = b.pow(2 as u32);

Note that the exponent has to be positive, since a negative one could change the type of the base to floating point.

Square root

let x: f64 = 10.0;
let y: f64 = x.sqrt();

Not defined for integer types.

Mutability

Variable doesn’t need to be mutable if it’s initialized only once:

let x: i32;

if check() {
    x = 1;
} else {
    x = 2;
}

Attributes

Attributes are in the form #[foo] and placed on top of functions, structs and modules to add metadata to it, e.g.

#[test]
fn test_invalid_argument() {
    ...
}

or

#[derive(Clone, Debug, Eq)]
struct Tree {
    ...
}

Modules

Declaration:

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

    pub fn sub(a: i32, b: i32) -> i32 {
        a - b
    }
}

Usage:

fn f() {
    math::sub(1, 2);
}

Nested

Modules can be nested but because of visibility being private by default, inner modules must be made explicitly public:

mod outer {
    pub mod inner {
        pub fn f() {
            println!("Hello world");
        }
    }
}

fn f() {
    outer::inner(1, 2);
}

Ownership

In Rust we have 3 ways of passing data from one variable to another:

There’s a convention for classes to implement these different modes.

Mode / semantics API
Copy to_
Borrow as_
Move into_

Reference: Rust API Guidelines