INDEX

Setting/Unsetting Values

1
2
3
4
5
6
set number      # set
set nonumber # unset
set number! # toggle

set numberwidth=3 # key=value
set number numberwidth=3 # Multiple setting at once

Saving session

1
mksession my-work-session.vim

Auto complete

Press Ctrl+n in insert mode.

File Explorer

Give ::Explore command

Repeating commands

  • Press qX where X=any letter to start recording.
  • Use some commands
  • Press @X where X=previously used letter.

You can use @@ after using @X once.

Bookmarks

  • mX where X=a-z for setting local bookmarks(In same file).
  • mX where X=A-Z for setting global bookmarks(In any file).
  • ‘X where X=a-zA-Z to go to the bookmarks.
  • :marks to show all the bookmarks.

Searching Text

Type / in normal mode, then insert keyword and press enter.
Cursor will go to the first occurrence of the keyword. Press
n to go to the next occurrence or N to go to previous
occurrence.

Pressing * will search the current word under cursor.

Indentation

= is used to indent a line.

Managing Tabs

  • :tabm i - Move current tab to i th position.
  • :tabm +i - Move current tab i place right.
  • :tabm -i - Move current tab i place left.

Managing Windows

Resize:

  • Ctrl + w + < - Decrease window width
  • Ctrl + w + > - Increase window width
  • Ctrl + w + - - Decrese window height
  • Ctrl + w + + - Increase window height

Move placement:

  • Ctrl + w + R - Move window up/left
  • Ctrl + w + r - Move window down/right

Intro

Hello world!

1
2
3
fn main() {
println!("Hello, world!");
}

Language Overview

Overview of the language with a guessing game program:

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
27
28
29
30
31
32
use std::io;
use rand::Rng;
use std::cmp::Ordering;

fn main() {
println!("NEW GAME: Guess a number!");

let rnum = rand::thread_rng().gen_range(1..=100);

println!("Secret number: {rnum}");

loop {
println!("Enter your number:");

let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Failed to read line");

let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};

match guess.cmp(&rnum) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}

Common Programming Concepts

Variables and mutability

  • Variables are by default immutable. Use mut keyword to make it mutable.
1
2
3
4
5
let x = 5;
x = x + 1 // Error: x is immutable

let mut y = 10;
y = y * 2 // Correct
  • Use const keyword to specify constants. Must annotate type.
1
2
3
4
fn main() {
const LIGHT_SPEED: f64 = 3e8;
println!("Light speed: {LIGHT_SPEED}");
}
  • Redefining variables shadows the old variable.
1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let x = 5;

let x = x + 1; // Shadowed old x. x is now 6 and still immutable

{
let x = x * 2;
println!("The value of x in the inner scope is: {x}"); // Will print 12
}

println!("The value of x is: {x}"); // Will print 6 as outer scopped value is 6
}

Data Types

Scalar Types

  • Integer data types: i8, u8, i32, u32, i64, u64, i128, u128, isize, usize.
    Default i32.
  • Floating point types: f32, f64. Default f64.
  • Boolean type: true, false.
  • Char type: ‘x’. 4 bytes, so unicode char is also supported. let x: char = 'x'.

Compound Types

Tuples:

  • Fixed size in compile time.
  • Can contain different types.
  • Use vaiable_name.INDEX to specify individual elements: INDEX starts from 0.
1
2
3
4
5
6
7
8
let tup = (500, 'x', 10.5);
let (x, y, z) = tup; // Destructuring

println!("2nd value is {y}");

// You can also index specific elements
let second_value = tup.1;
println!("2nd value is {second_value}");

Array:

  • Contains elements of fixed types.
1
2
3
let a = [1, 2, 3, 4, 5];
let a: [i32; 5] = [1, 2, 3, 4, 5]; // With size and type annotated
let a = [3; 5]; // 5 elements as 3

Functions

1
2
3
4
5
6
7
8
9
fn main() {
let x = plus_one(5);

println!("The value of x is: {x}");
}

fn plus_one(x: i32) -> i32 {
x + 1
}

Statements vs Expressions

  • Expressions evaluate to values.
  • Expressions doesn’t end with semicolon.
  • Expressions can be converted to statements by using semicolon at the end.
  • Function return is specified using expressions in rust.

Control Flow

if/else

  • Condition must be boolean
1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
let number = 6;

if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
  • If is an expression so can be used in let statement
1
2
3
4
5
6
fn main() {
let condition = true;
let number = if condition { 5 } else { 6 };

println!("The value of number is: {number}");
}

Looping

loop keyword:

  • Loops indefinitely.
  • break can use expression to return value.
  • Loops can be named and used in break
    Loop labels must begin with a single quote.

Example: break with expression

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
let mut counter = 0;

let result = loop {
counter += 1;

if counter == 10 {
break counter * 2;
}
};

println!("The result is {result}");
}

Example: named loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fn main() {
let mut count = 0;
// Loop label have to start with '
'counting_up: loop {
println!("count = {count}");
let mut remaining = 10;

loop {
println!("remaining = {remaining}");
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}

count += 1;
}
println!("End count = {count}");
}

while keyword:

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut number = 3;

while number != 0 {
println!("{number}!");

number -= 1;
}

println!("LIFTOFF!!!");
}

for keyword:

  • Used to loop through a collection:

Example 1:

1
2
3
4
5
6
7
fn main() {
let a = [10, 20, 30, 40, 50];

for element in a {
println!("the value is: {element}");
}
}

Example 2:

1
2
3
4
5
6
fn main() {
for number in (1..4).rev() {
println!("{number}!");
}
println!("LIFTOFF!!!");
}

Ownership in Rust

Ownership Rules

  • Each value in Rust has an owner.
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped.

In rust the memory is automatically returned
once the variable that owns it goes out of scope.
When a variable goes out of scope rust calls a special
method drop(). This is implemented on the specific type.
This function is responsible for cleaning up memory
for that type.

Variables and Data Interacting with Move

Rust always do shallow copy.

1
2
3
4
5
6
7
8
let s1 = String::from("hello");
// s1 and s2 are pointing to the same data
// as data isn't copied.
//
// s1's ownership is moved to s2. s1 no longer valid
let s2 = s1;

println!("{}, world!", s1); // Won't compile s1 not valid

Variables and Data Interacting with Clone

It is possible to do deep copy with clone() method.

1
2
3
4
let s1 = String::from("hello");
let s2 = s1.clone();

println!("s1 = {}, s2 = {}", s1, s2);

Ownership and Functions

Ownership is also move in function parameters:

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
fn main() {
let s = String::from("hello"); // s comes into scope

takes_ownership(s); // s's value moves into the function...
// ... and so is no longer valid here

let x = 5; // x comes into scope

makes_copy(x); // x would move into the function,
// but i32 is Copy, so it's okay to still
// use x afterward

// This won't compile as ownership is moved
println!("String {s}");

} // Here, x goes out of scope, then s. But because s's value was moved, nothing
// special happens.

fn takes_ownership(some_string: String) { // some_string comes into scope
println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing
// memory is freed.

fn makes_copy(some_integer: i32) { // some_integer comes into scope
println!("{}", some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.

Return Values and Scope

  • Return values can also transfer ownership
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
27
28
29
fn main() {
let s1 = gives_ownership(); // gives_ownership moves its return
// value into s1

let s2 = String::from("hello"); // s2 comes into scope

let s3 = takes_and_gives_back(s2); // s2 is moved into
// takes_and_gives_back, which also
// moves its return value into s3
} // Here, s3 goes out of scope and is dropped. s2 was moved, so nothing
// happens. s1 goes out of scope and is dropped.

fn gives_ownership() -> String { // gives_ownership will move its
// return value into the function
// that calls it

let some_string = String::from("yours"); // some_string comes into scope

some_string // some_string is returned and
// moves out to the calling
// function
}

// This function takes a String and returns one
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
// scope

a_string // a_string is returned and moves out to the calling function
}

Reference and Borrowing

To use functions with parameters you need
to give ownership of the parameters to the
function and then return it back:

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
let s1 = String::from("hello");

let (s2, len) = calculate_length(s1);

println!("The length of '{}' is {}.", s2, len);
}

fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() returns the length of a String

(s, length)
}

To remove this hassle one can use references to refer
to some value without taking ownership. This is called
borrowing in rust terminology.

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let s1 = String::from("hello");

let len = calculate_length(&s1);

println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
s.len()
}

& is used to specify references.

To change the value of the variable that
a reference is pointing to, the reference also
need to be mutable. For example following code
won’t compile:

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let s = String::from("hello");

change(&s);
}

// The reference some_string isn't mutable
// so it won't compile as it is changing
// the value of the variable it is poiting to.
fn change(some_string: &String) {
some_string.push_str(", world");
}

Use mut keyword to specify mutable reference:

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut s = String::from("hello");

change(&mut s);

println!("{}", s);
}

fn change(some_string: &mut String) {
some_string.push_str(", world");
}

Rules for mutable references:

  • There can be as many as immutable references.
  • There can be only one mutable reference simultaneously.
  • Mutable reference can’t be mixed up with immutable reference
    as immutable reference is expecting value won’t change.
  • This restrictions are given to remove data race at compile time.

Example 1: This won’t compile as two mutable reference
of the same variable.

1
2
3
4
5
6
7
8
fn main() {
let mut s = String::from("hello");

let r1 = &mut s;
let r2 = &mut s;

println!("{}, {}", r1, r2);
}

Example 2:

1
2
3
4
5
6
7
8
9
fn main() {
let mut s = String::from("hello");

{
let r1 = &mut s;
} // r1 goes out of scope here, so we can make a new reference with no problems.

let r2 = &mut s;
}

Example 3:

1
2
3
4
5
6
7
8
9
fn main() {
let mut s = String::from("hello");

let r1 = &s; // no problem
let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM

println!("{}, {}, and {}", r1, r2, r3);
}

Example 4:

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let mut s = String::from("hello");

let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{} and {}", r1, r2);
// variables r1 and r2 will not be used after this point

let r3 = &mut s; // no problem
println!("{}", r3);
}

Dangling References

It is not possible to create dangling references
in rust. Compiler does a lifetime analysis and will
be caught.

This will not compile and give compiler error:

1
2
3
4
5
6
7
8
9
fn main() {
let reference_to_nothing = dangle();
}

fn dangle() -> &String {
let s = String::from("hello");

&s
}

The Rules of References

  • At any given time, you can have either one mutable
    reference or any number of immutable references.

  • References must always be valid.

Slice Type

Slices are a kind of reference which points
to a portion of a value.

&str - type of string slice.
&[i32] - Type of i32 array slice.
&[T] - Type of slice of array of type T.

String Slices

Type of string slice is &str

Example 1:

1
2
3
4
5
6
7
#![allow(unused)]
fn main() {
let s = String::from("hello world");

let hello = &s[0..5];
let world = &s[6..11];
}

Example 2:

1
2
3
4
5
6
7
#![allow(unused)]
fn main() {
let s = String::from("hello");

let slice = &s[0..2];
let slice = &s[..2];
}

Example 3:

1
2
3
4
5
6
7
8
9
#![allow(unused)]
fn main() {
let s = String::from("hello");

let len = s.len();

let slice = &s[3..len];
let slice = &s[3..];
}

Example 4:

1
2
3
4
5
6
7
8
9
#![allow(unused)]
fn main() {
let s = String::from("hello");

let len = s.len();

let slice = &s[0..len];
let slice = &s[..];
}

Example 5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();

for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}

&s[..]
}

fn main() {
let mut s = String::from("hello world");

let word = first_word(&s);

s.clear(); // error!

println!("the first word is: {}", word);
}

Structs

Defining and Instantiating Structs

Definition:

1
2
3
4
5
6
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}

Using the struct:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}

fn main() {
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
}

Specific field is specified using the dot notation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}

fn main() {
// All the fields are mutable now
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};

user1.email = String::from("anotheremail@example.com");
}
  • It’s not possible to set a specific field as mutable.
    All the fields have to be mutable.

Struct instantiation is an expression so it is possible to use
as functions return:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}

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

fn main() {
let user1 = build_user(
String::from("someone@example.com"),
String::from("someusername123"),
);
}

Using the Field Init Shorthand

If field name and parameter name is same
then you don’t need to specify field names:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}

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

fn main() {
let user1 = build_user(
String::from("someone@example.com"),
String::from("someusername123"),
);
}

Creating Instances From Other Instances With Struct Update Syntax

The syntax .. specifies that the remaining fields
not explicitly set should have the same value as
the fields in the given instance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct User {
active: bool,
username: String,
email: String,
sign_in_count: u64,
}

fn main() {
// --snip--

let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};

let user2 = User {
email: String::from("another@example.com"),
..user1
};
}

Update syntax uses move semantics. So if any of the fields
uses move semantic then the ownership will be moved.
For example user1 will be invalid
after using the update syntaxt on user2.
as email and username field ownership will be moved

Using Tuple Structs without Named Fields to Create Different Types

1
2
3
4
5
6
7
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

fn main() {
let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);
}

Unit-Like Structs Without Any Fields

Unit-like structs can be useful when you need to
implement a trait on some type but don’t have any data
that you want to store in the type itself

1
2
3
4
5
struct AlwaysEqual;

fn main() {
let subject = AlwaysEqual;
}

Ownership of Struct Data

To use a reference in a struct field you
need to specifiy lifetime to make sure
that the reference will be valid as long as
the struct is valid.

Defining methods of a struct

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
// &self is short for self: &self
fn area(&self) -> u32 {
self.width * self.height
}
}

fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};

println!(
"The area of the rectangle is {} square pixels.",
rect1.area()
);
}

Rust doesn’t have an equivalent to the -> operator;
instead, Rust has a feature called automatic referencing and dereferencing.

Methods with More Parameters

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
27
28
29
30
31
32
33
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

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

fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}

fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};

println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

Associated Functions

  • All functions defined within an impl block are called
    associated functions because they’re associated with
    the type named after the impl.

  • We can define associated functions that don’t have
    self as their first parameter.

  • Associated functions that aren’t methods are often
    used for constructors that will return a new instance of the struct.

  • To call this associated functions we use :: after struct name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

impl Rectangle {
fn square(size: u32) -> Self {
Self {
width: size,
height: size,
}
}
}

fn main() {
let sq = Rectangle::square(3);
}

It is allowed to have multiple impl block for the
same struct.

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
27
28
29
30
31
32
33
34
35
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}

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

impl Rectangle {
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}

fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};

println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));
}

Enum and Pattern Matching

Defining Enum

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum IpAddrKind {
V4,
V6,
}

fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

route(IpAddrKind::V4);
route(IpAddrKind::V6);
}

fn route(ip_kind: IpAddrKind) {}

Enum’s can have data in them.
Consider following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fn main() {
enum IpAddrKind {
V4,
V6,
}

struct IpAddr {
kind: IpAddrKind,
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"),
};
}

This same program can be expresses with:

1
2
3
4
5
6
7
8
9
10
fn main() {
enum IpAddr {
V4(String),
V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));

let loopback = IpAddr::V6(String::from("::1"));
}

Methods on Enums

Similar to structs it is possible to implement
method on enums:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn main() {
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();
}

The Option Enum and Its Advantages Over Null Values

  • Rust doesn’t have NULL

  • The Option type encodes the scenario in which
    a value could be something or it could be nothing.

  • Expressing this concept in terms of the type system
    means the compiler can check whether you’ve handled
    all the cases you should be handling

  • Option is implemented as following enum:

    1
    2
    3
    4
    enum Option<T> {
    None,
    Some(T),
    }

Example:

1
2
3
4
5
6
fn main() {
let some_number = Some(5);
let some_char = Some('e');

let absent_number: Option<i32> = None;
}
  • Option<T> can be used with T. Option<T> can
    be converted to T by handling null case.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
}

fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}

The match Control Flow Construct

  • Can be used to compare a value agains a
    series of patters and execute code based
    on the pattern matched.

  • This confirms the compiler that all possible
    cases are handled.

  • Each pattern is called an arm

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}

fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}

fn main() {}

Another Example 2:

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
27
28
29
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// --snip--
}

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

fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("State quarter from {:?}!", state);
25
}
}
}

fn main() {
value_in_cents(Coin::Quarter(UsState::Alaska));
}
  • Matches are exhaustive. You have to handle
    all the possible cases.

Catch-all Patterns and the _ Placeholder

Example 1:

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
other => move_player(other),
}

fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn move_player(num_spaces: u8) {}
}
  • Rust will warn if any arm is specified after the catch all
    pattern.

  • It is possbile to use _ to add a catch all pattern
    but don’t use the value.

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => reroll(),
}

fn add_fancy_hat() {}
fn remove_fancy_hat() {}
fn reroll() {}
}
  • It also possible to take no action
    for an arm:
1
2
3
4
5
6
7
8
9
10
11
fn main() {
let dice_roll = 9;
match dice_roll {
3 => add_fancy_hat(),
7 => remove_fancy_hat(),
_ => (),
}

fn add_fancy_hat() {}
fn remove_fancy_hat() {}
}

Concise Control Flow with if let

  • Works like reduced match. Only handle one case
    and ignore all the other case.

Consider following code:

1
2
3
4
5
6
7
8
9
fn main() {
let config_max = Some(3u8);
// if config_max has value then print it
// if it is None do nothing
match config_max {
Some(max) => println!("The maximum is configured to be {}", max),
_ => (),
}
}

This can be expressed more consciously with if/let combo:

1
2
3
4
5
6
7
8
9
fn main() {
let config_max = Some(3u8);
if let Some(max) = config_max {
println!("Maximum value {max}");
}
else {
println!("Maximum isn't set");
}
}

Common Collection

Vectors

  • Stores values of same type
  • You must annotate the type while creating
    a new vector.
1
let v: Vec<i32> = Vec::new();
  • If initial values are provided rust will infer the type.
1
let v = vec![1, 2, 3];
  • Updating a vecotr:
1
2
3
4
5
6
7
8
fn main() {
let mut v = Vec::new();

v.push(5);
v.push(6);
v.push(7);
v.push(8);
}
  • There are two ways to reference a value stored in a vector:
    via indexing or using the get method. If get method is used
    Option<&T> is returned.
1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let v = vec![1, 2, 3, 4, 5];

let third: &i32 = &v[2];
println!("The third element is {third}");

let third: Option<&i32> = v.get(2);
match third {
Some(third) => println!("The third element is {third}"),
None => println!("There is no third element."),
}
}
  • Ownership and borrowing rules are applicable for references
    to a vector. Following code won’t compile:
1
2
3
4
5
6
7
8
9
fn main() {
let mut v = vec![1, 2, 3, 4, 5];

let first = &v[0];

v.push(6);

println!("The first element is: {first}");
}
  • Iterating over the values of a vector:

Immutable reference:

1
2
3
4
5
6
fn main() {
let v = vec![100, 32, 57];
for i in &v {
println!("{i}");
}
}

Mutable reference: To change the value that the mutable reference
refers to, we have to use the * dereference operator to get to the value .

1
2
3
4
5
6
fn main() {
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
}
  • Using Enum to store multiple types: Vectors can only store values
    that are the same type. Enums can be used as workaround for this.
    With enum definitions different types can coexists in a vector.
1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}

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

Hashmap

  • All keys have to be same type and
    all values have to same type.
1
2
3
4
5
6
7
8
fn main() {
use std::collections::HashMap;

let mut scores = HashMap::new();

scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
}
  • Accesing values in a hashmap: Values can be accesed
    with get method. Get method returns Options<&T>.
    copied method can be used to convert to Options<T>.
    In the following example unwrap_or is used to convert
    the value to 0 if returned value is None (If no such key).
1
2
3
4
5
6
7
8
9
10
11
fn main() {
use std::collections::HashMap;

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).copied().unwrap_or(0);
}
  • Hashmap and ownership: For types that implement the Copy trait,
    like i32, the values are copied into the hash map.
    For owned values like String, the values will be moved and
    the hash map will be the owner of those values
1
2
3
4
5
6
7
8
9
10
11
fn main() {
use std::collections::HashMap;

let field_name = String::from("Favorite color");
let field_value = String::from("Blue");

let mut map = HashMap::new();
map.insert(field_name, field_value);
// field_name and field_value are invalid at this point, try using them and
// see what compiler error you get!
}
  • Inserting a value for the same key will overwrite it’s value

  • Adding a Key and Value Only If a Key Isn’t Present:
    entry method returns an enum Entry that represents
    a value that might or might not exist. The or_insert
    method on Entry is defined to return a mutable reference
    to the value for the corresponding Entry key if that key exists,
    and if not, inserts the parameter as the new value
    for this key and returns a mutable reference to the new value.

1
2
3
4
5
6
7
8
9
10
11
fn main() {
use std::collections::HashMap;

let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);

scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Blue")).or_insert(50);

println!("{:?}", scores);
}
  • Updating a Value Based on the Old Value:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn main() {
use std::collections::HashMap;

let text = "hello world wonderful world";

let mut map = HashMap::new();

for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}

println!("{:?}", map);
}

Error Handling

  • Rust doesn’t have execptions.
  • Result<T, E> for recoverable errors.
  • panic!() macro for unrecoverable erros.

Recoverable Errors with Result

  • Result Enum:
    1
    2
    3
    4
    enum Result<T, E> {
    Ok(T),
    Err(E),
    }

Example 1:

1
2
3
4
5
6
7
8
9
10
use std::fs::File;

fn main() {
let greeting_file_result = File::open("hello.txt");

let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}

Example 2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use std::fs::File;
use std::io::ErrorKind;

fn main() {
let greeting_file_result = File::open("hello.txt");

let greeting_file = match greeting_file_result {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),
},
other_error => {
panic!("Problem opening the file: {:?}", other_error);
}
},
};
}
  • Using closures to write above program without match syntax
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    use std::fs::File;
    use std::io::ErrorKind;

    fn main() {
    let greeting_file = File::open("hello.txt").unwrap_or_else(|error| {
    if error.kind() == ErrorKind::NotFound {
    File::create("hello.txt").unwrap_or_else(|error| {
    panic!("Problem creating the file: {:?}", error);
    })
    } else {
    panic!("Problem opening the file: {:?}", error);
    }
    });
    }

Shortcuts for Panic on Error: unwrap and expect

The unwrap method is a shortcut method implemented
just like the match expression. If the Result value is
the Ok variant, unwrap will return the value inside the Ok.
If the Result is the Err variant, unwrap will call the panic! macro

1
2
3
4
5
use std::fs::File;

fn main() {
let greeting_file = File::open("hello.txt").unwrap();
}
  • expects works like same but gives the ability
    to write custom panic message:
1
2
3
4
5
6
use std::fs::File;

fn main() {
let greeting_file = File::open("hello.txt")
.expect("hello.txt should be included in this project");
}

Propagating erros

Instead of handling function returns
error to the caller to handle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#![allow(unused)]
fn main() {
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
let username_file_result = File::open("hello.txt");

let mut username_file = match username_file_result {
Ok(file) => file,
Err(e) => return Err(e),
};

let mut username = String::new();

match username_file.read_to_string(&mut username) {
Ok(_) => Ok(username),
Err(e) => Err(e),
}
}
}

A Shortcut for Propagating Errors: the ? Operator

The ? placed after a Result value is defined to work
in almost the same way as the match expressions is
defined to handle the Result values.
If the value of the Result is an Ok,
the value inside the Ok will get returned from this expression,
and the program will continue. If the value is an Err,
the Err will be returned from the whole function as if
the return keyword is used so that the error value gets
propagated to the calling code.

There is a difference between what the match expression does
and what the ? operator does: error values that have the ? operator
called on them go through the from function, defined in the From trait
in the standard library, which is used to convert values from
one type into another. When the ? operator calls the from function,
the error type received is converted into the error type
defined in the return type of the current function.
This is useful when a function returns one error type to represent
all the ways a function might fail,
even if parts might fail for many different reasons.

1
2
3
4
5
6
7
8
9
10
11
12
#![allow(unused)]
fn main() {
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
let mut username_file = File::open("hello.txt")?;
let mut username = String::new();
username_file.read_to_string(&mut username)?;
Ok(username)
}
}

This code can be made even shorter:

1
2
3
4
5
6
7
8
9
10
11
12
13
#![allow(unused)]
fn main() {
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
let mut username = String::new();

File::open("hello.txt")?.read_to_string(&mut username)?;

Ok(username)
}
}

The ? operator can only be used in functions
whose return type is compatible with the value the ? is used on.
This operator can be used in a function that returns Result, Option,
or another type that implements FromResidual.

? operator can be used on a Result in a function that returns Result,
and you can be use on an Option in a function that returns Option.

To use ? in main() return type of main needs to be Result<(), E>:

1
2
3
4
5
6
7
8
std::error::Error;
use std::fs::File;

fn main() -> Result<(), Box<dyn Error>> {
let greeting_file = File::open("hello.txt")?;

Ok(())
}

Generic Data Types

In Function Definitions

Example 1: This won’t compile as std::cmp::PartialOrd
trait isn’t implemented:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fn largest<T>(list: &[T]) -> &T {
let mut largest = &list[0];

for item in list {
if item > largest {
largest = item;
}
}

largest
}

fn main() {
let number_list = vec![34, 50, 25, 100, 65];

let result = largest(&number_list);
println!("The largest number is {}", result);

let char_list = vec!['y', 'm', 'a', 'q'];

let result = largest(&char_list);
println!("The largest char is {}", result);
}

In Struct Definitions

Example 1:

1
2
3
4
5
6
7
8
9
10
11
struct Point<T> {
x: T,
y: T,
}

fn main() {
// Both x and y have to be same type
// as the struct definition has same
// type T for both fields
let wont_work = Point { x: 5, y: 4.0 };
}

Example 2:

1
2
3
4
5
6
7
8
9
10
struct Point<T, U> {
x: T,
y: U,
}

fn main() {
let both_integer = Point { x: 5, y: 10 };
let both_float = Point { x: 1.0, y: 4.0 };
let integer_and_float = Point { x: 5, y: 4.0 };
}

In Enum Definitions

Example 1:

1
2
3
4
5
6
7
#![allow(unused)]
fn main() {
enum Option<T> {
Some(T),
None,
}
}

Example 2:

1
2
3
4
5
6
7
#![allow(unused)]
fn main() {
enum Result<T, E> {
Ok(T),
Err(E),
}
}

In Method Definitions

Example 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Point<T> {
x: T,
y: T,
}

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

fn main() {
let p = Point { x: 5, y: 10 };

println!("p.x = {}", p.x());
}

It is possible to implement method on a concrete type.
This will restrict the method for that specific type.
If a method is implemented on P<f64> that method will
only be implemented for P<f64>. Other P<T> won’t
have that method.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct Point<T> {
x: T,
y: T,
}

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

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

fn main() {
let p = Point { x: 5, y: 10 };

println!("p.x = {}", p.x());
}

Example 2: A more generic example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct Point<X1, Y1> {
x: X1,
y: Y1,
}

impl<X1, Y1> Point<X1, Y1> {
fn mixup<X2, Y2>(self, other: Point<X2, Y2>) -> Point<X1, Y2> {
Point {
x: self.x,
y: other.y,
}
}
}

fn main() {
let p1 = Point { x: 5, y: 10.4 };
let p2 = Point { x: "Hello", y: 'c' };

let p3 = p1.mixup(p2);

println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

Monomorphization: Rust compiler turns the generic
code into concrete type.

Traits

  • A trait defines functionality a particular type has
    and can share with other types.

  • We can use trait bound to specify that a generic
    type can be any type that has certain behaviour.

Defining a Trait

Example:

1
2
3
pub trait Summary {
fn summarize(&self) -> String;
}

Implementing a Trait on a Type

Example:

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
27
28
29
pub trait Summary {
fn summarize(&self) -> String;
}

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 {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}

A trait can be implemented on a type
if at least one of the trait or the type
it local to the crate. For example,
we can implement standard library traits
like Display on a custom type like Tweet
as part of our aggregator crate functionality,
because the type Tweet is local to our aggregator crate.
We can also implement Summary on Vec in our
aggregator crate, because the trait Summary is local
to our aggregator crate. But we can’t implement
the Display trait on Vec within our aggregator crate,
as both are external to our aggregator crate.

Default Implementations

  • If implementation is provided for a
    trait method, then it is not required
    to implement that method for a type.
    If a type doesn’t implement it the
    default implementation of the function
    will be used.
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
27
28
29
30
pub trait Summary {
fn summarize(&self) -> String {
String::from("(Read more...)")
}
}

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

// An empty impl block is required
// to specify that this type has
// Summary trait
impl Summary for NewsArticle {}

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

impl Summary for Tweet {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}
  • If a default implementation isn’t provided,
    for a method, then a implementation must be
    provided by the types that are using the
    trait.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pub trait Summary {
// Default implementation isn't provided
fn summarize_author(&self) -> String;

fn summarize(&self) -> String {
format!("(Read more from {}...)", self.summarize_author())
}
}

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

// Tweet only have to implement summarize_author method
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("@{}", self.username)
}
}

Trait As Parameter

If a Trait is used a parameter to a function,
then that function will accept any type that
implements that Trait.

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
pub trait Summary {
fn summarize(&self) -> String;
}

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 {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}

pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}

fn main() {
let tweet = Tweet {
username: String::from("aagontuk"),
content: String::from("Hello, world!"),
retweet: 0,
reaction: 0,
};

notify(tweet);
}

Trait Bound Syntax

Function signatures can be written
more concisely with trait bound box.
For example notice following function:

1
2
3
pub fn notify(item: &impl Summary) {
println!("Breaking news! {}", item.summarize());
}

With trait bounds this can be written:

1
2
3
pub fn notify<T: Summary>(item: &T) {
println!("Breaking news! {}", item.summarize());
}

Benefit of this will be more clear with multiple
Trait parameter:

1
pub fn notify(item1: &impl Summary, item2: &impl Summary) {

With Trait bound syntax:

1
pub fn notify<T: Summary>(item1: &T, item2: &T) {

Specifying Multiple Trait Bounds with the + Syntax

We can specify more than one Trait bound.
Following function will accept any type that
implements Summary and Display trait.

Without Trait bound syntax:

1
pub fn notify(item: &(impl Summary + Display)) {

With Trait bound syntax:

1
pub fn notify<T: Summary + Display>(item: &T) {

Clearer Trait Bounds with where Clauses

Consider following function with Trait bounds:

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

With where clause this can be written:

1
2
3
4
5
6
7
fn some_function<T, U>(t: &T, u: &U) -> i32
where
T: Display + Clone,
U: Clone + Debug,
{

}

Returning Types that Implement Traits

  • impl Trait syntax can be used in function
    return to return a type that implements that
    trait.

  • impl Trait can only be used if the function
    returns a single type.

Example:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
pub trait Summary {
fn summarize(&self) -> String;
}

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 {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}

// Uses impl Summary as return type
fn returns_summarizable() -> impl Summary {
Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
}
}

Following code won’t compile as it can return different
types:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
pub trait Summary {
fn summarize(&self) -> String;
}

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 {
fn summarize(&self) -> String {
format!("{}: {}", self.username, self.content)
}
}

// This functions can return either NewsArticle or Tweet
fn returns_summarizable(switch: bool) -> impl Summary {
if switch {
NewsArticle {
headline: String::from(
"Penguins win the Stanley Cup Championship!",
),
location: String::from("Pittsburgh, PA, USA"),
author: String::from("Iceburgh"),
content: String::from(
"The Pittsburgh Penguins once again are the best \
hockey team in the NHL.",
),
}
} else {
Tweet {
username: String::from("horse_ebooks"),
content: String::from(
"of course, as you probably already know, people",
),
reply: false,
retweet: false,
}
}
}

Using Trait Bounds to Conditionally Implement Methods

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
27
use std::fmt::Display;

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

// new() method is implemented for
// all inner type T
impl<T> Pair<T> {
fn new(x: T, y: T) -> Self {
Self { x, y }
}
}

// cmp_display() method is only
// implemented for inner types
// that implements Display and PartialOrd
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest member is x = {}", self.x);
} else {
println!("The largest member is y = {}", self.y);
}
}
}

We can also conditionally implement a trait for
any type that implements another trait.
Implementations of a trait on any type that
satisfies the trait bounds are called blanket implementations.

1
2
3
impl<T: Display> ToString for T {

}

Lifetime

  • Lifetimes ensure that references are valid
    as long as we need them to be.

  • Most of the cases lifetime is automatically inferred.

  • we must annotate lifetimes when the
    lifetimes of references could be related in a few different ways.
    Rust requires us to annotate the relationships
    using generic lifetime parameters to ensure
    the actual references used at runtime will definitely be valid.

  • The main aim of lifetimes is to prevent dangling references,
    which cause a program to reference data other than
    the data it’s intended to reference.

The Borrow Checker

  • The Rust compiler has a borrow checker that compares
    scopes to determine whether all borrows are valid.

  • Following program will be rejected because the
    borrow checker will see that x’s lifetime ‘b is
    shorter than r’s lifetime ‘a which is referencing
    x.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fn main() {
    let r; // ---------+-- 'a
    // |
    { // |
    let x = 5; // -+-- 'b |
    r = &x; // | |
    } // -+ |
    // |
    println!("r: {}", r); // |
    } // ---------+
  • Following code will be accepted x’s lifetime ‘b
    is longer than r’s lifetime ‘a. So the compiler
    knows r will be always valid when x valid.

    1
    2
    3
    4
    5
    6
    7
    8
    fn main() {
    let x = 5; // ----------+-- 'b
    // |
    let r = &x; // --+-- 'a |
    // | |
    println!("r: {}", r); // | |
    // --+ |
    } // ----------+

Lifetime Annotation Syntax

1
2
3
&i32        // a reference
&'a i32 // a reference with an explicit lifetime
&'a mut i32 // a mutable reference with an explicit lifetime

Lifetime Annotations in Function Signatures

Following function signature tells Rust that
for some lifetime ‘a, the function takes two parameters,
both of which are string slices that live
at least as long as lifetime ‘a.
The function signature also tells Rust
that the string slice returned from the function
will live at least as long as lifetime ‘a.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";

let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
  • When returning a reference from a function,
    the lifetime parameter for the return type
    needs to match the lifetime parameter for one of the parameters.

Lifetime Annotations in Struct Definitions

  • We can define structs to hold references,
    but in that case we would need to add
    a lifetime annotation on every reference
    in the struct’s definition.
1
2
3
4
5
6
7
8
9
10
11
struct ImportantExcerpt<'a> {
part: &'a str,
}

fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
}
  • This annotation means an instance of ImportantExcerpt
    can’t outlive the reference it holds in its part field.

Lifetime Elision

The compiler uses three rules to figure out
the lifetimes of the references when there aren’t
explicit annotations. The first rule applies to
input lifetimes, and the second and third rules
apply to output lifetimes. If the compiler
gets to the end of the three rules and there are still
references for which it can’t figure out lifetimes,
the compiler will stop with an error.
These rules apply to fn definitions as well as impl blocks.

  • The first rule is that the compiler assigns
    a lifetime parameter to each parameter that’s a reference.
    In other words, a function with one parameter gets
    one lifetime parameter: fn foo<'a>(x: &'a i32);
    a function with two parameters gets two separate
    lifetime parameters: fn foo<'a, 'b>(x: &'a i32, y: &'b i32); and so on.

  • The second rule is that, if there is
    exactly one input lifetime parameter,
    that lifetime is assigned to all output
    lifetime parameters: fn foo<'a>(x: &'a i32) -> &'a i32.

  • The third rule is that, if there
    are multiple input lifetime parameters,
    but one of them is &self or &mut self because this is a method,
    the lifetime of self is assigned to all output lifetime parameters.

Lifetime Annotations in Method Definitions

Example 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct ImportantExcerpt<'a> {
part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
fn level(&self) -> i32 {
3
}
}

impl<'a> ImportantExcerpt<'a> {
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention please: {}", announcement);
self.part
}
}

fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().expect("Could not find a '.'");
let i = ImportantExcerpt {
part: first_sentence,
};
}

The Static Lifetime

  • 'static lifetime denotes that the
    affected reference can live for
    the entire duration of the program.

  • All string literals have the 'static lifetime

Generic Type Parameters, Trait Bounds, and Lifetimes Together

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
27
28
29
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";

let result = longest_with_an_announcement(
string1.as_str(),
string2,
"Today is someone's birthday!",
);
println!("The longest string is {}", result);
}

use std::fmt::Display;

fn longest_with_an_announcement<'a, T>(
x: &'a str,
y: &'a str,
ann: T,
) -> &'a str
where
T: Display,
{
println!("Announcement! {}", ann);
if x.len() > y.len() {
x
} else {
y
}
}

Closures: Anonymous Functions that Capture Their Environment

Capturing the Environment with Closures

  • The unwrap_or_else() method on Option<T>
    is defined by the standard library.
    It takes one argument: a closure without any arguments
    that returns a value T (the same type stored
    in the Some variant of the Option<T>)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#[derive(Debug, PartialEq, Copy, Clone)]
enum ShirtColor {
Red,
Blue,
}

struct Inventory {
shirts: Vec<ShirtColor>,
}

impl Inventory {
fn giveaway(&self, user_preference: Option<ShirtColor>) -> ShirtColor {
user_preference.unwrap_or_else(|| self.most_stocked())
}

fn most_stocked(&self) -> ShirtColor {
let mut num_red = 0;
let mut num_blue = 0;

for color in &self.shirts {
match color {
ShirtColor::Red => num_red += 1,
ShirtColor::Blue => num_blue += 1,
}
}
if num_red > num_blue {
ShirtColor::Red
} else {
ShirtColor::Blue
}
}
}

fn main() {
let store = Inventory {
shirts: vec![ShirtColor::Blue, ShirtColor::Red, ShirtColor::Blue],
};

let user_pref1 = Some(ShirtColor::Red);
let giveaway1 = store.giveaway(user_pref1);
println!(
"The user with preference {:?} gets {:?}",
user_pref1, giveaway1
);

let user_pref2 = None;
let giveaway2 = store.giveaway(user_pref2);
println!(
"The user with preference {:?} gets {:?}",
user_pref2, giveaway2
);
}

Closure Type Inference and Annotation

  • Closures usually don’t require to specify
    types of the parameter or the return value
    like functions.

  • Closures are stored in variables and used
    without naming them and expose them only
    to the user of the specific library.

  • Closures are short and relevant only
    within a narrow context.

  • Within these limited contexts,
    the compiler can infer the types
    of the parameters and the return type

  • It’s also possible to annotate type
    like variables.

Example:

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
27
28
29
30
31
use std::thread;
use std::time::Duration;

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

if intensity < 25 {
println!("Today, do {} pushups!", expensive_closure(intensity));
println!("Next, do {} situps!", expensive_closure(intensity));
} else {
if random_number == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!(
"Today, run for {} minutes!",
expensive_closure(intensity)
);
}
}
}

fn main() {
let simulated_user_specified_value = 10;
let simulated_random_number = 7;

generate_workout(simulated_user_specified_value, simulated_random_number);
}
  • Comparison with functions syntax:

    1
    2
    3
    4
    fn  add_one_v1   (x: u32) -> u32 { x + 1 }
    let add_one_v2 = |x: u32| -> u32 { x + 1 };
    let add_one_v3 = |x| { x + 1 };
    let add_one_v4 = |x| x + 1 ;
  • If type isn’t annotated compiler infers
    closure’s type.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    fn main() {
    let example_closure = |x| x;

    // Compiler infers x as String and return type as String
    // and locks this inference.
    let s = example_closure(String::from("hello"));

    // This will produce a compiler error
    // as the compiler expects the parameter to be
    // a string
    let n = example_closure(5);
    }

Capturing References or Moving Ownership

Closures can capture values from their environment in three ways,
which directly map to the three ways a function can take a parameter:
borrowing immutably, borrowing mutably, and taking ownership.
The closure will decide which of these to use based on
what the body of the function does with the captured values.

  • Captures an immutable reference:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fn main() {
    let list = vec![1, 2, 3];
    println!("Before defining closure: {:?}", list);

    let only_borrows = || println!("From closure: {:?}", list);

    println!("Before calling closure: {:?}", list);
    only_borrows();
    println!("After calling closure: {:?}", list);
    }
  • Captures a mutable reference:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fn main() {
    let mut list = vec![1, 2, 3];
    println!("Before defining closure: {:?}", list);

    let mut borrows_mutably = || list.push(7);

    borrows_mutably();
    println!("After calling closure: {:?}", list);
    }

    Between the closure definition and the closure call,
    an immutable borrow to print isn’t allowed because no other
    borrows are allowed when there’s a mutable borrow.

  • To force the closure to take ownership of the values it uses in the environment
    even though the body of the closure doesn’t strictly need ownership,
    you can use the move keyword before the parameter list.
    This technique is mostly useful when passing a closure to a new thread
    to move the data so that it’s owned by the new thread.

1
2
3
4
5
6
7
8
9
10
use std::thread;

fn main() {
let list = vec![1, 2, 3];
println!("Before defining closure: {:?}", list);

thread::spawn(move || println!("From thread: {:?}", list))
.join()
.unwrap();
}

Moving Captured Values Out of Closures and the Fn Traits

Once a closure has captured a reference or captured ownership of a value
from the environment where the closure is defined (thus affecting what,
if anything, is moved into the closure), the code in the body of the closure
defines what happens to the references or values when the closure is evaluated later
(thus affecting what, if anything, is moved out of the closure).
A closure body can do any of the following: move a captured value out of the closure,
mutate the captured value, neither move nor mutate the value,
or capture nothing from the environment to begin with.

The way a closure captures and handles values from the environment affects
which traits the closure implements, and traits are how functions and structs
can specify what kinds of closures they can use. Closures will automatically
implement one, two, or all three of these Fn traits, in an additive fashion,
depending on how the closure’s body handles the values:

  1. FnOnce applies to closures that can be called once.
    All closures implement at least this trait, because all closures can be called.
    A closure that moves captured values out of its body will only implement FnOnce
    and none of the other Fn traits, because it can only be called once.

  2. FnMut applies to closures that don’t move captured values out of their body,
    but that might mutate the captured values. These closures can be called more than once.

  3. Fn applies to closures that don’t move captured values out of their body
    and that don’t mutate captured values, as well as closures that capture nothing
    from their environment. These closures can be called more than once without mutating
    their environment, which is important in cases such as calling a closure multiple times concurrently.

  • Definition of unwrap_or_else(): Implements FnOnce

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    impl<T> Option<T> {
    pub fn unwrap_or_else<F>(self, f: F) -> T
    where
    F: FnOnce() -> T
    {
    match self {
    Some(x) => x,
    None => f(),
    }
    }
    }
  • Usage of sort_by_key(): Implements FnMut

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #[derive(Debug)]
    struct Rectangle {
    width: u32,
    height: u32,
    }

    fn main() {
    let mut list = [
    Rectangle { width: 10, height: 1 },
    Rectangle { width: 3, height: 5 },
    Rectangle { width: 7, height: 12 },
    ];

    // this closure is called multiple times
    list.sort_by_key(|r| r.width);
    println!("{:#?}", list);
    }

Iterators

Processing a Series of Items with Iterators

Example:

1
2
3
4
5
6
7
8
9
fn main() {
let v1 = vec![1, 2, 3];

let v1_iter = v1.iter();

for val in v1_iter {
println!("Got: {}", val);
}
}

The Iterator Trait and the next Method

All iterators implement a trait named Iterator that is defined in the standard library.
The definition of the trait looks like this:

1
2
3
4
5
6
7
pub trait Iterator {
type Item;

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

// methods with default implementations elided
}

To use iterator for a custom type it has to implement
Iterator trait. To implement Iterator define an Item type,
and use this Item type in the return type of the next method.

values we get from the calls to next are immutable references to the values in the vector.
The iter method produces an iterator over immutable references.
If we want to create an iterator that takes ownership of v1 and returns owned values,
we can call into_iter instead of iter.
Similarly, if we want to iterate over mutable references, we can call iter_mut instead of iter.

Methods that Consume the Iterator

  • Methods that call next are called consuming adaptors,
    because calling them uses up the iterator.

Example: sum method, which takes ownership of the iterator
and iterates through the items by repeatedly calling next, thus consuming the iterator.

1
2
3
4
5
6
7
8
9
10
11
12
13
#[cfg(test)]
mod tests {
#[test]
fn iterator_sum() {
let v1 = vec![1, 2, 3];

let v1_iter = v1.iter();

let total: i32 = v1_iter.sum();

assert_eq!(total, 6);
}
}

Methods that Produce Other Iterators

  • Iterator adaptors are methods defined on the Iterator trait
    that don’t consume the iterator. Instead, they produce
    different iterators by changing some aspect of the original iterator.
1
2
3
4
5
6
7
fn main() {
let v1: Vec<i32> = vec![1, 2, 3];

let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();

assert_eq!(v2, vec![2, 3, 4]);
}

Using Closures that Capture Their Environment

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#[derive(PartialEq, Debug)]
struct Shoe {
size: u32,
style: String,
}

fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
shoes.into_iter().filter(|s| s.size == shoe_size).collect()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn filters_by_size() {
let shoes = vec![
Shoe {
size: 10,
style: String::from("sneaker"),
},
Shoe {
size: 13,
style: String::from("sandal"),
},
Shoe {
size: 10,
style: String::from("boot"),
},
];

let in_my_size = shoes_in_size(shoes, 10);

assert_eq!(
in_my_size,
vec![
Shoe {
size: 10,
style: String::from("sneaker")
},
Shoe {
size: 10,
style: String::from("boot")
},
]
);
}
}

Fearless Concurrency

Using Threads to Run Code Simultaneously

  • The Rust standard library uses a 1:1 model of thread implementation,
    whereby a program uses one operating system thread per one language thread.

Smart Pointers

Box to Point to Data on the Heap

Situations:

  • To use a type whose size can’t be known at compile time
    and to use a value of that type in a context that requires
    an exact size.

  • To handle large amount of data and transfer ownership but
    unsure data won’t be copied while transfering ownership.

  • To own a value and you care only that it’s a type that implements
    a particular trait rather than being of a specific type

Usage

1
2
3
4
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}

Enabling Recursive Types with Boxes

  • Rust Doesn’t allow recursive types as their size can’t be
    known in compile time.
1
2
3
4
5
6
7
8
9
10
11
12
// Infinite recursion here
// when measuring space
enum List {
Cons(i32, List),
Nil,
}

use crate::List::{Cons, Nil};

fn main() {
let list = Cons(1, Cons(2, Cons(3, Nil)));
}

Using Box to Get a Recursive Type with a Known Size

1
2
3
4
5
6
7
8
9
10
enum List {
Cons(i32, Box<List>),
Nil,
}

use crate::List::{Cons, Nil};

fn main() {
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}

Treating Smart Pointers Like Regular References with the Deref Trait

Following the Pointer to the Value

1
2
3
4
5
6
7
fn main() {
let x = 5;
let y = &x;

assert_eq!(5, x);
assert_eq!(5, *y);
}

Using Box Like a Reference

1
2
3
4
5
6
7
fn main() {
let x = 5;
let y = Box::new(x);

assert_eq!(5, x);
assert_eq!(5, *y);
}

Defining Your Own Smart Pointer

Create a new type MyBox<T>

1
2
3
4
5
6
7
8
9
struct MyBox<T>(T);

impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}

fn main() {}

But deferencing won’t work on MyBox. Following code won’t compile:

1
2
3
4
fn main() {
let b = MyBox::new(5);
assert_eq!(5, *b);
}

Implementing the Deref Trait

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
use std::ops::Deref;

impl<T> Deref for MyBox<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

struct MyBox<T>(T);

impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}

fn main() {
let x = 5;
let y = MyBox::new(x);

assert_eq!(5, x);
assert_eq!(5, *y);
}

Implicit Deref Coercions with Functions and Methods

  • Deref coercion converts a reference to a type that implements
    the Deref trait into a reference to another type.

  • Deref coercion is a convenience Rust performs on arguments to
    functions and methods, and works only on types that implement the Deref trait.

  • It happens automatically when we pass a reference to a particular type’s
    value as an argument to a function or method that doesn’t match the parameter
    type in the function or method definition

Example: Reference to MyBox converts into &str

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
27
28
use std::ops::Deref;

impl<T> Deref for MyBox<T> {
type Target = T;

fn deref(&self) -> &T {
&self.0
}
}

struct MyBox<T>(T);

impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}

fn hello(name: &str) {
println!("Hello, {name}!");
}

fn main() {
let m = MyBox::new(String::from("Rust"));

// Deref coersion from &MyBox to &str
hello(&m);
}

How Deref Coercion Interacts with Mutability

Rust does deref coercion when it finds types and trait
implementations in three cases:

  1. From &T to &U when T: Deref<Target=U>
  2. From &mut T to &mut U when T: DerefMut<Target=U>
  3. From &mut T to &U when T: Deref<Target=U>
  • In 1: Converts &T to &U
  • In 2: Converts &mut T to &mut U
  • In 3: Converts &mut T to &U. But &T to &mut U isn’t allowed

Cleanup with the Drop Trait

Example implementing Drop trait:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct CustomSmartPointer {
data: String,
}

impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}

fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff"),
};
let d = CustomSmartPointer {
data: String::from("other stuff"),
};
println!("CustomSmartPointers created.");
}

Dropping a Value Early with std::mem::drop

It is not possible to disable drop functionality or
call drop method on a type directly but memory
can be freed earlier using the std::mem::drop function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct CustomSmartPointer {
data: String,
}

impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("Dropping CustomSmartPointer with data `{}`!", self.data);
}
}

fn main() {
let c = CustomSmartPointer {
data: String::from("some data"),
};
println!("CustomSmartPointer created.");
drop(c);
println!("CustomSmartPointer dropped before the end of main.");
}

Rc<T>, the Reference Counted Smart Pointer

  • Share ownership among multiple owners.

  • Only applicable for single threaded case. (Arc for multi threade).

  • Rc<T> is useful to allocate data from the heap for multiple
    parts of a program to read and it is not possible to determine at
    compile time which part will finish using the data last

  • Keeps a reference count of the owners. Will free the object when
    count goes to zero.

  • Convention is to use Rc::clone(&T) instead of T.clone().
    As in most cases T.clone() does deep copy. Though in this case
    it will do a referance count instead of deep copy. But by using
    Rc::clone(&T) it possible to visually tell that it is doing
    reference counting.

Example 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
enum List {
Cons(i32, Rc<List>),
Nil,
}

use crate::List::{Cons, Nil};
use std::rc::Rc;

fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
let b = Cons(3, Rc::clone(&a));
let c = Cons(4, Rc::clone(&a));
}

Example 2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum List {
Cons(i32, Rc<List>),
Nil,
}

use crate::List::{Cons, Nil};
use std::rc::Rc;

fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("count after creating a = {}", Rc::strong_count(&a));
let b = Cons(3, Rc::clone(&a));
println!("count after creating b = {}", Rc::strong_count(&a));
{
let c = Cons(4, Rc::clone(&a));
println!("count after creating c = {}", Rc::strong_count(&a));
}
println!("count after c goes out of scope = {}", Rc::strong_count(&a));
}

RefCell<T> and the Interior Mutability Pattern

Interior mutability: Interior mutability is a design pattern
in Rust that allows you to mutate data even when there are
immutable references to that data. To mutate data,
the pattern uses unsafe code inside a data structure
to bend Rust’s usual rules that govern mutation and borrowing.

  • With references and Box<T> borrowing rules are enforced
    at compile time but with RefCell<T> borrowing rules are
    enforced at runtime.

  • Can only be used in single threaded scenario.

  • Interior mutability is possible with RefCell<T>.

  • To get interior mutability enclose the type
    in RefCell<T>, then call borrow_mut() method
    on the object to get mutable reference and borrow()
    method to get immutable reference.

  • borrow() method returns Ref<T> and borrow_mut()
    returns RefMut<T>.

  • Same borrowing rules applied for RefCell<T> but
    enforced at runtime.

Example:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
pub trait Messenger {
fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T: Messenger> {
messenger: &'a T,
value: usize,
max: usize,
}

impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
LimitTracker {
messenger,
value: 0,
max,
}
}

pub fn set_value(&mut self, value: usize) {
self.value = value;

let percentage_of_max = self.value as f64 / self.max as f64;

if percentage_of_max >= 1.0 {
self.messenger.send("Error: You are over your quota!");
} else if percentage_of_max >= 0.9 {
self.messenger
.send("Urgent warning: You've used up over 90% of your quota!");
} else if percentage_of_max >= 0.75 {
self.messenger
.send("Warning: You've used up over 75% of your quota!");
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;

struct MockMessenger {
// With RefCell<T> it isn't
// possible to mutate this in
// send() method of Messanger trait
sent_messages: RefCell<Vec<String>>,
}

impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
sent_messages: RefCell::new(vec![]),
}
}
}

impl Messenger for MockMessenger {
fn send(&self, message: &str) {
self.sent_messages.borrow_mut().push(String::from(message));
}
}

#[test]
fn it_sends_an_over_75_percent_warning_message() {
// --snip--
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);

limit_tracker.set_value(80);

assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
}

Having Multiple Owners of Mutable Data by Combining Rc<T> and RefCell<T>

Rc<T> provides multiple ownership to immutable data.
But with combination of RefCell<T> we can get multiple
ownership of immutable data.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#[derive(Debug)]
enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil,
}

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
let value = Rc::new(RefCell::new(5));

let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));

let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));

*value.borrow_mut() += 10;

println!("a after = {:?}", a);
println!("b after = {:?}", b);
println!("c after = {:?}", c);
}

Regular Expression

Basic Regular Expression

  • ^A - Match with any line starts with A

  • A$ - Match with any line ends with A

  • A^ - Match with any line containging A^

  • $A - Match with any line containging $A

  • ^^ - Match with any line containging ^^

  • $$ - Match with any line containging $$

  • [XYZ] - Match with any line containging X, Y or Z

  • [0-9] - Match wiht any line containging 0,1,…,9

  • [^A-Z] - Matches that doesn’t contain A-Z.

  • . - Matches any character

  • * - Matches zero or more time

  • \{m\} - Matches if pattern repeats exactly m times.

  • \{m,n} - Matches if the pattern repeats m to n times.

  • \{,n} - Matches if the pattern repeats less or equal n times.

  • \{m,} - Matches if the pattern repeats more or equal to m times.

  • \<[tT]he\> - Matches exactly the/The.

  • \(a\)\1 - (PATTERN) remembers the pattern and the pattern can be used again by \n where n=[1,9].
    So total nine patterns can be remembered and backreferenced. Another example: \([a-z]\)\([a-z]\)[a-z]\2\1
    matches with any 5 letter palindrome.

Extended Regular Expression

  • ? - Matches zero or one time.
  • + - Matches one or more time.
  • (abc|def) - Matches abc or def.

grep example

1
echo -e "(123)-451-789\n(123)457-869" | grep "([0-9]\{3\})-[0-9]\{3\}-[0-9]\{3\}"

QT Cheatsheet

Index

  1. QString
  2. QTextStream
  3. Taking Commandline Arguments
  4. Making a basic QT class with signals and slots
  5. Basic QT Dialog with Button, Label and MessageBox
  6. Resource File
  7. Creating Actions for Menu Bar, Tool Bar
  8. Basic QMainWindow with Central Widget, Menu Bar, Tool Bar
  9. QList, QStringList, QStringList::iterator, QListIterator
  10. QDir, QFileInfo, QDirIterator
  11. QFile
  12. QDate
  13. QThread
  14. Serial Ports in QT
  15. Short GUI for Sending Characters to Arduino

QString

1
2
3
4
QString hello = "Hello, World";
QString text = QString(I have %1 taka!).arg(10);
QString *textPtr = new QString("A Beautiful Text");
qDebug() << hello << "\n";
1
2
3
4
int number = 10;
QString string = "This a cute string";
QString show = QString("My number is: %1 and my string is: %2\n").arg(number).arg(string);
qDebug() << show;

QTextStream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
QString qstr;
QTextStream cout(stdout); /* stream text to stdout */
QTextStream cin(stdin); /* take input from stdin */
QTextStream toStringObject(&qstr); /* store in a Qstring */

cout << "Hello, world!\n"
cout << QString("First prime number is: %1\n").arg(2);

QString inputString;
cin >> inputString;
cout << inputSting;

toStringObject << "This is a literal string\n" << QString("A QString with number %1\n").arg(10) << inputString << "\n";
cout << qstr;

Taking Commandline Arguments

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <QCoreApplication>
#include <QStringList>
#include <QDebug>

int main(int argc, char *argv[]){
QCoreApplication app(argc, argv);

QStringList argList = app.arguments();

foreach(const QString &str, argList){
qDebug() << str;
}

return EXIT_SUCCESS;
}

Project file.

simple.pro
1
2
3
4
5
6
7
8
TEMPLATE = app      # Type. Can be lib, subdir etc
TARGET = simple # Executable name

QT += core

INCLUDEPATH += .
HEADERS =
SOURCES = main.cpp

Making a basic QT class with signals and slots

We will create our own string class based on QString. Our class name will be MyString. All QT classes inherits QObject class as base class. Q_OBJECT macro will be used for signals and slots. Slots are just regular member functions. They are called when a signal is emitted. signals are only function prototypes. Their arguments must match with the slots, they are emitted. Slots can be called as regular member function too. In the constructor QObject argument is taken as parent so that others can make QObject as parent for safe memory deallocation.

MyString.h
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
27
#ifndef MYSTRING_H
#define MYSTRING_H

#include <QObject>
#include <QString>

class MyString: public QObject{

Q_OBJECT

private:
QString m_text;

public:
MyString(const QString& text, QObject *parent = 0);

const QString& text(void) const;
int getLengthOfText(void) const;

public slots:
void setText(const QString&);

signals:
void textChanged(const QString&);
};

#endif
MyString.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <QObject>
#include <QString>
#include "MyString.h"

MyString::MyString(const QString& text, QObject *parent)
: QObject(parent), m_text(text) {}

const QString& MyString::text(void) const {
return m_text;
}

int MyString::getLengthOfText() const {
return m_text.size();
}

void MyString::setText(const QString& text){
if(text == m_text)
return;

m_text = text;

emit textChanged(m_text);
}
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <QObject>
#include <QDebug>
#include "MyString.h"

int main(){
QObject parent;
MyString *a, *b;

a = new MyString(QString("Hello, World!"), &parent);
b = new MyString(QString("I am Bee"), &parent);

qDebug() << a->text();
qDebug() << b->text();

QObject::connect(a, SIGNAL(textChanged(const QString&)), b, SLOT(setText(const QString&)));

a->setText(QString("a changed so, b will change too"));

qDebug() << a->text();
qDebug() << b->text();

return 0;
}

Basic QT Dialog with Button, Label and MessageBox

Our class ingerits from QDialog. It has to public slots for two buttons.

mydialog.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef __DIALOG_H__
#define __DIALOG_H__

#include <QDialog>
#include <QObject>

class MyDialog: public QDialog{

Q_OBJECT

public:
MyDialog();

public slots:
void blueClicked();
void redClicked();
};

#endif
mydialog.cpp
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include "mydialog.h"
#include <QPushButton>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QLabel>

MyDialog::MyDialog(){
resize(300, 400);

QLabel *label = new QLabel("Select:");

QPushButton *blue = new QPushButton("Blue Pill");
QPushButton *red = new QPushButton("Red Pill");

QHBoxLayout *btnLayout = new QHBoxLayout;
QHBoxLayout *lblLayout = new QHBoxLayout;

btnLayout->addWidget(blue);
btnLayout->addStretch();
btnLayout->addWidget(red);


lblLayout->addStretch();

QVBoxLayout *mainLayout = new QVBoxLayout(this);

mainLayout->addStretch();

mainLayout->addLayout(btnLayout);
mainLayout->addStretch();

connect(blue, SIGNAL(clicked()), this, SLOT(blueClicked()));
connect(red, SIGNAL(clicked()), this, SLOT(redClicked()));
}

void MyDialog::blueClicked(){
QMessageBox::information(this, "Your Choice", "Welcome to Matrix!");
}

void MyDialog::redClicked(){
QMessageBox::information(this, "Your Choice", "Good Bye!");
}
main.cpp
1
2
3
4
5
6
7
8
9
10
11
#include <QApplication>
#include "mydialog.h"

int main(int argc, char *argv[]){
QApplication app(argc, argv);

MyDialog mdlg;
mdlg.show();

return app.exec();
}

Resource File

resource file is used to keep track of the resources used in the application. For resource file root is the project folder. Lets say we have kept new.png, cut.png and close.png int the images folder under project folder. We will add these to qt resource file. resource file’s file extension is qrc.

resources.qrc
1
2
3
4
5
6
7
8
9
<!DOCTYPE RCC><RCC version="1.0">

<qresource>
<file alias="new">images/new.png</file>
<file alias="cut">images/cut.png</file>
<file alias="close">images/close.png</file>
</qresource>

</RCC>

use: QIcon(“:/images/new.png”)

project.pro
1
RESOURCES += resources.qrc

Creating Actions for Menu Bar, Tool Bar

1
2
3
4
QAction *actionNew = new QAction(tr("&New"), this);
actionNew->setIcon(QIcon(":/images/new.png"));
actionNew->setShortcut(QKeySequence::New);
actionNew->setStatusTip(tr("Create New File"));

Basic QMainWindow with Central Widget, Menu Bar, Tool Bar

mywindow.h
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
27
28
29
30
31
32
33
34
#ifndef __MY_WINDOW_H__
#define __MY_WINDOW_H__

#include <QMainWindow>
#include <QOBject>

class QAction;
class QTextEdit;

class MyWindow: public QMainWindow{

Q_OBJECT

private:
QAction *actionNew;
QAction *actionCut;
QAction *actionAbout;
QAction *actionClose;

QTextEdit *editText;

void setupActions();
void setupMenu();
void setupToolBar();

public:
MyWindow();

public slots:
void newFile();
void cut();
};

#endif
mywindow.cpp
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <QMainWindow>
#include <QAction>
#include <QString>
#include <QTextEdit>
#include <QAction>
#include <QMenu>
#include <QMenuBar>
#include <QToolBar>
#include <QApplication>
#include "mywindow.h"

MyWindow::MyWindow(){
setWindowTitle(QString("Untitled[*]"));

editText = new QTextEdit(this);

setCentralWidget(editText);

connect(editText->document(), SIGNAL(modificationChanged(bool)), this, SLOT(setWindowModified(bool)));

setupActions();
setupMenu();
setupToolBar();
}

void MyWindow::setupActions(){
actionNew = new QAction(tr("New"), this);
actionNew->setShortcut(tr("Ctrl+N"));
actionNew->setStatusTip(tr("Open a new document"));

connect(actionNew, SIGNAL(triggered()), this, SLOT(newFile()));

actionCut = new QAction(tr("Cut"), this);
actionCut->setShortcut(tr("Ctrl+X"));
actionCut->setStatusTip(tr("Cut"));
actionCut->setEnabled(false);

connect(editText, SIGNAL(copyAvailable(bool)), actionCut, SLOT(setEnabled(bool)));
connect(actionCut, SIGNAL(triggered()), this, SLOT(cut()));

actionAbout = new QAction(tr("About QT"), this);
actionAbout->setStatusTip(tr("About QT Toolkit"));

connect(actionAbout, SIGNAL(triggered()), qApp, SLOT(aboutQt()));

actionClose = new QAction(tr("&Quit"), this);
actionClose->setShortcut(tr("Ctrl+Q"));
actionClose->setStatusTip(tr("Close"));

connect(actionClose, SIGNAL(triggered()), qApp, SLOT(quit()));
}

void MyWindow::setupMenu(){
QMenu *fileMenu = menuBar()->addMenu(tr("File"));
fileMenu->addAction(actionNew);
fileMenu->addSeparator();
fileMenu->addAction(actionClose);

QMenu *editMenu = menuBar()->addMenu(tr("Edit"));
editMenu->addAction(actionCut);

QMenu *helpMenu = menuBar()->addMenu(tr("Help"));
helpMenu->addAction(actionAbout);
}

void MyWindow::setupToolBar(){
QToolBar *toolBar;
toolBar = addToolBar(tr("Tool"));

toolBar->addAction(actionNew);
toolBar->addSeparator();
toolBar->addAction(actionCut);
}

void MyWindow::newFile(){

}

void MyWindow::cut(){

}
main.cpp
1
2
3
4
5
6
7
8
9
10
11
#include <QApplication>
#include "mywindow.h"

int main(int argc, char *argv[]){
QApplication app(argc, argv);

MyWindow mainwindow;
mainwindow.show();

return app.exec();
}

QList, QStringList, QStringList::iterator, QListIterator

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <QStringList>
#include <QDebug>

int main(){
QString heavy = "Metallica, Stentorian, Aurthohin";
QString alt = "Icons, Karnival, Nemesis";
QString symphonic = "Ionic, Eluveitie";

QStringList bands;
bands << heavy << alt << symphonic;

qDebug() << "Heavy: " << bands[0] << "\n";
qDebug() << "Alt: " << bands[1] << "\n";
qDebug() << "Symphonic: " << bands[2] << "\n";

QString allBands = bands.join(", ");
qDebug() << allBands << "\n";

QList<QString> singleBandList = allBands.split(", ");

foreach(const QString& str, bands){
qDebug() << QString("[%1]").arg(str);
}

qDebug() << "\n\n";

for(QStringList::iterator it = bands.begin();
it != bands.end(); it++){
qDebug() << QString("[[%1]]").arg(*it);
}

qDebug() << "\n";

QListIterator<QString> it(singleBandList);
while(it.hasNext()){
qDebug() << QString("{%1}").arg(it.next());
}

return 0;
}

QDir, QFileInfo, QDirIterator

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
#include <QCoreApplication>
#include <QStringList>
#include <QDebug>
#include <QDir>

int main(int argc, char *argv[]){
QCoreApplication app(argc, argv);

QDir dir = QDir::current();
if(app.arguments().size() > 1){
dir = app.arguments()[1];
}

dir.setSorting(QDir::Name);

QDir::Filters df = QDir::Files | QDir::NoDotAndDotDot;

QStringList dirList = dir.entryList(df, QDir::Name);

foreach(const QString &entry, dirList){
QFileInfo fileInfo(dir, entry);
qDebug() << fileInfo.absoluteFilePath();
}

return EXIT_SUCCESS;
}
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
27
28
29
30
31
32
33
34
/* Will search all mp3 file in a given directory
* And its subdirectories
*/

#include <QCoreApplication>
#include <QStringList>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QTextStream>

int main(int argc, char *argv[]){
QCoreApplication app(argc, argv);

QTextStream cout(stdout);
QTextStream cerr(stderr);

QDir dir = QDir::current();
if(app.arguments().size() > 1){
dir = app.arguments()[1];
}

if(!dir.exists()){
cerr << dir.path() << " doesn't exist!\n";
return -1;
}

QDirIterator qdi(dir.absolutePath(), QStringList() << "*.mp3", QDir::NoSymLinks | QDir::Files, QDirIterator::Subdirectories);
while(qdi.hasNext()){
cout << qdi.next() << "\n";
}

return EXIT_SUCCESS;
}

QFile

1
2
3
4
5
6
7
QFile file("myTextFile.txt");
file.open(QIODevice::WriteOnly);

QTextStream outFile(&file);
outFile << "Hello, World!" << "\n";

file.close();
1
2
3
4
5
6
7
8
QFile file("myTextFile.txt");
if(file.open(QIODevice::ReadOnly)){
QStringSteam inFile(&file);
QString text;

inFile >> text;
qDebug() << text;
}

QDate

QDate::currentDate() returns current date

QThread

Make a custom class inherited from QThread. Override Thread::run() method which will do the heavy works.
Each thread is started with Thread::start() method.

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <QCoreApplication>
#include "mythread.h"

int main(int argc, char **argv){
QCoreApplication app(argc, argv);

MyThread th1("A"), th2("B"), th3("C");

th1.start();
th2.start();
th3.start();

return app.exec();
}
mythread.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QString>

class MyThread: public QThread {

public:
explicit MyThread(QString str);

void run();

private:
QString name;
};

#endif
mythread.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "mythread.h"
#include <QDebug>

MyThread::MyThread(QString s)
: name(s)
{
}

void MyThread::run(){
for(int i = 0; i <= 100; i++){
qDebug() << this->name << ":" << i;
}
}

Serial Ports in QT

QSerialPortInfo

QList<QSerialPortInfo> QSerialPortInfo::availablePorts() - availablePorst() static function returns a list of
available devices. Available devices are returned as QSerialPortInfo object

Functions:

QString portName() - Return device port name. E.g ttyACM0, ttyUSB0

QString systemLocation() - Returns port path / location. E.g /dev/ttyACM0

QString description - Returns device description.

QString manufacturer - Returns device manufacturer.

QString serialNumber() - Return device serial number.

bool hasProductIdentifier() - True if the device has valid product identifier number.

quint16 productIdentifier() - Returns product identifier number.

bool hasVendorIdentifier() - Returns true if the device has valid product number.

quint16 vendorIdentifier() - Returns vendor identifier number.

bool isBusy() - returns True if the serial port is busy.

Short GUI for Sending Characters to Arduino

ping arduino

main.cpp
1
2
3
4
5
6
7
8
9
10
11
#include <QApplication>
#include "dialog.h"

int main(int argc, char *argv[]){
QApplication app(argc, argv);

Dialog dialog;
dialog.show();

return app.exec();
}
dialog.h
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include <QSerialPort>

class QLabel;
class QPushButton;
class QComboBox;
class QLineEdit;

class Dialog: public QDialog{
Q_OBJECT

public:
Dialog(QWidget *parent = 0);

public slots:
void connect();
void send();
void handlePortError(const QString &error);
void handleResponse();

signals:
void portError(const QString &error);

private:
QLabel *portLabel;
QComboBox *portComboBox;
QPushButton *connectButton;
QLabel *dataLabel;
QLineEdit *dataLineEdit;
QPushButton *sendButton;
QLabel *statusLabel;
QLabel *status;
QLabel *replyLabel;
QLabel *reply;
QSerialPort serialPort;
};

#endif
dialog.cpp
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include "dialog.h"
#include <QLabel>
#include <QPushButton>
#include <QComboBox>
#include <QLineEdit>
#include <QGridLayout>
#include <QSerialPortInfo>
#include <QByteArray>
#include <QDebug>

Dialog::Dialog(QWidget *parent)
: QDialog(parent)
, portLabel(new QLabel("Port:"))
, portComboBox(new QComboBox())
, connectButton(new QPushButton("Connect"))
, dataLabel(new QLabel("Data:"))
, dataLineEdit(new QLineEdit())
, sendButton(new QPushButton("Send"))
, statusLabel(new QLabel("Status:"))
, status(new QLabel())
, replyLabel(new QLabel("Reply:"))
, reply(new QLabel())

{
QList<QSerialPortInfo> infos = QSerialPortInfo::availablePorts();
for(const QSerialPortInfo &info : infos){
portComboBox->addItem(info.portName());
}

QGridLayout *mainLayout = new QGridLayout;
mainLayout->addWidget(portLabel, 0, 0);
mainLayout->addWidget(portComboBox, 0, 1);
mainLayout->addWidget(connectButton, 0, 2);
mainLayout->addWidget(dataLabel, 1, 0);
mainLayout->addWidget(dataLineEdit, 1, 1, 1, 2);
mainLayout->addWidget(sendButton, 2, 1, 1, 1);
mainLayout->addWidget(statusLabel, 3, 0);
mainLayout->addWidget(status, 3, 1, 1, 2);
mainLayout->addWidget(replyLabel, 4, 0);
mainLayout->addWidget(reply, 4, 1, 1, 2);
setLayout(mainLayout);

QObject::connect(connectButton, SIGNAL(clicked()), this, SLOT(connect()));
QObject::connect(sendButton, SIGNAL(clicked()), this, SLOT(send()));
QObject::connect(this, SIGNAL(portError(const QString)), this, SLOT(handlePortError(const QString)));
QObject::connect(&serialPort, SIGNAL(readyRead()), this, SLOT(handleResponse()));
}

void Dialog::connect(){
if(serialPort.isOpen()){
serialPort.close();
connectButton->setText("Connect");
return;
}

serialPort.setPortName(portComboBox->currentText());
serialPort.setBaudRate(QSerialPort::Baud9600);

if(!serialPort.open(QIODevice::ReadWrite)){
emit portError(QString("Error: %1").arg(serialPort.errorString()));
return;
}

status->setText(QString("Connected to %1.").arg(serialPort.portName()));

connectButton->setText("Disconnect");
return;
}

void Dialog::send(){
QByteArray ba = dataLineEdit->text().toLocal8Bit();

if(ba.isEmpty()){
status->setText(QString("No data!"));
return;
}

if(serialPort.isOpen() && serialPort.isWritable()){
qint64 bytesWritten = serialPort.write(ba);
serialPort.flush();

if(bytesWritten == -1){
emit portError(QString("Write failed! %1").arg(serialPort.errorString()));
return;
}
else if(bytesWritten != ba.size()){
emit portError(QString("Can't write all data! %1").arg(serialPort.errorString()));
return;
}

status->setText(QString("%1 bytes written!").arg(bytesWritten));
}

return;
}

void Dialog::handlePortError(const QString &error){
status->setText(error);
}

void Dialog::handleResponse(){
if(serialPort.isOpen() && serialPort.isReadable()){
QByteArray readData = serialPort.readAll();
while(serialPort.waitForReadyRead(5000)){
readData += serialPort.readAll();
}

reply->setText(readData);
return;
}

emit portError(QString("Port isn't open or not readable"));
}

Index

Python Object Types

Python Data Types

Object Type Example
Numbers 1.23, 123, 3+4j
Strings “hello, world”
Lists [1, ‘hello’, [‘I’, ‘am’, ‘list’, ‘in’, ‘a’, ‘list’], 4.5]
Dictionary {“key”:”value”, ‘foo’:0, ‘bar’:’drink’}
Tuples (4, ‘hello’, 3.14)
Others set, types, booleans, none

Numbers, Strings, Tupples are immutable. Means they can’t be changed after creation


Lists and Dictionaries are mutable

Strings

Example: ‘hello, world’, “I am me” etc

len() function gives the length of the string.

1
2
3
4
5
>>> str = "Hello, World"
>>> print len(str) # Python 2
12
>>> print(len(str)) # Python 3
12

Individual character can be indexed from a string. Index can be negative. Negative index will return chars backword.

1
2
3
4
5
6
>>> print str[0]
H
>>> print str[-1]
d
>>> print str[-2]
l

Slicing

Extract a section of a string in one step. str[m:n] will produce ‘str[m]str[n-1]’. Default value of m is 0 and n is the length of the string.

1
2
3
4
5
6
7
8
9
10
11
>>> print str[7:10]		# Slice from 7 to 10(non-inclusive)
'Wor'

>>> str[:] # Slice from start to end */
'Hello, World'

>>> str[:4] # Slice from start upto 4
'Hell'

>>> str[:-1] # Slice from start upto last item
'Hello, Worl'

General format of slicing is x[i:j:k]. Where k is the step size from i upto j.
k can be negative, in that case slice will occur from i upto j in reverse order. Default value of k is 1.

1
2
3
4
5
6
7
8
>>> "12345678"[1:6:2]	# From index 1 upto index 6 and step size 2
'246'

"hello"[::2] # Entire string, step size 2
'hlo'

"hello"[::-1] # Entire string. Step size 1. But as the step
'olleh' # size is negetive, slicing will be done in reverse order

Strings can be concatenated using + sign and repeated using * sign.

1
2
3
4
5
6
>>> foo = "I am little bunny foo foo"
>>> bar = " and bar is with me"
>>> foo + bar
'I am little bunny foo foo and bar is with me'
>>> foo * 3
'I am little bunny foo fooI am little bunny foo fooI am little bunny foo foo'

Every operation we have performed on string can be performed other sequence objects like lists and tupples

Some methods

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> string = "foo bar"
>>> string.find('o')
1
>>> string.replace('oo', 'uu')
'fuu bar'
>>> string
'foo bar' # String itself unchanged as immutable
>>> bands = "Warfaze, Black, Artcell, Bangla"
>>> bands.split(',') # Split according to the argument and return as list
['Warfaze', ' Black', ' Artcell', ' Bangla']
string = "Hello, how are you\n\n\n"
>>> string = string.rstrip() # Remove whitespaces from the rigth most side
>>> string
'Hello, how are you'

ord() function returns ASCII value of a character.

1
2
>>> ord('A')
65

chr() function converts ASCII value to character.

1
2
>>> chr(65)
'A'

\0 or null terminator doesn’t end python strings

1
2
3
4
5
6
7
>>> s = 'A\0B\0C'
>>> s
'A\x00B\x00C'
>>> print s
ABC
>>> len(s)
5

Multiline strings can be specified using three “ or ‘.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> str = """
... When this form is used
... all the lines are concatenated togather
... and end-of-the line characters are added
... where line breaks appear
... """
>>> str
'\nWhen this form is used\nall the lines are concatenated togather\n\tand end-of-the line characters are added\nwhere line breaks appear\n'
>>> print str

When this form is used
all the lines are concatenated togather
and end-of-the line characters are added
where line breaks appear

Lists

List object is sequence object. They are mutable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> list = [1, 2.3, 'foo']	# Creating list object
>>> len(list) # Finding length
3
>>> list[0] # Can be indexed
1
>>> list[-1]
'foo'
>>> list[1:3] # Slicing
[2.3, 'foo']
>>> list + ['bar'] # Concatenation
[1, 2.3, 'foo', 'bar']
>>> list = list + ['bar'] # Mutability
>>> list
[1, 2.3, 'foo', 'bar']

Type specific operations

  1. Can hold different types of object
  2. No fixed size
  3. Mutable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> list = [1, 'foo']
>>> list.append('bar') # Add an item at the end of the list
>>> list
[1, 'foo', 'bar']
>>> list.insert(1, 'baz') # Insert an item at a specific index
>>> list
[1, 'baz', 'foo', 'bar']
>>> list.pop(1) # return and remove an item from a specific
'baz' # index
>>> list
[1, 'foo', 'bar']
>>> list.remove('bar') # remove first occurance of an item found
>>> list # in the list
[1, 'foo']

As list is a mutable object there are methods that can change a list object in place.

1
2
3
4
5
6
7
>>> list = ['heaven', 'and', 'hell']
>>> list.sort() # Sort List
>>> list
['and', 'heaven', 'hell']
>>> list.reverse() # Reverse sort List
>>> list
['hell', 'heaven', 'and']

Lists can be nested.

1
2
3
4
5
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> matrix[0]
[1, 2, 3]
>>> matrix[0][0]
1

List Comprehensions

List comprehension is a way to build a new list running an expression on each item in a sequence, once at a time.

1
2
3
4
5
6
7
8
9
>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> col2 = [row[1] for row in matrix]
>>> col2
[2, 5, 8]
>>> list = [var for var in range(0, 11)]
>>> list
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[num for num in range(0, 11) if num % 3 == 0] # Filtering
[0, 3, 6, 9]

Dictionary

Dictionaries are used to store value in a key value pair.

  1. Mutable
  2. Values are stored in a key value pair
  3. Stores any type of objects
  4. Can be nested
1
2
3
4
5
6
7
8
band = {'name':'Bangla', 'albums':2}			# Creation
>>> band
{'albums': 2, 'name': 'Bangla'}
>>> band['name'] # Indexing by key
'Bangla'
>>> band['started'] = 1999 # Mutable
>>> band
{'started': 1999, 'albums': 2, 'name': 'Bangla'}

Type Specific Methods:

1
2
3
4
5
>>> dic = {'c':1, 'a':2, 'b':3}
>>> dic
{'a': 2, 'c': 1, 'b': 3}
>>> dic.keys() # Returns List of the keys
['a', 'c', 'b']

Iterating over a Dictionary:

1
2
3
4
5
6
7
8
9
>>> dic = {'c':1, 'a':2, 'b':3}
>>> dic
{'a': 2, 'c': 1, 'b': 3}
>>> for key in dic.keys():
... print "key: %s\tValue: %s" % (key, dic[key])
...
key: a Value: 2
key: c Value: 1
key: b Value: 3

Tuples

Python tuples are list like sequence object. But they are immutable.

1
2
3
4
5
6
7
8
9
10
11
>>> T = (1, 2, 3, 4)
>>> T
(1, 2, 3, 4)
>>> T[0]
1
>>> len(T)
4
>>> T[1] = 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment

Sets

Collection of unique elements. Supports usual mathematical set operations.

1
2
3
4
5
6
7
8
9
10
11
12
>>> x = set('hello')
>>> x
set(['h', 'e', 'l', 'o'])
>>> y = set(['w', 'o', 'r', 'l', 'd'])
>>> y
set(['d', 'r', 'o', 'w', 'l'])
>>> x & y
set(['l', 'o'])
>>> x | y
set(['e', 'd', 'h', 'l', 'o', 'r', 'w'])
>>> x - y
set(['h', 'e'])

Python Data Types In Details

Numbers

Numeric Display Formats

repr() display numbers as in code.

str() Convert numbers into string.

oct() converts decimal into octal

hex() converts decimal into hexadecimal

int(string, base) - converts strings into numbers.

float() - Coverts strings into floating point number.

bin() - Converts integer to binary

Decimal Object

Fixed precision representation of numbers.

1
2
3
4
5
6
7
8
9
10
11
12
>>> 0.1 + 0.2 - 0.3
5.551115123125783e-17 # The problem
>>> from decimal import Decimal
>>> Decimal(0.1) + Decimal(0.2) - Decimal(0.3) # Solution
Decimal('2.775557561565156540423631668E-17')
>>> Decimal('0.1') + Decimal('0.2') - Decimal('0.3')
Decimal('0.0')
>>> Decimal(1) / Decimal(7)
Decimal('0.1428571428571428571428571429')
>>> decimal.getcontext().prec = 4 # Precision
>>> Decimal(1) / Decimal(7)
Decimal('0.1429')

Strings

Different Forms:

  1. Single Quotes: ‘he said, “hello”‘
  2. Double Quotes: “Robin’s book”
  3. Block of strings: “””string block”””, ‘’’String Block’’’
  4. Raw strings: r’\thello, world\n’. Take each characters leterally, omits escape
    sequences. e.g. r”C:\new\values”. But raw string won’t work for a string ended
    with backslash. r”…"; This is invalid.
  5. Byte Strings: b’spam’ (Python 3)
  6. Unicode strings: u’\u0986\u09AE\u09BE\u09B0’ (in 2.6 only)

In python 3 there are three string types. str is used for unicode text(ASCII and others). bytes is used
for bynary data. bytearray is mutable variant of bytes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> print 'he said, "hello"'	# Python 2.6
he said, "hello"

>>> print ('he said, "hello"') # Have to use brackets in python 3
he said, "hello"

>>> print "Robin's book"
Robin's book

>>> print """Once upon a time
... there was a man
... one day"""
Once upon a time
there was a man
one day

>>> print r'\thello, world\n'
\thello, world\n

>>> print u'\u0986\u09AE\u09BE\u09B0'
আমার

Character Code Conversions

ord('char') convert char into ints ASCII value.


chr(value) convert from ASCII to character.


eval(str) - convert a string into python executable code.

1
2
3
4
5
6
7
8
9
>>> list = [1, 2, 3]

>>> str = '%s' % list # List is converted into a string

>>> str
'[1, 2, 3]'

>>> eval(str) # String is converted back to list
[1, 2, 3]

String Formating

String Formating Expression
1
2
3
4
5
>>> print("%s:%d\t%s:%d" % ("Alice", 40, "Bob", 50))
Alice:40 Bob:50
>>>
>>>print("My List: %s" % [1, 2, 3])
My List: [1, 2, 3]
Type Code Meaning
%s String
%r Raw string
%c Character
%d Integer
%i Integer
%u Unsigned Integer
%o Octal
%x Hexadecima(lower)
%X Hexadecimal(Upper)
%e Floating point Exponent
%E e, but uppercase
%f Floating point decimal
%g e or f
%G E or f
%% literal %

General conversion code structure: %[(name)][flags][width][.precision]typecode

Dictionary base string formating: “%(name)s has %(value)d taka” % {‘name’:’ashik’, ‘value’:50}

String Formating Method Calls
1
2
>>> 'Distance between {0} and {1} is {2} km'.format('Dhaka', 'Khulna', 279.6)
'Distance between Dhaka and Khulna is 279.6 km'

String Methods

string.replace('old', 'new') - replce old substring with new substring from string and return new string.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> str  = "hello, world"
>>> str.replace('world', 'bob')
'hello, bob'
>>> str
'hello, world'
>>>
>>>
>>> str = 'FooBarFooBar'
>>> str
'FooBarFooBar'
>>> str.replace('Bar', 'Baz') # Replace all occurance
'FooBazFooBaz'
>>> str.replace('Bar', 'Baz', 1) # Replace only first occurance
'FooBazFooBar'
>>>

string.find('substring') - Find the index of the first occurance of the substring from string.

1
2
3
4
5
6
7
8
9
>>> str = 'hello, world'
>>> str
'hello, world'
>>> where = str.find('ello')
>>> where
1
>>> str = str[:where] + 'ola' + str[(where + 4):] # Replace trick
>>> str
'hola, world'

list(string) - convert string into list.

1
2
3
4
5
>>> str
'FooBarFooBar'
>>> L = list(str)
>>> L
['F', 'o', 'o', 'B', 'a', 'r', 'F', 'o', 'o', 'B', 'a', 'r']

string.join(list) - join list items with string.

1
2
>>> ''.join(L)	# Join list item of L with empty string
'FooBarFooBar'

string.split(delimiters) - split string into a list according to specified delimiters. Default delimiter is whitespaces if nothing specified.

1
2
3
4
5
6
7
8
>>> str = 'Sing me song you are a singer'
>>> str.split() # No delimiters specified. Default whitespaces.
['Sing', 'me', 'song', 'you', 'are', 'a', 'singer']
>>>
>>>
>>> str = 'Warfaze, Black, Artcell'
>>> str.split(', ') # Used ', ' as delimiter
['Warfaze', 'Black', 'Artcell']

string.rstrip() - remove whitespaces from the end of the string.


string.upper() - Convert all characters into uppercase.


string.endswith(substring) - Check if the string ends with specified substring.

Lists

  • Some basic operations.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    >>> len([1, 2, 3])			# Length
    3
    >>>
    >>> [1, 2, 3] + [4, 5, 6] # Concatenation
    [1, 2, 3, 4, 5, 6]
    >>>
    >>> [1, 2] * 3 # Repetition
    [1, 2, 1, 2, 1, 2]
    >>>
    >>> 2 in [1, 2, 3] # Membership Check
    True
    >>>
    >>> for i in [1, 2, 3]: # Iteration
    ... print i
    ...
    1
    2
    3
  • Indexing, Slicing, Nesting

    1
    2
    3
    4
    5
    6
    7
    8
    9
    >>> list = [1, 2, 3]
    >>> list[0] # Indexing
    1
    >>>
    >>> list[1:3] # Slicing
    [2, 3]
    >>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] # Nesting
    >>> matrix[0][0]
    1
  • Mutability

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    >>> list = [1, 'foo', 2, 'bar']
    >>> list[3] = 'baz' # Changing in place using indexing
    >>> list
    [1, 'foo', 2, 'baz']
    >>> list[1:3] = [2, 3] # Changing in place using slicing
    >>> list
    [1, 2, 3, 'baz']
    >>>
    >>> del list[0] # Delete one item from list
    >>> list
    [2, 3, 'baz']
    >>>
    >>> del list[1:] # Delete a section from list
    >>> list
    [2]
    >>>
    >>> list = [1, 2, 3, 4]
    >>> list[1:3] = [] # Another way of deleting a section
    >>> list
    [1, 4]

List Methods

  • list.append(item) - To append item at the end of the list.
  • list.extend(argList) - Append multiple items at the end of the list at once.
  • list.insert(index, item) - To insert item at index index of the list.
  • list.pop([index]) - Remove item from list at index index. If index isn’t specified default is the last item.
  • list.remove(item) - Remove first occurence of the item from the list.
  • list.sort() - Sort List.
  • list.reverse() - Reverse sort the list.
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
27
28
29
30
31
32
33
34
35
36
37
>>> mList = [1, 2]
>>> mList.append(3) # Append at the end of the list
>>> mList
[1, 2, 3]
>>>
>>>
>>> mList.extend([4, 5]) # Extend list with multiple items
>>> mList
[1, 2, 3, 4, 5]
>>>
>>>
>>> mList.insert(1, 9) # Insert at specific index
>>> mList
[1, 9, 2, 3, 4, 5]
>>>
>>>
>>> mList.pop()
5
>>> mList
[1, 9, 2, 3, 4]
>>> mList.pop(1)
9
>>> mList
[1, 2, 3, 4]
>>>
>>>
>>> mList.remove(4)
>>> mList
[1, 2, 3]
>>>
>>>
>>> mList.reverse()
>>> mList
[3, 2, 1]
>>> mList.sort()
>>> mList
[1, 2, 3]

Dictionaries

  • Creation:

    1
    2
    3
    4
    5
    6
    >>> dic = {'foo':0, 'bar':1, 'baz':2}
    >>> dic
    {'baz': 2, 'foo': 0, 'bar': 1} # Notice Order Scambled
    >>>
    >>> dic['foo'] # Fetching value using key
    0
  • Some basic operations

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    >>> dic
    {'baz': 2, 'foo': 0, 'bar': 1}
    >>> len(dic) # Length
    3
    >>>
    >>> 'foo' in dic # Key membership check
    True
    >>> dic.has_key('bar') # Key membership check
    True
    >>> dic.keys() # Gives a list of all keys
    ['baz', 'foo', 'bar']
    >>>
    >>> dic
    {'goo': 3, 'foo': 5, 'bar': 1}
    >>> for key in dic.keys(): # Iterating
    ... print dic[key]
    ...
    3
    5
    1
  • Mutability

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    >>> dic
    {'baz': 2, 'foo': 0, 'bar': 1}
    >>> dic['goo'] = 3 # Adding new entry
    >>> dic
    {'baz': 2, 'goo': 3, 'foo': 0, 'bar': 1}
    >>>
    >>>
    >>> dic['foo'] = 5 # Changing existing entry
    >>> dic
    {'baz': 2, 'goo': 3, 'foo': 5, 'bar': 1}
    >>>
    >>>
    >>> del dic['baz'] # Deleting an entry
    >>> dic
    {'goo': 3, 'foo': 5, 'bar': 1}
  • Some methods

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    >>> dic
    {'goo': 3, 'foo': 5, 'bar': 1}
    >>> dic.keys() # Returns a list of all keys
    ['goo', 'foo', 'bar']
    >>>
    >>> dic.values() # Returns a list of all values
    [3, 5, 1]
    >>>
    >>> dic.items() # Returns a list of tupples of
    [('goo', 3), ('foo', 5), ('bar', 1)] # key/value pair
    >>>
    >>> dic.get('foo') # Fetching
    5
    >>>
    >>> dic2 = {'zoo':10, 'loo':40}
    >>> dic.update(dic2) # Exteding multiple items
    >>> dic
    {'goo': 3, 'bar': 1, 'foo': 5, 'loo': 40, 'zoo': 10}
    >>>
    >>> dic.pop('zoo') # Deliting item
    10
    >>> dic
    {'goo': 3, 'bar': 1, 'foo': 5, 'loo': 40}

Tuples

Almost all properties are same as list, except tupples are immutable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> t = (0, 1, 2)			# Creation
>>> t
(0, 1, 2)
>>> tt = (2,) # Single element tupple
>>>
>>> t[1] # Indexing
1
>>> t + (3, 4) # Concatenation
(0, 1, 2, 3, 4)
>>> (1, 2) * 3 # Repeatition
(1, 2, 1, 2, 1, 2)
>>> t
(0, 1, 2)
>>> t[1:] # Slicing
(1, 2)

File

open(path, mode, buffering) function opens a file and returns a file object which can be used to read and write files.

Open modes:

Mode Effect
r open file in read mode. Default, if nothing specified.
w Open file in write mode.
a Open file in append mode

Methods

  • file.write(str) - write a string to a file.

    1
    2
    3
    >>> outf = open('myfile', 'w')		# Creates a file object
    >>> outf.write('hello, world\n')
    >>> outf.close()
  • file.read() - Reads whole file.

    1
    2
    3
    4
    5
    6
    7
    8
    >>> fin = open('myfile')	# Default read mode
    >>> print fin.read()

    hello, world!
    I am foo.
    I am bar.

    >>>
  • file.readline() - Returns a line from a file including \n.

1
2
3
>>> fin.seek(0)
>>> fin.readline()
'hello, world!\n'
  • file.readlines() - Returns a list of all lines from a file.
1
2
3
4
5
6
>>> fin = open('myfile')
>>> fin.read()
'hello, world!\nI am foo.\nI am bar.'
>>> fin.seek(0)
>>> fin.readlines()
['hello, world!\n', 'I am foo.\n', 'I am bar.']
  • file.truncate(bytes) - File will be truncated to specified bytes. All previous data will be lost. Specified byte space will be zero filled.

Some important file operations

  • Check if a file/path exists
1
2
3
from os.path import exists

print(exists("/home/username/myfile.txt"))

Python Pickles

Tool to store python object in a file directly with no to-or-from string conversion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Storing Object

>>> list = [1, 2, 3]
>>> list
[1, 2, 3]
>>> fobj = open("file.bin", "wb")
>>> pickle.dump(list, fobj)
>>> fobj.close()

# Restoring object from file

>>> fobj = open("file.bin", "rb")
>>> l = pickle.load(fobj)
>>> l
[1, 2, 3]

Python Shelve

Packed Binary

Boolean Values In Python

Boolean values are two constant objects True and False.

  • Non zero numbers are true.
  • Non empty objects are true.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> bool(0)
False
>>> bool(1)
True
>>> bool(50)
True
>>>
>>> bool([])
False
>>> bool([1, 2])
True
>>>
>>> bool({})
False
>>> bool({'spam': '2$'})
True
>>>
>>> bool(None)
False

Python Statements And Syntax

Notes about python statements and syntax

  • Python statements are ended by new lines. No semicolon is needed.

    1
    2
    a = 40			# Statement
    x = a + 10 # Another Statement
  • Semicolons can be used to use multiple statements in a single line.

    1
    a = 40; b = 60; print(a + b)
  • Brackets (first, second or third) are used to span single statement in multiple
    lines.

    1
    2
    3
    4
    5
    x = (a + b +
    c + d)

    list = [1, 2, 3,
    4, 5, 6]
  • Also \ can be used to span single statement in multiple lines.

    1
    2
    x = a + b + \
    c + d
  • In python colons are used for compound statements (Statements which have nested
    blocks)

    1
    2
    if x > y:
    print("%d is larger" % x)
  • Nested blocks are nested using indentations. Curly braces are not needed.

    1
    2
    3
    if x > y:
    a = x
    b = y
  • For single line block statement can be placed right after colon.

    1
    if x > y: print(x)

Assignment Statement

  • Assignments create object references.
  • Names are created when first assigned.
  • Names must be assigned before being referenced.

Different types of assignment statement:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> spam = 'hello, world'				# Basic form
>>> spam
'hello, world'
>>> spam, ham = 'hello', 'world' # Tupple assignment(Positional)
>>> spam, ham
('hello', 'world')
>>> [spam, ham] = ['hello', 'world'] # List assignment(Positional)
>>> spam, ham
('hello', 'world')
>>>
>>> a, b, c, d = 'spam' # Sequence assignment
>>> a, b, c, d
('s', 'p', 'a', 'm')
>>>
>>> a, *b = 'spam' # Extended sequence unpacking
>>> a
's'
>>> b
['p', 'a', 'm']
>>> spam = ham = 'launch' # Multiple assignment
>>> spam, ham
('launch', 'launch')

In case of multiple assignment all the variables point to the same object(same piece of memory)

1
2
3
4
5
6
7
8
9
10
>>> spam = ham = [1, 2, 3]	# spam & ham are pointing to same object
>>> spam
[1, 2, 3]
>>> ham
[1, 2, 3]
>>> spam[1] = 4 # spam changed
>>> spam
[1, 4, 3]
>>> ham # ham changed too as they are pointing to same object
[1, 4, 3]

Concatenation VS In-place assignment:

In case of concatenation must create a new object, copy it in the object on the
left, then copy it on the right. So it is slower. On the other hand in case of
in-place assignment new item is just added at the end of the memory block.

1
2
3
4
5
6
7
8
>>> L = [1, 2]
>>> L = L + [3, 4] # Concatenation: Slower
>>> L
[1, 2, 3, 4]
>>> L.extend([5, 6]) # In-place assignment: Faster
>>> L
[1, 2, 3, 4, 5, 6]
>>> L += [7, 8] # Python automaticaly use the faster method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> L = [1, 2]
>>> M = L # M & L are shared object
>>> M
[1, 2]
>>> L
[1, 2]
>>>
>>> L = L + [3] # Concatenation
>>> L
[1, 2, 3] # L changed
>>> M
[1, 2] # But M isn't as concatenation creates new object
>>>
>>> M = L # M is pointing to L
>>> M
[1, 2, 3]
>>>
>>> L += [4] # In-place assignment
>>> L
[1, 2, 3, 4] # L is changed
>>> M
[1, 2, 3, 4] # M is changed too as augmented assignmets are in-place

Python variable naming convension

  • Names that begin with single underscore(_X) are not imported by from module
    import * statement

  • Names that begin with two leading and trailing underscore(__X__) are system
    defined and have special meaning to interpreter.

  • Names that begin with two leading underscore(__X) are localized to enclosing
    classes

  • Class names usually starts with uppercase letters.

  • Module names starts with lowercase letters.

Printing to stdout

see: Python String data type in details

A fun \r(carriage return trick):

1
2
3
while True:
for ch in ["\\", "-", "|", "/"]:
print("%s\r" % ch, end = "")

Printing To A File

Using file argument of the print() function:

1
2
3
>>> print("hello, world", file = open("hello.txt", "w"))
>>> open("hello.txt").read()
'hello, world\n'

Using sys.stdout:

1
2
3
4
5
6
7
8
9
>>> import sys
>>> tmp = sys.stdout
>>>
>>> sys.stdout = open("hello.txt", "w")
>>> print("hello, world!")
>>>
>>> sys.stdout = tmp
>>> open("hello.txt").read()
'hello, world!\n'

Taking Inputs

Taking inputs from stdin

Pyhton takes inputs as a string from stdin. In python 3 input("prompt") function is used to take input. In python 2.x raw_input("prompt") is used.

1
2
3
4
5
6
name = input("Enter your name: ")

# Inputs are taken as Strings. To convert to int.
age = int(input("Enter your age: "))

print("Hello, %s! Your age is %d" % (name, age))

Commandline Arguments

1
2
3
4
5
6
7
8
from sys inport argv

script, arg0, arg1, arg2 = argv

print("Script name: %s" % script)
print("First argument: %s" % arg0)
print("Second argument: %s" % arg1)
print("Third argument: %s" % arg2)

Python If Statement

Structure:

1
2
3
4
5
6
if <test1>:
<statement1>
elif <test2>:
<statement2>
else:
<statement3>

No switch case in python. Work around with if:

1
2
3
4
5
6
7
8
9
10
11
12
>>> choice = 'gam'
>>>
>>> if choice == 'spam':
... print("0$")
... elif choice == 'ham':
... print("1$")
... elif choice == 'gam':
... print("2$")
... else:
... print("Bad Choice!")
...
2$

Dictionary based work around (Less typing!):

1
2
3
4
5
6
>>> choice = 'jam'
>>>
>>> print({'spam': '0$',
... 'ham': '1$',
... 'gam': '2$'}.get(choice, 'Bad choice!'))
Bad choice!

Logical Operators in Python

and operator:

1
2
if X and Y:			# True if X and Y both True
# Do something

Return Value: and didn’t return True or False. Returns first object which is
false(zero or empty) from left to right. If expression is true returns right most
object.

1
2
3
4
5
6
>>> 1 and 2
2
>>> [] and 2
[]
>>> 1 and [] and 2
[]

or operator:

1
2
if X or Y:			# True if X or Y is True
# Do something

Return value: or didn’t return True or False. Returns first object which is true
(non-zero / non-empty) from left to right.

1
2
3
4
5
6
>>> 2 or 3
2
>>> [] or 3
3
>>> {} or 3 or []
3

not operator:

1
2
if not X:			# Invert logic
# Do something

Return value: Returns True or False

Range:

if 0 <= x <= 10:
DO SOMETHING

1
2
3
4
5
6
>>> rn = 1500
>>> if 2100 < rn < 4000:
... print("Transitional")
... else:
... print("Different flow pattern")
Different flow pattern

if/else Ternary Expression

Consider following code:

1
2
3
4
if X:
A = Y
else:
A = Z

Ternary Expression:

1
A = Y if X else Z
1
2
3
>>> count = 5
>>> "five" if count == 5 else "not five"
'five'

Python While and For Loops

Python While Loop

While loop’s general format

1
2
3
4
5
6
7
while <test1>:
<statement1>
if <test2>: break # Exit loop now. Skip else part
if <test3>: continue # Continue to test1

else: # Execute only if loop is exited without break
<statement2>

pass: Do nothing. Just an empty statement place holder.

Python For Loop

For loop’s general format

1
2
3
4
5
6
for <target> in <object>/<sequence>:
<statements>
if <test>: break
if <test>: continue
else:
<statements>
  • Any sequence works in for loop
1
2
3
4
5
6
7
8
9
10
11
12
13
>>> for i in [1, 2, 3]:
... print(i, end = '\t')
... else:
... print("")
...
1 2 3
>>>
>>> for ch in "abc":
... print(ch, end = '\t')
... else:
... print("")
...
a b c

Tupple assignment / Sequence Unpacking in for loop

1
2
3
4
5
>>> for (a, b) in [(1, 2), (3, 4)]:
... print("%d, %d" % (a, b))
...
1, 2
3, 4

Some sequence unpacking example

1
2
3
4
5
6
7
8
9
10
>>> for ((a, b), c) in [((1, 2), 3), ((4, 5), 6)]:
... print(a, b, c)
...
1 2 3
4 5 6
>>> for ((x, y), z) in [((1, 2), 3), ("XY", 'Z')]:
... print(x, y, z)
...
1 2 3
X Y Z

Using for loop for iterating through dictionary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> dict = {1: 'a', 2: 'b', 3: 'c'}
>>> for key in dict.keys():
... print("Key: %d\tValue: %c" % (key, dict[key]))
...
Key: 1 Value: a
Key: 2 Value: b
Key: 3 Value: c
>>>
>>> for key, value in list(dict.items()):
... print(key, value)
...
1 a
2 b
3 c

Range

range(FROM, UPTO, STEP) - Create a list of numbers FROM to UPTO in python 2.0. In python 3.0 it’s an iterator.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Python 2.0

>>> range(0, 10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
>>> range(0, 10, 2)
[0, 2, 4, 6, 8]

# Python 3.0

>>> list(range(0, 10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>
>>> list(range(0, 10, 2))
[0, 2, 4, 6, 8]

Range items can be negative numbers and can be in reverse order.

1
2
3
4
5
>>> list(range(8, 0, -1))
[8, 7, 6, 5, 4, 3, 2, 1]
>>>
>>> list(range(4, -4, -1))
[4, 3, 2, 1, 0, -1, -2, -3]

Range can be used as a iterator in for loops.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> X = "abcdefgh"
>>> for i in range(0, len(X), 2):
... print(X[i], end = '')
... else:
... print()
...
aceg

# Alternative good solution is with sclicing

>>> X
'abcdefgh'
>>> for ch in X[::2]:
... print(ch, end = '')
... else:
... print()
...
aceg

Zip

zip(seq1, seq2, ...) takes one or more sequence as argument and creates a list of Tuples from them. zip creates an iterable object. Which can be converted to list or other sequence object.

1
2
3
4
5
>>> l = [1, 2, 3]
>>> m = [4, 5, 6]
>>> n = [7, 8, 9]
>>> list(zip(l, m, n))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

zip() truncates if the size of the sequences mismatches.

1
2
3
4
>>> l = [1, 3, 5, 7]
>>> m = [2, 4]
>>> list(zip(l, m))
[(1, 2), (3, 4)] # Truncates

zip() can be used to iterate through multiple sequence in for loop.

1
2
3
4
5
6
>>> for x, y, z in zip(l, m, n):
... print(x, y, z)
...
1 4 7
2 5 8
3 6 9

zip() can be used to construct a dictionary from key and value lists.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> key = ["spam", "ham", "egg"]
>>> value = [1, 2, 3]
>>>
>>> D = {}
>>> for k, v in list(zip(key, value)):
... D[k] = v
...
>>> D
{'ham': 2, 'spam': 1, 'egg': 3}
>>>

# Also dict constructor can be used

>>> D = dict(zip(key, value))
>>> D
{'ham': 2, 'spam': 1, 'egg': 3}

Map

Python 2.0: In python 2.0 map() gives similar functionality like zip().

1
2
3
4
5
6
# Python 2.0

>>> l = [1,2,3]
>>> m = [4,5,6]
>>> map(None, l, m)
[(1, 4), (2, 5), (3, 6)]

Python 3.0: In python 3.0 map takes a function and one or more sequence arguments and collects the results of calling the function.

1
2
>>> list(map(ord, "spam"))
[115, 112, 97, 109]

Enumerate

enumerate() is used to find the index and value of a sequence. It creates a generator object which is iterable.

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
27
# Normal Way

>>> S = "hello"
>>> index = 0
>>>
>>> for value in S:
... print(index, value)
... index += 1
...
0 h
1 e
2 l
3 l
4 o

# enumerate way

>>> S = "hello"
>>>
>>> for index, value in enumerate(S):
... print(index, value)
...
0 h
1 e
2 l
3 l
4 o

Iterators & Comprehension

An object is called iterable if it’s a sequence or an object that produce one result at a time.
Any object that supports iteration has __next__() method. It raises StopIteration exception
at the end of the series of results.

File Iterator

1
2
3
# Manual method


List Comprehension

General Structure: [expression for target1 in iterable1 if condition1 for target2 in iterable 2 if condition2 ...]

List Comprehension:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> L = [1, 2, 3]
>>> L = [x ** 2 for x in L]
>>> L
[1, 4, 9]
>>>

# Equavalent map

>>> list(map((lambda x: x ** 2, ), [1, 2, 3]))
[1, 4, 9]

>>> lines = [line for line in open('text')]
>>> lines
['hello, world!\n', 'Imagine\n']
>>>
>>> lines = [line.rstrip() for line in open('text')]
>>> lines
['hello, world!', 'Imagine']

Filtering:

1
2
3
4
5
6
7
8
>>> L = [x for x in range(0, 11) if x % 2 == 0]
>>> L
[0, 2, 4, 6, 8, 10]

# Equavalent using map using filter

>>> list(filter((lambda x: x % 2 == 0), range(0, 11)))
[0, 2, 4, 6, 8, 10]

Evaluating and Filtering:

1
2
3
4
5
6
7
>>> [x ** 2 for x in range(0, 11) if x % 2 == 0]
[0, 4, 16, 36, 64, 100]

# Equavalent

>>> list(map((lambda x: x ** 2), filter((lambda x: x % 2 == 0), range(0, 11))))
[0, 4, 16, 36, 64, 100]

Nested Loops:

1
2
3
4
5
>>> [x + y for x in [0, 1, 2] for y in [100, 99, 98]]
[100, 99, 98, 101, 100, 99, 102, 101, 100]

>>> [(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1]
[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]

Work with Matrices:

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> M = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
>>> M
[[0, 1, 2], [3, 4, 5], [6, 7, 8]]
>>>
>>> [M[row][0] for row in range(len(M))]
[0, 3, 6]
>>>
>>> N = [[9, 10, 11], [12, 13, 14], [15, 16, 17]]
>>>
>>> [M[row][col] * N[row][col] for row in range(len(M)) for col in range(len(M))]
[0, 10, 22, 36, 52, 70, 90, 112, 136]
>>> [[M[row][col] * N[row][col] for col in range(len(M))] for row in range(len(M))]
[[0, 10, 22], [36, 52, 70], [90, 112, 136]]

Dictionary and Set comprehension

1
2
3
4
5
>>> {key: value for key, value in list(zip("abc", [1, 2, 3]))}
{'c': 3, 'a': 1, 'b': 2}
>>>
>>> {x for x in "abccde"}
{'c', 'e', 'a', 'd', 'b'}

Documentation

dir() Funtion

Python dir() lists all the methods of an object.

1
2
>>> dir(list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']

Docstrings

Docstrings are used for documenting python module, class, class methods,
functions etc. Docstrings are placed as the first statement of the
module, class, methods, functions.

From PEP 257

A docstring is a string literal that occurs as the first statement in a module, function, class, or method definition. Such a docstring becomes the __doc__ special attribute of that object.

All modules should normally have docstrings, and all functions and classes exported by a module should also have docstrings. Public methods (including the __init__ constructor) should also have docstrings. A package may be documented in the module docstring of the __init__.py file in the package directory.

Example:

1
2
3
4
5
6
7
8
9
10
def add(x, y):
"""Add two numbers

Args:
x : first number
y : second number
Returns:
addition of the two number
"""
return x + y

PyDoc and help()

Python help() function gives the documentation of an object that are
created by docstrings

Functions

Example:

1
2
3
4
5
6
7
>>> def sum(x, y):
... return x + y
...
>>> sum(10, 5)
15
>>> sum(10, 10)
20

Function Attributes

Default Argumets:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> def func(a, b):
... print(a, b)
...
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>>
>>> func.__name__
'func'
>>>
>>> dir(func.__code__)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_kwonlyargcount', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
>>>
>>> func.__code__.co_varnames
('a', 'b')
>>> func.__code__.co_argcount
2

Custom Argumets:

1
2
3
4
5
>>> func.cusarg = 1
>>> print(func.cusarg)
1
>>> dir(func)
['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'cusarg']

Function Annotations

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> def func(a: 'spam' = 0, b: (1, 10) = 1, c: float = 2) -> int:
... print(a + b + c)
...
>>> func.__annotations__
{'return': <class 'int'>, 'c': <class 'float'>, 'a': 'spam', 'b': (1, 10)}
>>>
>>> for arg in func.__annotations__:
... print(arg, " => ", func.__annotations__[arg])
...
return => <class 'int'>
c => <class 'float'>
a => spam
b => (1, 10)

Scopes

global keyword

Cross File Changes

Enclosing Functions

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
27
28
>>> x = 10
>>>
>>> def func1():
... x = 20
... def func2():
... print(x) # func2() knows about x
# as enclosing function
... func2()
...
>>>
>>> func1()
20 # 20 not 10 because LEGB rule

# However this code doesn't work
# in previous versions. As searches only local scope
# before global scope. Work around:

>>> x = 10
>>>
>>> def func1():
... x = 20
... def func2(x = x):
... print(x)
... func2()
...
>>>
>>> func1()
20
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
27
28
29
30
31
32
33
34
35
36
# Supposed to make a list of functions
# With different values of i.

>>> def actionMaker():
... acts = []
... for i in range(0, 5):
... acts.append(lambda x: i ** x)
... return acts
...
>>>
>>> f = actionMaker()
>>>
>>> f[0](2) # Should print 0
16 # prints 16 instead
>>>
>>> f[1](2) # Should print 1
16 # prints 16 insead

# This doesn't work because eclosing namespace is looked up when
# the nested function is called. so i is looked up when
# f[]() is called. But after actionMaker() call i is 4
# Work around:
ctionMaker():
... acts = []
... for i in range(0, 5):
... acts.append(lambda x, i = i: i ** x)
... return acts
...
>>>
>>> f = actionMaker()
>>> f[0](2)
0
>>> f[1](2)
1
>>> f[2](2)
4

Forward Reference

1
2
3
4
5
6
7
8
9
10
>>> def func1():
... x = 10
... func2(x) # OK as long as def for func2() is
# executed before func1() is called
...
>>> def func2(x):
... print(x)
...
>>> func1()
10

Factory Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>> def maker(x):
... def action(y):
... return x ** y
... return action
...
>>>
>>> f = maker(2)
>>> f
<function maker.<locals>.action at 0x7f69aa6e99d8>
>>> print(f(5)) # Nested function is remembering 2
32
>>> print(f(6))
64
>>>
>>> g = maker(3) # Changing state
>>>
>>> print(g(5)) # Each function got its own state. g got its own state
243

nonlocal Statement

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
27
28
29
30
31
32
33
34
>>> def tester(state):
... def nested(local):
... print(state, local)
... return nested
...
>>> f = tester(0)
>>> f('hello')
0 hello
>>> f('gello')
0 gello
>>>
>>> def tester(state):
... def nested(local):
... print(state, local)
state += 1 # Can't do this. Raise error.
# Nested function can't change
# enclosing variable. Can only read.
... return nested

# Use nonlocal to change enclosing variable in a nested function.

>>> def tester(state):
... def nested(local):
... nonlocal state
... print(state, local)
... state += 1
... return nested
...
>>> f = tester(0)
>>> f('hello')
0 hello
>>> f('gello')
1 gello
>>>

Function Arguments

Avoiding Mutable Argument Changes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
>>> L = [1, 2]
>>>
>>> def changer(x):
... x[0] = 10
...
>>> changer(L)
>>> L
[10, 2] # Function can change mutable objects
>>> L = [1, 2]
>>>
>>> changer(L[:]) # Call by copying to prevent change
>>> L
[1, 2]
>>>
>>> def changer(x):
... x = x[:] # Make copy to prevent change.
... x[0] = 10
...
>>> changer(L)
>>> L
[1, 2]
>>>
>>> changer(tuple(L)) # Really prevent change. Will raise error
# in attempting change

Special Argument Matching Modes

Different types of function call

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> def func(a, b, c, d):
... print(a, b, c, d)
...
>>> func(1, 2, 3, 4) # Normal Call
1 2 3 4
>>>
>>> func(1, *(2, 3), 4) # Tuple unpacking
1 2 3 4
>>>
>>> func(1, **{'b': 2, 'c': 3, 'd': 4}) # Dictionary unpacking
# keyword should be matched
# with function argument
1 2 3 4
>>>
>>> func(1, *(2,), **{'c': 3, 'd': 4}) # Mixed
1 2 3 4

Different way of function arguments

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
######################################################################

>>> def func(a, b): # Normal Arguments
... print(a, b)
...
>>> func(1, 2)
1 2
>>>

######################################################################

>>> def func(a, b = 0, c = 1): # Function with default arguments
... print(a, b, c)
...
>>> func(1)
1 0 1
>>> func(1, 2)
1 2 1
>>> func(1, 2, 3)
1 2 3
>>>

######################################################################

>>> def func(*targs): # Using Tuple. Can take variable number of argumets
... print(targs)
...
>>> func(1, 2)
(1, 2)
>>> func(1, 2, 3)
(1, 2, 3)
>>>

######################################################################

>>> def func(**dargs): # Dictionary based. Can take variable numer of arguments
... print(dargs)
...
>>> func(a = 1, b = 2) # But argumets have to be passed in key = value
# pair
{'b': 2, 'a': 1}
>>> func(a = 1, b = 2, c = 3)
{'c': 3, 'b': 2, 'a': 1}

######################################################################

>>> def func(a, *targs, **dargs): # Mixed
... print(a, targs, dargs)
...
>>> func(1, 2, 3, d = 4, e = 5)
1 (2, 3) {'e': 5, 'd': 4}

#####################################################################

# in function defination every argumet afte * are keyword only
# argumets. They have to be passed using key = value format in the
# function

>>> def func(a, *b, c): # c is keyword only argumet
... print(a, b, c)
...
>>> func(1, 2, c = 3) # c is passed using key = value format
1 (2,) 3
>>> func(1, 2, 3) # Raise error as c is keyword only
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func() missing 1 required keyword-only argument: 'c'
>>>
>>> def func(a, *, c): # without variable argumet, only two argumet
... print(a, c)
...
>>> func(1, c = 3)
1 3
>>> func(1, 2, c = 3) # Raise error as 3 argumet passed instead of two
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: func() takes 1 positional argument but 2 positional arguments (and 1 keyword-only argument) were given

Argumet order: func(normal, *tupple, keyword only, **dictionary)

Lambda Functions

General form: lambda arg1, arg2, ...: expression

1
2
3
>>> x = lambda a = 1, b = 2, c = 3: a + b + c 
>>> x(10, 20, 30)
60

lambda is an expression not a statement. So it can be used where def can’t. Like inside a list or dictionary.

1
2
3
4
5
6
>>> L = [lambda x: x ** 2, lambda x: x ** 3, lambda x: x ** 4]
>>> L[0](5)
25
>>>
>>> {'to': lambda x: x ** 2, 'lo': lambda x: x ** 3, 'mo': lambda x: x ** 4}['to'](10)
100

Generator Functions and Expressions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> def genSeq(N):
... for i in range(N):
... yield i ** 2
...
>>>
>>> I = iter(genSeq(3))
>>> I.__next__()
0
>>> I.__next__()
1
>>> I.__next__()
4
>>> I.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration

Send:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> def gen(n):
... for i in range(n):
... x = yield i
... print(x)
...
>>>
>>> g = gen(10)
>>> next(g)
0
>>> next(g)
None
1
>>> next(g)
None
2
>>> next(g)
None
3
>>> g.send(80)
80
4
1
2
3
4
5
6
7
>>> G = (x ** 2 for x in range(10))
>>> G.__next__()
0
>>> G.__next__()
1
>>> G.__next__()
4
  • Both generator function and expression are their own iterators. So they can have only one active iterator. I multiple iterator is assign, they all will point to same iteration point.
1
2
3
4
5
6
7
8
9
10
11
>>> G = (x ** 2 for x in range(10))
>>> G is iter(G)
True
>>> it1 = iter(G)
>>> next(it1)
0
>>> next(it1)
1
>>> it2 = iter(G)
>>> next(it2)
4

Decorators

Decorators are used to change any callable objects behaviour like
function or class etc. There are two type of decorators. Function decorator
and class decorator. One is work for function and another is work for
classes. But both work in same way.

Consider this senario where a function func_decorator is changing other
functions behaviour. This function takes other functions as argument and
change is behaviour using a wrapper function. Then this wrapper function is
assigned to the original function to change original functions behaviour.

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
27
28
#!/usr/bin/env python3

def func_decorator(func):
def func_wrapper(x):
print("Function " + func.__name__ + " does:")
func(x)
print("I have changed its behaviour")

return func_wrapper

def foo(x):
print("hello, " + x)

def bar(x):
print("gello, " + x)

print("Befor using func_decorator:\n")

foo("tux")
bar("tux")

foo = func_decorator(foo)
bar = func_decorator(bar)

print("\nAfter using func_decorator:\n")

foo("tux")
bar("tux")

Output:

1
2
3
4
5
6
7
8
9
10
11
12
13
Befor using func_decorator:

hello, tux
gello, tux

After using func_decorator:

Function foo does:
hello, tux
I have changed its behaviour
Function bar does:
gello, tux
I have changed its behaviour

We can find the same behaviour by using python decorator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python3

def func_decorator(func):
def func_wrapper(x):
print("Function " + func.__name__ + " does:")
func(x)
print("I have changed its behaviour")

return func_wrapper

@func_decorator
def foo(x):
print("hello, " + x)

@func_decorator
def bar(x):
print("gello, " + x)

foo("tux")
bar("tux")

Output:

1
2
3
4
5
6
Function foo does:
hello, tux
I have changed its behaviour
Function bar does:
gello, tux
I have changed its behaviour

Module Files

  • Module files run only once after the first import
mod1.py
1
2
print("hello")
spam = 1
Interactive session
1
2
3
4
5
6
7
8
9
10
>>> import mod1
hello
>>> mod1.spam
1
>>> mod1.spam = 2
>>> mod1.spam
2
>>> import mod1
>>> mod1.spam # Didn't reinitialized as modules run only once
2
  • If two module contains same name and both are imported via from. Then namespace will be resolved with the latest from.
mod1.py
1
2
def func():
print("mod1")
mod2.py
1
2
def func():
print("mod2")
Interactive session
1
2
3
4
>>> from mod1 import func
>>> from mod2 import func
>>> func()
mod2 # Resolved with the last from statement
  • Import module mod which in ./dir1/dir2 location.
./dir1/dir2/mod.py
1
print("./dir1/dir2/mod.py imported")
Interactive Session
1
2
>>> import dir1.dir2.mod
./dir1/dir2/mod.py imported

Python Object Oriented Programming

OOP Basics

Basic Class Syntax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/python

class Point:
def setX(self, x):
self.x = x

def setY(self, y):
self.y = y

def setPoint(self, x, y):
self.x = x
self.y = y

def getPoint(self):
return self.x, self.y

p = Point()
p.setX(5)
p.setY(5)
print(p.getPoint())

Constructor

In class a special method __init__() works as a constructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/python

class Point:
# Constructor
def __init__(self, x, y):
print("Executing constructor!")
self.x = x
self.y = y

def getPoint(self):
return self.x, self.y

p = Point(2, 3)
print(p.getPoint())

Destructor

A special method __del__ works as a destructor.

1
2
3
4
5
6
7
8
9
10
11
12
class Point:
def __init__(self, x = 0, y = 0):
self.x = x
self.y = y
print("Created (%f, %f)" % (self.x, self.y))

def __del__(self):
print("Removing (%f, %f)" % (self.x, self.y))

if __name__ == "__main__":
p = Point(2, 3)
del(p)

Output:

1
2
Created (2.000000, 3.000000)
Removing (2.000000, 3.000000)

Encapsulation

  • var - Public variable. Can be accessed from outside of the class.
  • _var - Protected variable. Can be accessed from outside of the class
    but it should be accessed only from the sub class.
  • __var - Private variable. Can be accessed only within the class.

Getter and Setter Using Property

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#!/usr/bin/env python3

class Point:
""" Point class - Creates a point in 2D space """

def __init__(self, x = 0, y = 0):
""" Initiate a point object with x and y

Args:
x : x coordinate
y : y coordinate
Returns:
None
"""
self.x = x
self.y = y

# Getter for x
@property
def x(self):
""" Returns value of x coordinate"""
return self.__x

# Setter for x
@x.setter
def x(self, x):
""" Set the value of x coordinate"""
self.__x = x

# Getter for y
@property
def y(self):
"""Returns value of y coordinate"""
return self.__y

# Setter for y
@y.setter
def y(self, y):
""" Set the value of y coordinate """
self.__y = y

if __name__ == "__main__":
p = Point(2, 4)

print(p.x) # calling getter
print(p.y) # calling getter

p.x = 5 # calling setter
p.y = 3 # calling setter

print(p.x) # calling getter
print(p.y) # calling getter

Static Methods

Static methods can be called by both the class
and an instance of the class.

Problem with normal method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python3

class Skynet(object):
__id = 0

def __init__(self): # can be called only by an instance
type(self).__id += 1

def get_id(self):
return Skynet.__id

if __name__ == "__main__":
moly = Skynet()
print(moly.get_id()) # This is ok
print(Skynet.get_id()) # This will generate an error

And in this case:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python3

class Skynet(object):
__id = 0

def __init__(self):
type(self).__id += 1

def get_id(): # Can be called only by class itself
return Skynet.__id

if __name__ == "__main__":
moly = Skynet()
print(moly.get_id()) # will generate error
print(Skynet.get_id()) # ok

To solve this problem static method can be used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3

class Skynet(object):
__id = 0

def __init__(self):
type(self).__id += 1

@staticmethod # can be called both by class
def get_id(): # and class instance
return Skynet.__id

if __name__ == "__main__":
moly = Skynet()
print(moly.get_id()) # calling using an instance
print(Skynet.get_id()) # calling using the class

Class Methods

Like static methods class methods are not bound to specific instance.
But take a class as the first argument.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3

class Skynet(object):
__id = 0

def __init__(self):
type(self).__id += 1

@classmethod
def get_id(cls):
return cls.__id

if __name__ == "__main__":
moly = Skynet()
print(moly.get_id()) # calling using an instance
print(Skynet.get_id()) # calling using the class

Inheritance Syntax and Example

1
2
3
4
5
6
7
8
9
class C1:
# C1 class methods

class C2:
# C2 class methods

# class C3 inherited class C1 and C2
class C3(C1, C2):
# C3 class mathods

Example:

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
#!/usr/bin/env python3

class Robot:
""" Robot class """
def __init__(self, name, dimension):
self.name = name
self.dimension = dimension

def __str__(self):
return "Name: " + self.name + "\n" + "Dimension: " + str(self.dimension)

class LazyRobot(Robot):
def __init__(self, name, dimension, speciality):
super().__init__(name, dimension)
self.speciality = speciality

def __str__(self):
return super().__str__() + "\n" + "Speciality: " + self.speciality

if __name__ == "__main__":
ceaser = Robot("ceaser", 5)
print(ceaser)

walle = LazyRobot("walle", 2, "waiting")
print(walle)

Using class like C/C++ Structure

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
27
28
29
30
31
32
#!/bin/python

class Rec: pass

Rec.name = "Name"
Rec.age = 0
Rec.sex = "Sex"

# Instance of the class
x = Rec()

# Automatically inherites class attributes
print("%s | %d | %s" % (x.name, x.age, x.sex))

# Can assign its own value
x.name = "Bob"
x.age = 43
x.sex = "Male"

# Can defaine its own attributes
x.married = True

# Another instance of the class
y = Rec()

y.name = "Alice"
y.age = 19
y.sex = "Female"
y.married = False

print("%s | %d | %s | Married? %d" %(x.name, x.age, x.sex, x.married))
print("%s | %d | %s | Married? %d" %(y.name, y.age, y.sex, y.married))

Output:

1
2
3
Name | 0 | Sex
Bob | 43 | Male | Married? 1
Alice | 19 | Female | Married? 0

Operator Overloading and Magic Methods

There are special methods in python with double underscore
at the begining and end like __add__ which can be used
for operator overloading. Following example shows
overloading __str__ method, which will be called when
printing the object or str(object):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/python

class Person:
def __init__(self, name, job = None, pay = 0):
self.name = name
self.job = job
self.pay = pay

# Operator Overloading
# Automaticaly called when object is in print statement
def __str__(self):
return '[Person: %s, %s, %s]' % (self.name, self.job, self.pay)

# Execute when run as a script
if __name__ == '__main__':
bob = Person("Bob Smith", "Kernel Developer", "10000")
print(bob)

Output:

1
[Person: Bob Smith, Kernel Developer, 10000]

Here is another example overloading __add__() method
and __str__() method:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/usr/bin/env python3

class Money(object):
""" This class represents money """
def __init__(self, value, unit = "USD", rate = 1):
""" Initialize Money object

Args:
value : Money value
unit : Unit of the money. e.g: USD, BDT
rate : Exchange rate in USD
Returns:
None
"""
self.value = value
self.unit = unit
self.rate = rate

def __str__(self):
return str(self.value) + " " + self.unit

class BDT(Money):
""" This class represents Bangladesh Taka """
def __init__(self, value, rate = 0.012):
super().__init__(value, "BDT", rate)

def __add__(self, other):
"""
overloaded __add__ method for BDT
convert into dollar then add them
returns the result in BDT
"""

return BDT((self.rate * self.value \
+ other.rate * other.value) \
/ self.rate)

class EURO(Money):
"This class represents Euro"
def __init__(self, value, rate = 1.17):
super().__init__(value, "EURO", rate)

def __add__(self, other):
"""
overloaded __add__ method for EURO
convert into dollar then add them
returns the result in EURO
"""

return EURO((self.rate * self.value \
+ other.rate * other.value) \
/ self.rate)

if __name__ == "__main__":
print(BDT(5) + EURO(1) + BDT(2))

A list of python magic methods can be found
here

For each binary overloading methods, there is a
reverse method. Like for __add__() method there
is a __radd__() method to handle situation like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3

class BDT:
def __init__(self, value):
self.value = value

def __str__(self):
return str(self.value) + " " + "BDT"

def __add__(self, other):
if type(other) == int or type(other) == float:
return BDT(self.value + other)
else:
return BDT(self.value + other.value)

if __name__ == "__main__":
print(BDT(50) + BDT(5))
print(BDT(10) + 2)
print(3 + BDT(13))

Output:

1
2
3
4
5
6
55 BDT
12 BDT
Traceback (most recent call last):
File "./radd.py", line 19, in <module>
print(3 + BDT(13))
TypeError: unsupported operand type(s) for +: 'int' and 'BDT'

This problem can be solved using reverse method. Python first
search the overloaded method within the class, then the
corresponding reverse method in the class. Then search the
method in the parent class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/env python3

class BDT:
def __init__(self, value):
self.value = value

def __str__(self):
return str(self.value) + " " + "BDT"

def __add__(self, other):
if type(other) == int or type(other) == float:
return BDT(self.value + other)
else:
return BDT(self.value + other.value)

def __radd__(self, value):
return self.__add__(value)

if __name__ == "__main__":
print(BDT(50) + BDT(5))
print(BDT(10) + 2)
print(3 + BDT(13))

Output:

1
2
3
55 BDT
12 BDT
16 BDT

Slots

Normally we can create class attributes dynamically outside of the class.

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/env python3

class Point:
def __init__(self, x, y):
self.x = x
self.y = y

if __name__ == "__main__":
p = Point(2, 3)

p.z = 4 # Dynamically adding attribute
print(p.z)

Slots are used to prevent this dynamic attribute creation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python3

class Point:
__slots__ = ['x', 'y']

def __init__(self, x, y):
self.x = x
self.y = y

if __name__ == "__main__":
p = Point(2, 3)

# Will generate error as we have added slots in the class.
# Now the number of class attributes are fixed.
p.z = 4
print(p.z)

Output:

1
2
3
4
Traceback (most recent call last):
File "./slots.py", line 15, in <module>
p.z = 4
AttributeError: 'Point' object has no attribute 'z'

A Class Example

Python Keywords and Symbols

At a Glance

  • [] - Make a list.
  • {} - Make a dictionary.
  • () - Make a Touple.
  • if - if block
  • else - else block
  • while - While loop
  • in - Membership check
  • ‘not’ - Logical not
  • ‘and’ - Logical and
  • ‘or’ - Logical or

Tricks

List All Attributes of an Object

dir() function returns a list of all the attributes of an objetc.

1
2
3
>>> s = "I am a string"
>>> dir(s)
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

details of an object’s methods or attributes can be found using help() function.

>>> help(s.join)
Help on built-in function join:

join(...)
    S.join(iterable) -> string

    Return a string which is the concatenation of the strings in the
    iterable.  The separator between elements is S.

INDEX

  1. GNU Make
  2. Serial Programming
  3. C Programming
  4. x 86 Assembly Programming

GNU Make

  1. Writing Make Files - Short HowTo
  2. What is a Makefile and how does it work?

Serial Programming

  1. Serial Programming Guide for POSIX Operating Systems - Michael R. Sweet
  2. Serial Port Programming on Linux - xanthium
  3. Example by me based xanthium
  4. Serial Programming Wikibook
  5. Arduino Serial C Program by todbot
  6. Understanding UNIX termios VMIN and VTIME

C Programming

  1. Multithreading in C, POSIX style

x86 Assembly Programming

  1. A Tiny Guide to Programming in 32-bit x86 Assembly Language
  2. x86 Assembly Guide

Locks and Keys

  1. Thread Locks

NCURSES API

Index

Initialization Functions

  • initscr() - Start curses mode.

  • raw() and cbreak() - Normally the terminal driver buffers the characters a user types until a new line or carriage return is encountered. But most programs require that the characters be available as soon as the user types them. The above two functions are used to disable line buffering. The difference between these two functions is in the way control characters like suspend (CTRL-Z), interrupt and quit (CTRL-C) are passed to the program. In the raw() mode these characters are directly passed to the program without generating a signal. In the cbreak() mode these control characters are interpreted as any other character by the terminal driver.

  • echo() and noecho() - if noecho() is used key press will not echoed in the screen. other do the oposite.

  • keypad(stdscr, TRUE) - To get special inputs like F1, F2, Arrow keys etc.

Input / Output Functions

For each input/output functions there are four types of function.

  • Normal : printw(string) - print in the current position of the cursor in the standard screen(stdscr).
  • w less mv type: mvprintw(y, x, string) - move cursor to x, y then print in that position in the stdscr.
  • w type : wprintw(window, string) - print string in the current cursor position of the specified window.
  • w ed mv type : mvwprintw(window, y, x, string) - move to x, y and then print string in the specified window.

Output Functions

  • printw(string) - print string.
  • addch(ch, attributes) - print single character.
  • addstr(string) - print string.

NB: Each function has corresponding mv, w, mvw functions.

Input Functions

  • ch = getch() - to get a character from keyboard.
  • scanw() - scanf() like function.
  • getstr(str) - to get a string.

NB: Each function has corresponding mv, w, mvw functions.

Attributes

Attributes are used to set different text properties. Like Bold, Underlined, Italic etc.

  • attron(attribute1 | attrubute2 | ...) - To set some attribute on.
  • attroff(attr1 | attr2 | ...) - to set some attribute off.
  • attrset(attr1 | attr2 | ...) - Remove any prevoius attributes and set specified attributes.

Here is a list of some attributes:

Attribute Effect
A_NORMAL Normal Output
A_STANDOUT Best highlighting mode of the terminal
A_UNDERLINE Underlined
A_REVERSE Reverse video
A_BLINK Blinking
A_DIM Half bright
A_BOLD Extra bright or bold
A_PROTECT Protected mode
A_INVIS Invisible or blank mode
A_ALTCHARSET Alternate character set
A_CHARTEXT Bit-mask to extract a character
COLOR_PAIR(n) Color-pair number n

NB: There is also corresponding w functions like wattron() which work on specific window.

  • chgat(nChar, attribute, color_pair_token, null) - This function set an attribute on texts without changing cursor position. nChar is the number of char on which attribute will be set. Negative nChar means end of the line. color_pair_token is for color pair. last is always null.

  • mvchgat(y, x, nChar, attribute, color_pair_token, null) - chgat() from x, y position.

Managing Windows

  • WINDOW *newwin(height, width, starty, startx) - To create a new window. It takes height and width of the window as argument. Also takes x, y position of the window. Returns a WINDOW pointer.

  • delwin(WINDOW *win) - To remove a window.

  • box(window, vchar, hchar) - vchar & hchar is ther char which will be used to draw boxes vertical and horizontal lines.

  • wborder(window, ls, rs, ts, bt, tl, tr, bl, br) - 2 - 8 arguments are character for drawing ls(left side), rs(right side) of the box.

MySQL Queries

User Administration

Syntax:

1
CREATE USER '<username>'@'<ip address>' IDENTiFIED BY '<password>'

Example:

1
CREATE USER 'root'@'192.168.1.10' IDENTIFIED BY 'mysecretpass'

Give all access:

1
GRANT ALL PRIVILEGES ON *.* TO '<username>'@'<ip>' IDENNTIFIED BY '<password>'

Give access to specific DB:

1
GRANT ALL PRIVILEGES ON <dbname>.* TO '<username>'@'<ip>' IDENTIFIED BY '<password>'

Database Queries

To show all the databases:

1
SHOW DATABASES;

Creating a database:

1
CREATE DATABASE database_name;

Delete database:

1
DROP DATABASE db_name;

Selecting the database:

1
USE database_name;

List all the table in a DB:

1
SHOW TABLES;

Table Queries

Show all the information of a table:

1
DESCRIBE tb_name;

Creating a table:

Syntax:

1
CREATE TABLE tb_name(column1_name column1_type, column2_name column2_type, ...);

Types:

  • CHAR(size) - Character column | Holds 255 chars.
  • VARCHAR(size) - String | Holds 255 chars.
  • TEXT - String | Larger than 255.
  • INT(size) - 32 bit integer | size in the number of digits.
  • UNSIGNED INT - 32 bit unsigned integer.
  • FLOAT(size, d) - Floating number | size is the number of digits | d is the number of digits after decimal point.
  • DOUBLE(size, d) - Same as FLOAT but 64 bit.
  • DATE() - YYYY-MM-DD
  • DATETIME() - YYYY-MM-DD HH:MI:SS
  • TIMESTAMP - YYYY-MM-DD HH:MI:SS

Example:

1
CREATE TABLE movies(id INT(11) NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), rating FLOAT(5), cast VARCHAR(255), story TEXT, TIMESTAMP);

Inserting records in a table:

1
INSERT INTO tb_name(column1, column2, ...) VALUES(val1, val2, ...);

Changing existing record of a table:

1
UPDATE tb_name SET column1 = value1, column2 = value2 WHERE condition;

Count number of rows/records in a table:

1
SELECT COUNT(*) FROM table_name;

Delete records from table:

1
2
DELETE FROM tb_name WHERE condition;
DELETE FROM tb_name; --Will delete all records

Altering the table data(adding new column, deleting column, changing data type of a column):

1
2
3
ALTER TABLE tb_name ADD column_name data_type;
ALTER TABLE tb_name DROP column_name;
ALTER TABLE tb_name MODIFY column_name new_data_tyep;

Delete all the table data but not the table:

1
TRUNCATE TABLE tb_name;

Delete table:

1
DROP TABLE tb_name;

Fetching Data From a Table

Selecting records from table:

1
2
3
4
5
SELECT column1, column2, ... FROM tb_name;
SELECT * FROM tb_name;
SELECT DISTINCT column1, column2, ... FROM tb_name; --Will return only uniqe values
SELECT * FROM tb_name WHERE condition;
SELECT CAST(float_column as DECIMAL(30, 16)) FROM tb_name;

Automatic Variables

  • $@ - Filename of the target.
  • $< - The name of the first prerequisites.
  • $? - Name of all the prerequisites that are newer than the target.
  • $^ - Name of all the prerequisites.
  • $+ - Same as $^ with duplicate prerequisites.

VPATH and vpath

Path to search the targets.

Assignments to variables

  • = - Recursively expanded assignment. ${VAR} = ${VAR} val not allowed.
  • := - Simply expanded assignment. ${VAR} = ${VAR} val allowed.
  • ?= - Only assign if doesn’t exist.
  • != - Execute a shell script and assign output to variable. Can
    be replaced with {VAR} := $(shell command)

Trick to append different flag for different target

1
2
3
4
5
6
7
8
9
10
11
CFLAGS += -Wall -Wextra

all: CFLAGS += -O2
all: target1 target2

target1: dep1
gcc $(CFLAGS) $< -o $@

target2: CFLAGS += -fPIC -shared
target2: dep2
gcc $(CFLAGS) $< -o $@

User Administration

Adding a user to a group

1
$ usermod -a -G GROUP USER  # Add USER to GROUP

Image manipulation

Converting image formats

  • Using imageMagick. Image format is guessed from the extension. You can also specify new image height and width.
1
$ convert FROM_IMG TO_IMAGE
  • Using inkscape. Gives better result than imageMagic for SVG image formats
1
$ inkscape -z -e TO_IMAGE FROM_IMAGE

Taking Screenshots

  • Using imagemagick. Issuing this command will change the cusrsor to select a rectagular area. If clicked rather than selecting area entire active window will be captured. Image format will be guessed from the extension provided with the image name.
1
$ import NAME
0%