0% found this document useful (0 votes)
31 views

Lets Get Rusty Cheat Sheet

This document provides an overview of Rust programming concepts, including basic types, control flow, ownership, borrowing, error handling, and generics. It covers various data structures like arrays, structs, and enums, as well as functions and iterators. Additionally, it includes examples of pattern matching and error handling techniques in Rust.

Uploaded by

metesusam06
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)
31 views

Lets Get Rusty Cheat Sheet

This document provides an overview of Rust programming concepts, including basic types, control flow, ownership, borrowing, error handling, and generics. It covers various data structures like arrays, structs, and enums, as well as functions and iterators. Additionally, it includes examples of pattern matching and error handling techniques in Rust.

Uploaded by

metesusam06
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


String::from("Golang Dojo")
Basic Types & Variables ).or_insert(3);
bool - Boolean
Struct
Unsigned integers
// Definition
u8, u16, u32, u64, u128
struct User {
username: String,
Signed integers
active: bool,
i8, i16, i32, i64, i128
}

Floating point numbers


// Instantiation
f32, f64
let user1 = User {
username: String::from("bogdan"),
Platform specific integers
active: true,
usize - Unsigned integer. Same number of bits as the
};
platform's pointer type.
// Tuple struct
isize - Signed integer. Same number of bits as the
struct Color(i32, i32, i32);
platform's pointer type.
let black = Color(0, 0, 0);

char - Unicode scalar value


&str - String slice Enum
String - Owned string // Definition
enum Command {
Tuple Quit,
let coordinates = (82, 64); Move { x: i32, y: i32 },
let score = ("Team A", 12) Speak(String),
ChangeBGColor(i32, i32, i32),
}
Array & Slice
// Arrays must have a known length and all // Instantiation
elements must be initialized let msg1 = Command::Quit;
let array = [1, 2, 3, 4, 5]; let msg2 = Command::Move{ x: 1, y: 2 };
let array2 = [0; 3]; // [0, 0, 0] let msg3 = Command::Speak(
String::from("Hi")
// Unlike arrays the length of a slice is );
determined at runtime let msg4 = Command::ChangeBGColor(0, 0, 0);
let slice = &array[1 .. 3];
Constant
HashMap
const MAX_POINTS: u32 = 100_000;
use std::collections::HashMap;
Static variable
let mut subs = HashMap::new();
subs.insert(String::from("LGR"), 100000); // Unlike constants static variables are
// Insert key if it doesn't have a value // stored in a dedicated memory location
subs.entry( // and can be mutated.
static MAJOR_VERSION: u32 = 1; // This breaks the inner loop
static mut COUNTER: u32 = 0; break;
// This breaks the outer loop
break 'outer;
}
Mutability }

let mut x = 5; Returning from loops


x = 6; let mut counter = 0;

Shadowing let result = loop {


counter += 1;
let x = 5;
let x = x * 2; if counter == 10 {
break counter;
Type alias }
};
// `NanoSecond` is a new name for `u64`.
type NanoSecond = u64;
while and while let

Control Flow while n < 101 {


n += 1;
if and if let }
let num = Some(22);
let mut optional = Some(0);
if num.is_some() {
println!("number is: {}", num.unwrap()); while let Some(i) = optional {
} print!("{}", i);
}
// match pattern and assign variable
if let Some(i) = num { for loop
println!("number is: {}", i);
} for n in 1..101 {
println!("{}", n);
}
loop
let mut count = 0; let names = vec!["Bogdan", "Wallace"];
loop {
count += 1; for name in names.iter() {
if count == 5 { println!("{}", name);
break; // Exit loop }
}
} match
let optional = Some(0);
Nested loops & labels
'outer: loop { match optional {
'inner: loop { Some(i) => println!("{}", i),
None => println!("No value.") let s2 = s1.clone(); // Deep copy
}
// Valid because s1 isn't moved
println!("{}", s1);

References, Ownership, and


Borrowing Ownership and functions

Ownership rules fn main() {


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

let s1 = String::from("hello world!");


fn takes_copy(some_integer: i32) {
let s1_ref = &s1; // immutable reference
println!("{}", some_integer);
}
let mut s2 = String::from("hello");
let s2_ref = &mut s2; // mutable reference
fn takes_ownership(some_string: String) {
println!("{}", some_string);
s2_ref.push_str(" world!");
} // some_string goes out of scope and drop
is called. The backing memory is freed.
Copy, Move, and Clone
// Simple values which implement the Copy fn gives_ownership() -> String {
trait are copied by value let some_string = String::from("LGR");
let x = 5; some_string
let y = x; }

println!("{}", x); // x is still valid fn takes_and_gives_back(some_string:


String) -> String {
// The string is moved to s2 and s1 is some_string
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 shape = Shape::Circle(10);

match shape {
Shape::Rectangle { x, y } => //...
Shape::Circle(radius) => //...
}
Pattern Matching
Basics Ignoring values

let x = 5; struct SemVer(i32, i32, i32);

match x { let version = SemVer(1, 32, 2);


// matching literals
1 => println!("one"), match version {
// matching multiple patterns SemVer(major, _, _) => {
2 | 3 => println!("two or three"), println!("{}", major);
// matching ranges }
4..=9 => println!("within range"), }
// matching named variables
x => println!("{}", x), let numbers = (2, 4, 8, 16, 32);
// default case (ignores value)
_ => println!("default Case") match numbers {
} (first, .., last) => {
println!("{}, {}", first, last);
}
Destructuring }
struct Point {
x: i32, Match guards
y: i32,
} let num = Some(4);

let p = Point { x: 0, y: 7 }; match num {


Some(x) if x < 5 => println!("less than
match p { five: {}", x),
Point { x, y: 0 } => { Some(x) => println!("{}", x),
println!("{}" , x); None => (),
}, }
Point { x, y } => {
println!("{} {}" , x, y); @ bindings
}, struct User {
} id: i32
}
enum Shape {
Rectangle { width: i32, height: i32 }, let user = User { id: 5 };
Circle(i32),
} match user {
User {
id: id_variable @ 3..=7, }
} => println!("id: {}", id_variable), }
User { id: 10..=12 } => { }
println!("within range");
},
User { id } => println!("id: {}", id),
}

Error Handling
Iterators
Throw unrecoverable error
Usage
panic!("Critical error! Exiting!");
// Methods that consume iterators
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter(); Option enum
let total: i32 = v1_iter.sum(); fn getUserId(username: &str) -> Option<u32>
{
// Methods that produce new iterators if database.userExists(username) {
let v1: Vec<i32> = vec![1, 2, 3]; Some(database.getIdForUser(username))
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 getUser(id: u32) -> Result<User, Error>
Implementing the Iterator trait
{
struct Counter { if isLoggedInAs(id) {
count: u32, Ok(getUserObject(id))
} }

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


fn new() -> Counter { }
Counter { count: 0 }
}
? operator
}
fn getSalary(db: Database, id: i32) ->
impl Iterator for Counter { Option<u32> {
type Item = u32; db.getUser(id)?.getJob()?.salary;
}
fn next(&mut self) -> Option<Self::Item>
{ fn connect(db: Database) ->
if self.count < 5 { Result<Connection, Error> {
self.count += 1; let conn =
Some(self.count) db.getActiveInstance()?.connect()?;
} else { Ok(conn)
None }
struct CustomError;

impl fmt::Display for CustomError {


fn fmt(&self, f: &mut fmt::Formatter) ->
fmt::Result {
write!(f, "custom error message")
}
}
Combinators
Boxing errors
.map
use std::error;
let maybe_some_string =
Some(String::from("LGR"));
type Result<T> = std::result::Result<T,
Box<dyn error::Error>>;
let maybe_some_len =
maybe_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 stringResult: Result<String, Error> =
.into_iter()
Ok(String::from("Bogdan"));
.filter_map(|s| s.parse::<i32>().ok())
.collect();
let userResult: Result<User, Error> =
stringResult.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)]
let errors: Vec<_> = errors }
.into_iter()
.map(Result::unwrap_err) Default implementations with Derive
.collect();
// A tuple struct that can be printed
#[derive(Debug)]
struct Inches(i32);

Generics, Traits, and Lifetimes


Using generics Trait bounds

struct Point<T, U> { fn largest<T: PartialOrd + Copy>(list:


x: T, &[T]) -> T {
y: U, let mut largest = list[0];
}
for &item in list {
impl<T, U> Point<T, U> { if item > largest {
fn mixup<V, W>(self, other: Point<V, W>) largest = item;
-> Point<T, W> { }
Point { }
x: self.x,
y: other.y, largest
} }
}
} impl trait
fn make_adder_function(y: i32) -> impl
Defining traits Fn(i32) -> i32 {
trait Animal { let closure = move |x: i32| { x + y };
fn new(name: &'static str) -> Self; closure
fn noise(&self) -> &'static str { "" } }
}
Trait objects
struct Dog { name: &'static str } pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
impl Dog { }
fn fetch() { // ... }
}
Operator overloading
impl Animal for Dog { use std::ops::Add;
fn new(name: &'static str) -> Dog {
Dog { name: name } #[derive(Debug, Copy, Clone, PartialEq)]
} struct Point {
x: i32,
fn noise(&self) -> &'static str { y: i32,
"woof!" }
}
impl Add for Point { struct Point { x: i32, y: i32, }
type Output = Point;
impl Point {
fn add(self, other: Point) -> Point { // Associated function
Point { fn new(x: i32, y: i32) -> Point {
x: self.x + other.x, Point { x: x, y: y }
y: self.y + other.y, }
}
} // Method
} fn getX(&self) -> i32 { self.x }
}
Supertraits
use std::fmt; Function pointers
fn do_twice(f: fn(i32) -> i32, arg: i32) ->
trait Log: fmt::Display { i32 {
fn log(&self) { f(arg) + f(arg)
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) ->
&'a str { Returning closures
if x.len() > y.len() {
x fn add_one() -> impl Fn(i32) -> i32 {
} else { |x| x + 1
y }
}
} fn add_or_subtract(x: i32) -> Box<dyn
Fn(i32) -> i32> {
if x > 10 {
Lifetimes in struct definitions Box::new(move |y| y + x)
struct User<'a> { } else {
full_name: &'a str, Box::new(move |y| y - x)
} }
}
Static lifetimes
let s: &'static str = "Let’s Get Rusty!"; Closure traits
● FnOnce - consumes the variables it captures
from its enclosing scope.
Functions, Function Pointers & ● FnMut - mutably borrows values from its
Closures enclosing scope.
● Fn - immutably borrows values from its enclosing
Associated functions and methods scope.
Store closure in struct borrowing rules at runtime instead of compile time.
struct Cacher<T> let num = 5;
where let r1 = RefCell::new(5);
T: Fn(u32) -> u32, // Ref - immutable borrow
{ let r2 = r1.borrow();
calculation: T, // RefMut - mutable borrow
value: Option<u32>, let r3 = r1.borrow_mut();
} // RefMut - second mutable borrow
let r4 = r1.borrow_mut();

Multiple owners of mutable data


let x = Rc::new(RefCell::new(5));

Function that accepts closure or function pointer


fn do_twice<T>(f: T, x: i32) -> i32
Packages, Crates, and Modules
where T: Fn(i32) -> i32 Definitions
{ ● Packages - A Cargo feature that lets you build,
f(x) + f(x) test, and share crates.
} ● 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.
References ● Paths - A way of naming an item, such as a
let mut num = 5; struct, function, or module.
let r1 = &num; // immutable reference
let r2 = &mut num; // mutable reference Creating a new package with a binary crate
$ cargo new my-project
Raw pointers
let mut num = 5; Creating a new package with a library crate
// immutable raw pointer $ cargo new my-project --lib
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


Box<T> - for allocating values on the heap pub mod inner_module { // public module
let b = Box::new(5); pub fn inner_public_function() {
super::super::some_function();
}
Rc<T> - multiple ownership with reference counting
let a = Rc::new(5); fn inner_private_function() {}
let b = Rc::clone(&a); }
}
Ref<T>, RefMut<T>, and RefCell<T> - enforce
fn main() {
// absolute path
crate::outer_module::
inner_module::inner_public_function();

// relative path path


outer_module::
inner_module::inner_public_function();

// bringing path into scope


use outer_module::inner_module;
inner_module::inner_public_function();
}

Renaming with as keyword


use std::fmt::Result;
use std::io::Result as IoResult;

Re-exporting with pub use


mod outer_module {
pub mod inner_module {
pub fn inner_public_function() {}
}
}

pub use crate::outer_module::inner_module;

Defining modules in separate files


// src/lib.rs
mod my_module;

pub fn some_function() {
my_module::my_function();
}

// src/my_module.rs
pub fn my_function() {}

You might also like