Show Menu
Cheatography

Draft - Rust Book Notes - Not a Cheat Sheet by

Instal­lation

curl https://sh.rustup.rs -sSf | sh 
export PATH="$HOME/.cargo/bin:$PATH"

Hello World

fn main() {  printl­n!(­"­Hello, world!­"); }
compile: rustc main.rs
prinln! = macro!

create variables with let

let foo =5;
// immutable (default)
let mut foo=5;
//mutable
let foo=5;
 let foo="he­llo­";
Shadowing allows reuse of variable. useful in type conver­sions
'let foo: u32 =5;
//anno­tating with type using :
let foo=5; foo = 6;
error[­E0384]: cannot assign twice to immutable variable
foo

const

const MAX_POINTS: u32 = 100_000; 
// no mut allowed on const. 
// it should be annotated with datatype.
// It is visible with in the scope it's declared.
// only constant expression assignment.. not return value from function or expression evaluated at runtime

Prelude

Rust inserts
extern crate std;

into the crate root of every crate, and
use std::p­rel­ude­::v­1::*;

into every module.
std::p­rel­ude::v1
Prelude is set of types Rust imports.

Import external crate (library)

extern crate rand; //external dependency 
use rand::Rng; //bring Rng trait which defines the methods into scope

those in prelude, need not to extern the crate.
just use std::io; //brings io trait into scope

&mut and stdin() and io::Result

io::stdin()  // stdin() = std::io::Stdin instance a handle to standard input 
    .read_line(&mut guess) // &mut - pass by reference and make it mutable
    .expect("Failed to read line")  // io::Result -> If returns Err, it crashes displaying the message
io::Result -> Result, Enumer­ations Ok, Err. If ok, returns the value, if Err, it crashes program. Without expect, compiler warning - Unused io::Result which must be used.

Floati­ng-­Point Types

f32 single precision
f64 - double precision (default)

Math Operators

+, -, *, /

bool

true, false
let t = true; let f: bool = false;

Character Type

let c='z'; let k: char = 'a';
char is a unicode scalar value.

Tuple Type

let x:(u32­,f6­4,u8) = (6,3.2,1); x.0 => 6, x.1 =>3.2
destru­cturing -
let (a,b,c) =x;
a=> 6
first index in tuple is 0. ex: x.0 => 6

Array Types

Fixed Size vs Vector's size can change
let a =[1,2,3,4]
Elements of same type
access by index: a[0], a[1]
Invalid Access a[10] :Runtime error: Index out of bounds

match => arms; arm : pattern => code

match guess.cmp(&secret_number) { => arms
        Ordering::Less => println!("Too small!"), //pattern => code
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => {  //code block
                println!("You win!");
                
            }
    }
//similar to switch case? but concise !

Integer Types

Length
Signed
Unsigned
8-bit
i8
u8
16-bit
i16
u16
32-bit
i32
u32
64-bit
i64
u64
arch
isize
usize
------­---­---­---­---­---­---­---­------
arch 32/64 bit system - isize =i32/64, usize=­u32/u64
Signed
-(2n - 1) to 2n - 1 - 1
Unsigned
0 to 2n-1
arch (useful in indexing collec­tions)
type suffix
57u8
visual separator
1_000
default type is u32 even on 64bit arch

Functions fn

snake case - lowercase words separated by (_) underscore
fn one_two() { }
parame­ters: name : type
fn foo_bar(x: i32, y:u32) { }
statement vs expression
statement ends with ; and does not evaluate to a value;
expression doesn't end with ; and evaluates to a value
code block expression {} x+1 is expression which is returned
let y = {         let x = 3;         x + 1     }
value of y will be 4;
Error: expected expres­sion, found statement (
let
)
let a = (let b =2);
functions with return value -> type
fn five() -> i32 {  5 };
5 is expression as no colon, and return value as it's last expression
fn plus_o­ne(x: i32) -> i32 {     x + 1; }
; turns into statement, and empty tuple () will returned, will be a compiler error as () is not i32

enums - methods

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
impl Message {
    fn call(&self) {  
        // method body would be defined here
    }
}

let m = Message::Write(String::from("hello"));
m.call();

option­s<T> alternate Null implem­ent­ation

enum Option<T> {
    Some(T),
    None,
}
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;

//error
let x: i8 = 5;
let y: Option<i8> = Some(5);

let sum = x + y; //error as x and y are two different types
error[E0277]: the trait bound 
i8: std::ops::Add<std::option::Option<i8>>
is not satisfied

match - _ placeh­older

let some_u8_value = 0u8;
match some_u8_value {
    1 => println!("one"),
    3 => println!("three"),
    5 => println!("five"),
    7 => println!("seven"),
    _ => (), unit value 
}

Control flow - if { } else if { } else { }

if number == 3 { }
arm
condition should evaluate to bool type
or mismatched types error
if condition { } else if condition { } else { }
blocks of code called arms
let a = if a == 3 { 2 } else { 5}
expres­sions in all arms should evaluate to same type

match handling Result

let guess: u32 = match guess.trim().parse() {
    Ok(num) => num, //Ok receives num from return Result, which is returned by match 
    Err(_) => continue, // _ underscore catches all values
};

println!

No argument indices.. just simple brace {}
printl­n!(­"x = {} and y = {}", x, y);

Control flows - while and for

while number != 3 { number = number +1;  }
let a =[1,2]; for element in a.iter()  { } 
for number in (1..4).rev() {
alternate approach to while

Ownership

My first reaction to the concept of Ownership
WOW!
Value has a variable
Owner
When owner goes out of scope
Value is dropped
variable assign­ment, Passing to as function parameter or returning from function, it is moved.
let s1 = String­::f­rom­("he­llo­"); let s2 = s1;
s1 is no longer valid. It is moved. Only s2 is the owner
error[­E0382]: use of moved value:
s1
Reference doesn't have ownership
when it goes out of scope, nothing happens.
Pass by reference
it is not moved. It's just borrowed.
References are immutable by default
Mutable References
fn main() {  
let mut s = String­::f­rom­("he­llo­"); change(&mut s); }
fn change­(so­me_­string: &mut String) { some_s­tri­ng.p­us­h_s­tr(­", world"); }
Memory is managed through a system of ownership with a set of rules that the compiler checks at compile time. At compile time!!!

Cargo

cargo --version
cargo new hello_­cargo --bin
creates
Cargo.toml
and main.rs
--bin=­bin­(ary) or library.
source control: default git --vcs=
Cargo is Rust’s build system and package manager.

Cargo.toml

[package]
name = "hello_cargo"  #name of the executable
version = "0.1.0"  #version
authors = ["Your Name <you@example.com>"] #Cargo gets name and email from the environment

[dependencies] #packages aka crates
TOML: Tom’s Obvious, Minimal Language

import crate toml

[dependencies] 
  rand = "0.3.14" # SimVer ~ ^0.3.14 any version that is compatible with 0.3.14
Adding crates to toml file.

cargo build , cargo run , cargo check

cargo build # creates an executable file in target­/de­bug­/he­llo­_cargo
cargo run  # build and run
cargo check #compi­lation check, no building executable
cargo build --release

error[­E0308]: mismatched types

  error[E0308]: mismatched types  --> src/main.rs:23:21
   
     match guess.cmp(&secret_number) 
             ^^^^^^ expected struct 
std::string::String
, found integral variable   = note: expected type
&std::string::String
   = note: found type
&{integer}

cargo.lock

Cargo maintains versions in cargo.lock file

cargo update

cargo updates all versions upto next symver

registry

Cargo fetches external depend­encies and their depend­encies from registry, a copy from the crates.io.
crates.io is a public repo

loop

loop { }
break;
exits the loop

cargo test

#[cfg(test)] 
mod tests {
    #[test]  --> test  
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
Doc-tests adder docume­ntation tests? to have examples
assert! false value, assetseq! == assertive! !=
[shoul­d_p­anic] to expect panic!
[ignore]
cargo test -- --test­-th­reads=1 stop parallel run
cargo test --noca­pture , no print output
cargo test add //runs tests containing add

tests organi­zation

#[cfg(­test)] mod tests { }
compile only cfg is test
tests folder
integr­ation tests

struct

struct User {
    username: String, field => name :type
    email: String,
    sign_in_count: u64,
    active: bool,
}
// Instantiating 
let user1 = User {  //struct name 
    email: String::from("someone@example.com"), //and 
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};
//dot notation
let mut user2 = User { }; 
user2.email = String::from("someone@example.com");
unit-like structs without fileds () .. Used to implement traits with out data on the type.

ownership - lifetimes?
 

Comments

//
/* */
//! //

tuple structs

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

struct - instan­tiating options

// .. shorthand 
let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1 //  .. remaining fields should be from user1 instance
};
// shorthand - when variables and fields have same names
let email ="";
let user2 = User {
  email //shortHand
}

struct methods

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

impl Rectangle {
    fn area(&self) -> u32 { //first parameter should be self , instance of the struct
        self.width * self.height
    }


    fn square(size: u32) -> Rectangle { //associated function Rectangle::square
        Rectangle { width: size, height: size }
    }

}
&mut self to modify struct

struct #[deri­ve(­Debug)]

printl­n!(­"­rect1 is {}", rect1);
error[­E0277]: the trait bound
Rectangle: std::f­mt:­:Di­splay
is not satisfied
{:?} ? to use Debug Trait
#[deri­ve(­Debug)] 
struct Rectangle {
width: u32, height: u32
}
{:#?} to pretty print
Derived Traits

struct as expression

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}

modules

mod network {
    fn connect() {
    }

    mod client {  //nested module
        fn connect() {
        }
    }
}
networ­k::­cli­ent­::c­onn­ect();
networ­k:c­onn­ect();

module - refere­ncing a submodule

mod client; => mod client  { //client.rs contents here }
mod network {
 //snippet
}
//contents of clents.rs
fn connect { // No need to add mod declaration  

}

module­s-tree

communicator
 ├── client
 └── network
     └── server
└── src
    ├── client.rs
    ├── lib.rs  // mod client; mod network;
    └── network
        ├── mod.rs mod server;
        └── server.rs

modules- rules

└── foo
    ├── bar.rs (contains the declarations in 
foo::bar
)     └── mod.rs (contains the declarations in
foo
, including
mod bar
)
If a module named foo has no submod­ules, you should put the declar­ations for foo in a file named foo.rs.

If a module named foo does have submod­ules, you should put the declar­ations for foo in a file named foo/mo­d.rs.

pub - privacy rules

If an item is public
it can be accessed through any of its parent modules
If an item is private
it can be accessed only by its immediate parent module and any of the parent’s child modules

use

bring modules into scope.
pub mod a {
    pub mod series {
        pub mod of {
            pub fn nested_modules() {}
        }
    }
}
fn main() {
    a::series::of::nested_modules();
}
use a::series::of;
fn main() {
    of::nested_modules();
}
In use statement, paths are relative to the crate root by default
super:: confusing? if the module privacy rules state that parent and its immediate children of the parent can access private items, then why we need Super?

Ownership - References Rules

Only one mutable reference in a particular scope. Prevents datarace 
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s; // error[E0499]: cannot borrow s as mutable more than once at a time 
 Combination of mutable and immutable references are not allowed.  to guarantee immutability. 
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; //  error[E0502]: cannot borrow s as mutable because it is also borrowed as immutable
One mutable reference restri­ction prevents data race.
Only All Readers or just One Writer are allowed.

Dangling References

fn main() {
    let reference_to_nothing = dangle();
}
fn dangle() -> &String {
    let s = String::from("hello");
    &s // It is returning reference, borrowed value.. requiring s to be live outside this scope 
} //Compiler check
error[­E0106]: missing lifetime specifier
= help: this function's return type contains a borrowed value, but there is
no value for it to be borrowed from
= help: consider giving it a 'static lifetime

Slices

fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    s.clear(); // Error!
}
fn first_word(s: &String) -> &str //immutable {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i]; //borrowed as immutable
        }
    }
    &s[..]
}
error[­E0502]: cannot borrow s as mutable because it is also borrowed as immutable.
Slicing : [..1] to start at 0, [2..] to the end.

enums

enum IpAddrKind {                       
    V4,
    V6,
}
struct IpAddr {                         
    kind: IpAddrKind, // type
    address: String,
}
let home = IpAddr {
    kind: IpAddrKind::V4,
    address: String::from("127.0.0.1"),
};
let loopback = IpAddr {
    kind: IpAddrKind::V6,
    address: String::from("::1"),
};

enums - variations

enum IpAddr {
    V4(u8, u8, u8, u8),  
    V6(String),
}
struct Ipv4Addr {
    // --snip--
}
struct Ipv6Addr {
    // --snip--
}
enum IpAddr {
    V4(Ipv4Addr),
    V6(Ipv6Addr),
}
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}

enum - bringing some variants into scope

enum TrafficLight {
    Red,
    Yellow,
    Green,
}

use TrafficLight::{Red, Yellow};

fn main() {
    let red = Red;
    let yellow = Yellow;
    let green = TrafficLight::Green;
}
or 
use TrafficLight::*;
glob operator * to bring all items in a namespace.

match - enum

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {  // state value is bind to the variable
            println!("State quarter from {:?}!", state);
            25
        },
    }
}

if let

let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}
or 
//if let concise for one pattern
if let Some(3) = some_u8_value { 
    println!("three");
}

vectors Vec<T>, vec! macro

let v: Vec<i3­2> = Vec::n­ew();
annotate type when not initia­lized with data
let v = vec![1, 2, 3];
macro vec! to create instance and hold data
 let mut v = Vec::n­ew();  v.push(5); v.push(6); 
mut to update the vector. Rush infers the datatype from push.
Inferring datatype from push?

fn function pointer

Use existing functions in place of closure :
fn add_one(x: i32) -> i32 {
    x + 1
}

fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
    f(arg) + f(arg)
}

fn main() {
    let answer = do_twice(add_one, 5);

    println!("The answer is: {}", answer);
}

//return the closure
fn returns_closure() -> Box<Fn(i32) -> i32> {
    Box::new(|x| x + 1)
}

Vector access elements

let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2]; => access with reference 
let third: Option<&i32> = v.get(2); //return None
let hundredth: &i32 = &v[100]; //panic , use get 

//  
let mut v = vec![1, 2, 3, 4, 5];
let first = &v[0]; // immutable borrowing
v.push(6); //immutable borrow above line, it's error to borrow mutable reference again

Vector iterator

let v = vec![100, 32, 57];
for i in &v { 
    println!("{}", i);
}
//mutable vector and i to dereference using 
let mut v = vec![100, 32, 57];
for i in &mut v {
    *i += 50;
}

Vector: enum to store different types

enum SpreadsheetCell {
    Int(i32),
    Float(f64),
    Text(String),
}

let row = vec![
    SpreadsheetCell::Int(3),
    SpreadsheetCell::Text(String::from("blue")),
    SpreadsheetCell::Float(10.12),
];

String, str

str:  string literals are stored in the binary program. 
format: UTF8
OsString, OsStr, CString, and CStr are string variant libraries.

//creating strings
let mut s = String::new();
let s = "initial contents".to_string(); //to_string and from are matter of style
let s = String::from("initial contents");
let hello = String::from("नमस्ते"); //utf-8

String update and concat­enation +/ format!

let mut s = String::from("foo");
s.push_str("bar"); // foobar append , it takes slice so no ownership transfer

let mut s = String::from("lo");
s.push('l'); //character , lol
// concatenation +
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // Note s1 has been moved here and can no longer be used
fn add(self, s: &str) -> String { // s2 string => str deref coersion &s2 => &s2[..]
//more than two string, use 
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");

let s = format!("{}-{}-{}", s1, s2, s3);

strings indexing

let s1 = String­::f­rom­("he­llo­"); let h = s1[0];
error[­E0277]: the trait bound
std::s­tri­ng:­:St­ring: std::o­ps:­:In­dex­<{i­nte­ger­}>
is not satisfied
A String is a wrapper over a Vec<u8­>. UTF8, 2 bytes for some charac­ters, 1byte for some

let len = String­::f­rom­("Ho­la").len(); => 4

let len = String­::f­rom­("Зд­рав­ств­уйт­е").l­en(); => 24

Error - Recove­rable Result, Unreco­verable panic!

RUST_B­ACK­TRACE=1 cargo run
stack trace ? (enable debug symbols)
Err(ref error) if error.k­ind() == ErrorK­ind­::N­otFound =>
error.k­ind()
unwrap
error => panic, ok=> returns value
expect
message when error occurs
let mut f = File::­ope­n("h­ell­o.t­xt")?;
? to propagate error. From trait. From::­from, error type should implement from
File::­ope­n("h­ell­o.t­xt")­?.r­ead­_to­_st­rin­g(&mut s)?;
? for return type Result
Stack unwinding -
[profi­le.r­el­ease]
panic = 'abort'

Cargo in depth

//Cargo profiles
[profile.dev]
opt-level = 0 //overriding defaults 

[profile.release]
opt-level = 3

//re exporting API
pub use kinds::PrimaryColor;
pub use kinds::SecondaryColor;
pub use utils::mix;

cargo.io login: cargo login abcdefghijklmnopqrstuvwxyz012345 
unique package name  to publish and cargo publish
cargo yank --vers 1.0.1
 

HashMa­p<K, V>

use std::collections::HashMap; keys of the same type and values of the same type.
let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);

//creating a hash map from two vectors
let teams  = vec![String::from("Blue"), String::from("Yellow")];
let initial_scores = vec![10, 50];

let scores: HashMap<_, _> = teams.iter().zip(initial_scores.iter()).collect();
//HashMap to get the desired type from collect .. strange way of specifying return value 
// _, _ Rust infers the data types of Key and Value

//access value
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
let score = scores.get(&team_name); //get

//iterating 
for (key, value) in &scores { }

//To only insert if key doesn't have a value scores.entry(String::from("Yellow")).or_insert(50); //returns mutable reference

// let count = map.entry(word).or_insert(0); //to insert 0, for first time key insertion
BuildH­asher type: default is crypto­gra­phi­cally secure hashing, can be slow
Values are moved, and Hashmap takes the ownership of keys and values.

Hasmap insert doesn't take the ownership.
insert of a existing key overrides the value

Generic data types <T>

fn largest<T>(list: &[T]) -> T 

struct Point<T> {
    x: T,
    y: T,
}

struct Point<T, U> {
    x: T,
    y: U,
}

enum Option<T> {
    Some(T),
    None,
}

struct Point<T> {
    x: T,
    y: T,
}


impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

impl Point<f32> { //specific type
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

struct Point<T, U> {
    x: T,
    y: U,
}
//mixup
impl<T, U> Point<T, U> {
    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}
Monomo­rph­ization to specify concrete code at compile time

trait

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

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author()) //default can call other methods in the trait.
    }
}

pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}

impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}

pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}

impl Summary for Tweet { //for 
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}
//default implementation
fn summarize(&self) -> String {
        String::from("(Read more...)")
    }

//to use default implementation,
impl Summary for NewsArticle {} //empty block
we can implement a trait on a type only if either the trait or the type is local to your crate.
cohere­nce­/orphan rule:p­eople’s code can’t break your code and vice versa

Generic constr­aints

// + 
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {

//where clause: with mutilple trait bounds
fn some_function<T, U>(t: T, u: U) -> i32
    where T: Display + Clone,
          U: Clone + Debug
{
Condit­ionally implement on bounds: impl<T: Display + Partia­lOr­d> Pair<T> {

lifetimes

{
    let r;
    {
        let x = 5;
        r = &x;
    }
    println!("r: {}", r);
}
error[E0597]: x does not live long enough

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
error[E0106]: missing lifetime specifier
//lifetime annotations with generics -- means all x, y are has the same lifetime
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}
smaller lifetime is chosen.
static - let s: &'­static str = "I have a static lifeti­me."­;

Iterator

trait Iterator {
    type Item;

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

    // methods with default implementations elided
}
1.The iter method produces an iterator over immutable refere­nces.
2. into_iter to take over ownership of the parent and returns owned values
3. iter_mut - iterate over mutable references
4. consuming adaptors -> uses up iterator such as sum()
5. chain of iterator adaptors following a consumer adaptor gets you the results (ex: collect())
6.

Box<T>, RC<­T>, RefCel­l<T>

RC<­T>
Box<T>
RefCel­l<T>
multiple
Single
Single
Data Ownership
immutable
immutable or mutable
immutable or mutable
Borrowing
compile time
compile time
runtime
checked at
Because RefCel­l<T> allows mutable borrows checked at runtime, we can mutate the value inside the RefCel­l<T> even when the RefCel­l<T> is immutable
let y = &mut x;? let mut y = x; confusion?

threads

 let handle = std::thread::spawn(|| { //spawn new thread
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            std::thread::sleep(std::time::Duration::from_millis(1));
        }
    });
    handle.join().unwrap(); //wait to finish
let v = vec![1, 2, 3];

    let handle = thread::spawn(move || { => to let capture take the ownership
        println!("Here's a vector: {:?}", v);
    });
error[E0373]: closure may outlive the current function, but it borrows v,
which is owned by the current function
 after move drop(v) ^ error value used here after move
JoinHandle is an owned value ?

Closures - Enviro­nment capture - Traits

Taking ownership FnOnce
as it takes ownership of variables it uses from the enviro­nment, closure can be called once
Borrowing mutably FnMut
It borrows mutably, so it can change the enviro­nment
Borrowing immutably "­Fn"
Can FnMut be called multiple times? (which tries borrow mutably in every call) Yes, as call finishes, the variables are available for borrowing.
let equal_to_x = move |z| z == x; to move the ownership of x to the closure.

threads - channel mpsc

use std::sync::mpsc;

let (tx, rx) = mpsc::channel(); tx: transmitter, rx: receiver

 thread::spawn(move || {
        let val = String::from("hi");
        tx.send(val).unwrap();//send takes the ownership of val
    });

let tx1 = mpsc::Sender::clone(&tx); //clone a transmitter
mpsc : multiple producer, single consumer => multiple senders and one receiver

Mutex<­T> , Arc<T>

let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter); //clone
        let handle = thread::spawn(move || {
            let mut num = counter.lock().unwrap();

            *num += 1;
        });
        handles.push(handle);
    }

Sync and Send

concur­rency is part of the standard library not the language.
two concur­rency concepts embedded in the language: the std::m­arker traits Sync and Send
The Send marker trait indicates that ownership of the type implem­enting Send can be transf­erred between threads
except Rc<­T> multiple references but can't be shared between threads
The Sync marker trait indicates that it is safe for the type implem­enting Sync to be referenced from multiple threads
In other words, any type T is Sync if &T (a reference to T) is Send, meaning the reference can be sent safely to another thread

closures

let expensive_closure = |num: u32| -> u32 {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        num
    };

// type inference 
let example_closure = |x| x;

let s = example_closure(String::from("hello"));
let n = example_closure(5); //error type inference only one  type
// memoization or lazy evaluations
impl<T> Cacher<T>
    where T: Fn(u32) -> u32
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.value {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            },
        }
    }
}
let mut expensive_result = Cacher::new(|num| {
        println!("calculating slowly...");
        thread::sleep(Duration::from_secs(2));
        num
    });

//memoization

unsafe

Implem­enting unsafe trait
unsafe trait Foo { } 
 
unsafe impl Foo for i32 { }
mutating static is unsafe
extern "­C" { } Foreign Function Interface (FFI)
call in unsafe block
C : applic­ation binary interface (ABI)
extern "­C" {     fn abs(input: i32) -> i32; }
Calling Rust from other languages
#[no_m­angle] 
pub extern "­C" fn call_f­rom_c() {
printl­n!(­"Just called a Rust function from C!");
}
unsafe block to call unsafe functions
1. Derefe­rence a raw pointer
2. Call an unsafe function or method
3. Access or modify a mutable static variable
4. Implement an unsafe trait

Raw pointers

Different from references and smart pointers, keep in mind that raw pointers:

1. Are allowed to ignore the borrowing rules and have both immutable and mutable pointers, or multiple mutable pointers to the same location
2. Aren’t guaranteed to point to valid memory
3. Are allowed to be null
4. Don’t implement any automatic clean-up

let mut num = 5;

let r1 = &num as *const i32; 
let r2 = &mut num as *mut i32;

unsafe { //dereferencing 
    println!("r1 is: {}", *r1);
    println!("r2 is: {}", *r2);
}

const i32 and mut i32 raw pointers that both pointed to the same memory location, that of num. If instead we’d tried to create an immutable and a mutable reference to num, this would not have compiled because Rust’s ownership rules don’t allow a mutable reference at the same time as any immutable references. With raw pointers, can create mutable pointer and an immutable pointer to the same location, and change data through the mutable pointer, potentially creating a data race.

Lifetimes adv

In our definition of Parser, in order to say that 's (the lifetime of the string slice) is guaranteed to live at least as long as 'c (the lifetime of the reference to Context), we change the lifetime declarations to look like this:


struct Parser<'c, 's: 'c> {
    context: &'c Context<'s>,
}
// lifetime bounds on references to Generic Types
struct StaticRef<T: 'static>(&'static T);

struct Ref<'a, T: 'a>(&'a T);

//Inference of Tait life times
The default lifetime of a trait object is 'static.
With &'a Trait or &'a mut Trait, the default lifetime is 'a.
With a single T: 'a clause, the default lifetime is 'a.
With multiple T: 'a-like clauses, there is no default; we must be explicit.
Box<Red + 'a> or Box<Red + 'static> 
 Just as with the other bounds, this means that any implementor of the Red trait that has references inside must have the same lifetime specified in the trait object bounds as those references

Advanced Traits

pub trait Iterator {
    type Item; // place holder
    fn next(&mut self) -> Option<Self::Item>;
}

//With Generic, needs to annotate the type 
//Default generic type 
trait Add<RHS=Self> { // RHS is self type a = a + a
    type Output;
    fn add(self, rhs: RHS) -> Self::Output;
}
fn main() {
    let person = Human;
    Pilot::fly(&person); //quality to avoid ambiguity 
    Wizard::fly(&person);
    person.fly();
}

Assosciated functions:
//As no self, it can infer 
<Dog as Animal>::baby_name() 

//NewType
use tuple to creat traits on external types
Implem­ented directly on the type has precedence over trait impls

Advanced Types

type Kilometers = i32;
Type aliases
let f: Box<Fn() + Send + 'stati­c> = Box::n­ew(|| printl­n!(­"­hi"));
type Thunk = Box<Fn() + Send + 'stati­c>;
type Result­<T> = Result­<T, std::i­o::­Err­or>;
! never type = void

iterating over string - bytes() and chars()

for c in "नमस्ते".chars() {
    println!("{}", c);
}
न
म
स
्
त
े

for b in "नमस्ते".bytes() {
    println!("{}", b);
}
224
164
// --snip--
165
135

Cargo workspaces

[workspace]
members = [
    "adder",
]

├── Cargo.lock
├── Cargo.toml
├── add-one
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

[dependencies]
add-one = { path = "../add-one" } -- explicit

cargo run -p adder //to run 

cargo install $HOME/.cargo/bin
cargo-something => cargo something
worksp­aces: all related crates share Cargo.lock and output directory.
depend­encies should be added to cargo.toml files to extern crate
 

Comments

No comments yet. Add yours below!

Add a Comment

Your Comment

Please enter your name.

    Please enter your email address

      Please enter your Comment.

          Related Cheat Sheets

          Helix Keyboard Shortcuts