diff --git a/.obsidian/app.json b/.obsidian/app.json index 4d49ab3..5cabf38 100644 --- a/.obsidian/app.json +++ b/.obsidian/app.json @@ -1,4 +1,5 @@ { "promptDelete": false, - "alwaysUpdateLinks": true + "alwaysUpdateLinks": true, + "useMarkdownLinks": false } \ No newline at end of file diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index 908f458..cdc934d 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -13,12 +13,12 @@ "state": { "type": "markdown", "state": { - "file": "data_types.md", + "file": "ownership.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "data_types" + "title": "ownership" } }, { @@ -27,12 +27,12 @@ "state": { "type": "markdown", "state": { - "file": "Modules and Use.md", + "file": "Vector.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "Modules and Use" + "title": "Vector" } }, { @@ -41,16 +41,16 @@ "state": { "type": "markdown", "state": { - "file": "Collection of Common Data Structs.md", + "file": "data_types.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "Collection of Common Data Structs" + "title": "data_types" } } ], - "currentTab": 2 + "currentTab": 1 } ], "direction": "vertical" @@ -192,14 +192,16 @@ "command-palette:Open command palette": false } }, - "active": "b6a35c226bb40634", + "active": "b80f5219fa24358f", "lastOpenFiles": [ - "Modules and Use.md", "Collection of Common Data Structs.md", - "Packages.md", + "Vector.md", "data_types.md", - "Crates.md", + "Modules and Use.md", + "ownership.md", "Project Organization.md", + "Packages.md", + "Crates.md", "crates.io.md", "Paths.md", "does_not_compile.svg", @@ -211,7 +213,6 @@ "Good and Bad Code/Commenting Pratices", "Good and Bad Code", "Data Types.md", - "ownership.md", "Variables.md", "README.md", "Constants.md" diff --git a/Collection of Common Data Structs.md b/Collection of Common Data Structs.md index fefbab1..673adf7 100644 --- a/Collection of Common Data Structs.md +++ b/Collection of Common Data Structs.md @@ -1 +1,19 @@ # Collection of Common Data Structs +The std library includes a number of useful data structures called collections + +Collections can contain multiple values unlike built in array and tuples types these are all stored on the heap which means the amount of data does not need to be known at compile time and can grow or shrink as the program runs + +each kind of collection has different capabilities and cost +choosing the right one is a skill that is developed over time + +## Common Collections +- [*Vector*](Vector.md) - allows for storing a variable number of values next to each other +- [*String*](String.md) - a collection of characters +- *Hash Map* - allows you to associate a value with a specific key + - Its particular implementation is a more general version of a general data struct called a map + - { + - 1: data, + - 2: more_data, + - 3: this is a map + - } + diff --git a/Vector.md b/Vector.md new file mode 100644 index 0000000..ba997d6 --- /dev/null +++ b/Vector.md @@ -0,0 +1,156 @@ +# Vector + +Vectors allow you to store more and one value in a single data structure that puts all of the values next to each other in the memory + +Vectors can only store values of the same type hence the generic in the ``Vec`` keyword + +useful when you have a list of items, like the lines of text in a file or the prices of items in a shopping cart (this would be better with a hash map (more obj relation)) + +## Creating a new Vector +to create a empty vector we call + +``let v: Vec = Vec::new();`` + +Type annotation is need because a value is not given here and the compiler will not know what type of data will be stored here + +hence why the generic must be specified instead of inferred + +More often a Vector will have initial values and Rust will infer the type of value stored + +Rust has a Macro for this ``vec!`` +this will create a new vector that holds the values given to it + +```rust +let v = vec![1, 2, 3]; +``` + +this is implicitly a ``Vec`` + +## Updating a Vector +Add values to the vector you can use the push method +```rust +let mut v = Vec::new(); + +v.push(5); +v.push(6); +v.push(7); +v.push(8); +``` + +This must be mutable in order to do this +this is another way to implicitly start up a ``Vec`` variable + +## Reading Elements of Vectors +There a two ways to reference a value sorted in a vector + +via indexing or by using the ``get`` method + +here is an example of using both +```rust + 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."), + } +``` + +this uses the index of 2 to get the third element +vectors are indexed starting at zero + +using ``&`` and ``[]`` gives us a reference to the element at the index value + +when using get it returns an ``Option<&T>`` which is a referenced generic which is then specified to a i32 + +as you can see the ``get`` method is more safe because it accounts for when it way be null value or out of range + +```rust +let does_not_exist = &v[100]; +let does_not_exist = v.get(100); +``` + +the first one would cause the program to panic but the second wouldn't cause a panic instead it would require the situation to be dealt with in a match pattern for when it is None + +see [``Option``](data_types.md#The Option Enum and Advantages over Null types) for more details + +when the program has a valid reference the borrow checker ensures this reference and any other references must be valid to the contents of where it refers to. + +One case where this could easily be broken is creating a reference then trying to add an element to the end then trying to use the reference later in the function + +```rust + let mut v = vec![1, 2, 3, 4, 5]; + + let first = &v[0]; + + v.push(6); + + println!("The first element is: {first}"); +``` + +this would result in a borrow checker error +because vector.push uses a mutable reference to the vector +check here for more details [[ownership#Rules]] + +This is also due to how vectors are implemented, what happens when the allocated space on memory runs out, then it must be copied to another place in memory, then the old vector is freed + +In that case where the reference is pointing to may be deallocated and therefore not valid + +For implementation details of ``Vec`` type see [The Rustonomicon](https://doc.rust-lang.org/nomicon/vec/vec.html) + +## Iterating Over the Values in a Vector +We would rather iterate over the elements rather than over the index ands access each one one at a time +here is an example that give an immutable reference for each element +```rust + let v = vec![100, 32, 57]; + for i in &v { + println!("{i}"); + } +``` + +can also iterate over mutable referneces +here is an example where we add 50 to each element using the ``*`` or dereference operator to assign values to the reference +```rust + let mut v = vec![100, 32, 57]; + for i in &mut v { + *i += 50; + } +``` + +We cannot add or remove from the vector when iterating over it because of borrow checking rules, the reference to the whole vector prevents that + +## Using an Enum to Store Multiple Types +Vectors can only store values that are of the same type, this can be inconvenient sometimes. + +But you can use variants of an enum which is considered the same type + +this is allowed +```rust + enum SpreadsheetCell { + Int(i32), + Float(f64), + Text(String), + } + + let row = vec![ + SpreadsheetCell::Int(3), + SpreadsheetCell::Text(String::from("blue")), + SpreadsheetCell::Float(10.12), + ]; +``` + +Rust needs to know what types will be in the vector at compile time so it knows exactly how much memory to store per element in the vector so it must be specific when initializing a vector + +using an enum plus a match ensures that all possible cases are handled which ensures that a panic will not happen + +If you do not know the exhaustive set of types a program will get at runtime to store in a vector then the enum technique will not work instead use a trait object + +Here is a the API documentation for a Vector with all of the methods defined on a ``Vec`` + +For example a ``pop`` removes and returns the last element + +## Dropping a Vector Drops Its Elements +This is like any other struct, when a vector is freed/goes out of scope then so will its inner elements \ No newline at end of file diff --git a/data_types.md b/data_types.md index 78f006d..d400049 100644 --- a/data_types.md +++ b/data_types.md @@ -718,7 +718,7 @@ can implement behavior in relation to an enum ## The Option Enum and Advantages over Null types -this is a specail case where the variants are nothing nas something +this is a special case where the variants are nothing nas something this is part of the standard library can can be included this should be handled so that the compiler can check for handling all types @@ -727,9 +727,9 @@ this then handle the case of what if it is empty a feature of rust is excluding null references if you try to use a null value as a not null value you get an error -this is due to null or not null as pervasive and extrememly easy to make this kind of error +this is due to null or not null as pervasive and extremely easy to make this kind of error -null is still useful for expressing a vlaue that is not present or not valid for some reason +null is still useful for expressing a value that is not present or not valid for some reason this is a problem of implementation rust doesnt have nulls but can be expressed as the enum Option diff --git a/ownership.md b/ownership.md index 85f7d3a..2b9b2ad 100644 --- a/ownership.md +++ b/ownership.md @@ -106,8 +106,7 @@ fn calculate_length(s: String) -> (String, usize) { # References ## Rules -1. At any given time, you can have either one mutable reference or any number of -immutable references. +1. At any given time, you can have either one mutable reference or any number of immutable references. 1. References must always be valid Giving access to a variable without transfering ownership/moving it