0% found this document useful (0 votes)
267 views11 pages

LGR Cheat Sheet

This document provides a summary of the topics covered in a Rust YouTube tutorial series. The topics include basic Rust types and variables, control flow, references and ownership, pattern matching, iterators, error handling, generics, traits and lifetimes, functions, pointers, and modules. The YouTube channel provides videos on these Rust concepts and features.

Uploaded by

Douglas Matias
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
267 views11 pages

LGR Cheat Sheet

This document provides a summary of the topics covered in a Rust YouTube tutorial series. The topics include basic Rust types and variables, control flow, references and ownership, pattern matching, iterators, error handling, generics, traits and lifetimes, functions, pointers, and modules. The YouTube channel provides videos on these Rust concepts and features.

Uploaded by

Douglas Matias
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

version 1.0.

Table of Contents

Basic Types & Variables


Control Flow
References, Ownership, and Borrowing
Pattern Matching
Iterators
Error Handling
Generics, Traits, and Lifetimes
Functions, Function Pointers & Closures
Pointers
Packages, Crates, and Modules

YouTube Channel: https://www.youtube.com/c/LetsGetRust


Basic Types & Variables Struct
// Definition
bool - Boolean
struct User {
username: String,
Unsigned integers
active: bool,
u8, u16, u32, u64, u128
}

Signed integers
// Instantiation
i8, i16, i32, i64, i128
let user1 = User {
username: String::from("bogdan"),
Floating point numbers
active: true,
f32, f64
};

Platform specific integers


// Tuple struct
usize - Unsigned integer. Same number of bits as the
struct Color(i32, i32, i32);
platform's pointer type.
let black = Color(0, 0, 0);

isize - Signed integer. Same number of bits as the


platform's pointer type. Enum
// Definition
char - Unicode scalar value enum Command {
&str - String slice Quit,
String - Owned string Move { x: i32, y: i32 },
Speak(String),
Tuple ChangeBGColor(i32, i32, i32),
let coordinates = (82, 64); }
let score = ("Team A", 12)
// Instantiation
let msg1 = Command::Quit;
Array & Slice
let msg2 = Command::Move{ x: 1, y: 2 };
// Arrays must have a known length and all let msg3 = Command::Speak("Hi".to_owned());
elements must be initialized let msg4 = Command::ChangeBGColor(0, 0, 0);
let array = [1, 2, 3, 4, 5];
let array2 = [0; 3]; // [0, 0, 0]
Constant

// Unlike arrays the length of a slice is const MAX_POINTS: u32 = 100_000;


determined at runtime
let slice = &array[1 .. 3]; Static variable
// Unlike constants static variables are
HashMap // stored in a dedicated memory location
use std::collections::HashMap; // and can be mutated.
static MAJOR_VERSION: u32 = 1;
let mut subs = HashMap::new(); static mut COUNTER: u32 = 0;
subs.insert(String::from("LGR"), 100000);
// Insert key if it doesn't have a value
subs.entry("Golang Dojo".to_owned())
.or_insert(3);
Mutability Returning from loops
let mut x = 5; let mut counter = 0;
x = 6;
let result = loop {
Shadowing counter += 1;

let x = 5; if counter == 10 {
let x = x * 2; break counter;
}
Type alias };
// `NanoSecond` is a new name for `u64`.
type NanoSecond = u64; while and while let
while n < 101 {
Control Flow n += 1;
}
if and if let
let num = Some(22); let mut optional = Some(0);

if num.is_some() { while let Some(i) = optional {


println!("number is: {}", num.unwrap()); print!("{}", i);
} }

// match pattern and assign variable for loop


if let Some(i) = num {
println!("number is: {}", i); for n in 1..101 {
} println!("{}", n);
}

loop let names = vec!["Bogdan", "Wallace"];


let mut count = 0;
loop { for name in names.iter() {
count += 1; println!("{}", name);
if count == 5 { }
break; // Exit loop
} match
}
let optional = Some(0);

Nested loops & labels match optional {


'outer: loop { Some(i) => println!("{}", i),
'inner: loop { None => println!("No value.")
// This breaks the inner loop }
break;
// This breaks the outer loop
break 'outer;
}
}
References, Ownership, and Ownership and functions

Borrowing fn main() {
let x = 5;
Ownership rules takes_copy(x); // x is copied by value
1. Each value in Rust has a variable that’s called its
owner. let s = String::from("Let’s Get Rusty!");
2. There can only be one owner at a time. // s is moved into the function
3. When the owner goes out of scope, the value will takes_ownership(s);
be dropped.
// return value is moved into s1
Borrowing rules let s1 = gives_ownership();
1. At any given time, you can have either one
mutable reference or any number of immutable let s2 = String::from("LGR");
references. let s3 = takes_and_gives_back(s2);
2. References must always be valid. }

Creating references fn takes_copy(some_integer: i32) {


let s1 = String::from("hello world!"); println!("{}", some_integer);
let s1_ref = &s1; // immutable reference }

let mut s2 = String::from("hello"); fn takes_ownership(some_string: String) {


let s2_ref = &mut s2; // mutable reference println!("{}", some_string);
} // some_string goes out of scope and drop
s2_ref.push_str(" world!"); is called. The backing memory is freed.

fn gives_ownership() -> String {


Copy, Move, and Clone let some_string = String::from("LGR");
// Simple values which implement the Copy some_string
trait are copied by value }
let x = 5;
let y = x; fn takes_and_gives_back(some_string:
String) -> String {
println!("{}", x); // x is still valid some_string
}
// The string is moved to s2 and s1 is
invalidated
let s1 = String::from("Let's Get Rusty!");
let s2 = s1; // Shallow copy a.k.a move

println!("{}", s1); // Error: s1 is invalid

let s1 = String::from("Let's Get Rusty!");


let s2 = s1.clone(); // Deep copy

// Valid because s1 isn't moved


println!("{}", s1);
Pattern Matching Ignoring values
struct SemVer(i32, i32, i32);
Basics
let x = 5; let version = SemVer(1, 32, 2);

match x { match version {


// matching literals SemVer(major, _, _) => {
1 => println!("one"), println!("{}", major);
// matching multiple patterns }
2 | 3 => println!("two or three"), }
// matching ranges
4..=9 => println!("within range"), let numbers = (2, 4, 8, 16, 32);
// matching named variables
x => println!("{}", x), match numbers {
// default case (ignores value) (first, .., last) => {
_ => println!("default Case") println!("{}, {}", first, last);
} }
}
Destructuring
struct Point { Match guards
x: i32, let num = Some(4);
y: i32,
} match num {
Some(x) if x < 5 => println!("less than
let p = Point { x: 0, y: 7 }; five: {}", x),
Some(x) => println!("{}", x),
match p { None => (),
Point { x, y: 0 } => { }
println!("{}" , x);
},
@ bindings
Point { x, y } => {
println!("{} {}" , x, y); struct User {
}, id: i32
} }

enum Shape { let user = User { id: 5 };


Rectangle { width: i32, height: i32 },
Circle(i32), match user {
} User {
id: id_variable @ 3..=7,
let shape = Shape::Circle(10); } => println!("id: {}", id_variable),
User { id: 10..=12 } => {
match shape { println!("within range");
Shape::Rectangle { x, y } => //... },
Shape::Circle(radius) => //... User { id } => println!("id: {}", id),
} }
Iterators Error Handling
Usage Throw unrecoverable error
// Methods that consume iterators panic!("Critical error! Exiting!");
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter(); Option enum
let total: i32 = v1_iter.sum();
fn get_user_id(name: &str) -> Option<u32> {
// Methods that produce new iterators if database.user_exists(name) {
let v1: Vec<i32> = vec![1, 2, 3]; return Some(database.get_id(name))
let iter = v1.iter().map(|x| x + 1); }

// Turning iterators into a collection None


let v1: Vec<i32> = vec![1, 2, 3]; }
let v2: Vec<_> = v1.iter().map(|x| x +
1).collect(); Result enum
fn get_user(id: u32) -> Result<User, Error>
Implementing the Iterator trait {
struct Counter { if is_logged_in_as(id) {
count: u32, return Ok(get_user_object(id))
} }

impl Counter { Err(Error { msg: "not logged in" })


fn new() -> Counter { }
Counter { count: 0 }
} ? operator
}
fn get_salary(db: Database, id: i32) ->
Option<u32> {
impl Iterator for Counter {
Some(db.get_user(id)?.get_job()?.salary)
type Item = u32;
}

fn next(&mut self) -> Option<Self::Item>


fn connect(db: Database) ->
{
Result<Connection, Error> {
if self.count < 5 {
let conn =
self.count += 1;
db.get_active_instance()?.connect()?;
Some(self.count)
Ok(conn)
} else {
}
None
}
}
}
Combinators Boxing errors

.map use std::error;

let some_string = Some("LGR".to_owned());


type Result<T> = std::result::Result<T,
Box<dyn error::Error>>;
let some_len = some_string.map(|s|
s.len());
Iterating over errors
struct Error { msg: String } Ignore failed items with filter_map()
struct User { name: String }
let strings = vec!["LGR", "22", "7"];
let numbers: Vec<_> = strings
let string_result: Result<String, Error> =
.into_iter()
Ok("Bogdan".to_owned());
.filter_map(|s| s.parse::<i32>().ok())
.collect();
let user_result: Result<User, Error> =
string_result.map(|name| {
User { name } Fail the entire operation with collect()
}); let strings = vec!["LGR", "22", "7"];

.and_then let numbers: Result<Vec<_>, _> = strings


.into_iter()
let vec = Some(vec![1, 2, 3]);
.map(|s| s.parse::<i32>())
let first_element = vec.and_then(
.collect();
|vec| vec.into_iter().next()
);
Collect all valid values and failures with partition()
let string_result: Result<&'static str, _> let strings = vec!["LGR", "22", "7"];
= Ok("5");
let number_result = let (numbers, errors): (Vec<_>, Vec<_>) =
string_result strings
.and_then(|s| s.parse::<u32>()); .into_iter()
.map(|s| s.parse::<i32>())
Multiple error types .partition(Result::is_ok);

Define custom error type


let numbers: Vec<_> = numbers
type Result<T> = std::result::Result<T, .into_iter()
CustomError>; .map(Result::unwrap)
.collect();
#[derive(Debug, Clone)]
struct CustomError; let errors: Vec<_> = errors
.into_iter()
impl fmt::Display for CustomError { .map(Result::unwrap_err)
fn fmt(&self, f: &mut fmt::Formatter) -> .collect();
fmt::Result {
write!(f, "custom error message")
}
}
Generics, Traits, and Lifetimes Trait bounds
fn largest<T: PartialOrd + Copy>(list:
Using generics
&[T]) -> T {
struct Point<T, U> { let mut largest = list[0];
x: T,
y: U, for &item in list {
} if item > largest {
largest = item;
impl<T, U> Point<T, U> { }
fn mixup<V, W>(self, other: Point<V, W>) }
-> Point<T, W> {
Point { largest
x: self.x, }
y: other.y,
}
impl trait
}
} fn make_adder_function(y: i32) -> impl
Fn(i32) -> i32 {
let closure = move |x: i32| { x + y };
Defining traits
closure
trait Animal { }
fn new(name: &'static str) -> Self;
fn noise(&self) -> &'static str { "" }
Trait objects
}
pub struct Screen {
struct Dog { name: &'static str } pub components: Vec<Box<dyn Draw>>,
}
impl Dog {
fn fetch() { // ... } Operator overloading
}
use std::ops::Add;
impl Animal for Dog {
#[derive(Debug, Copy, Clone, PartialEq)]
fn new(name: &'static str) -> Dog {
struct Point {
Dog { name: name }
x: i32,
}
y: i32,
}
fn noise(&self) -> &'static str {
"woof!"
impl Add for Point {
}
type Output = Point;
}

fn add(self, other: Point) -> Point {


Default implementations with Derive Point {
// A tuple struct that can be printed x: self.x + other.x,
#[derive(Debug)] y: self.y + other.y,
struct Inches(i32); }
}
}
Supertraits Function pointers
use std::fmt; fn do_twice(f: fn(i32) -> i32, arg: i32) ->
i32 {
trait Log: fmt::Display { f(arg) + f(arg)
fn log(&self) { }
let output = self.to_string();
println!("Logging: {}", output); Creating closures
}
} let add_one = |num: u32| -> u32 {
num + 1
};
Lifetimes in function signatures
fn longest<'a>(x: &'a str, y: &'a str) -> Returning closures
&'a str {
if x.len() > y.len() { fn add_one() -> impl Fn(i32) -> i32 {
x |x| x + 1
} else { }
y
} fn add_or_subtract(x: i32) -> Box<dyn
} Fn(i32) -> i32> {
if x > 10 {
Box::new(move |y| y + x)
Lifetimes in struct definitions } else {
struct User<'a> { Box::new(move |y| y - x)
full_name: &'a str, }
} }

Static lifetimes Closure traits


let s: &'static str = "Let’s Get Rusty!"; ● FnOnce - consumes the variables it captures
from its enclosing scope.
● FnMut - mutably borrows values from its
Functions, Function Pointers & enclosing scope.
Closures ● Fn - immutably borrows values from its enclosing
scope.
Associated functions and methods
struct Point { x: i32, y: i32, } Store closure in struct
struct Cacher<T>
impl Point { where
// Associated function T: Fn(u32) -> u32,
fn new(x: i32, y: i32) -> Point { {
Point { x: x, y: y } calculation: T,
} value: Option<u32>,
}
// Method
fn getX(&self) -> i32 { self.x }
}
Function that accepts closure or function pointer Packages, Crates, and Modules
fn do_twice<T>(f: T, x: i32) -> i32
Definitions
where T: Fn(i32) -> i32
● Packages - A Cargo feature that lets you build,
{
test, and share crates.
f(x) + f(x)
● Crates - A tree of modules that produces a
}
library or executable.
● Modules and use - Let you control the
Pointers organization, scope, and privacy of paths.
● Paths - A way of naming an item, such as a
References
struct, function, or module.
let mut num = 5;
let r1 = &num; // immutable reference Creating a new package with a binary crate
let r2 = &mut num; // mutable reference
$ cargo new my-project

Raw pointers
Creating a new package with a library crate
let mut num = 5;
$ cargo new my-project --lib
// immutable raw pointer
let r1 = &num as *const i32;
// mutable raw pointer Defining and using modules
let r2 = &mut num as *mut i32; fn some_function() {}

Smart pointers mod outer_module { // private module


pub mod inner_module { // public module
Box<T> - for allocating values on the heap
pub fn inner_public_function() {
let b = Box::new(5); super::super::some_function();
}
Rc<T> - multiple ownership with reference counting
fn inner_private_function() {}
let a = Rc::new(5);
}
let b = Rc::clone(&a);
}

Ref<T>, RefMut<T>, and RefCell<T> - enforce fn main() {


borrowing rules at runtime instead of compile time. // absolute path
let num = 5; crate::outer_module::
let r1 = RefCell::new(5); inner_module::inner_public_function();
// Ref - immutable borrow
let r2 = r1.borrow(); // relative path path
// RefMut - mutable borrow outer_module::
let r3 = r1.borrow_mut(); inner_module::inner_public_function();
// RefMut - second mutable borrow
let r4 = r1.borrow_mut(); // bringing path into scope
use outer_module::inner_module;
inner_module::inner_public_function();
Multiple owners of mutable data
}
let x = Rc::new(RefCell::new(5));
Renaming with as keyword Defining modules in separate files
use std::fmt::Result; // src/lib.rs
use std::io::Result as IoResult; mod my_module;

Re-exporting with pub use pub fn some_function() {


my_module::my_function();
mod outer_module { }
pub mod inner_module {
pub fn inner_public_function() {} // src/my_module.rs
} pub fn my_function() {}
}

pub use crate::outer_module::inner_module;

You might also like