Basic Rust Cheat Sheet
Cargo
Cargo is Rust’s package manager and build system. It simplifies managing dependencies, building projects, and running tests.
1
2
3
4
5
6
7
8
9
10
11
# Create a new project
cargo new my_project
# Build the project
cargo build
# Run the project
cargo run
# Run tests
cargo test
Unit Tests
- Rust has built-in support for writing unit tests.
1
2
3
4
5
6
7
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
}
Variables
- Variables are immutable by default. Use mut to make them mutable.
1
2
3
let x = 5;
let mut y = 10;
y += 5;
Comments
- Single-line comments start with //, multi-line comments are enclosed with /…/.
1
2
3
4
5
// This is a single-line comment
/*
This is a multi-line comment
*/
Data Type
- Rust is statically typed. Basic types include integers, floats, booleans, and characters.
1
2
3
4
let x: i32 = 42;
let y: f64 = 3.14;
let is_true: bool = true;
let c: char = 'A';
Numbers
- Integer and floating-point numbers can be declared with different bit sizes.
1
2
let x: u8 = 255;
let y: f32 = 2.5;
Numerical Operations
- Basic arithmetic operations: addition, subtraction, multiplication, division, and remainder.
1
2
3
4
5
let sum = 5 + 10;
let difference = 95.5 - 4.3;
let product = 4 * 30;
let quotient = 56.7 / 32.2;
let remainder = 43 % 5;
Boolean
- Boolean type with values true and false.
1
2
let t: bool = true;
let f: bool = false;
Comparison Operators
- Operators: ==, !=, <, >, <=, >=.
1
2
let is_equal = 1 == 1;
let is_greater = 5 > 3;
Boolean Operators
Logical operators: && (and), (or), ! (not).
1
2
3
let and = true && false;
let or = true || false;
let not = !true;
Char
- Character type represents a single Unicode scalar value.
1
2
let c: char = 'z';
let heart_eyed_cat = '😻';
Tuples
- Tuples group multiple values with different types.
1
2
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup;
Arrays
- Arrays store multiple values of the same type.
1
2
let a = [1, 2, 3, 4, 5];
let first = a[0];
Constant
- Constants are immutable and declared with const. They must have a type.
1
const MAX_POINTS: u32 = 100_000;
Variable Scope
- Variables are scoped to the block in which they are declared.
1
2
3
4
{
let x = 42;
}
// x is not accessible here
Memory Management
- Rust uses a system of ownership with rules that the compiler checks at compile time.
Strings
- Strings can be immutable string slices (&str) or mutable String objects.
1
2
let s1 = "hello"; // &str
let s2 = String::from("hello");
Ownership
- Each value in Rust has a variable that’s called its owner. There can only be one owner at a time. When the owner goes out of scope, the value is dropped.
1
2
3
{
let s = String::from("hello");
} // s is dropped here
If Expression
- if expressions can be used to make decisions in the code.
1
2
3
4
5
6
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
Loops
- Repeated execution of a block of code.
1
2
3
4
loop {
println!("again!");
break;
}
While Loop
- while runs while a condition is true.
1
2
3
4
5
let mut number = 3;
while number != 0 {
println!("{}", number);
number -= 1;
}
For Loop
- Iterating over a collection.
1
2
3
4
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
Functions
- Functions are defined with the fn keyword.
1
2
3
4
5
6
7
fn main() {
println!("Hello, world!");
}
fn add(x: i32, y: i32) -> i32 {
x + y
}
Ownership in Function
- Functions can take ownership of parameters.
1
2
3
4
5
6
7
fn takes_ownership(some_string: String) {
println!("{}", some_string);
}
let s = String::from("hello");
takes_ownership(s);
// s is no longer valid here
References and Borrowing
- References allow you to refer to some value without taking ownership.
1
2
3
4
5
6
fn calculate_length(s: &String) -> usize {
s.len()
}
let s1 = String::from("hello");
let len = calculate_length(&s1);
Slice
- Slices allow you to reference a contiguous sequence of elements in a collection rather than the whole collection.
1
2
3
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
String Slice
- A specific type of slice.
1
2
3
let s = "hello world";
let hello = &s[0..5];
let world = &s[6..11];
Struct
- Structs are used to create custom data types.
1
2
3
4
5
6
7
8
9
10
11
12
13
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
let user1 = User {
username: String::from("someone@example.com"),
email: String::from("someone@example.com"),
sign_in_count: 1,
active: true,
};
Method
- Methods are functions defined within the context of a struct.
1
2
3
4
5
6
7
8
9
10
11
12
13
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
}
let rect = Rectangle { width: 30, height: 50 };
println!("The area of the rectangle is {} square pixels.", rect.area());
Enums
- Enums allow you to define a type by enumerating its possible values.
1
2
3
4
5
6
7
8
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
let m = Message::Write(String::from("hello"));
Pattern Matching
- match expressions are used for pattern matching.
1
2
3
4
5
6
7
8
9
10
let number = 7;
match number {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
4 => println!("four"),
5 => println!("five"),
_ => println!("something else"),
}
Type Alias
- Type aliases provide a way to create a new name for an existing type.
1
2
3
type Kilometers = i32;
let x: Kilometers = 5;
Modules
- Modules are used to organize code into namespaces.
1
2
3
4
5
6
7
8
9
mod my_module {
pub fn my_function() {
println!("Hello from my_function!");
}
}
fn main() {
my_module::my_function();
}
Crate
- A crate is a binary or library. The Cargo.toml file defines a crate’s dependencies.
1
2
[dependencies]
rand = "0.8.4"
Traits
- Traits are used to define shared behavior.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
pub trait Summary {
fn summarize(&self) -> String;
}
pub struct Article {
pub headline: String,
pub location: String,
pub author: String,
pub content: String,
}
impl Summary for Article {
fn summarize(&self) -> String {
format!("{}, by {} ({})",
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
let article = Article {
headline: String::from("Rust Programming"),
location: String::from("The Internet"),
author: String::from("Rustacean"),
content: String::from("Rust is great!"),
};
println!("New article available! {}", article.summarize());
Generic
- Generics allow for writing flexible and reusable code.
1
2
3
4
5
6
7
8
9
10
11
12
fn largest<T: PartialOrd>(list: &[T]) -> &T {
let mut largest = &list[0];
for item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
let numbers = vec![34, 50, 25, 100, 65];
println!("The largest number is {}", largest(&numbers));
Overloadable Operators
- Operators can be overloaded using traits.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use std::ops::Add;
#[derive(Debug, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
let p1 = Point { x: 1, y: 0 };
let p2 = Point { x: 2, y: 3 };
println!("{:?}", p1 + p2);
Optional Values
- The Option type is used when a value could be something or nothing.
1
2
3
4
5
6
let some_number = Some(5);
let absent_number: Option<i32> = None;
if let Some(n) = some_number {
println!("The number is {}", n);
}
Comparing
- The PartialEq and Eq traits are used for equality comparisons.
1
2
3
4
5
6
7
8
9
#[derive(Debug, PartialEq)]
struct Rectangle {
width: u32,
height: u32,
}
let rect1 = Rectangle { width: 30, height: 50 };
let rect2 = Rectangle { width: 30, height: 50 };
println!("Are rectangles equal? {}", rect1 == rect2);
String Manipulation
- Rust provides extensive support for string manipulation.
1
2
3
let mut s = String::from("hello");
s.push_str(", world");
println!("{}", s);
Formatting
- String formatting with the format! macro.
1
2
let s = format!("{}-{}", "hello", "world");
println!("{}", s);
Closure
- Closures are anonymous functions you can save in a variable or pass as arguments.
1
2
3
4
let add_one = |x: i32| x + 1;
println!("{}", add_one(5));
Collection
Rust's standard library provides collections like vectors, hash maps, and hash sets.
Sequences
- Vectors are the most commonly used sequence collection.
1
2
3
let mut v = vec![1, 2, 3];
v.push(4);
println!("{:?}", v);
Maps
- Hash maps store a mapping of keys to values.
1
2
3
4
5
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
Sets
- Hash sets store unique values.
1
2
3
4
use std::collections::HashSet;
let mut books = HashSet::new();
books.insert("The Rust Programming Language");
Iterators
- Iterators allow you to process a series of elements.
1
2
3
4
5
let v = vec![1, 2, 3];
let v_iter = v.iter();
for val in v_iter {
println!("{}", val);
}
Error Handling
- Rust handles errors with the Result type.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use std::fs::File;
use std::io::ErrorKind;
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(ref error) if error.kind() == ErrorKind::NotFound => {
match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),
}
},
Err(error) => panic!("Problem opening the file: {:?}", error),
};
Lifetime
- Lifetimes ensure that references are valid as long as needed.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
let string1 = String::from("long string is long");
let result;
{
let string2 = String::from("xyz");
result = longest(string1.as_str(), string2.as_str());
}
Attributes
- Attributes are metadata applied to some module, crate, or item.
1
2
3
4
5
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
Smart Pointers
- Smart pointers are data structures that act like a pointer but also have additional metadata and capabilities.
1
2
3
4
use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a);
Dereference
- The dereference operator (*) allows access to the value the pointer is pointing to.
1
2
3
4
let x = 5;
let y = &x;
assert_eq!(5, *y);
Cleanup
- Rust uses the Drop trait to run code when an object goes out of scope.
1
2
3
4
5
6
7
8
9
10
11
struct CustomSmartPointer {
data: String,
}
impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}
let c = CustomSmartPointer { data: String::from("my stuff") };
Multiple Ownership
- The Rc type allows for multiple ownership of data.
1
2
3
4
use std::rc::Rc;
let a = Rc::new(5);
let b = Rc::clone(&a);
Interior Mutability
- Interior mutability allows you to mutate data even when there are immutable references to that data, using RefCell or Mutex.
1
2
3
4
use std::cell::RefCell;
let x = RefCell::new(42);
*x.borrow_mut() += 1;
Static
- Static variables have a ‘static lifetime.
1
static HELLO_WORLD: &str = "Hello, world!";
Macros
- Macros are a way of writing code that writes other code (metaprogramming).
1
2
3
4
5
6
7
macro_rules! say_hello {
() => {
println!("Hello!");
};
}
say_hello!();
This post is licensed under CC BY 4.0 by the author.