{"id":15032011,"url":"https://github.com/iba4/rust_notes","last_synced_at":"2025-04-09T21:22:59.972Z","repository":{"id":117612315,"uuid":"250185318","full_name":"IBA4/rust_notes","owner":"IBA4","description":"Notes on almost every topic from the book : https://doc.rust-lang.org/book/","archived":false,"fork":false,"pushed_at":"2024-04-18T09:25:33.000Z","size":36,"stargazers_count":11,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-23T23:15:32.613Z","etag":null,"topics":["notes","rust","rust-lang","rust-language","rust-notes"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/IBA4.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-26T07:05:58.000Z","updated_at":"2024-04-18T09:24:18.000Z","dependencies_parsed_at":"2024-04-18T10:43:49.369Z","dependency_job_id":null,"html_url":"https://github.com/IBA4/rust_notes","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IBA4%2Frust_notes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IBA4%2Frust_notes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IBA4%2Frust_notes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/IBA4%2Frust_notes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/IBA4","download_url":"https://codeload.github.com/IBA4/rust_notes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248112993,"owners_count":21049767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["notes","rust","rust-lang","rust-language","rust-notes"],"created_at":"2024-09-24T20:17:07.948Z","updated_at":"2025-04-09T21:22:59.936Z","avatar_url":"https://github.com/IBA4.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Notes on Rust programming language, from *[the book](https://doc.rust-lang.org/book/)*\n\nI just typed what I felt like should be noted while reading *the book* simultaneously.\n\n\u003e Install `rustup` as a toolchain along with `cargo`.\n\n\u003e Don't be afraid of compiler errors. They are very helpful and give very good information.\n\n[TOC]\n\n\n\n## Cargo\n\n- very good package manager for rust\n\n```sh\ncargo new \u003cproject_name\u003e #create new project (binary)\ncargo build #compile and check\ncargo run #compile(if-not) and run the binary program\n```\n## variables\n\n- by default, variable is immutable.\n\u003e make it mutable by  `mut`\n```rust\nlet mut x = 5;\n```\n\n### constant vs immutable variables\n\n- use `const` for constants because they are not meant to be changed ever.\n- can't use `const` for values computed at runtime. like result of function calls. \n- naming convention : use uppercase with underscores.\n```rust\nconst MAX_POINTS: u32 = 1000_00;\n```\n\n### Shadowing\n\n- *shadowing* is declaring a new variable with the same\nname as a previous variable.\n```rust\nlet x = 5;\nlet x = x+1;// don't use mut!\n```\n- it is not reassigning like we do with `mut` variables.\n- *benefit* : we don't have to create a variable with different type for same data. If we need a num but the input is in string. we can use the same name to get the numeric value because it is literally *redeclaring*.\n\n## Data types\n\n\u003e rust is *statically typed*\n\n- rust usually guesses the type but we can explicitly tell it , ob. \n### Scalars \n\n#### Integers\n - signed i8 (integer8-bit) up to 128-bit or isize(architecture size)\n - similar but **u**8\n\n##### literals\n- 98_222 = 98222 (!you can use _ as separator)\n- 0xfff hex, Oo77 oct, 0b1111_000 binary, b'A' Byte(u8 only).\n\n- while using `--release` flag, rust won't check for *integer overflow* . It performs *two's compliment wrapping*. ex: for u8 , 256 becomes 0,\n    - prevents *panic* \n    - you can use standard library type `Wrapping` for explicitly wrapping\n\n#### floating points\n\n- default is f64, else we have f32.\n\n### Numeric operations\n\n- +, - , * , / , % . Same as usual\n\n### Boolean\n\n- `true` or `false`\n\n### characters\n\n- `char` : 4 bytes ,  Unicode Scalar Values range from\nU+0000 to U+D7FF and U+E000 to U+10FFFF inclusive \n\n### Compound Types\n\n#### Tuple type\n\n```rust\nlet tup: (i32, f64, u8) = (500, 6.4, 1);\nlet (x, y, z) = tup;// pattern matching : x = 500\nlet first_element = tup.0;\n```\n- *destructuring*: use pattern matching to get value breaks single tuple into multiple parts\n- we can use (.) followed by index too!! \n\n#### Array type\n\n- ***Fixed sized***\n\n```rust\nlet a: [i32; 5] = [1, 2, 3, 4, 5];\nlet a = [3; 5]; //is same as\nlet a = [3, 3, 3, 3, 3];// 5 elements\n```\n\n- stack based , not as flexible as vectors\n- accessing using index `a[index]`\n- gives `index out of bounds` error if out of bounds\n\n## Functions\n\n\u003e convention: use snake case.\n\n### Function parameters\n\n- must declare type of each parameters\n\n```rust\nfn another_function(x: i32, y: i32) {...}\n```\n\n### Statements and Expressions\n\n\u003e rust is an expression-based language\n\n- `let x = (let y = 4);` is invalid unlike C because let y = 4 doesn't yield a vlaue ; nothing to bind for x\n- calling a function or macro , block creating new scopes ,{}, is an expression\n\n```rust\nlet y = {\n    let x = 3;\n    x+1// look at this. NO SEMICOLON!!\n} // this block/scope returns value 4\n```\n### Function with return values\n\n- we declare type of return values after an arrow (-\u003e)\n- *implicitly* : return value is the last expression. keep in mind the facts about statement vs expressions.\n- *explicitly* : we can use `return` keyword\n\n```rust\nfn main() {\n    let x = plus_one(5);\n    println!(\"The value of x is: {}\", x);\n}\nfn plus_one(x: i32) -\u003e i32 {\n    x + 1 // Again no semicolon here.\n}\n```\n - no evaluation leads to `()` empty tuple.\n\n## Comments\n\n- no multi-line comment syntax.\n- continue `//` for each line.\n- `///` *documentation comment* : generates HTML ; used with `crates`\n\n## Control Flow\n\n### if/else if expressions\n- Same as C or JS for syntax \n- condition must be `bool`.\n- Blocks of code associated with the conditions in if expressions are sometimes called `arms`\n\n#### Using if in a let statement\n```rust\nlet number = if condition { 5 } else { \"six\" }; // error: both arms should evaluate to same data type\n```\n\n## Loops\n\n### loop{}\n\n- simple just loops code inside the block \n\n```rust\nlet result = loop {\n    counter += 1;\n    if counter == 10 {\n        break counter * 2;\n    }\n};\n```\n - use this with `break`.\n - use case : One of the uses of a loop is to retry an operation you know might fail, such\nas checking whether a thread has completed its job. However, you might\nneed to pass the result of that operation to the rest of your code. \n\n### while loops\n\n - same as C/JS.\n\n## for loops\n\n```rust\nlet a = [10, 20, 30, 40, 50];\nfor element in a.iter() {\n    println!(\"the value is: {}\", element);\n}\n```\n- Range based: `for number in (1..4).rev()` . `rev()` is to reverse\n\n# Unique features in Rust\n\n## Ownership\n\n\u003e How rust lays data in memory\n\n\u003e ensures memory safety without garbage collector \n\n- In other languages, programmer must explicitly handle the memory (allocation \u0026 deletion)\n- In rust, we have ownership concept.\n\n### stack vs heap\n\n- We use heap when the size of values at compile time is unknown.\n- pushing in stack is faster because OS never has to search for a place to store new data unlike heap where OS has to search for space big enough to hold data\n - accessing data in heap is slower because we have to follow a pointer to get there.\n\n\u003e so cleaning up unused data , minimizing the amount of duplicate data so that we don't run out of space are all problems that ownership addresses.\n\n### Ownership rules\n\n- Each value Rust has a variable that’s called its `owner`.\n- There can be only one `owner` at a time.\n- When the owner goes out of scope, the value will be dropped.\n\n### variable scopes\n\n- Block scoped. like in JS/C++\n\n### String types and heap\n\n- `String` types are allocated in heaps.\n- `let s = String::from(\"hello\");` creating `String` from `string literal`\n- `string literals` cannot be mutated but `Strings` can. Because we know the size of literals and can be hardcoded in the program. `Strings are growable`.\n\n### Memory and Allocation\n\n- memory must be created and destroyed simultaneously. because there is no garbage collector; doing this is very hard. `allocate` and `free`\n- rust is automatically returned once the variable goes out of scope. Calls `drop` function.\n\n### data interaction : Move\n```rust\nlet s1 = String::from(\"hello\");\nlet s2 = s1;\n```\n- Here, a stack is created with (ptr,len,capacity) ptr pointing to the heap of memory with data(hello)\n- `s2 = s1` : copies the ptr of s1(stack) to s2 ; pointing to same heap.\n- **But**, when we go out of scope, both of them will try to free the same memory. Oops! `double free` error.\n\n\u003e Rust manages this by making s1 invalid , which is what it calls `move` . Only s2 can free the memory.\n\n### data interaction : Clone\n\n- `let s2  = s1.clone()` , `deep`ly copies the heap data not just the stack data. \n\n### Stack only data: Copy\n\n```rust\nlet x = 5;\nlet y = x;\n```\n- As we have learned, these basic types are stored in stack at compile time. So no need of any allocation,freeing or making it invalid.\n- no need of `clone` here.\n\n \u003e we have to keep in mind the `Copy` and `Drop` traits. Normally all those basic data types have `Copy` trait.\n\n### Ownership and functions\n\n- going into function is going out of scope. non `Copy` traits are borrowed  by the function and made invalid.\n\n### Return Values and Scope\n\n- multiple values can be returned using tuples\n- Just like function taking the ownership , returning it gives the ownership back. *It is just changing the scopes*\n- If no one takes the ownership , going out of scope just destroys it.\n\n```rust\nfn main() {\n    let s1 = String::from(\"hello\");\n    let (s2, len) = calculate_length(s1);\n    println!(\"The length of '{}' is {}.\", s2, len);\n}\nfn calculate_length(s: String) -\u003e (String, usize) {\n    let length = s.len(); // len() returns the length of a String\n    (s, length)\n}\n```\n### References and Borrowing\n\n- So to avoid all those hassle, we can use *refrence* variables. `\u0026String`\n- We call it *borrowing* , like in real world\n- also has dereferencing with *deference operator*\n\n\u003e To make the reference variable mutable, we use `\u0026mut` for `mut` variable\n\n- We cannot borrow mutable reference more than once at a time in a particular scope.\n- this is to prevent *data race* :\n    - two or more pointers accessing same data\n    - At least one is being used to write\n    - no mechanism being used to synchronize access to the data\n\n- Just use curly braces { ... } to create a new scope.😉 \n- *We also cannot have a mutable reference while we have an immutable one*\n- ```\n      let mut s = String::from(\"hello\");\n\n    let r1 = \u0026s; // no problem\n    let r2 = \u0026s; // no problem\n    println!(\"{} and {}\", r1, r2);\n    // variables r1 and r2 will not be used after this point\n\n    let r3 = \u0026mut s; // no problem\n    println!(\"{}\", r3);\n```\n\n### Dangling references\n\n- *`lifetimes`* help us a lot for such *dangling pointers*: pointers referencing a location in memory that may have been given to someone else.\n\n### The Slice type\n\n- while slicing byte by byte, we are normally working on starting index and ending index. Though these indices may be found, we can't possibly use this since the state of `String` that we are working on might change it's state.\n\n#### String Slices\n\n```rust\nlet s = String::from(\"hello world\");\nlet hello = \u0026s[0..5];\nlet world = \u0026s[6..11];\n```\n- new slice with different pointer is created.\n- `[starting_index..ending_index]`\n- `[0..2]` \u003c=\u003e `[..2]` ; `[3..length_of_string]` \u003c=\u003e `[3..]` ; `[0..length_of_string]` \u003c=\u003e `[..]`\n- these types of slices are returned/used as `\u0026str` (string slice) types\n- `\u0026str` are immutable.\n\n\u003e And.. String literals are `\u0026str`.\n\n- **Don't mess up with immutable/mutable borrowing rule**\n\n- UTF-8 encoded text might be multibyte. So we need to take care of that too\n\n#### String Slices as parameters\n\n- It is better to pass string slice `\u0026string[..]` (whole string) as parameter instead of whole string `\u0026string`.\n\n#### Other slices \n\n- We can use slices for arrays too `let slice = \u0026array[1..3];`\n\n## Using Structs to structure related data\n\n```rust\nstruct User {\n    username: String,\n    email: String,\n    sign_in_count: u64,\n    active: bool,\n} // struct definition\n```\n- data inside curly braces are called `fields`\n\n```rust\nlet user1 = User {\n    //key:value\n    email: String::from(\"someone@example.com\"),\n    username: String::from(\"someusername123\"),\n    active: true,\n    sign_in_count: 1,   \n};// creaing an instance of the User struct\n```\n- To change the value, instance must be mutable too!\n\n### Fields init Shorthand\n\n- When the parameters are same as the key , we can omit that\n\n```rust\nfn build_user(email: String, username: String) -\u003e User {\n    User {\n        email, // instead of email:email;\n        username,\n        active: true,\n        sign_in_count: 1,\n    }\n}\n```\n### Creating instances from Other Instance\n\n\u003e `struct update syntax`\n\n```rust\nlet user2 = User {\n        email: String::from(\"another@example.com\"),\n        username: String::from(\"anotherusername567\"),\n        ..user1 // rest of the values are same as that of user1\n    };\n```\n\n### Create different Types using `tuple structs`\n\n\u003e `Tuple structs` : structs without named fields\n\n```rust\nstruct Color(i32, i32, i32);\nlet black = Color(0, 0, 0);\n```\n\n- You cannot pass many `tuple structs` with same fields as a parameter for a function : Behaves like tuples\n\n- again, keep in mind the `lifetime` parameter. (Explained later)\n\n### Printing structure for debugging\n\n```rust\n// here add this. This is called deriving Debug Trait\n#[derive(Debug)] \nstruct Rectangle {\n    width: u32,\n    height: u32,\n}\nfn main() {\n    let rect1 = Rectangle {\n        width: 30,\n        height: 50,\n    };\n    println!(\"rect1 is {:?}\", rect1); //Notice the :? inside\n    // prints rect1 is Rectangle { width: 30, height: 50 }\n}\n```\n- `println!(\"rect1 is {:?#}\", rect1);` even prints with line breaks (pretty print)\n\n### Methods\n\n#### Defining Methods\n\n```rust\n//after defining struct in above code\nimpl Rectangle {\n    fn area(\u0026self) -\u003e u32 {\n        self.width * self.height\n}\n```\n- `\u0026self` knows that it is `\u0026Rectangle` because it is implemented inside `imp Rectangle` context.\n- `\u0026self` because we want to *borrow* the ownership when we have to read.\n- we'd use `\u0026mut self` to change the instance that we’ve called the method on as part of what the method does.\n\n#### Where's the -\u003e operator?\n\n- Rust has *automatic referencing and dereferencing* while calling methods. so it automatically adds in `\u0026`, `\u0026mut`, or `*` so object matches the signature of the\nmethod.\n- This automatic referencing behavior works\nbecause methods have a clear receiver—the type of self\n\n#### Methods with more parameters\n\n- `rect1.can_hold(\u0026rect2));`. here one instance has a method taking another instance as a parameter.\n- To use this we add this in the `imp Rectangle` block:\n\n```rust\nfn can_hold(\u0026self, other: \u0026Rectangle) -\u003e bool {\n    ...\n}\n```\n\u003e so `\u0026self` makes rust clear which instance it is taking about. \n\n#### Associated Functions\n\n\u003e useful for creating *Constructors*\n\n```rust\nimpl Rectangle {\n    fn square(size: u32) -\u003e Rectangle {\n        Rectangle { width: size, height: size }\n    }\n}\n```\n\n- to call this we use `let sq = Rectangle::square(3)`.\n- The function is namespaced by the struct\n\n#### Multiple impl Blocks\n\n\u003e We can use many such `impl { }` blocks for the same struct. (useful for generic types and traits)\n\n## Enums and pattern matching\n\n- *enums* : enumerations : types that enumerates its possible values.\n\n### Defining an enum\n\n ```rust\nenum IpAddressKind {\n    V4,\n    V6,\n}\n ```\n- `let four = IpAddrKind::V4;` : variants of the enum are namespaced under its identifier and we use `::` to resolve the namespace.\n- enums are useful when used along with structs since an enum is a type like this:\n\n```rust\nstruct IpAddr {\n    kind: IpAddrKind,\n    address: String,\n}\nfn main() {\n    let home = IpAddr {\n        kind: IpAddrKind::V4,\n        address: String::from(\"127.0.0.1\"),\n    };\n}\n\n```\n\n- But we can use it directly too like this:\n```rust\nenum IpAddressKind {\n    V4(u8,u8,u8,u8),\n    V6(String),\n}\nlet home = IpAddr::V4(192,168,1,1);\nlet loopback = IpAddr::V6(String::from(\"::1\"));\n ```\n \u003e checkout standard library for IpAddr\n\n- The real benefit of enum rather than using multiple structs is to define a function. Remember `impl` from structure using `\u0026self`.\n\n### *Option* Enum and its advantages over Null values\n\n- There is no `Null` value in rust. Why? \n\u003eThe problem with null values is that if you try to use a null value as a not-null value, you’ll get an error of some kind. Because this null or not-null property is pervasive, it’s extremely easy to make this kind of error.\n- But *null* concept is still useful so rust has an *Option* 😉. It has `Option\u003cT\u003e`enum for that.\n\n```rust\nenum Option\u003cT\u003e {\n    Some(T),\n    None,\n}\n```\n- Being so useful, it can be used without bringing it to scope. which means we can use `Some(T)` and `None` without `Option::` prefix.\n\n- `let some_number = Some(5);` we know what value is present\n- `let absent_number: Option\u003ci32\u003e = None;` we don't have a value; essentially a null value.\n\n\u003e **Remember! `Option\u003cT\u003e` and `T` are not the same type**\n\n- The reason why `Option\u003cT\u003e` is that we are explicitly deciding that we are going to handle cases that might have null or some value.\n- So to use a value of type `T` from `Option\u003cT\u003e` when the code is handled , what control flow operator can be used ? *match* operator\n\n## `match` control flow operator\n\n- compares values against a series of pattern.\n- Patterns can be made up of literal values,\nvariable names, wildcards, and many other thing\n- code associated with the pattern matched first will be executed.\n\n```rust\nenum UsState {\n    Alabama,\n    Alaska,\n    // --snip--\n}\nenum Coin {\n    Penny,\n    Nickel,\n    Dime,\n    Quarter(UsState),\n}\n\nvalue_in_cents(Coin::Quarter(UsState::Alaska)); //will match with\n\nfn value_in_cents(coin: Coin) -\u003e u8 {\n    match coin {\n        Coin::Penny =\u003e 1,\n        Coin::Nickel =\u003e 5,\n        Coin::Dime =\u003e 10,\n        Coin::Quarter(state) =\u003e { // will match with this \n            println!(\"State quarter from {:?}!\", state);\n            25\n        }\n    }\n}\n```\n- as we can see, we can match the enums within the enums.\n\n#### Matching with Option\u003cT\u003e\n\n- to get the value of T from Option\u003cT\u003e we use it like this:\n\n```rust\nfn plus_one(x: Option\u003ci32\u003e) -\u003e Option\u003ci32\u003e {\n    match x {\n        None =\u003e None,\n        Some(i) =\u003e Some(i + 1), // plus_one(five) below matched this\n    }\n}\nlet five = Some(5);\nlet six = plus_one(five);\n```\n- We pass *Some* value to match an `Option\u003cT\u003e` type and match it.\n\n\u003e Matches are exhaustive : So there must be an *match `arm` for every possible cases. Rust will throw error otherwise. It's good. Handles everything. But there is a solution for that too. (`_` palceholder)\n\n#### The `_` placeholder\n\n- whenever we don't want/have to list all possible value , we can use `_` pattern and link it `=\u003e` with `()`:a unit value  which is basically nothing.\n\n```rust\nlet some_u8_value = Some(0u8);\nmatch some_u8_value {\n    Some(3) =\u003e println!(\"three\"),\n    _ =\u003e (),\n}\n```\n\n### if let control flow\n\n- the above code is equivalent to:\n```rust\nif let Some(3) = some_u8_value {\n    println!(\"three\");\n}\n```\n- We want to do something with the `Some(3)` match but do nothing with any other `Some\u003cu8\u003e` value or the None value\n- works the same way as `match` . However, we loose the exhaustive checking.\n- also works with the same logic that we used for matching enums within enums\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiba4%2Frust_notes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiba4%2Frust_notes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiba4%2Frust_notes/lists"}