diff --git a/README.md b/README.md index 36ae5f1..67d7f38 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Trainings To jest lista traningów jakie odbyłem +## Fujitsu ### Atakowanie i Ochrona Aplikacji Webowych - 08-09-02.2022 ### Agile - 28.04.2023 @@ -8,3 +9,6 @@ To jest lista traningów jakie odbyłem ### Zaawansowane Tehcniki Programowania C++ - 26-26.04.2024 ### PythonAI 25.03-21.05.2025 7 dni w 3 turach + +## Ericsson +### Rust 14-16.09.2026 diff --git a/Rust_2026/rust-bs-2026-09-14/.devcontainer/Dockerfile b/Rust_2026/rust-bs-2026-09-14/.devcontainer/Dockerfile new file mode 100644 index 0000000..c5fb37d --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/.devcontainer/Dockerfile @@ -0,0 +1,7 @@ +FROM infotrainingdev/rust-bs-notebook:latest + +# [Optional] Uncomment this section to install additional packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends your-package-list-here + +WORKDIR /root \ No newline at end of file diff --git a/Rust_2026/rust-bs-2026-09-14/.devcontainer/devcontainer.json b/Rust_2026/rust-bs-2026-09-14/.devcontainer/devcontainer.json new file mode 100644 index 0000000..962ba24 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/.devcontainer/devcontainer.json @@ -0,0 +1,37 @@ +{ + "name": "Rust", + "build": { + "dockerfile": "Dockerfile", + + }, + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "files.watcherExclude": { + "**/target/**": true + }, + "rust-analyzer.checkOnSave.command": "clippy" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "vadimcn.vscode-lldb", + "rust-lang.rust-analyzer", + "ms-toolsai.jupyter", + "ms-toolsai.vscode-jupyter-powertoys" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "chown -R $(whoami) /usr/local/cargo/", + + // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + //"remoteUser": "vscode" +} diff --git a/Rust_2026/rust-bs-2026-09-14/Cargo.toml b/Rust_2026/rust-bs-2026-09-14/Cargo.toml new file mode 100644 index 0000000..4db82fe --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["code/hello-world", "exercises/primes", "exercises/vector"] diff --git a/Rust_2026/rust-bs-2026-09-14/README.md b/Rust_2026/rust-bs-2026-09-14/README.md new file mode 100644 index 0000000..e129c5f --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/README.md @@ -0,0 +1,47 @@ +# Programowanie w języku Rust - Szkolenie + +## Docs + +* https://doc.rust-lang.org/book/ +* https://doc.rust-lang.org/std/index.html + +## Konfiguracja środowiska + +Proszę wybrać jedną z poniższych opcji: + +### Lokalna + +Przed szkoleniem należy zainstalować: + +* Rust - https://www.rust-lang.org/tools/install +* [Visual Studio Code](https://code.visualstudio.com/) + * Zainstalować wtyczki + * rust-analyzer + * Jupyter + * Live Share +* Rust REPL - `evcxr_repl` + + ```bash + cargo install evcxr_repl + cargo install evcxr_jupyter + evcxr_jupyter --install + ``` + + +### Docker + Visual Studio Code + +Jeśli uczestnicy szkolenia korzystają w pracy z Docker'a, to należy zainstalować: + +#### Docker + +* Instalacja Docker Desktop - https://www.docker.com/get-started/ + +#### Visual Studio Code + +* [Visual Studio Code](https://code.visualstudio.com/) +* Zainstalować wtyczki + * Live Share + * Dev Containers ([wymagania](https://code.visualstudio.com/docs/devcontainers/containers#_system-requirements)) + * po instalacji wtyczki - należy otworzyć w VS Code folder zawierający sklonowane repozytorium i + z palety poleceń (Ctrl+Shift+P) wybrać opcję **Dev Containers: Rebuild and Reopen in Container** + diff --git a/Rust_2026/rust-bs-2026-09-14/code/hello-world/Cargo.toml b/Rust_2026/rust-bs-2026-09-14/code/hello-world/Cargo.toml new file mode 100644 index 0000000..52968ee --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/code/hello-world/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "hello-world" +version = "0.1.0" +edition = "2021" + +[dependencies] + +[dev-dependencies] +rstest = "0.23.0" \ No newline at end of file diff --git a/Rust_2026/rust-bs-2026-09-14/code/hello-world/src/main.rs b/Rust_2026/rust-bs-2026-09-14/code/hello-world/src/main.rs new file mode 100644 index 0000000..acd1d1d --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/code/hello-world/src/main.rs @@ -0,0 +1,5 @@ +fn main() { + let mut message = "Hello, world!".to_string(); + message += "!!"; + println!("{message}"); +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/primes/Cargo.toml b/Rust_2026/rust-bs-2026-09-14/exercises/primes/Cargo.toml new file mode 100644 index 0000000..71b3dac --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/primes/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "primes" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/primes/src/main.rs b/Rust_2026/rust-bs-2026-09-14/exercises/primes/src/main.rs new file mode 100644 index 0000000..fcc9533 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/primes/src/main.rs @@ -0,0 +1,55 @@ +// TODO + +#[test] +fn test_is_prime() { + // assert!(!is_prime(0)); + // assert!(!is_prime(1)); + // assert!(is_prime(2)); + // assert!(is_prime(3)); + // assert!(is_prime(5)); + // assert!(is_prime(7)); + // assert!(is_prime(11)); + // assert!(is_prime(13)); + // assert!(is_prime(17)); + // assert!(is_prime(19)); + // assert!(!is_prime(4)); + // assert!(!is_prime(6)); + // assert!(!is_prime(8)); + // assert!(!is_prime(9)); + // assert!(!is_prime(10)); + // assert!(is_prime(113)); +} + +#[test] +fn test_n_primes() { + // let primes = n_primes(0); + // assert_eq!(primes, Vec::new()); + + // let primes = n_primes(10); + // assert_eq!(primes, vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); + + // let primes = n_primes(20); + // assert_eq!(primes, vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]); +} + +#[test] +fn test_primes_in_range() { + // let primes = primes_in_range(2..100); + + // assert_eq!(primes, vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + // 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + // 73, 79, 83, 89, 97]); +} + +#[test] +fn test_all_primes() { + // let primes = all_primes().take(10).collect::>(); + // assert_eq!(primes, vec![2, 3, 5, 7, 11, 13, 17, 19, 23, 29]); +} + +fn main() { + // println!("First 100 primes:"); + // for prime in all_primes().take(100) { + // println!("{}", prime); + // } +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/10_iterators.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/10_iterators.ipynb new file mode 100644 index 0000000..fa95980 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/10_iterators.ipynb @@ -0,0 +1,178 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Iterators" + ] + }, + { + "cell_type": "markdown", + "id": "09116649", + "metadata": {}, + "source": [ + "1. Use iterator methods to sum the values in a vector." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9fc4b114", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let values = vec![1, 2, 3, 4];\n", + " let total: i32 = __;\n", + "\n", + " assert_eq!(total, 10);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();\n" + ] + }, + { + "cell_type": "markdown", + "id": "c1558bef", + "metadata": {}, + "source": [ + "2. Transform each item using `map()` and collect the results into a new vector." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3a7a9e2b", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let values = vec![1, 2, 3];\n", + " let doubled: Vec = __;\n", + "\n", + " assert_eq!(doubled, vec![2, 4, 6]);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();\n" + ] + }, + { + "cell_type": "markdown", + "id": "829e3f48", + "metadata": {}, + "source": [ + "3. Filter the iterator to keep only even numbers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47285545", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let values = vec![1, 2, 3, 4, 5];\n", + " let evens: Vec = __;\n", + "\n", + " assert_eq!(evens, vec![2, 4]);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();\n" + ] + }, + { + "cell_type": "markdown", + "id": "545e9a65", + "metadata": {}, + "source": [ + "4. Use `enumerate()` to find the index of the first value greater than 5." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "622ea110", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let values = vec![2, 4, 6, 8];\n", + " let idx = __;\n", + "\n", + " assert_eq!(idx, Some(2));\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "id": "fbde588e", + "metadata": {}, + "source": [ + "5. Calculate sum of `Things` that are even using iterator adaptors\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c3cd0dc", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "#[derive(Clone, Debug, PartialEq, Eq)]\n", + "struct Thing(u64);\n", + "\n", + "let collection: Vec = vec![Thing(1), Thing(2), Thing(665), Thing(42), Thing(3), Thing(4)];\n", + "\n", + "let result: u64 = __;\n", + "\n", + "assert_eq!(result, 48);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/1_basic_types.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/1_basic_types.ipynb new file mode 100644 index 0000000..a04ef0b --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/1_basic_types.ipynb @@ -0,0 +1,469 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Variables" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Binding & mutablity" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. A variable can be used only if it has been initialized." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix the error below with least amount of modification to the code\n", + "fn main() {\n", + " let x: i32; // Uninitialized but used, ERROR !\n", + " let y: i32; // Uninitialized but also unused, only a Warning !\n", + "\n", + " assert_eq!(x, 5);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Use mut to mark a variable as mutable." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "\n", + "// Fill the blanks in the code to make it compile\n", + "fn main() {\n", + " let __ __ = 1;\n", + " __ += 2; \n", + " \n", + " assert_eq!(x, 3);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Destructuring" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. We can use a pattern with let to destructure a tuple to separate variables." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix the error below with least amount of modification\n", + "fn main() {\n", + " let (x, y) = (1, 2);\n", + " x += 2;\n", + "\n", + " assert_eq!(x, 3);\n", + " assert_eq!(y, 2);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Use tuple, slice, and struct patterns as the left-hand side of an assignment." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let (x, y);\n", + " (x,..) = (3, 4);\n", + " [.., y] = [1, 2];\n", + " // Fill the blank to make the code work\n", + " assert_eq!([x,y], __);\n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Basic Types" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. Types are inferred by the compiler." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Remove something to make it work\n", + "fn main() {\n", + " let x: i32 = 5;\n", + " let mut y: u32 = 5;\n", + "\n", + " y = x;\n", + " \n", + " let z = 10; // Type of z ? \n", + " assert!((&z as &dyn std::any::Any).is::<__>());\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Cast types using the `as` keyword." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fill the blank\n", + "fn main() {\n", + " let v: u16 = 38_u8 as __;\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Guess the type inferred by the compiler." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Get the type of given variable, return a string representation of the type , e.g \"i8\", \"u8\", \"i32\", \"u32\"\n", + "fn type_of(_: &T) -> String {\n", + " format!(\"{}\", std::any::type_name::())\n", + "}\n", + "\n", + "// Modify `assert_eq!` to make it work\n", + "fn main() {\n", + " let x = 5;\n", + " assert_eq!(\"__\".to_string(), type_of(&x));\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Type `char` represents a single Unicode scalar value." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "\n", + "// Make it work\n", + "use std::mem::size_of_val;\n", + "\n", + "fn main() {\n", + " let c1 = 'a';\n", + " assert_eq!(size_of_val(&c1), __); \n", + "\n", + " let c2 = '❤'; // heart utf-8\n", + " assert_eq!(size_of_val(&c2), __); \n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Unit type `()` has only one value `()`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Make it work, don't modify `implicitly_ret_unit` !\n", + "fn main() {\n", + " let _v: () = ();\n", + "\n", + " let v = (1, 2);\n", + " assert_eq!(v, implicitly_ret_unit());\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "fn implicitly_ret_unit() {\n", + " println!(\"I will return a ()\");\n", + "}\n", + "\n", + "// Don't use this one\n", + "fn explicitly_ret_unit() -> () {\n", + " println!(\"I will return a ()\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::mem::size_of_val;\n", + "\n", + "fn main() {\n", + " let unit: () = ();\n", + " assert!(size_of_val(&unit) == __);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Statements & Expressions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Make it work with two ways\n", + "fn main() {\n", + " let v = {\n", + " let mut x = 1;\n", + " x += 2\n", + " };\n", + " \n", + " assert_eq!(v, 3);\n", + " \n", + " println!(\"Success!\");\n", + " }\n", + "\n", + " main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let s = sum(1 , 2);\n", + " assert_eq!(s, 3);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "fn sum(x: i32, y: i32) -> i32 {\n", + " x + y;\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Functions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " // Don't modify the following two lines!\n", + " let (x, y) = (1, 2);\n", + " let s = sum(x, y);\n", + "\n", + " assert_eq!(s, 3);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "fn sum(x, y: i32) {\n", + " x + y;\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " print();\n", + " }\n", + " \n", + " // Replace i32 with another type\n", + " fn print() -> i32 {\n", + " println!(\"Success!\");\n", + " }\n", + "\n", + " main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/2_ownership_borrowing.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/2_ownership_borrowing.ipynb new file mode 100644 index 0000000..fd67a1b --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/2_ownership_borrowing.ipynb @@ -0,0 +1,313 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Ownership" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " // Use as many approaches as you can to make it work\n", + " let x = String::from(\"Hello world\");\n", + " let y = x;\n", + " println!(\"{}, {}\", x, y);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Don't modify code in main!\n", + "fn main() {\n", + " let s1 = String::from(\"Hello world\");\n", + " let s2 = take_ownership(s1);\n", + "\n", + " println!(\"{}\", s2);\n", + "}\n", + "\n", + "// Only modify the code below!\n", + "fn take_ownership(s: String) {\n", + " println!(\"{}\", s);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let s = give_ownership();\n", + " println!(\"{}\", s);\n", + "}\n", + "\n", + "// Only modify the code below!\n", + "fn give_ownership() -> String {\n", + " let s = String::from(\"Hello world\");\n", + " // Convert String to Vec\n", + " let _s = s.into_bytes();\n", + " s\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix the error without removing any code\n", + "fn main() {\n", + " let s = String::from(\"Hello World\");\n", + "\n", + " print_str(s);\n", + "\n", + " println!(\"{}\", s);\n", + "}\n", + "\n", + "fn print_str(s: String) {\n", + " println!(\"{}\",s)\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Don't use clone, use copy instead\n", + "fn main() {\n", + " let x = (1, 2, (), \"hello\".to_string());\n", + " let y = x.clone();\n", + " println!(\"{:?}, {:?}\", x, y);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// make the necessary variable mutable\n", + "fn main() {\n", + " let s = String::from(\"Hello \");\n", + " \n", + " let s1 = s;\n", + "\n", + " s1.push_str(\"World!\");\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Reference & Borrowing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let x = 5;\n", + " \n", + " // Fill the blank\n", + " let p = __;\n", + " \n", + " println!(\"the memory address of x is {:p}\", p); // One possible output: 0x16fa3ac84\n", + " }\n", + "\n", + " main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix error\n", + "fn main() {\n", + " let mut s = String::from(\"hello, \");\n", + "\n", + " borrow_object(s);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "fn borrow_object(s: &String) {}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix error\n", + "fn main() {\n", + " let mut s = String::from(\"hello, \");\n", + "\n", + " push_str(s);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "fn push_str(s: &mut String) {\n", + " s.push_str(\"world\")\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Borrowing" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Remove something to make it work\n", + "// Don't remove a whole line !\n", + "fn main() {\n", + " let mut s = String::from(\"hello\");\n", + "\n", + " let r1 = &mut s;\n", + " let r2 = &mut s;\n", + "\n", + " println!(\"{}, {}\", r1, r2);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " // Fix error by modifying this line\n", + " let s = String::from(\"hello, \");\n", + "\n", + " borrow_object(&mut s);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "fn borrow_object(s: &mut String) {}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/3_arrays_vectors_strings.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/3_arrays_vectors_strings.ipynb new file mode 100644 index 0000000..1fccac0 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/3_arrays_vectors_strings.ipynb @@ -0,0 +1,282 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# String" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix error without adding new line\n", + "fn main() {\n", + " let s: str = \"hello, world\";\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fill the blank\n", + "fn main() {\n", + " let mut s = __;\n", + " s.push_str(\"hello, world\");\n", + " s.push('!');\n", + "\n", + " assert_eq!(s, \"hello, world!\");\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix all errors without adding newline\n", + "fn main() {\n", + " let s = String::from(\"hello\");\n", + " s.push(',');\n", + " s.push(\" world\");\n", + " s += \"!\".to_string();\n", + "\n", + " println!(\"{}\", s);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix error with at least two solutions\n", + "fn main() {\n", + " let s = \"hello, world\";\n", + " greetings(s)\n", + "}\n", + "\n", + "fn greetings(s: String) {\n", + " println!(\"{}\", s)\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Array" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " // Fill the blank with proper array type\n", + " let arr: __ = [1, 2, 3, 4, 5];\n", + "\n", + " // Modify the code below to make it work\n", + " assert!(arr.len() == 4);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " // We can ignore parts of the array type or even the whole type, let the compiler infer it for us\n", + " let arr0 = [1, 2, 3];\n", + " let arr: [_; 3] = ['a', 'b', 'c'];\n", + " \n", + " // Fill the blank\n", + " // Arrays are stack allocated, `std::mem::size_of_val` returns the bytes which an array occupies\n", + " // A char takes 4 bytes in Rust: Unicode char\n", + " assert!(std::mem::size_of_val(&arr) == __);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Slice" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix the errors, DON'T add new lines!\n", + "fn main() {\n", + " let arr = [1, 2, 3];\n", + " let s1: [i32] = arr[0..2];\n", + "\n", + " let s2: str = \"hello, world\" as str;\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let arr: [i32; 5] = [1, 2, 3, 4, 5];\n", + " // Fill the blanks to make the code work\n", + " let slice: __ = __;\n", + " assert_eq!(slice, &[2, 3, 4]);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/4_struct-enum.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/4_struct-enum.ipynb new file mode 100644 index 0000000..4dc54f6 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/4_struct-enum.ipynb @@ -0,0 +1,442 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Struct" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix the error\n", + "struct Person {\n", + " name: String,\n", + " age: u8,\n", + " hobby: String\n", + "}\n", + "\n", + "fn main() {\n", + " let age = 30;\n", + " let p = Person {\n", + " name: String::from(\"sunface\"),\n", + " age,\n", + " };\n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix the errors and fill the blanks\n", + "struct Color(i32, i32, i32);\n", + "\n", + "fn main() {\n", + " let v = (0, 127, 255);\n", + " check_color(v);\n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "fn check_color(p: Color) {\n", + " let (x, _, _) = p;\n", + " assert_eq!(x, 0);\n", + " assert_eq!(p.1, 127);\n", + " assert_eq!(__, 255);\n", + " }\n", + "\n", + " main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "\n", + "// Fill the blank and fix the error without adding/removing new line\n", + "struct Person {\n", + " name: String,\n", + " age: u8,\n", + "}\n", + "fn main() {\n", + " let age = 18;\n", + " let p = Person {\n", + " name: String::from(\"john\"),\n", + " age,\n", + " };\n", + "\n", + " // How can you believe sunface is only 18? \n", + " p.age = 30;\n", + "\n", + " // Fill the blank\n", + " __ = String::from(\"John\");\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fill the blank to make the code work\n", + "struct User {\n", + " active: bool,\n", + " username: String,\n", + " email: String,\n", + " sign_in_count: u64,\n", + "}\n", + "fn main() {\n", + " let u1 = User {\n", + " email: String::from(\"someone@example.com\"),\n", + " username: String::from(\"sunface\"),\n", + " active: true,\n", + " sign_in_count: 1,\n", + " };\n", + "\n", + " let u2 = set_email(u1);\n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "fn set_email(u: User) -> User {\n", + " User {\n", + " email: String::from(\"contact@im.dev\"),\n", + " __\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fill the blanks to make the code work\n", + "#[__]\n", + "struct Rectangle {\n", + " width: u32,\n", + " height: u32,\n", + "}\n", + "\n", + "fn main() {\n", + " let scale = 2;\n", + " let rect1 = Rectangle {\n", + " width: dbg!(30 * scale), // Print debug info to stderr and assign the value of `30 * scale` to `width`\n", + " height: 50,\n", + " };\n", + "\n", + " dbg!(&rect1); // Print debug info to stderr\n", + "\n", + " println!(__, rect1); // Print debug info to stdout\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Enum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix the errors\n", + "enum Number {\n", + " Zero,\n", + " One,\n", + " Two,\n", + "}\n", + "\n", + "enum Number1 {\n", + " Zero = 0,\n", + " One,\n", + " Two,\n", + "}\n", + "\n", + "// C-like enum\n", + "enum Number2 {\n", + " Zero = 0,\n", + " One = 1,\n", + " Two = 2,\n", + "}\n", + "\n", + "\n", + "fn main() {\n", + " // An enum variant can be converted to a integer by `as`\n", + " assert_eq!(Number::One, Number1::One);\n", + " assert_eq!(Number1::One, Number2::One);\n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fill in the blank\n", + "enum Message {\n", + " Quit,\n", + " Move { x: i32, y: i32 },\n", + " Write(String),\n", + " ChangeColor(i32, i32, i32),\n", + "}\n", + "\n", + "fn main() {\n", + " let msg1 = Message::Move{__}; // Instantiating with x = 1, y = 2 \n", + " let msg2 = Message::Write(__); // Instantiating with \"hello, world!\"\n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "\n", + "// Fill in the blank and fix the error\n", + "enum Message {\n", + " Quit,\n", + " Move { x: i32, y: i32 },\n", + " Write(String),\n", + " ChangeColor(i32, i32, i32),\n", + "}\n", + "\n", + "fn main() {\n", + " let msg = Message::Move{x: 1, y: 2};\n", + "\n", + " if let Message::Move{__} = msg {\n", + " assert_eq!(a, b);\n", + " } else {\n", + " panic!(\"NEVER LET THIS RUN!\");\n", + " }\n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fill in the blank and fix the errors\n", + "enum Message {\n", + " Quit,\n", + " Move { x: i32, y: i32 },\n", + " Write(String),\n", + " ChangeColor(i32, i32, i32),\n", + "}\n", + "\n", + "fn main() {\n", + " let msgs: __ = [\n", + " Message::Quit,\n", + " Message::Move{x:1, y:3},\n", + " Message::ChangeColor(255,255,0)\n", + " ];\n", + "\n", + " for msg in msgs {\n", + " show_message(msg)\n", + " }\n", + "} \n", + "\n", + "fn show_message(msg: Message) {\n", + " println!(\"{}\", msg);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Impl" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. Implement functions for `struct`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "struct ContactEntry(String, String);\n", + "\n", + "// TODO: implement functions for ContactEntry\n", + "\n", + "fn main() {\n", + " let c1 = ContactEntry::new(\"John\", \"666-66-66-66\");\n", + " \n", + " assert_eq!(c1.name(), \"John\");\n", + " assert_eq!(c1.phone(), \"666-66-66-66\"); \n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/5_pattern-match.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/5_pattern-match.ipynb new file mode 100644 index 0000000..1fc0269 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/5_pattern-match.ipynb @@ -0,0 +1,213 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Match" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Fill the blanks\n", + "enum Direction {\n", + " East,\n", + " West,\n", + " North,\n", + " South,\n", + "}\n", + "\n", + "fn main() {\n", + " let direction = Direction::South;\n", + " match direction {\n", + " Direction::East => println!(\"East\"),\n", + " __ => { // Matching South or North here\n", + " println!(\"South or North\");\n", + " },\n", + " _ => println!(__),\n", + " };\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Match is an expression, so we can use it in assignments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn main() {\n", + " let boolean = true;\n", + "\n", + " // Fill the blank with a match expression:\n", + " //\n", + " // boolean = true -> binary = 1\n", + " // boolean = false -> binary = 0\n", + " let binary = __;\n", + "\n", + " assert_eq!(binary, 1);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + " main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Using match to get the data an enum variant holds." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Fill in the blanks\n", + "enum Message {\n", + " Quit,\n", + " Move { x: i32, y: i32 },\n", + " Write(String),\n", + " ChangeColor(i32, i32, i32),\n", + "}\n", + "\n", + "fn main() {\n", + " let msgs = [\n", + " Message::Quit,\n", + " Message::Move{x:1, y:3},\n", + " Message::ChangeColor(255,255,0)\n", + " ];\n", + "\n", + " for msg in msgs {\n", + " show_message(msg)\n", + " }\n", + "\n", + " println!(\"Success!\");\n", + "} \n", + "\n", + "fn show_message(msg: Message) {\n", + " match msg {\n", + " __ => { // match Message::Move\n", + " assert_eq!(a, 1);\n", + " assert_eq!(b, 3);\n", + " },\n", + " Message::ChangeColor(_, g, b) => {\n", + " assert_eq!(g, __);\n", + " assert_eq!(b, __);\n", + " }\n", + " __ => println!(\"no data in these variants\")\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# If let" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "fn main() {\n", + " let o = Some(7);\n", + "\n", + " // Remove the whole `match` block, using `if let` instead \n", + " match o {\n", + " Some(i) => {\n", + " println!(\"This is a really long string and `{:?}`\", i);\n", + "\n", + " println!(\"Success!\");\n", + " }\n", + " _ => {}\n", + " };\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "// Fill in the blank\n", + "enum Foo {\n", + " Bar(u8)\n", + "}\n", + "\n", + "fn main() {\n", + " let a = Foo::Bar(1);\n", + "\n", + " __ {\n", + " println!(\"foobar holds the value: {}\", i);\n", + "\n", + " println!(\"Success!\");\n", + " }\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/6_errors.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/6_errors.ipynb new file mode 100644 index 0000000..ce259a4 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/6_errors.ipynb @@ -0,0 +1,118 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Errors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// FILL in the blanks and FIX the errors\n", + "use std::num::ParseIntError;\n", + "use std::num::IntErrorKind::InvalidDigit\n", + "\n", + "fn multiply(n1_str: &str, n2_str: &str) -> Result {\n", + " let n1 = n1_str.parse::();\n", + " let n2 = n2_str.parse::();\n", + " \n", + " n1 * n2\n", + "}\n", + "\n", + "fn main() {\n", + " let result = multiply(\"10\", \"2\");\n", + " assert_eq!(result, Ok(20));\n", + "\n", + " let result = multiply(\"t\", \"2\");\n", + " assert_eq!(result.err().unwrap().kind(), &InvalidDigit);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::fs;\n", + "use std::io;\n", + "use std::num;\n", + "\n", + "enum CliError {\n", + " IoError(io::Error),\n", + " ParseError(num::ParseIntError),\n", + "}\n", + "\n", + "impl From for CliError {\n", + " // TODO: implement the trait\n", + "}\n", + " \n", + "impl From for CliError {\n", + " // TODO: implement the trait\n", + "}\n", + "\n", + "fn open_and_parse_file(file_name: &str) -> Result {\n", + " // ? automatically converts io::Error to CliError\n", + " let contents = fs::read_to_string(&file_name)?;\n", + " // num::ParseIntError -> CliError\n", + " let num: i32 = contents.trim().parse()?;\n", + " Ok(num)\n", + "}\n", + "\n", + "fn main() {\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/7_traits.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/7_traits.ipynb new file mode 100644 index 0000000..0935fe0 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/7_traits.ipynb @@ -0,0 +1,421 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Traits" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fill in the two impl blocks to make the code work.\n", + "// DON'T modify the code in `main`.\n", + "trait Greetings {\n", + " fn say_hi(&self) -> String {\n", + " String::from(\"Hi\")\n", + " }\n", + "\n", + " fn say_something(&self) -> String;\n", + "}\n", + "\n", + "struct Student {}\n", + "\n", + "impl Greetings for Student {\n", + "}\n", + "\n", + "struct Teacher {}\n", + "\n", + "impl Greetings for Teacher {\n", + "}\n", + "\n", + "fn main() {\n", + " let s = Student {};\n", + " assert_eq!(s.say_hi(), \"Hi\");\n", + " assert_eq!(s.say_something(), \"I'm a good student\");\n", + "\n", + " let t = Teacher {};\n", + " assert_eq!(t.say_hi(), \"Hi, I'm your new teacher\");\n", + " assert_eq!(t.say_something(), \"I'm not a bad teacher\");\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "struct Fender {}\n", + "struct Gibson {}\n", + "\n", + "trait Guitar {\n", + " fn sound(&self) -> String;\n", + "}\n", + "\n", + "impl Guitar for Fender {\n", + " fn sound(&self) -> String {\n", + " \"like Jimmy!\".to_string()\n", + " }\n", + "}\n", + "\n", + "impl Guitar for Gibson {\n", + " fn sound(&self) -> String {\n", + " \"like Slash!\".to_string()\n", + " }\n", + "}\n", + "\n", + "// Returns some struct that implements Guitar, but we don't know which one at compile time.\n", + "// FIX the errors here, you can make a fake random, or you can use trait object.\n", + "fn random_guitar(random_number: f64) -> impl Guitar {\n", + " if random_number < 0.5 {\n", + " Fender {}\n", + " } else {\n", + " Gibson {}\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let random_number = 0.234;\n", + " let guitar = random_guitar(random_number);\n", + " println!(\"You've randomly chosen an guitar, and it sounds {}\", guitar.sound());\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Trait bound" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "struct Fender {}\n", + "struct Gibson {}\n", + "\n", + "trait Guitar {\n", + " fn sound(&self) -> String;\n", + "}\n", + "\n", + "impl Guitar for Fender {\n", + " fn sound(&self) -> String {\n", + " \"like Jimmy!\".to_string()\n", + " }\n", + "}\n", + "\n", + "impl Guitar for Gibson {\n", + " fn sound(&self) -> String {\n", + " \"like Slash!\".to_string()\n", + " }\n", + "}\n", + "\n", + "\n", + "// Implement `fn use_guitar` with trait bound in two ways.\n", + "fn use_guitar(g: &G) {\n", + " println!(\"Sounds {}\", g.sound());\n", + "}\n", + "\n", + "// Implement `fn sum` with trait bound in two ways.\n", + "fn sum(x: T, y: T) -> T {\n", + " x + y\n", + "}\n", + "\n", + "fn main() {\n", + " let g = Fender{};\n", + " use_guitar(&g);\n", + " assert_eq!(sum(1, 2), 3);\n", + "\n", + " \n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// FIX the errors.\n", + "struct Pair {\n", + " x: T,\n", + " y: T,\n", + "}\n", + "\n", + "impl Pair {\n", + " fn new(x: T, y: T) -> Self {\n", + " Self {\n", + " x,\n", + " y,\n", + " }\n", + " }\n", + "}\n", + "\n", + "impl Pair {\n", + " fn cmp_display(&self) {\n", + " if self.x >= self.y {\n", + " println!(\"The largest member is x = {:?}\", self.x);\n", + " } else {\n", + " println!(\"The largest member is y = {:?}\", self.y);\n", + " }\n", + " }\n", + "}\n", + "\n", + "struct Unit(i32);\n", + "\n", + "fn main() {\n", + " let pair = Pair{\n", + " x: Unit(1),\n", + " y: Unit(3)\n", + " };\n", + "\n", + " pair.cmp_display();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "5. Array with trait objects" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "trait Guitar {\n", + " fn sound(&self);\n", + "}\n", + "\n", + "struct Stratocaster;\n", + "\n", + "impl Stratocaster {\n", + " fn get_name(&self) {\n", + " println!(\"Fender Stratocaster\");\n", + " }\n", + "}\n", + "\n", + "struct LesPaul;\n", + "\n", + "impl LesPaul {\n", + " fn get_name(&self) {\n", + " println!(\"Gibson Les Paul\");\n", + " }\n", + "}\n", + "\n", + "impl Guitar for Stratocaster {\n", + " fn sound(&self) {\n", + " println!(\"{}\", \"Sounds like Jimmy!\");\n", + " }\n", + "}\n", + "\n", + "impl Guitar for LesPaul {\n", + " fn sound(&self) {\n", + " println!(\"{}\", \"Sounds like Slash!\");\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " // // FILL in the blank to make the code work.\n", + " let s = Stratocaster{};\n", + " let lp1 = LesPaul{};\n", + " let blacky = Stratocaster{};\n", + "\n", + " let guitars: [__; 3]= [__];\n", + "\n", + " // Iterate through the guitars and make them sound.\n", + " todo!();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "6. &dyn & Box" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// FILL in the blanks.\n", + "trait Draw {\n", + " fn draw(&self) -> String;\n", + "}\n", + "\n", + "impl Draw for u8 {\n", + " fn draw(&self) -> String {\n", + " format!(\"u8: {}\", *self)\n", + " }\n", + "}\n", + "\n", + "impl Draw for f64 {\n", + " fn draw(&self) -> String {\n", + " format!(\"f64: {}\", *self)\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let x = 1.1f64;\n", + " let y = 8u8;\n", + "\n", + " // Draw x.\n", + " draw_with_box(__);\n", + "\n", + " // Draw y.\n", + " draw_with_ref(&y);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "fn draw_with_box(x: Box<__>) {\n", + " x.draw();\n", + "}\n", + "\n", + "fn draw_with_ref(x: __) {\n", + " x.draw();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "7. Static and Dynamic dispatch" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "\n", + "trait Foo {\n", + " fn method(&self) -> String;\n", + "}\n", + "\n", + "impl Foo for u8 {\n", + " fn method(&self) -> String { format!(\"u8: {}\", *self) }\n", + "}\n", + "\n", + "impl Foo for String {\n", + " fn method(&self) -> String { format!(\"string: {}\", *self) }\n", + "}\n", + "\n", + "// IMPLEMENT below with generics. (static polymorphism)\n", + "fn static_dispatch...\n", + "\n", + "// Implement below with trait objects. (dynamic polymorphism)\n", + "fn dynamic_dispatch...\n", + "\n", + "fn main() {\n", + " let x = 5u8;\n", + " let y = \"Hello\".to_string();\n", + "\n", + " static_dispatch(x);\n", + " dynamic_dispatch(&y);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/8_generics.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/8_generics.ipynb new file mode 100644 index 0000000..d5eb8d0 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/8_generics.ipynb @@ -0,0 +1,202 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "19ddc3b6", + "metadata": {}, + "source": [ + "# Generics\n", + "\n", + "## Functions" + ] + }, + { + "cell_type": "markdown", + "id": "dc4012b9", + "metadata": {}, + "source": [ + "1." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f850fa6", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "\n", + "// Fill in the blanks to make it work\n", + "struct A; // Concrete type `A`.\n", + "struct S(A); // Concrete type `S`.\n", + "struct SGen(T); // Generic type `SGen`.\n", + "\n", + "fn reg_fn(_s: S) {}\n", + "\n", + "fn gen_spec_t(_s: SGen) {}\n", + "\n", + "fn gen_spec_i32(_s: SGen) {}\n", + "\n", + "fn generic(_s: SGen) {}\n", + "\n", + "fn main() {\n", + " // Using the non-generic functions\n", + " reg_fn(__); // Concrete type.\n", + " gen_spec_t(__); // Implicitly specified type parameter `A`.\n", + " gen_spec_i32(__); // Implicitly specified type parameter `i32`.\n", + "\n", + " // Explicitly specified type parameter `char` to `generic()`.\n", + " generic::(__);\n", + "\n", + " // Implicitly specified type parameter `char` to `generic()`.\n", + " generic(__);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "id": "584e4753", + "metadata": {}, + "source": [ + "2." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f005be9", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Implement the generic function below.\n", + "fn sum\n", + "\n", + "fn main() {\n", + " assert_eq!(5, sum(2i8, 3i8));\n", + " assert_eq!(50, sum(20, 30));\n", + " assert_eq!(2.46, sum(1.23, 1.23));\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "id": "ad822adf", + "metadata": {}, + "source": [ + "## Struct and impl" + ] + }, + { + "cell_type": "markdown", + "id": "a3df7fa0", + "metadata": {}, + "source": [ + "3." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "52333cde", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Implement struct Point to make it work.\n", + "\n", + "fn main() {\n", + " let pt_integer = Point { x: 5, y: 10 };\n", + " println!(\"pt_integer: {:?}\", pt_integer);\n", + " assert_eq!(pt_integer.x(), 5);\n", + " assert_eq!(pt_integer.y(), 10);\n", + "\n", + " let pt_float = Point { x: 1.0, y: 4.0 };\n", + " println!(\"pt_float: {:?}\", pt_float);\n", + " assert_eq!(pt_float.x(), 1.0);\n", + " assert_eq!(pt_float.y(), 4.0);\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "id": "a1621757", + "metadata": {}, + "source": [ + "4." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e5292ca", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Fix the errors to make the code work.\n", + "struct Point {\n", + " x: T,\n", + " y: T,\n", + "}\n", + "\n", + "impl Point {\n", + " fn distance_from_origin(&self) -> f32 {\n", + " (self.x.powi(2) + self.y.powi(2)).sqrt()\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let p = Point{x: 5, y: 10};\n", + " println!(\"{}\",p.distance_from_origin());\n", + "\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/9_closures.ipynb b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/9_closures.ipynb new file mode 100644 index 0000000..bd8d19f --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/9_closures.ipynb @@ -0,0 +1,187 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Closures" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "1. Define a closure that adds two numbers and use it in `main`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "900b12e8", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "\n", + "fn main() {\n", + " let add = __;\n", + " \n", + " let result = add(2, 3);\n", + " assert_eq!(result, 5);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();\n" + ] + }, + { + "cell_type": "markdown", + "id": "81717c46", + "metadata": {}, + "source": [ + "2. Create a closure that captures a variable from the surrounding scope." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15a2bd11", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let multiplier = 3;\n", + " let multiply = __;\n", + "\n", + " assert_eq!(multiply(4), 12);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();\n" + ] + }, + { + "cell_type": "markdown", + "id": "8a3becbc", + "metadata": {}, + "source": [ + "3. Pass a closure into a generic function that accepts `Fn(i32) -> i32`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "015aebe2", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn apply(f: F, value: i32) -> i32\n", + "where\n", + " F: Fn(i32) -> i32,\n", + "{\n", + " f(value)\n", + "}\n", + "\n", + "fn main() {\n", + " let add_one = __;\n", + " let result = apply(add_one, 10);\n", + "\n", + " assert_eq!(result, 11);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();\n" + ] + }, + { + "cell_type": "markdown", + "id": "5ba5c844", + "metadata": {}, + "source": [ + "4. Use a mutable closure that updates a captured counter each time it is called." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b37b49eb", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let mut counter = 0;\n", + " let mut increment = __;\n", + "\n", + " assert_eq!(increment(), 1);\n", + " assert_eq!(increment(), 2);\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();\n" + ] + }, + { + "cell_type": "markdown", + "id": "637a4a78", + "metadata": {}, + "source": [ + "5. Create a `move` closure that takes ownership of a captured `String`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58d65ccc", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let message = String::from(\"Hello\");\n", + " let consume = __;\n", + "\n", + " let result = consume();\n", + " assert_eq!(result, \"Hello world!!!\");\n", + " println!(\"Success!\");\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/vector/Cargo.toml b/Rust_2026/rust-bs-2026-09-14/exercises/vector/Cargo.toml new file mode 100644 index 0000000..102210f --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/vector/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "vector" +version = "0.1.0" +edition = "2021" + +[dependencies] \ No newline at end of file diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/vector/src/main.rs b/Rust_2026/rust-bs-2026-09-14/exercises/vector/src/main.rs new file mode 100644 index 0000000..768984f --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/vector/src/main.rs @@ -0,0 +1,11 @@ +mod vector; + +// use vector::Vector2D; + +fn main() { + // let v1 = Vector2D::new(1.0, 2.0); + // let v2 = Vector2D::new(1.0, 2.0); + + // println!("v1 == v2: {}", v1 == v2); + // println!("v1 + v2: {:?}", v1 + v2); +} diff --git a/Rust_2026/rust-bs-2026-09-14/exercises/vector/src/vector.rs b/Rust_2026/rust-bs-2026-09-14/exercises/vector/src/vector.rs new file mode 100644 index 0000000..415eb75 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/exercises/vector/src/vector.rs @@ -0,0 +1,143 @@ +#[cfg(test)] +mod tests_vector2d { +// use super::*; + +// #[test] +// fn explicit_construction() { +// let v = Vector2D { x: 1.0, y: 2.0 }; +// assert_eq!(v.x, 1.0); +// assert_eq!(v.y, 2.0); +// } + +// #[test] +// fn new_method() { +// let v = Vector2D::new(1.0, 2.0); +// assert_eq!(v.x, 1.0); +// assert_eq!(v.y, 2.0); +// } + +// #[test] +// fn default_trait() { +// let v = Vector2D::default(); +// assert_eq!(v.x, 0.0); +// assert_eq!(v.y, 0.0); +// } + +// #[test] +// fn is_copyable() { +// let v = Vector2D::new(1.0, 2.0); +// let v2 = v; +// assert_eq!(v, v2); +// } + +// #[test] +// fn is_clonable() { +// let v = Vector2D::new(1.0, 2.0); +// let v2 = v.clone(); +// assert_eq!(v, v2); +// } + +// #[test] +// fn debug_format() { +// let v = Vector2D::new(1.0, 2.0); +// assert_eq!(format!("{:?}", v), "Vector2D { x: 1.0, y: 2.0 }"); +// } + +// #[test] +// fn length() { +// let v = Vector2D::new(3.0, 4.0); +// assert_eq!(v.length(), 5.0); +// } + +// #[test] +// fn scale() { +// let v = Vector2D::new(1.0, 2.0); +// let scaled_v = v.scale(2.0); +// assert_eq!(scaled_v, Vector2D::new(2.0, 4.0)); +// } + +// #[test] +// fn partial_eq() { +// let v1 = Vector2D::new(1.0, 2.0); +// let v2 = Vector2D::new(1.0, 2.0); +// assert_eq!(v1, v2); +// } + +// #[test] +// fn zero_associated_constant() { +// let z = Vector2D::ZERO; +// assert_eq!(z, Vector2D::new(0.0, 0.0)); +// } + +// #[test] +// fn unit_vectors() { +// let unit_x = Vector2D::UNIT_X; +// assert!(unit_x.x == 1.0 && unit_x.y == 0.0); + +// let unit_y = Vector2D::UNIT_Y; +// assert!(unit_y.x == 0.0 && unit_y.y == 1.0); +// } + +// #[test] +// fn negation() { +// let v = Vector2D::new(1.0, 2.0); +// let neg_v = -v; +// assert_eq!(neg_v, Vector2D::new(-1.0, -2.0)); +// } + +// #[test] +// fn addition() { +// let v1 = Vector2D::new(1.0, 2.0); +// let v2 = Vector2D::new(3.0, 4.0); +// let v3 = v1 + v2; +// assert_eq!(v3, Vector2D::new(4.0, 6.0)); +// } + +// #[test] +// fn subtraction() { +// let v1 = Vector2D::new(1.0, 2.0); +// let v2 = Vector2D::new(3.0, 4.5); +// let v3 = v1 - v2; + +// assert_eq!(v3, Vector2D::new(-2.0, -2.5)); +// } + +// #[test] +// fn scalar_product() { +// let v1 = Vector2D::new(1.0, 2.0); +// let v2: Vector2D = Vector2D::new(2.0, 3.0); +// let product = v1 * v2; +// assert_eq!(product, 8.0); +// } + +// #[test] +// fn multiplication_with_scalar() { +// let v = Vector2D::new(1.0, 2.0); +// let v_scaled = v * 2.0; +// assert_eq!(v_scaled, Vector2D::new(2.0, 4.0)); +// } + +// #[test] +// #[should_panic] +// fn index() { +// let v = Vector2D::new(1.0, 2.0); +// assert_eq!(v[0], 1.0); +// assert_eq!(v[1], 2.0); +// let _ = v[2]; +// } + +// #[test] +// fn index_mut() { +// let mut v = Vector2D::new(1.0, 2.0); +// v[0] = 3.0; +// v[1] = 4.0; +// assert_eq!(v, Vector2D::new(3.0, 4.0)); +// } + +// #[test] +// fn convert_from_tuple() { +// let v_tuple = (1.0, 2.0); +// let v = Vector2D::from(v_tuple); +// assert_eq!(v, Vector2D::new(1.0, 2.0)); +// } +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/01_variables_data_types.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/01_variables_data_types.ipynb new file mode 100644 index 0000000..c7a46cc --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/01_variables_data_types.ipynb @@ -0,0 +1,1573 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Hello world in Rust" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This is the simplest program you can write in Rust. It simply prints \"Hello, world!\" to the console." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// first program in Rust\n", + "\n", + "fn main() {\n", + " println!(\"Hello, World!\");\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n" + ] + } + ], + "source": [ + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Comments" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Comments in Rust are similar to comments in other languages. They can be single-line or multi-line." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// this is a comment\n", + "\n", + "/* this is\n", + " multiline\n", + " comment */\n", + "\n", + "/// this is a doc comment - rustdoc can compile this comments into project documentation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Variables and Data Types" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Declaring variables\n", + "\n", + "* Variables in Rust are declared using the `let` keyword.\n", + "* Rust is a statically typed language, which means that you need to specify the type of the variable when you declare it: " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let age: u8 = 30;\n", + "let pi: f64 = 3.14159;\n", + "let is_ok: bool = true;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Or the compiler can infer the type of the variable from the value assigned to it:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let n = 0; // n: int32\n", + "let e = 2.718f64; // e: f64\n", + "let is_done = false; // is_done: bool" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Variables are **immutable** by default" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot assign twice to immutable variable `step`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0384] Error:\u001b[0m cannot assign twice to immutable variable `step`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_7:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider making this binding mutable: `mut `", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m first assignment to `step`", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;115mNote\u001b[0m: You can change an existing variable to mutable like: `let mut x = x;`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let step = 1;\n", + "println!(\"step={}\", step);\n", + "step = 2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* All variables must be initialized before use" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "used binding `id` isn't initialized", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0381] Error:\u001b[0m used binding `id` isn't initialized", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_9:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249m3\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m binding declared here but left uninitialized", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m help: consider assigning a value: ` = 42`", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `id` used here but it isn't initialized", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let id: u32;\n", + "println!(\"id={}\", id);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* but the initialization can be done later" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rust is programming language\n" + ] + } + ], + "source": [ + "let programming_language;\n", + "\n", + "programming_language = \"Rust\";\n", + "\n", + "println!(\"{} is programming language\", programming_language);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Mutable variables\n", + "\n", + "* You can make a variable mutable by using the `mut` keyword" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initial count: 0\n", + "Updated count: 1\n", + "Updated count: 2\n" + ] + } + ], + "source": [ + "let mut count = 0;\n", + "println!(\"Initial count: {}\", count);\n", + "count += 1;\n", + "println!(\"Updated count: {count}\");\n", + "count += 1;\n", + "println!(\"Updated count: {count}\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Shadowing\n", + "\n", + "* You can declare a new variable with the same name as an existing variable, and the new variable will \"shadow\" the existing variable" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "block variable: 3.14\n", + "block variable outer: 99\n", + "outer variable: 42\n" + ] + } + ], + "source": [ + "fn main() { \n", + " let outer = 42; \n", + " { // start of code block \n", + " let inner = 3.14; \n", + " println!(\"block variable: {}\", inner); \n", + " let outer = 99; // shadows the first outer variable \n", + " println!(\"block variable outer: {}\", outer); \n", + " } // end of code block \n", + " println!(\"outer variable: {}\", outer); \n", + " } \n", + "\n", + " main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Constants\n", + "\n", + "* Constants are:\n", + " * always immutable\n", + " * evaluated at compile time.\n", + "* Constants can be declared in any scope, including the global scope\n", + "* By convention, constants are named using uppercase letters with underscores separating words (e.g., `MAX_SIZE`)." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "const SECONDS_IN_MINUTE: u32 = 60;\n", + "const MINUTES_IN_HOUR: u32 = 60;\n", + "const SECONDS_IN_HOUR: u32 = SECONDS_IN_MINUTE * MINUTES_IN_HOUR;" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "seconds in one hour: 3600\n" + ] + } + ], + "source": [ + "println!(\"seconds in one hour: {}\", SECONDS_IN_HOUR);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Static Variables\n", + "\n", + "* Rust also has static variables, which are similar to constants but have some differences:\n", + " * Lifetimes: static variables have a `'static` lifetime, meaning they live for the entire duration of the program.\n", + " * Mutability: static variables can be mutable if declared with static mut, but this requires unsafe code to access or modify them.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "creating a shared reference to mutable static", + "output_type": "error", + "traceback": [ + "\u001b[31m[static_mut_refs] Error:\u001b[0m creating a shared reference to mutable static", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_2:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m15 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mC\u001b[0m\u001b[38;5;249mO\u001b[0m\u001b[38;5;249mU\u001b[0m\u001b[38;5;249mN\u001b[0m\u001b[38;5;249mT\u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mC\u001b[0m\u001b[38;5;54mO\u001b[0m\u001b[38;5;54mU\u001b[0m\u001b[38;5;54mN\u001b[0m\u001b[38;5;54mT\u001b[0m\u001b[38;5;54mE\u001b[0m\u001b[38;5;54mR\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m shared reference to mutable static", + "\u001b[38;5;246m────╯\u001b[0m" + ] + } + ], + "source": [ + "static MESSAGE: &str = \"Hello Rust!\";\n", + "static mut COUNTER: u32 = 0;\n", + "\n", + "fn click() {\n", + " unsafe {\n", + " COUNTER += 1;\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " println!(\"{}\", MESSAGE);\n", + " click();\n", + " click();\n", + " unsafe {\n", + " println!(\"COUNTER: {}\", COUNTER);\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Integer types\n", + "\n", + "| Length | Signed | Unsigned |\n", + "|--------|--------|----------|\n", + "| 8-bit | i8 | u8 |\n", + "| 16-bit | i16 | u16 |\n", + "| 32-bit | i32 | u32 |\n", + "| 64-bit | i64 | u64 |\n", + "| 128-bit| i128 | u128 |\n", + "| arch | isize | usize |\n", + "\n", + "* the `isize` and `usize` types depend on the architecture of the computer your program is running on, which is denoted in the table as “arch”: 64 bits if you’re on a 64-bit architecture and 32 bits if you’re on a 32-bit architecture." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// signed integers\n", + "let int8: i8 = -128;\n", + "let int16: i16 = 32767;\n", + "let int32: i32 = 2147483647;\n", + "let int64: i64 = 9223372036854775807;\n", + "let int128: i128 = 170141183460469231731687303715884105727;\n", + "let intsize: isize = -1; // depends on the architecture (32-bit or 64-bit)\n", + "\n", + "\n", + "// unsigned integers\n", + "let uint8: u8 = 255;\n", + "let uint16: u16 = 65535;\n", + "let uint32: u32 = 4294967295;\n", + "let uint64: u64 = 18446744073709551615;\n", + "let uint128: u128 = 340282366920938463463374607431768211455;\n", + "let uintsize: usize = 1; // depends on the architecture (32-bit or 64-bit)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Type suffixes\n", + "\n", + "* You can specify integer literals using type suffixes, such as `13u8` or `13u16`." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let n = 42; // i32 by default\n", + "let f = 3.14; // f64 by default" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let n = 43i32;\n", + "let clicks_number = 0u32;\n", + "let the_anwser = 42i64;" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "### Number literals\n", + "\n", + "| Number literals | Example |\n", + "|-----------------|---------------|\n", + "| Decimal | 1_000_999 |\n", + "| Hex | 0xff |\n", + "| Octal | 0o77 |\n", + "| Binary | 0b1111_0000 |\n", + "| Byte (u8 only) | b'A' |" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let one_million = 1_000_000;\n", + "let flag = 0xff;\n", + "let one_byte: u8 = 0b1111_0000;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Overflow\n", + "\n", + "* In debug mode, Rust includes checks for integer overflow. If overflow occurs, Rust will panic and exit the program. " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2147483647" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let big_number = std::i32::MAX;\n", + "big_number" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "thread '' panicked at src\\lib.rs:164:19:\n", + "attempt to add with overflow\n", + "stack backtrace:\n", + " 0: std::panicking::begin_panic_handler\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\std\\src\\panicking.rs:697\n", + " 1: core::panicking::panic_fmt\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:75\n", + " 2: core::panicking::panic_const::panic_const_add_overflow\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\num\\overflow_panic.rs:8\n", + " 3: std::panic::catch_unwind\n", + " 4: run_user_code_16\n", + " 5: \n", + " 6: \n", + " 7: \n", + " 8: \n", + " 9: \n", + " 10: \n", + " 11: \n", + " 12: \n", + " 13: BaseThreadInitThunk\n", + " 14: RtlUserThreadStart\n", + "note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n" + ] + } + ], + "source": [ + "let next_number = big_number + 1; // panic: arithmetic operation overflowed" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* In release mode overflow will wrap to negative value (unlike in C++ where it is undefined behavior)\n", + "* When you want to wrap arithemtic you can use `wrapping_add`, `wrapping_sub`, `wrapping_mul` etc." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-2147483648" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let next_number = big_number.wrapping_add(1); // -2147483648\n", + "next_number" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Floating point types\n", + "\n", + "* Rust’s floating-point types are:\n", + " * `f32` - 32 bits in size\n", + " * `f64` - 64 bits in size" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x = 2, y = 3, pi_double = 3.14159, pi_float = 3.14\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let x = 2.0; // f64 by default\n", + " let y: f32 = 3.0; // f32\n", + "\n", + " // type suffixes\n", + " let pi_double = 3.14159f64; \n", + " let pi_float = 3.14159f32;\n", + "\n", + " println!(\"x = {x}, y = {y}, pi_double = {pi_double}, pi_float = {pi_float:0.2}\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Constants and Math \n", + "\n", + "* The `std::f32` and `std::f64` define constants for IEEE-754 floating point numbers, such as `INFINITY`, `NEG_INFINITY`, `NAN`, `MIN` and `MAX`.\n", + "* The `std::f64::consts` and `std::f32::consts` modules provide a number of useful constants, such as `PI` and `E`." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "assert!((-1. / std::f32::INFINITY).is_sign_negative());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The `f32` and `f64` types provide a full complement of methods for mathematical calculations, such as: `sqrt`, `exp`, `log`, `sin`, `cos`, `tan`, etc.\n", + "* Calling these functions we have to explicitly provide a type hint (numeric type cannot be ambiguous)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "assert_eq!(5f32.sqrt() * 5f32.sqrt(), 5.0);\n", + "assert!((-1.01f64).floor() == -2.0);" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "can't call method `sqrt` on ambiguous numeric type `{float}`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0689] Error:\u001b[0m can't call method `sqrt` on ambiguous numeric type `{float}`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_26:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249m(\u001b[0m\u001b[38;5;100m2\u001b[0m\u001b[38;5;100m.\u001b[0m\u001b[38;5;100m0\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mq\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: you must specify a concrete type for this numeric value, like `f32`: `2.0_f32`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m error: can't call method `sqrt` on ambiguous numeric type `{float}`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "(2.0).sqrt()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1.4142135623730951\n", + "1.4142135623730951\n" + ] + } + ], + "source": [ + "println!(\"{}\", (2.0_f64).sqrt());\n", + "println!(\"{}\", f64::sqrt(2.0));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Numeric operations\n", + "\n", + "* Rust supports the standard arithmetic operators: `+`, `-`, `*`, `/`, and `%`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Addition" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "15" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let sum = 5 + 10;\n", + "sum" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Subtraction" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "50.1" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let difference = 95.5 - 45.4;\n", + "difference" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Multiplication" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let product = 21 * 2;\n", + "product" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Division" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4.685950413223141" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let quotient = 56.7 / 12.1;\n", + "quotient" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let truncated = 15 / 7;\n", + "truncated" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Modulo" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let reminder = 15 % 7;\n", + "reminder" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Compound assignment operators\n", + "\n", + "* Rust also supports compound assignment operators, such as `+=`, `-=`, `*=`, `/=`, and `%=`." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x = 2.5\n" + ] + } + ], + "source": [ + "let mut x = 5f64;\n", + "\n", + "x /= 2f64;\n", + "\n", + "println!(\"x = {}\", x);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The Boolean Type\n", + "\n", + "* Rust has a built-in boolean type called `bool`. It has two possible values: `true` and `false`." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "is_ok=false\n" + ] + } + ], + "source": [ + "let mut is_ok: bool = true;\n", + "\n", + "is_ok = false;\n", + "\n", + "println!(\"is_ok={}\", is_ok);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Rust `as` operator can be used to convert from a boolean to an integer" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "true as i32" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Rust does not have automatic conversion from integers to booleans" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot cast `i32` as `bool`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0054] Error:\u001b[0m cannot cast `i32` as `bool`" + ] + } + ], + "source": [ + "1 as bool" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The Character Type\n", + "\n", + "* `char` char in Rust can represent any Unicode scalar value, including alphabetic characters, symbols, control characters, and more.\n", + "* `char` literals are enclosed in single quotes ('A').\n" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Letter: A\n", + "Emoji: 😊\n", + "Special Character: ñ\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let letter = 'A'; // A single character\n", + " let emoji = '😊'; // An emoji character\n", + " let special_character = 'ñ'; // A special alphabetic character\n", + "\n", + " println!(\"Letter: {}\", letter); // Output: Letter: A\n", + " println!(\"Emoji: {}\", emoji); // Output: Emoji: 😊\n", + " println!(\"Special Character: {}\", special_character); // Output: Special Character: ñ\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `char` is always four bytes in size" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "4" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "std::mem::size_of::()" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let v = vec!['h', 'e', 'l', 'l', '😊'];\n", + "\n", + "// five elements times four bytes for each element\n", + "assert_eq!(20, v.len() * std::mem::size_of::());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Convertions for `char`:\n", + "* from `u8` to `char`" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Character: A\n" + ] + } + ], + "source": [ + "let ascii_value = 65u8;\n", + "let character = ascii_value as char;\n", + "println!(\"Character: {}\", character); // Output: Character: A" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* from `char` to `u32` - ou can get the Unicode code point of a char using the `as` keyword." + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Unicode code point: 128522\n" + ] + } + ], + "source": [ + "let character = '😊';\n", + "let code_point = character as u32;\n", + "println!(\"Unicode code point: {}\", code_point);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Type checking and conversions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Rust does not allow implicit type conversion between numeric types. You can convert between types using the `as` keyword." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "mismatched types", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0308] Error:\u001b[0m mismatched types", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_43:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mu\u001b[0m\u001b[38;5;100m3\u001b[0m\u001b[38;5;100m2\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mc\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m expected due to this", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m expected `u32`, found `i32`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m help: you can convert an `i32` to a `u32` and panic if the converted value doesn't fit: `.try_into().unwrap()`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let score: i32 = 42;\n", + "let final_score: u32 = score;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Explicit conversion using `as` (also called a casting)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let score: i32 = 42;\n", + "let final_score: u32 = score as u32;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Type must be convertible to the new type" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "casting `&str` as `i32` is invalid", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0606] Error:\u001b[0m casting `&str` as `i32` is invalid", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_45:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249m3\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54m3\u001b[0m\u001b[38;5;54m2\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m error: casting `&str` as `i32` is invalid", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let name = \"Rusty\";\n", + "let id: i32 = name as i32;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuples\n", + "\n", + "* Tuples are a way of grouping together a number of values with a variety of types into one compound type.\n", + "* Tuples have a fixed length: once declared, they cannot grow or shrink in size." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let person = (\"James\", \"Bond\", 42);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Tuples are indexed by position, starting at zero" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"James\"" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "person.0" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Bond\"" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "person.1" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "42" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "person.2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Destructuring a tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "first=James, last=Bond, age=42\n" + ] + } + ], + "source": [ + "let person = (\"James\", \"Bond\", 42);\n", + "let (first, last, age) = person;\n", + "println!(\"first={}, last={last}, age={age}\", first);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit\n", + "\n", + "* The tuple without any values has a special name, **unit**. This value and its corresponding type are both written `()` and represent an empty value or an empty return type. \n", + "* Expressions implicitly return the unit value if they don’t return any other value." + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let unit = ();\n", + "unit" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/02_control_flow.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/02_control_flow.ipynb new file mode 100644 index 0000000..98c52b7 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/02_control_flow.ipynb @@ -0,0 +1,674 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Control flow" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Statements & Expressions\n", + "\n", + "### Statements\n", + "\n", + "* Statements must end with a semicolon" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "y = 30\n" + ] + } + ], + "source": [ + "let x = 10;\n", + "let mut y = 20;\n", + "y += x;\n", + "println!(\"y = {y}\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Expressions\n", + "\n", + "* Rust is an **expression-oriented language**, which means that most pieces of code are in fact expressions\n", + "* A code block is also an expression, which returns the value of its last expression if we omit the semicolon" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "result = 30\n" + ] + } + ], + "source": [ + "let result = {\n", + " let a = 10;\n", + " let b = 20;\n", + " a + b // no semicolon before end of the block\n", + "};\n", + "\n", + "println!(\"result = {}\", result);" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let result = {\n", + " let a = 10;\n", + " let b = 20;\n", + " a + b; // now result of an expression is ()\n", + "};\n", + "\n", + "result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Conditional Statements with `if` and `else`" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The number 7 is odd.\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let n = 7;\n", + "\n", + "if n % 2 == 0 {\n", + " println!(\"The number {n} is even.\");\n", + "} else {\n", + " println!(\"The number {n} is odd.\");\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `if-else` as an expression\n", + "\n", + "* if else condition is an expression that returns a value\n", + "* all branches always must return a value of the same type" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"adult\"" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let age = 44;\n", + "let is_adult = if age >= 18 { true } else { false };\n", + "let description = if is_adult { \"adult\" } else { \"minor\" };\n", + "description" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `while` loop" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Current count: 0\n", + "Current count: 1\n", + "Current count: 2\n", + "Current count: 3\n", + "Current count: 4\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut count = 0;\n", + "\n", + "while count < 5 {\n", + " println!(\"Current count: {}\", count);\n", + " count += 1;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Infinity `loop`\n", + "\n", + "Use loop to write infinite loops. It executes the block repeatedly forever (or until a break or return is reached, or the thread panics)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 * 1 = 1\n", + "3 * 3 = 9\n", + "5 * 5 = 25\n", + "7 * 7 = 49\n", + "9 * 9 = 81\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut n = 0;\n", + "let mut power: u32;\n", + "let max_power = 100;\n", + "\n", + "loop {\n", + " n += 1;\n", + " power = n * n;\n", + "\n", + " if power >= max_power {\n", + " break; \n", + " }\n", + "\n", + " if power % 2 == 0 {\n", + " continue;\n", + " }\n", + "\n", + " println!(\"{} * {} = {}\", n, n, power); \n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Nested loops\n", + "\n", + "* When loops are nested inside each other, the `break` and `continue` statements apply to the immediate enclosing loop. \n", + "* Any loop statement (also the while and for loop) can be preceded by a label (denoted as labelname:) to allow us to jump to the next or outer enclosing loop" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Enter the outer loop\n", + "Enter the inner loop\n", + "Exited the outer loop\n" + ] + } + ], + "source": [ + "'outer: loop {\n", + " println!(\"Enter the outer loop\");\n", + " 'inner: loop {\n", + " println!(\"Enter the inner loop\");\n", + " break 'outer; // breaks the outer loop\n", + " }\n", + " println!(\"This will never be printed...\");\n", + "}\n", + "println!(\"Exited the outer loop\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `for` loops" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* looping where variable `i` begins from a start value `a` to an end value `b` (exclusive)\n", + "* each subsequent value is bound to the variable `i` and used in the next loop iteration" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "i = 0\n", + "i = 1\n", + "i = 2\n", + "i = 3\n", + "i = 4\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for i in 0..5 {\n", + " println!(\"i = {}\", i);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* inclusive version of `for`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "i = 1\n", + "i = 2\n", + "i = 3\n", + "i = 4\n", + "i = 5\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for i in 1..=5 {\n", + " println!(\"i = {}\", i);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* if we don't need the value of the variable `i` in the loop, we can replace it with an `_` (underscore):" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Do it again!\n", + "Do it again!\n", + "Do it again!\n", + "Do it again!\n", + "Do it again!\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for _ in 0..5 { println!(\"Do it again!\"); } " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* iteration in reverse using `(a..b).rev()`" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "i: 10\n", + "i: 9\n", + "i: 8\n", + "i: 7\n", + "i: 6\n", + "i: 5\n", + "i: 4\n", + "i: 3\n", + "i: 2\n", + "i: 1\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for i in (1..=10).rev() {\n", + " println!(\"i: {}\", i);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Looping through the collection with `for`" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "item: 1\n", + "item: 64\n", + "item: 42\n", + "item: 665\n", + "item: -123\n", + "item: -2\n", + "item: 6\n", + "item: 0\n", + "item: 57\n", + "item: -1\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let data: [i32; 10] = [1, 64, 42, 665, -123, -2, 6, 0, 57, -1];\n", + "\n", + "for item in data {\n", + " println!(\"item: {}\", item);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2 is prime!\n", + "3 is prime!\n", + "5 is prime!\n", + "7 is prime!\n", + "11 is prime!\n", + "13 is prime!\n", + "17 is prime!\n", + "19 is prime!\n", + "23 is prime!\n", + "29 is prime!\n", + "31 is prime!\n", + "37 is prime!\n", + "41 is prime!\n", + "43 is prime!\n", + "47 is prime!\n", + "53 is prime!\n", + "59 is prime!\n", + "61 is prime!\n", + "67 is prime!\n", + "71 is prime!\n", + "73 is prime!\n", + "79 is prime!\n", + "83 is prime!\n", + "89 is prime!\n", + "97 is prime!\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let max_prime = 100;\n", + "\n", + "for n in 2..=max_prime {\n", + " let mut is_prime = true;\n", + " for d in 2..=((n as f64).sqrt() as i32) {\n", + " if n % d == 0 {\n", + " is_prime = false;\n", + " break;\n", + " }\n", + " }\n", + "\n", + " if is_prime {\n", + " println!(\"{} is prime!\", n);\n", + " }\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/03_ownership.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/03_ownership.ipynb new file mode 100644 index 0000000..abed705 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/03_ownership.ipynb @@ -0,0 +1,912 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Ownership\n", + "\n", + "* Memory is managed through a system of **ownership** with a set of rules that the compiler checks. \n", + "* If any of the rules are violated, the program won’t compile. \n", + "* None of the features of ownership will slow down your program while it’s running.\n", + "\n", + "## Ownership rules\n", + "\n", + "* Each value in Rust has an owner.\n", + "* There can only be one owner at a time.\n", + "* When the owner goes out of scope, the value will be dropped.\n", + "\n", + "## Variable scope\n", + "\n", + "* The **variable** is valid from the point at which it’s declared until the end of the current **scope**\n", + "* There are two important points in time here:\n", + " * when `s` comes into scope, it is valid.\n", + " * it remains valid until it goes out of scope." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rusty Cage!\n" + ] + } + ], + "source": [ + "{\n", + " let mut s = String::from(\"Rusty\"); // s allocates memory on the heap\n", + " s.push_str(\" Cage!\"); // ... do something with 's'\n", + " println!(\"{}\", s);\n", + " \n", + "} // 's' goes out of scope, and memory is deallocated" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Copy & Move for objects" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Copy\n", + "\n", + "* If a type implements the `Copy` trait, variables that use it do not move, but rather are trivially copied, making them still valid after assignment to another variable.\n", + "\n", + "* Primitive types that implement `Copy` trait are:\n", + " * all the integer types, such as `u32`.\n", + " * the Boolean type, `bool`, with values `true` and `false`.\n", + " * all the floating-point types, such as `f64`.\n", + " * the character type, `char`.\n", + " * tuples, if they only contain types that also implement `Copy`. For example, `(i32, i32)` implements `Copy`, but `(i32, String)` does not.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=10; y=10\n" + ] + } + ], + "source": [ + "let x = 10;\n", + "let y = x;\n", + "\n", + "println!(\"x={x}; y={y}\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Move\n", + "\n", + "* Objects that allocate memory on heap are moved by default (shallow copy is disabled by default)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rusty\n" + ] + } + ], + "source": [ + "let s1 = String::from(\"Rusty\");\n", + "let s2 = s1;\n", + "\n", + "println!(\"{s2}\");" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "borrow of moved value: `s1`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0382] Error:\u001b[0m borrow of moved value: `s1`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_17:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100m1\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m ", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m move occurs because `s1` has type `String`, which does not implement the `Copy` trait", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54m1\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m value moved here", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m help: consider cloning the value if the performance cost is acceptable: `.clone()`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let s1 = String::from(\"Rusty\");\n", + "let s2 = s1;\n", + "\n", + "s1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* How it works\n", + "\n", + "\"rust-move\"" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "* if we do want to make a deep copy the heap data of the `String`, not just the stack data, we can use a common method called `clone()`." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "s1 = hello, s2 = hello\n" + ] + } + ], + "source": [ + "let s1 = String::from(\"hello\");\n", + "let s2 = s1.clone();\n", + "\n", + "println!(\"s1 = {s1}, s2 = {s2}\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"rust-clone\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Move and Indexed Content\n", + "\n", + "* When we move a vector, the vector itself is moved, but its indexed content cannot be moved." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot move out of index of `Vec`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0507] Error:\u001b[0m cannot move out of index of `Vec`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_6:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mw\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54m[\u001b[0m\u001b[38;5;54m0\u001b[0m\u001b[38;5;54m]\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider borrowing here: `&`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m move occurs because value has type `String`, which does not implement the `Copy` trait", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m help: consider cloning the value if the performance cost is acceptable: `.clone()`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "cannot move out of index of `Vec`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0507] Error:\u001b[0m cannot move out of index of `Vec`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_6:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m4 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mw\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54m[\u001b[0m\u001b[38;5;54m1\u001b[0m\u001b[38;5;54m]\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider borrowing here: `&`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m move occurs because value has type `String`, which does not implement the `Copy` trait", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m help: consider cloning the value if the performance cost is acceptable: `.clone()`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let mut words = vec![\"hello\".to_string(), \"world\".to_string(), \"!!!\".to_string()];\n", + "\n", + "let first_word = words[0];\n", + "let second_word = words[1];" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* We can move (consume) items of a vector in a loop:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\"hello\", \"world\", \"!!!\"]\n" + ] + } + ], + "source": [ + "let mut target_words: Vec = Vec::new();\n", + "\n", + "let mut words = vec![\"hello\".to_string(), \"world\".to_string(), \"!!!\".to_string()];\n", + "\n", + "for word in words {\n", + " target_words.push(word);\n", + "}\n", + "\n", + "println!(\"{:?}\", target_words);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Ownership and functions\n", + "\n", + "* The mechanics of passing a value to a function are similar to those when assigning a value to a variable\n", + "* Passing a variable to a function will move or copy, just as assignment does" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "number = 3.14159\n", + "pi = 3.14159\n", + "text = TEXT\n" + ] + } + ], + "source": [ + "fn takes_ownership(text: String) {\n", + " println!(\"text = {text}\");\n", + "} \n", + "\n", + "fn takes_copy(number: f64) {\n", + " println!(\"number = {number}\");\n", + "}\n", + "\n", + "fn main() {\n", + " let pi = 3.14159;\n", + " takes_copy(pi);\n", + " println!(\"pi = {pi}\");\n", + "\n", + " let text = String::from(\"TEXT\");\n", + " takes_ownership(text);\n", + " // println!(\"text = {text}\"); // comment-out this line\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Return Values and Scope\n", + "\n", + "* Returning values can also transfer ownership" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ownership!!!\n", + "Ownership!!!\n" + ] + } + ], + "source": [ + "fn gives_ownership() -> String {\n", + " let text = String::from(\"Ownership!!!\");\n", + "\n", + " text\n", + "}\n", + "\n", + "fn transfers_ownership(text: String) -> String {\n", + " text\n", + "}\n", + "\n", + "fn main() {\n", + " let txt1 = gives_ownership();\n", + " let txt2 = transfers_ownership(txt1);\n", + " // println!(\"{txt1}\"); // comment-out this line\n", + " println!(\"{txt2}\");\n", + "\n", + " println!(\"{}\", transfers_ownership(gives_ownership())); \n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# References & Borrowing\n", + "\n", + "* A reference lets you access a value without affecting its ownership. References come in two kinds:\n", + " * A **shared reference** lets you read but not modify its referent. However, you can have as many shared references to a particular value at a time as you like. The expression `&e` yields a shared reference to `e`’s value; if `e` has the type `T`, then `&e` has the type `&T`, pronounced “ref T”. Shared references are `Copy`\n", + " * If you have a **mutable reference** to a value, you may both read and modify the value. However, you may not have any other references of any sort to that value active at the same time. The expression `&mut e` yields a mutable reference to `e`’s value; you write its type as `&mut T`, which is pronounced “ref mute T”. Mutable references are not `Copy`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* In Rust, references are created explicitly with the `&` operator, and dereferenced explicitly with the `*` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let x = 10;\n", + " let rx = &x; // &x is a shared reference to x\n", + " \n", + " assert!(*rx == 10); // explcitly dereference rx\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* To create a mutable reference, use the `&mut` operator:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let mut y = 42;\n", + " let ry = &mut y; // &mut y is a mutable reference to y\n", + " *ry += 10;\n", + " \n", + " assert!(y == 52);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Assigning References" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Assigning to a Rust reference makes it point at a new value:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let flag = true;\n", + "\n", + " let x = 10;\n", + " let y = 20;\n", + " let mut r: &i32 = &x;\n", + "\n", + " if flag { r = &y; }\n", + "\n", + " assert!(*r == 10 || *r == 20);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### References to References\n", + "\n", + "* Rust permits references to references.\n", + "* Explicit dereferencing requires as many `*` as levels of indirections\n", + "* The `.` operator follows as many references as it takes to find its target." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "struct Point { x: i32, y: i32 }\n", + "\n", + "fn ref_to_ref() {\n", + " let point = Point { x: 1000, y: 729 };\n", + "\n", + " let ry: &i32 = &point.y;\n", + " let rry: &&i32 = &ry;\n", + " let rrry: &&&i32 = &rry;\n", + " assert!(***rrry == 729);\n", + "\n", + " let r: &Point = &point;\n", + " let rr: &&Point = &r;\n", + " let rrr: &&&Point = &rr;\n", + " assert!(rrr.y == 729);\n", + "}\n", + "\n", + "ref_to_ref();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Comparing References\n", + "\n", + "* Like the `.` operator, Rust’s comparison operators “see through” any number of references, as long as both operands have the same type.\n", + "* If you actually want to know whether two references point to the same memory, you can use `std::ptr::eq`, which compares them as addresses:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let x = 10;\n", + " let y = 10;\n", + "\n", + " let rx = &x;\n", + " let ry = &y;\n", + "\n", + " assert!(rx == ry); // their referents are equal\n", + " assert!(!std::ptr::eq(rx, ry)); // but occupy different addresses\n", + " \n", + " let rrx = ℞\n", + " let rry = &ry;\n", + "\n", + " assert!(**rrx <= **rry); // explicit dereferencing using **\n", + " assert!(rrx == rry); // implicit dereferencing using **\n", + " assert!(rrx <= rry);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### References Are Never Null\n", + "* Rust references are never null. \n", + " * there’s no analogue to C’s `NULL` or C++’s `nullptr`\n", + " * and Rust won’t convert integers to references (outside of unsafe code), so you can’t convert zero into a reference." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Borrowing References to Arbitrary Expressions\n", + "\n", + "* Rust lets you borrow a reference (apply `&`) to the value of any sort of expression:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "fn factorial(n: usize) -> usize {\n", + " (1..n+1).fold(1, |a, b| a * b)\n", + "}\n", + "\n", + "fn main() {\n", + " let r = &factorial(6);\n", + "\n", + " // Arithmetic operators can see through one level of references.\n", + " assert_eq!(r + &1009, 1729);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Rust simply creates an anonymous variable to hold the expression’s value, and makes the reference point to that. The lifetime of this anonymous variable depends on what you do with the reference:\n", + "\n", + " * if you immediately assign the reference to a variable in a `let` statement (or make it part of some struct or array that is being immediately assigned), then Rust makes the anonymous variable live as long as the variable the `let` initializes. In the preceding example, Rust would do this for the referent of `r`.\n", + " * Otherwise, the anonymous variable lives to the end of the enclosing statement. In our example, the anonymous variable created to hold `1009` lasts only to the end of the `assert_eq!` statement." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reference safety\n", + "\n", + "### Borrowing a Local \n", + "\n", + "* You can’t borrow a reference to a local variable and take it out of the variable’s scope:" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "`x` does not live long enough", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0597] Error:\u001b[0m `x` does not live long enough", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_29:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mx\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m4\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m binding `x` declared here", + " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;54mx\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m borrowed value does not live long enough", + " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100m}\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m `x` dropped here while still borrowed", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m9 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;37m*\u001b[0m\u001b[38;5;37mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m─\u001b[0m\u001b[38;5;37m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m borrow later used here", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "{\n", + " let r;\n", + "\n", + " {\n", + " let x = 42;\n", + " r = &x;\n", + " }\n", + "\n", + " assert!(*r == 1);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Receiving References as Parameters" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Static variables\n", + "\n", + "* Rust’s equivalent of a global variable is called a **static**\n", + "* It’s a value that’s created when the program starts and lasts until it terminates\n", + "* Every static must be initialized\n", + "* **Mutable statics** are inherently not thread-safe (after all, any thread can access a static at any time), and even in single-threaded programs, they can fall prey to other sorts of reentrancy problems. For these reasons, you may access a mutable static only within an `unsafe` block.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Assignment to static reference\n", + "\n", + "* Static references can only refer to other statics - this avoids dangling reference\n", + "* In the code below variable `p` has some lifetime (but it is not a static) `=>` Rust rejects this code - assignment `p` to `STASH` could lead to dangling reference " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "lifetime may not live long enough", + "output_type": "error", + "traceback": [ + "\u001b[31mError:\u001b[0m lifetime may not live long enough", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_9:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249mf\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249m3\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m let's call the lifetime of this reference `'1`", + " \u001b[38;5;246m ·\u001b[0m ", + " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mS\u001b[0m\u001b[38;5;100mT\u001b[0m\u001b[38;5;100mA\u001b[0m\u001b[38;5;100mS\u001b[0m\u001b[38;5;100mH\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100m=\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100mp\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ", + " \u001b[38;5;246m ·\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m assignment requires that `'1` must outlive `'static`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "static mut STASH: &i32 = &128;\n", + "\n", + "fn put_on_stash(p: &i32) {\n", + " unsafe {\n", + " STASH = p;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* In order to compile `put_on_stash()` function we have annotate that `p` is a reference to static data" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "static mut STASH: &i32 = &10;\n", + "\n", + "fn put_on_stash(p: &'static i32) {\n", + " unsafe {\n", + " STASH = p;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "665" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "static SOME_DATA: i32 = 665;\n", + "\n", + "put_on_stash(&SOME_DATA);\n", + "\n", + "unsafe { *STASH }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Returning references" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Functions can return references\n", + "* Dangling references are reported at compile time" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "fn smallest(v: &[i32]) -> &i32 {\n", + " let mut smallest_value: &i32 = &v[0];\n", + "\n", + " for value in &v[1..] {\n", + " if *value < *smallest_value {\n", + " smallest_value = value;\n", + " } \n", + " }\n", + " smallest_value\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot find function `smallest` in this scope", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0425] Error:\u001b[0m cannot find function `smallest` in this scope", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_30:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m4 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m)\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m not found in this scope", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let v;\n", + "{\n", + " let data = [5, 345, 1, 4, 42, 665];\n", + " v = smallest(&data)\n", + "}\n", + "assert!(*v == 1);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Correct version of the snippet above" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot find function `smallest` in this scope", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0425] Error:\u001b[0m cannot find function `smallest` in this scope", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_31:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m not found in this scope", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "{\n", + " let data = [5, 345, 1, 4, 42, 665];\n", + " let v = smallest(&data);\n", + " assert!(*v == 1);\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/04_arrays_vectors_slices.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/04_arrays_vectors_slices.ipynb new file mode 100644 index 0000000..5e87e16 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/04_arrays_vectors_slices.ipynb @@ -0,0 +1,2377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Arrays, Vectors & Slices" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Arrays\n", + "\n", + "* The type `[T; N]` represents an array of `N` values, each of type `T`.\n", + "* An array’s size is a constant determined at compile time, and is part of the type; \n", + " * you can’t append new elements, \n", + " * or shrink an array." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating arrays" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 1, 4, 9, 16, 25]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let data: [i32; 6] = [0, 1, 4, 9, 16, 25];\n", + "data" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"zero\", \"one\", \"two\", \"three\", \"four\", \"five\"]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let words: [&str; 6] = [\"zero\", \"one\", \"two\", \"three\", \"four\", \"five\"];\n", + "words" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[[1, 2, 3], [4, 5, 6], [7, 8, 9]]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut matrix: [[i32; 3]; 3] = [\n", + " [1, 2, 3],\n", + " [4, 5, 6],\n", + " [7, 8, 9],\n", + "];\n", + "matrix" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Assignment of `[V; N]` fills an array of size `N` with a value `V`" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut buffer = [0u8; 32]; // 32-byte buffer, filled with zero bytes\n", + "buffer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Accessing Array Elements\n", + "\n", + "* Accessing an element of an array can be done using the index operator `[]`\n", + "* Indexing is 0-based\n", + "* Accessing an element outside the bounds of an array will cause a panic" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"two\"" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let numbers = [\"zero\", \"one\", \"two\", \"three\", \"four\", \"five\"];\n", + "numbers[2]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Safe indexing can be done using the `get` method of the slice\n", + "* The `get` method returns an `Option` type, which is `Some` if the index is in bounds, and `None` if it is out of bounds" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\"two\"\n" + ] + } + ], + "source": [ + "match numbers.get(2) {\n", + " Some(&x) => println!(\"{:?}\", x),\n", + " None => println!(\"No such element\"),\n", + "};" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Modifying Array Elements" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 42, 3, 4, 665]" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut numbers = [1, 2, 3, 4, 5];\n", + "\n", + "numbers[1] = 42;\n", + "\n", + "if let Some(x) = numbers.get_mut(4) {\n", + " *x = 665;\n", + "}\n", + "\n", + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterating Through Arrays\n", + "\n", + "* `for` loop" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Monday\n", + "Tuesday\n", + "Wednesday\n", + "Thursday\n", + "Friday\n", + "Saturday\n", + "Sunday\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let days = [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"];\n", + "\n", + "// using a for-in loop\n", + "for day in days {\n", + " println!(\"{day}\");\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* is using the `iter()` method that creates an iterator over the elements of the array" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Monday\n", + "Tuesday\n", + "Wednesday\n", + "Thursday\n", + "Friday\n", + "Saturday\n", + "Sunday\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// using a for loop with an iterator\n", + "for day in days.iter() {\n", + " println!(\"{}\", day);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* it is also possible to iterate over elements of an array using indexes" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 - Monday\n", + "1 - Tuesday\n", + "2 - Wednesday\n", + "3 - Thursday\n", + "4 - Friday\n", + "5 - Saturday\n", + "6 - Sunday\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "// using a for loop with index\n", + "for index in 0..days.len() {\n", + " println!(\"{} - {}\", index, days[index])\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `iter_mut()` method that creates an iterator over mutable references to the elements of the array" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 4, 6, 8, 10]" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut numbers = [1, 2, 3, 4, 5];\n", + "\n", + "for number in &mut numbers {\n", + " *number *= 2;\n", + "}\n", + "\n", + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[5, 7, 9, 11, 13]" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for number in numbers.iter_mut() {\n", + " *number += 3;\n", + "}\n", + "\n", + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Array Length" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "7" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let array_size = days.len();\n", + "array_size" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Copying Arrays\n", + "\n", + "* Arrays are copied by value (if the element type is `Copy`)" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4]\n", + "[42, 2, 3, 4]\n" + ] + } + ], + "source": [ + "let mut original: [i32; 4] = [1, 2, 3, 4];\n", + "let mut copied: [i32; 4] = original;\n", + "\n", + "copied[0] = 42;\n", + "assert_eq!(&copied, &[42, 2, 3, 4]); \n", + "\n", + "println!(\"{:?}\", original);\n", + "println!(\"{:?}\", copied);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* When elements are not `Copy`, the array is moved" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [], + "source": [ + "let mut words: [String; 4] = [\"zero\".to_string(), \"one\".to_string(), \"two\".to_string(), \"three\".to_string()];\n", + "let target = words; // move words into target" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* To copy an array, you can use the `clone()` method" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\"zero!!!\", \"one\", \"two\", \"three\"]\n", + "[\"zero\", \"one\", \"two\", \"three\"]\n" + ] + } + ], + "source": [ + "let mut words: [String; 4] = [\"zero\".to_string(), \"one\".to_string(), \"two\".to_string(), \"three\".to_string()];\n", + "let backup_words = words.clone();\n", + "\n", + "words[0].push_str(\"!!!\");\n", + "assert_eq!(words[0], \"zero!!!\");\n", + "\n", + "println!(\"{:?}\", words);\n", + "println!(\"{:?}\", backup_words);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### to_owned()\n", + "\n", + "* The `to_owned()` method can be applied to borrowed array to create a copy of the array" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "original_array = [\"one\", \"two\", \"three\", \"four\"] - [&str; 4]\n", + "borrowed_array = [\"one\", \"two\", \"three\", \"four\"] - &[&str; 4]\n", + "owned_vector = [\"one\", \"two\", \"three\", \"four\"] - [&str; 4]\n" + ] + } + ], + "source": [ + "use std::any::{TypeId, type_name};\n", + "\n", + "fn get_type(_: &T) -> String {\n", + " format!(\"{}\", type_name::())\n", + "}\n", + "\n", + "fn main() {\n", + " let original_array: [&str; 4] = [\"one\", \"two\", \"three\", \"four\"];\n", + " let borrowed_array: &[&str; 4] = &original_array;\n", + " let owned_vector = borrowed_array.to_owned();\n", + "\n", + " println!(\"original_array = {original_array:?} - {}\", get_type(&original_array));\n", + " println!(\"borrowed_array = {original_array:?} - {}\", get_type(&borrowed_array));\n", + " println!(\"owned_vector = {owned_vector:?} - {}\", get_type(&owned_vector));\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Arrays - Example" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[false, false, true, true, false, true, false, true, false, false, false, true, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, true, false, true, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false]" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fn is_prime(n: usize) -> bool {\n", + " if n < 2 {\n", + " return false;\n", + " }\n", + " for i in 2..=((n as f64).sqrt() as usize) {\n", + " if n % i == 0 {\n", + " return false;\n", + " }\n", + " }\n", + " true\n", + "}\n", + "\n", + "const N: usize = 100;\n", + "\n", + "let mut primes_sieve = [true; N];\n", + "primes_sieve[0] = false;\n", + "primes_sieve[1] = false;\n", + "\n", + "for i in 2..10 {\n", + " if primes_sieve[i] {\n", + " let mut j = i * i;\n", + " while j < N {\n", + " primes_sieve[j] = false;\n", + " j += i;\n", + " }\n", + " }\n", + "}\n", + "\n", + "primes_sieve" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "primes_sieve.iter().enumerate()\n", + " .filter_map(|(index, &is_prime)| if is_prime { Some(index) } else { None })\n", + " .collect::>()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Multi-dimensionals Arrays" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "_ X O \n", + "O X _ \n", + "X _ O \n", + "\n", + "Updated Tic-Tac-Toe Board:\n", + "_ X O \n", + "O X X \n", + "X O O \n" + ] + } + ], + "source": [ + "fn main() {\n", + " let mut tic_tac_toe: [[char; 3]; 3] = [\n", + " ['_', 'X', 'O'],\n", + " ['O', 'X', '_'],\n", + " ['X', '_', 'O']\n", + " ];\n", + "\n", + " print_tic_tac_toe(&tic_tac_toe);\n", + "\n", + " tic_tac_toe[1][2] = 'X';\n", + " tic_tac_toe[2][1] = 'O';\n", + "\n", + " println!(\"\\nUpdated Tic-Tac-Toe Board:\");\n", + " print_tic_tac_toe(&tic_tac_toe);\n", + "}\n", + "\n", + "fn print_tic_tac_toe(board: &[[char; 3]; 3]) {\n", + " for row in board {\n", + " for cell in row {\n", + " print!(\"{cell} \");\n", + " }\n", + " println!();\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Vectors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A vector `Vec` is a resizable array of elements of type `T`, allocated on the heap." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating Vectors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `Vec::new()` - creates empty vector" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut v = Vec::::new();\n", + "assert_eq!(v.len(), 0);\n", + "\n", + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* vector is resized automatically when elements are added" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 3, 7, 11]" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "v.push(2);\n", + "v.push(3);\n", + "v.push(7);\n", + "v.push(11);\n", + "\n", + "assert_eq!(v.len(), 4);\n", + "\n", + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `vec!` macro allows to create a vector initialized with given items" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 3, 7, 11]" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut v = vec![2, 3, 7, 11];\n", + "\n", + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `vec![V; N]` - creates a vector with size `N` filled with `V`" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fn create_pixel_buffer(rows: usize, cols: usize) -> Vec {\n", + " vec![0u8; rows * cols]\n", + "}\n", + "\n", + "create_pixel_buffer(4, 3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Another possibility is to build a vector from the values produced by an iterator:" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let numbers: Vec = (1..=10).collect();\n", + "\n", + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Accessing and Modifying Elements" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [], + "source": [ + "let mut words: Vec = [\"one\".to_string(), \"two\".to_string(), \"three\".to_string()].to_vec();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Indexing" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"one\"" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "words[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"one\", \"two and a half\", \"three\"]" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "words[1].push_str(\" and a half\");\n", + "words" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* moving elements out of a vector when indexing is not allowed by default\n", + "* to move elements, use `drain` or `into_iter`" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot move out of index of `Vec`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0507] Error:\u001b[0m cannot move out of index of `Vec`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_70:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mw\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54m[\u001b[0m\u001b[38;5;54m0\u001b[0m\u001b[38;5;54m]\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider borrowing here: `&`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m move occurs because value has type `String`, which does not implement the `Copy` trait", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m help: consider cloning the value if the performance cost is acceptable: `.clone()`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let first = words[0];\n", + "first" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"one\", \"two and three quarters\", \"three\"]" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let second: &String = &words[1];\n", + " let second_mutable: &mut String = &mut words[1];\n", + " *second_mutable = \"two and three quarters\".to_string();\n", + "}\n", + "\n", + "words" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Safe Access\n", + "* indexing with `[]` operator - panics if index is out of bounds\n", + "* `get()` method - safe access returning `Option<&T>`\n", + "* `get_mut()` method - safe mutable access returning `Option<&mut T>`\n" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "thread '' panicked at src\\lib.rs:188:6:\n", + "index out of bounds: the len is 3 but the index is 3\n", + "stack backtrace:\n", + " 0: std::panicking::begin_panic_handler\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\std\\src\\panicking.rs:697\n", + " 1: core::panicking::panic_fmt\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:75\n", + " 2: core::panicking::panic_bounds_check\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:280\n", + " 3: \n", + " 4: \n", + " 5: \n", + " 6: \n", + " 7: \n", + " 8: \n", + " 9: \n", + " 10: \n", + " 11: \n", + " 12: \n", + " 13: BaseThreadInitThunk\n", + " 14: RtlUserThreadStart\n", + "note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n" + ] + } + ], + "source": [ + "words[3]" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "No third word\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 74, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let third: Option<&String> = words.get(3);\n", + "\n", + " match third {\n", + " Some(word) => println!(\"{}\", word),\n", + " None => println!(\"No third word\")\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Length & Capacity\n", + "\n", + "* A `Vec` consists of three values: \n", + " * a pointer to the heap-allocated buffer allocated to hold the elements; \n", + " * the number of elements that buffer has the capacity to store; \n", + " * and the number it actually contains now (in other words, its length). \n", + "* When the buffer has reached its capacity, adding another element to the vector entails allocating a larger buffer, copying the present contents into it, updating the vector’s pointer and capacity to describe the new buffer, and finally freeing the old one." + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [], + "source": [ + "let mut v = Vec::with_capacity(2);\n", + "assert_eq!(v.len(), 0);\n", + "assert_eq!(v.capacity(), 2);\n", + "\n", + "v.push(1);\n", + "v.push(2);\n", + "assert_eq!(v.len(), 2);\n", + "assert_eq!(v.capacity(), 2);\n", + "\n", + "v.push(3);\n", + "assert_eq!(v.len(), 3);\n", + "assert_eq!(v.capacity(), 4);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Removing elements" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `pop()` method removes the last element and returns it as `Option`:\n", + " * `None` if the vector was already empty, \n", + " * or `Some(v)` if its last element had been `v`" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [], + "source": [ + "let mut guitars = vec![\"fender\", \"gibson\"];\n", + "assert_eq!(guitars.pop(), Some(\"gibson\"));\n", + "assert_eq!(guitars.pop(), Some(\"fender\"));\n", + "assert_eq!(guitars.pop(), None);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `remove(index)` - removes item with a given index" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"fender\", \"yamaha\"]" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut guitars = vec![\"fender\", \"gibson\", \"yamaha\"];\n", + "guitars.remove(1);\n", + "guitars" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iterating over Vectors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Using `for` loop" + ] + }, + { + "cell_type": "code", + "execution_count": 79, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1 2 3 4 5 \n" + ] + } + ], + "source": [ + "let numbers = vec![1, 2, 3, 4, 5];\n", + "\n", + "for number in numbers {\n", + " print!(\"{} \", number);\n", + "}\n", + "\n", + "println!();" + ] + }, + { + "cell_type": "code", + "execution_count": 80, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "borrow of moved value: `words`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0382] Error:\u001b[0m borrow of moved value: `words`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_80:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mw\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m[\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m]\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m move occurs because `words` has type `Vec`, which does not implement the `Copy` trait", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249mf\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mw\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m help: consider iterating over a slice of the `Vec`'s content to avoid moving into the `for` loop: `&`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `words` moved due to this implicit call to `.into_iter()`", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;115mNote\u001b[0m: note: `into_iter` takes ownership of the receiver `self`, which moves `words`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let words = vec![\"one\".to_string(), \"two\".to_string(), \"three\".to_string()];\n", + "\n", + "for word in words {\n", + " println!(\"{}\", word);\n", + "}\n", + "\n", + "words" + ] + }, + { + "cell_type": "code", + "execution_count": 81, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "one\n", + "two\n", + "three\n" + ] + }, + { + "data": { + "text/plain": [ + "[\"one\", \"two\", \"three\"]" + ] + }, + "execution_count": 81, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let words = vec![\"one\".to_string(), \"two\".to_string(), \"three\".to_string()];\n", + "\n", + "for word in words.iter() {\n", + " println!(\"{}\", word);\n", + "}\n", + "\n", + "words" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Using `iter_mut()` for Mutable Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `iter_mut()` creates an iterator over mutable references to the elements of the vector" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[11, 12, 13, 14, 15]" + ] + }, + "execution_count": 83, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut numbers = vec![1, 2, 3, 4, 5];\n", + "\n", + "for n in numbers.iter_mut() {\n", + " *n += 10;\n", + "}\n", + "\n", + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* the same can be achieved using iteration over mutable slice - `&mut collection`" + ] + }, + { + "cell_type": "code", + "execution_count": 84, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[33, 36, 39, 42, 45]" + ] + }, + "execution_count": 84, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for n in &mut numbers {\n", + " *n *= 3;\n", + "}\n", + "\n", + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Using `enumerate` for Index & Value" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index: 0 - fender\n", + "Index: 1 - gibson\n", + "Index: 2 - yamaha\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut guitars = vec![\"fender\", \"gibson\", \"yamaha\"];\n", + "\n", + "for (index, guitar) in guitars.iter().enumerate() {\n", + " println!(\"Index: {} - {}\", index, guitar);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Slices" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A **slice**, written `[T]` without specifying the length, is a region of an array or vector. \n", + "* Since a slice can be any length, slices can’t be stored directly in variables or passed as function arguments. **Slices are always passed by reference**.\n", + "* A reference to a slice is a **fat pointer**: a two-word value comprising a pointer to the slice’s first element, and the number of elements in the slice.\n", + "* A reference to a slice is a non-owning pointer to several values" + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "sv: 0 0.707 1 0.707 \n", + "sa: 0 -0.707 -1 -0.707 \n" + ] + } + ], + "source": [ + "fn main() {\n", + " let v: Vec = vec![0.0, 0.707, 1.0, 0.707];\n", + " let a: [f64; 4] = [0.0, -0.707, -1.0, -0.707];\n", + "\n", + " let sv: &[f64] = &v;\n", + " let sa: &[f64] = &a;\n", + "\n", + " fn print(data: &[f64], desc: &str) {\n", + " print!(\"{desc}: \");\n", + " for item in data {\n", + " print!(\"{} \", item);\n", + " }\n", + " println!();\n", + " }\n", + "\n", + " print(sv, \"sv\");\n", + " print(sa, \"sa\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![image](./images/rust-slices.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "### Creating slices" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "slice_all: [ 1 2 3 4 5 6 7 8 9 10 ]\n", + "slice_first_five: [ 1 2 3 4 5 ]\n", + "slice_last_five: [ 6 7 8 9 10 ]\n", + "data: [ 1 2 3 4 0 0 0 8 9 10 ]\n" + ] + } + ], + "source": [ + "// Function to print elements of a slice with a description\n", + "fn print(data: &[i32], desc: &str) {\n", + " print!(\"{desc}: [ \");\n", + " for item in data {\n", + " print!(\"{} \", item);\n", + " }\n", + " println!(\"]\");\n", + "}\n", + "\n", + "// Function to set all elements of a mutable slice to zero\n", + "fn zero(data: &mut [i32]) {\n", + " for item in data {\n", + " *item = 0;\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let mut data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n", + "\n", + " let slice_all: &[i32] = &data[..];\n", + " print(slice_all, \"slice_all\");\n", + "\n", + " let slice_first_five: &[i32] = &data[0..5];\n", + " print(slice_first_five, \"slice_first_five\");\n", + "\n", + " let slice_last_five: &[i32] = &data[5..];\n", + " print(slice_last_five, \"slice_last_five\");\n", + "\n", + " let slice_middle_three: &mut [i32] = &mut data[4..7];\n", + " zero(slice_middle_three);\n", + " print(&data, \"data\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "working_days: [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\"]\n", + "weekend: [\"Saturday\", \"Sunday\"]\n", + "days: [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"]\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let days = [\"Monday\".to_string(), \"Tuesday\".to_string(), \"Wednesday\".to_string(), \"Thursday\".to_string(), \"Friday\".to_string(), \"Saturday\".to_string(), \"Sunday\".to_string()];\n", + "\n", + " let working_days = &days[..5];\n", + " println!(\"working_days: {:?}\", working_days);\n", + "\n", + " let weekend: &[String] = &days[5..];\n", + " println!(\"weekend: {:?}\", weekend);\n", + "\n", + " println!(\"days: {:?}\", days);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Accessing Slice Elements" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "20\n", + "30\n", + "40\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let data: [i32; 5] = [10, 20, 30, 40, 50];\n", + " let slice: &[i32] = &data[1..4];\n", + "\n", + " // Accessing elements by index\n", + " let second_element = slice[0];\n", + " assert!(second_element == 20);\n", + "\n", + " // Accessing elements by get method\n", + " let third_element: Option<&i32> = slice.get(2);\n", + " assert!(third_element == Some(&40));\n", + "\n", + " // Accessing elements by iteration\n", + " for item in slice {\n", + " println!(\"{}\", item);\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Modifing Slice Elements" + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", + "data: [-1, -2, -3, -4, -5, 6, 7, 8, 9, 10]\n" + ] + } + ], + "source": [ + "fn negate(data: &mut[i32]) {\n", + " for item in data {\n", + " *item = -*item;\n", + " }\n", + "}\n", + "\n", + "\n", + "fn main() {\n", + " let mut data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n", + " println!(\"data: {:?}\", data);\n", + "\n", + " let n = data.len()/2;\n", + " negate(&mut data[0..n]);\n", + " \n", + " println!(\"data: {:?}\", data);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## String Types" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### String Literals" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "And now \"something\" completely different...\n" + ] + } + ], + "source": [ + "let text: &str = \"And now \\\"something\\\" completely different...\\n\";\n", + "\n", + "print!(\"{text}\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* raw string literals - `r\"...\"`" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "metadata": {}, + "outputs": [], + "source": [ + "let default_win_install_path: &str = r\"C:\\Program Files\\Gorillas\";\n", + "\n", + "let regex_pattern: &str = r\"\\d+(\\.\\d+)*\";" + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " This raw string started with 'r###\"'.\n", + " Therefore it does not end until we reach a quote mark ('\"')\n", + " followed immediately by three pound signs ('###'):\n", + "\n" + ] + } + ], + "source": [ + "println!(r###\"\n", + " This raw string started with 'r###\"'.\n", + " Therefore it does not end until we reach a quote mark ('\"')\n", + " followed immediately by three pound signs ('###'):\n", + "\"###);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Byte Strings" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "* A string literal with the `b` prefix is a **byte string**. \n", + "* Raw byte strings start with `br\"`\n", + "* Such a string is a slice of `u8` values—that is, bytes—rather than Unicode text:" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "metadata": {}, + "outputs": [], + "source": [ + "let method: &[u8; 3] = b\"GET\";\n", + "assert_eq!(method, &[b'G', b'E', b'T']);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Strings in Memory" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Rust strings are sequences of Unicode characters**, but they are not stored in memory as arrays of chars. \n", + "\n", + "Instead, they are stored using UTF-8, a variable-width encoding. Each ASCII character in a string is stored in one byte. Other characters take up multiple bytes." + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 110, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let noodles = \"noodles\".to_string();\n", + " let oodles = &noodles[1..];\n", + " let poodles = \"ಠ_ಠ\";\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `&str`\n", + "\n", + "* A `&str` (pronounced “stir” or “string slice”) is a reference to a run of UTF-8 text owned by someone else: it “borrows” the text\n", + "* It is a **fat pointer**, containing both the address of the actual data and its length - `&str` is nothing more than a `&[u8]` that is guaranteed to hold well-formed UTF-8\n", + "* A string literal is a `&str` that refers to preallocated text, typically stored in read-only memory along with the program’s machine code\n", + "* It is impossible to modify a `&str`:" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "the type `str` cannot be indexed by `{integer}`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0277] Error:\u001b[0m the type `str` cannot be indexed by `{integer}`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_113:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m[\u001b[0m\u001b[38;5;54m0\u001b[0m\u001b[38;5;249m]\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m'\u001b[0m\u001b[38;5;249mH\u001b[0m\u001b[38;5;249m'\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m string indices are ranges of `usize`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "no method named `push` found for reference `&str` in the current scope", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0599] Error:\u001b[0m no method named `push` found for reference `&str` in the current scope", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_113:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mh\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m'\u001b[0m\u001b[38;5;249m\\\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m'\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m method not found in `&str`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let mut text = \"hello\";\n", + "text[0] = 'H';\n", + "text.push('\\n');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A `String` or `&str`’s .`len()` method returns its length. The length is measured in bytes, not characters:" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [], + "source": [ + "assert_eq!(\"ಠ_ಠ\".len(), 7);\n", + "assert_eq!(\"ಠ_ಠ\".chars().count(), 3);\n", + "\n", + "\n", + "let first_char = \"ಠ_ಠ\".chars().nth(0).unwrap();\n", + "assert_eq!(first_char, 'ಠ');" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `String`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A `String` has a resizable buffer holding UTF-8 text. \n", + "* The buffer is allocated on the heap, so it can resize its buffer as needed or requested. \n", + "* You can think of a `String` as a `Vec` that is guaranteed to hold well-formed UTF-8; in fact, this is how `String` is implemented." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Creating Strings" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Creating an empty `String` with `String::new()`" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "metadata": {}, + "outputs": [], + "source": [ + "let empty_string = String::new();\n", + "assert_eq!(empty_string.len(), 0);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Converting &str to String with `to_string()` or `String::from()` or `into()`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "let error_message: String = \"too many arguments\".to_string();\n", + "let warning_message: String = String::from(\"deprecated function\");\n", + "let info_message: String = \"operation successful\".into();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Using `format!` to create formatted strings" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "let location = format!(\"{}°{:02}′{:02}″N\", 24, 5, 23);\n", + "assert_eq!(location, \"24°05′23″N\".to_string());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Concatenating vector of strings with `concat()` or `join()`" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"venividivici\"" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let words: Vec<&str> = vec![\"veni\", \"vidi\", \"vici\"];\n", + "words.concat()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"veni, vidi, vici\"" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "words.join(\", \")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* If you have a vector of UTF-8 bytes, you can create a `String` from it with the from_utf8 method:" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "💖\n" + ] + } + ], + "source": [ + "// some bytes, in a vector\n", + "let sparkle_heart = vec![240, 159, 146, 150];\n", + "\n", + "// We know these bytes are valid, so we'll use `unwrap()`.\n", + "let sparkle_heart = String::from_utf8(sparkle_heart).unwrap();\n", + "\n", + "assert_eq!(\"💖\", sparkle_heart);\n", + "\n", + "println!(\"{}\", sparkle_heart);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### String like Vec" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"one!\\ntwo!\\nthree!\\n\"" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut text = \"one\".to_string();\n", + "\n", + "text.push('!');\n", + "text.push('\\n');\n", + "text.push_str(\"two!\\n\");\n", + "text.push_str(\"three!\\n\");\n", + "\n", + "text" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"one!\\ntwo!\\nthree!\"" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.pop();\n", + "text" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "| | Vec | String |\n", + "| ------------------------------------------------ | ------------------- | ------------------- |\n", + "| Automatically frees buffers | Yes | Yes |\n", + "| Growable | Yes | Yes |\n", + "| `::new()` and `::with_capacity()` static methods | Yes | Yes |\n", + "| `.reserve()` and `.capacity()` methods | Yes | Yes |\n", + "| `.push()` and `.pop()` methods | Yes | Yes |\n", + "| Range syntax `v[start..stop]` | Yes, returns `&[T]` | Yes, returns `&str` |\n", + "| Automatic conversion | `&Vec` to `&[T]` | `&String` to `&str` |\n", + "| Inherits methods | From `&[T]` | From `&str` |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Comparing Strings\n", + "\n", + "* Strings support the `==` and `!=` operators" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [], + "source": [ + "assert!(\"ONE\".to_lowercase() == \"one\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Deref\n", + "\n", + "* String implements `Deref`, and so inherits all methods `str`\n", + "* You can use string slices (`&str`) and `String` interchangeably in many cases\n", + "* You can also pass a `&String` where a `&str` is expected" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\"Rusty Cage\"\n" + ] + } + ], + "source": [ + "fn takes_str(text: &str) {\n", + " println!(\"{:?}\", text);\n", + "}\n", + "\n", + "let s = String::from(\"Rusty Cage\");\n", + "takes_str(&s);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Using Strings" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "veni\n", + "vidi\n", + "vici\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "assert!(\"peanut\".contains(\"nut\"));\n", + "assert_eq!(\"ಠ_ಠ\".replace(\"ಠ\", \"■\"), \"■_■\");\n", + "assert_eq!(\" clean\\n\".trim(), \"clean\");\n", + "\n", + "for word in \"veni, vidi, vici\".split(\", \") {\n", + " assert!(word.starts_with(\"v\"));\n", + " println!(\"{word}\");\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Other String Like Types\n", + "\n", + "Rust’s solution is to offer a few string-like types for these situations:\n", + "\n", + "* Stick to `String` and `&str` for Unicode text.\n", + "* When working with filenames, use `std::path::PathBuf` and `&Path` instead.\n", + "* When working with binary data that isn’t character data at all, use `Vec` and `&[u8]`.\n", + "* When working with environment variable names and command-line arguments in the native form presented by the operating system, use `OsString` and `&OsStr`.\n", + "* When interoperating with C libraries that use null-terminated strings, use `std::ffi::CString` and `&CStr`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Real-World Examples" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### String Manipulation" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "File extension: txt\n" + ] + } + ], + "source": [ + "let filename = \"example.txt\";\n", + "let extension: &str = &filename[filename.len() - 3..];\n", + "println!(\"File extension: {}\", extension);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Data Processing" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average: 2.62\n" + ] + } + ], + "source": [ + "fn calculate_average(slice: &[f64]) -> f64 {\n", + " let sum: f64 = slice.iter().sum();\n", + " sum / slice.len() as f64\n", + "}\n", + "\n", + "fn main() {\n", + " let data: [f64; 6] = [2.5, 3.0, 1.5, 4.0, 2.0, 3.5];\n", + " let slice: &[f64] = &data[1..5];\n", + " let average = calculate_average(slice);\n", + " println!(\"Average: {:.2}\", average);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Text Tokenization" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Words: [\"Rust\", \"is\", \"a\", \"systems\", \"programming\", \"language.\"]\n" + ] + } + ], + "source": [ + "fn tokenize_sentence(sentence: &str) -> Vec<&str> {\n", + " sentence.split_whitespace().collect()\n", + "}\n", + "\n", + "let text: &str = \"Rust is a systems programming language.\";\n", + "let words = tokenize_sentence(text);\n", + "println!(\"Words: {:?}\", words);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Binary Data Handling" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "fn parse_header(header_data: &[u8]) {\n", + " // Parse the binary header data here\n", + "}\n", + "\n", + "fn main() {\n", + " let binary_data: [u8; 16] = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x52, 0x75, 0x73, 0x74, 0x21, 0x00, 0x00, 0x0A, 0x0D];\n", + " let header_slice: &[u8] = &binary_data[0..8];\n", + " parse_header(header_slice);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Memory mapping" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Read 30 bytes: [84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 102, 105, 108, 101, 32, 119, 105, 116, 104, 32, 115, 111, 109, 101, 32, 116, 101, 120, 116, 46]\n" + ] + } + ], + "source": [ + "use std::fs::File;\n", + "use std::io::{self, Read};\n", + "\n", + "fn main() -> io::Result<()> {\n", + " let mut file = File::open(\"file.txt\")?;\n", + " let mut buffer = [0; 1024];\n", + "\n", + " // read a portion of the file to the buffer\n", + " let bytes_read = file.read(&mut buffer)?;\n", + "\n", + " // process the buffer as a slice\n", + " let slice = &buffer[0..bytes_read];\n", + " println!(\"Read {} bytes: {:?}\", bytes_read, slice);\n", + "\n", + " Ok(())\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/05_structs.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/05_structs.ipynb new file mode 100644 index 0000000..4958b07 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/05_structs.ipynb @@ -0,0 +1,564 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Structs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A *struct* assembles several values of assorted types together into a single value, so you can deal with them as a unit. \n", + "* Given a struct, you can read and modify its individual components. \n", + "* A struct can have methods associated with it that operate on its components." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Named-Field Structs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Declaring a Struct" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "// A rectangle of eight-bit grayscale pixels.\n", + "struct GrayscaleMap {\n", + " pixels: Vec,\n", + " size: (usize, usize)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating a Struct Value" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "let width = 800;\n", + "let height = 600;\n", + "\n", + "let image = GrayscaleMap {\n", + " pixels: vec![0; width * height],\n", + " size: (width, height)\n", + "};" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "fn create_image(size: (usize, usize)) -> GrayscaleMap {\n", + " let pixels = vec![0; size.0 * size.1];\n", + " GrayscaleMap{pixels, size} // struct expression\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Accessing Struct Fields" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "let image = create_image((800, 600));\n", + "\n", + "assert!(image.size == (800, 600));\n", + "assert!(image.pixels.len() == image.size.0 * image.size.1);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Visibility of Struct" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "* Structs are private by default, visible only in the module where they’re declared. \n", + "* You can make a struct visible outside its module by prefixing its definition with `pub`" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "// A rectangle of eight-bit grayscale pixels.\n", + "pub struct GrayscaleMap {\n", + " pub pixels: Vec,\n", + " pub size: (usize, usize)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tuple-Like Structs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The second kind of struct type is called a tuple-like struct, because it resembles a tuple:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "The type of the variable image was redefined, so was lost.\n" + ] + } + ], + "source": [ + "struct BoundingBox(usize, usize);\n", + "\n", + "let image_bbox = BoundingBox(800, 600);\n", + "\n", + "assert!(image_bbox.0 * image_bbox.1 == 480_000);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Individual elements of a tuple-like struct may be public or not:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "pub struct BoundingBox(pub usize, pub usize);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tuple-Like Struct as Newtypes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Tuple-like structs allow you to create types that behave like tuples but have a name and can implement traits\n", + "* This allows you to create newtypes for stricter type checking\n", + "* This is a workaround for the *orphan rule* - [New Type Pattern](https://doc.rust-lang.org/book/ch20-02-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "The type of the variable image_bbox was redefined, so was lost.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Distance traveled: 50 kilometers\n" + ] + } + ], + "source": [ + "struct Distance(f64);\n", + "struct Velocity(f64);\n", + "struct Time(f64);\n", + "\n", + "// A newtype for stricter type checking\n", + "fn calculate_distance(velocity: Velocity, time: Time) -> Distance {\n", + " Distance(velocity.0 * time.0)\n", + "}\n", + "\n", + "let velocity = Velocity(10.0);\n", + "let time = Time(5.0);\n", + "let distance = calculate_distance(velocity, time);\n", + "\n", + "println!(\"Distance traveled: {} kilometers\", distance.0);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unit-Like Structs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The third kind of struct is a little obscure: it declares a struct type with no elements at all:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "struct Onesuch;\n", + "\n", + "let o = Onesuch;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* This allows to create types that can act as tags or markers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining Methods - `impl`" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "pub struct Queue {\n", + " older: Vec,\n", + " younger: Vec\n", + "}\n", + "\n", + "impl Queue {\n", + " pub fn push(&mut self, c: char) {\n", + " self.younger.push(c);\n", + " }\n", + "\n", + " pub fn pop(&mut self) -> Option {\n", + " if self.older.is_empty() {\n", + " if self.younger.is_empty() {\n", + " return None;\n", + " }\n", + "\n", + " use std::mem::swap;\n", + " swap(&mut self.older, &mut self.younger);\n", + " self.older.reverse();\n", + " }\n", + "\n", + " self.older.pop()\n", + " }\n", + "\n", + " pub fn is_empty(&self) -> bool {\n", + " self.older.is_empty() && self.younger.is_empty()\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "let mut q = Queue{ older: Vec::new(), younger: Vec::new() };\n", + "\n", + "q.push('@');\n", + "q.push('$');\n", + "assert!(q.pop() == Some('@'));\n", + "\n", + "q.push('*');\n", + "assert!(q.pop() == Some('$'));\n", + "assert!(q.pop() == Some('*'));\n", + "assert!(q.pop() == None);\n", + "\n", + "assert!(q.is_empty());" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "impl Queue {\n", + " pub fn split(self) -> (Vec, Vec) {\n", + " (self.older, self.younger)\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "let mut q = Queue { older: Vec::new(), younger: Vec::new() };\n", + "q.push('Q');\n", + "q.push('U');\n", + "assert!(q.pop() == Some('Q'));\n", + "q.push('X');\n", + "\n", + "let (older, younger) = q.split();\n", + "\n", + "assert_eq!(older, vec!['U']);\n", + "assert_eq!(younger, vec!['X']);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `new` Method" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Constructor function - doesn't take `self` as an argument (**static function**)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "impl Queue {\n", + " pub fn new() -> Self {\n", + " Queue { older: Vec::new(), younger: Vec::new() }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "let mut q = Queue::new();\n", + "\n", + "q.push('a');\n", + "q.push('b');\n", + "q.push('c');\n", + "\n", + "assert_eq!(q.pop(), Some('a'));\n", + "assert_eq!(q.pop(), Some('b'));\n", + "assert_eq!(q.pop(), Some('c'));\n", + "assert_eq!(q.pop(), None);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generic Structs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Generics allow you to write flexible, reusable functions and types that can work with any data type. Instead of specifying concrete types, you use type parameters, which are placeholders for types that will be specified when the function or type is used.\n", + "* Generics use parameters passed in angle brackets to specify the types they work with. For example, `fn example(param: T) {}` uses `T` as a generic type parameter." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "pub struct Stack {\n", + " items: Vec\n", + "}\n", + "\n", + "impl Stack {\n", + " pub fn new() -> Stack {\n", + " Stack{ items: Vec::new() }\n", + " }\n", + "\n", + " pub fn push(&mut self, item: T) {\n", + " self.items.push(item)\n", + " }\n", + "\n", + " pub fn pop(&mut self) -> Option {\n", + " self.items.pop()\n", + " }\n", + "\n", + " pub fn is_empty(&self) -> bool {\n", + " self.items.is_empty()\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* For static method calls, you can supply the type parameter explicitly using the turbofish `::<>` notation:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "let s = Stack::::new();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* But in practice, Rust can figure it out for you:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "let mut stack_1 = Stack::new();\n", + "let mut stack_2 = Stack::new();\n", + "\n", + "stack_1.push(\"Hello\");\n", + "stack_2.push(3.1415);\n", + "\n", + "stack_1.push(\"World\");\n", + "stack_2.push(2.71);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Deriving Common Traits for Structs" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Structs are not copyable or comparable by default\n", + "* You can derive the `Copy` and `Clone` traits for a struct type, but only if all its fields are copyable or clonable\n", + "* You can derive the `PartialEq` and `Eq` traits for a struct type, but only if all its fields are comparable for equality\n", + "* You can derive `Debug` for a struct type, but only if all its fields are printable" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Copy, Clone, Debug, PartialEq)]\n", + "struct Point {\n", + " x: f64,\n", + " y: f64\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pt = Point { x: 42.42, y: 1.0 }\n", + "other_pt = Point { x: 0.0, y: 1.0 }\n" + ] + } + ], + "source": [ + "let mut pt = Point{ x: 0.0, y: 1.0 };\n", + "\n", + "let other_pt = pt;\n", + "assert!(pt == other_pt);\n", + "\n", + "pt.x = 42.42;\n", + "assert!(pt != other_pt);\n", + "\n", + "println!(\"pt = {:?}\", pt);\n", + "println!(\"other_pt = {:?}\", other_pt);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/06_lifetimes-annotations.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/06_lifetimes-annotations.ipynb new file mode 100644 index 0000000..8eb6b62 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/06_lifetimes-annotations.ipynb @@ -0,0 +1,1742 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Lifetimes & Annotations" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "## Segments of memory in Rust\n", + "\n", + "* **Stack** - used to hold state related to currently executing functions:\n", + " - The parameters passed to function\n", + " - Local variables defined within function\n", + " - Temporary values calculated within function\n", + " - The return address of function" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The sum is: 30\n" + ] + } + ], + "source": [ + "fn add(x: i32, y: i32) -> i32 {\n", + " x + y\n", + "}\n", + "\n", + "fn caller() -> i32 {\n", + " let a = 10;\n", + " let b = 20;\n", + " let result = add(a, b);\n", + " result\n", + "}\n", + "\n", + "fn main() {\n", + " let sum = caller();\n", + " println!(\"The sum is: {}\", sum);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "* **Heap** - for dynamically allocated memory controlled via smart pointers or data structures (`Vec`, `String`, `Box`, etc)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Text: Hello, Rust!, Number on heap: 42\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let text = String::from(\"Hello, Rust!\");\n", + " let number_on_heap = Box::new(42);\n", + "\n", + " println!(\"Text: {}, Number on heap: {}\", text, number_on_heap);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* **Static** - static variables with fixed location in memory for the duration of the program" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, World!\n" + ] + } + ], + "source": [ + "static GREETINGS: &str = \"Hello, World!\";\n", + "\n", + "fn main() {\n", + " println!(\"{}\", GREETINGS);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scopes and Lifetimes\n", + "\n", + "The lifetime of a variable is the region of code where that variable is valid. In Rust, lifetimes are often associated with references to ensure memory safety.\n", + "\n", + "The **lifetime** of an item on stack is the period where item is guaranteed to stay in the same place in memory. This is exactly the period where **reference** (pointer) to that item **is valid**.\n", + "The lifetime starts when item is created, and extends to where is:\n", + "\n", + "* **dropped** (goes out of scope) or\n", + "* **moved** (ownership transferred to another variable)\n", + "\n", + "```rust" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Value 1: Value { content: 100 }, Value 2: Value { content: 200 }\n", + "Consuming value: Value { content: 200 }\n" + ] + } + ], + "source": [ + "#[derive(Debug)]\n", + "struct Value {\n", + " content: i32\n", + "}\n", + "\n", + "fn consume_value(val: Value) {\n", + " println!(\"Consuming value: {:?}\", val);\n", + "}\n", + "\n", + "fn main() {\n", + " let val1 = Value { content: 100 }; // val1 is created here\n", + " let val2 = Value { content: 200 }; // val2 is created here\n", + " println!(\"Value 1: {:?}, Value 2: {:?}\", val1, val2);\n", + "\n", + " consume_value(val2); // val2 is moved here\n", + "} // val1 is dropped here\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Borrowchecker tracks lifetimes of all references in the program to ensure that references do not outlive the data they point to." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example: Dangling Reference 1" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "`val` does not live long enough", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0597] Error:\u001b[0m `val` does not live long enough", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_6:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m4 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mv\u001b[0m\u001b[38;5;68ma\u001b[0m\u001b[38;5;68ml\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mV\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m3\u001b[0m\u001b[38;5;249m0\u001b[0m\u001b[38;5;249m0\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m binding `val` declared here", + " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249mh\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m borrowed value does not live long enough", + " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100m}\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m `val` dropped here while still borrowed", + " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mV\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m?\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;37mv\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m borrow later used here", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "fn main() {\n", + " let v: &Value;\n", + " {\n", + " let val = Value { content: 300 };\n", + " v = &val; // Error: val does not live long enough\n", + " }\n", + " println!(\"Value: {:?}\", v);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example: Dangling Reference 2" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "temporary value dropped while borrowed", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0716] Error:\u001b[0m temporary value dropped while borrowed", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_7:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m12 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249m3\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;54mV\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54m{\u001b[0m\u001b[38;5;54mc\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m5\u001b[0m\u001b[38;5;54m0\u001b[0m\u001b[38;5;54m0\u001b[0m\u001b[38;5;54m}\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;100m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m-\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m-\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mk\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mj\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m \u001b[38;5;100m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m creates a temporary value which is freed while still in use", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m temporary value is freed at the end of this statement", + " \u001b[38;5;246m13 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68mf\u001b[0m\u001b[38;5;68m_\u001b[0m\u001b[38;5;68mc\u001b[0m\u001b[38;5;68mo\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m borrow later used here", + "\u001b[38;5;246m────╯\u001b[0m" + ] + } + ], + "source": [ + "fn return_ref_(v: &Value) -> &i32 {\n", + " return &v.content;\n", + "}\n", + "\n", + "fn main() {\n", + " { // valid usage\n", + " let val = Value { content: 400 };\n", + " let ref_content: &i32 = return_ref_(&val);\n", + " println!(\"Reference to content: {}\", ref_content);\n", + " }\n", + "\n", + " let ref_content: &i32 = return_ref_(&Value{content: 500}); // invalid usage - temporary value does not live long enough - borrowchecker rejects the code\n", + " println!(\"Reference to content: {}\", ref_content);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Non-lexical Lifetimes (NLL)\n", + "\n", + "* NLL is a feature that allows the Rust borrow checker to be more flexible in how it determines the lifetimes of references.\n", + "* If the compiler can prove to itself that there is no use of a reference beyond a certain point in the code, then it treats the endpoint of the reference’s lifetime as the last place it’s used, rather than at the end of the enclosing scope" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Text: HELLO world\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let mut text = String::from(\"hello world\");\n", + "\n", + " // create a mutable reference to a portion of the string\n", + " let greeting = &mut text[..5];\n", + " greeting.make_ascii_uppercase();\n", + " // no use of grreeting after this point\n", + "\n", + " let ref_to_text = &text; // now this is allowed\n", + " println!(\"Text: {}\", ref_to_text);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Returning references" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* You can’t return a reference to local variable that only lives inside a function - it would be a *dangling reference*" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot return reference to local variable `s`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0515] Error:\u001b[0m cannot return reference to local variable `s`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_9:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;54ms\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m returns a reference to data owned by the current function", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "fn create_dangling_reference() -> &'static str {\n", + " let s = String::from(\"Hello, world!\");\n", + " &s\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* You can return a reference to static variable (like string literals) - return type needs `'static` *lifetime annotation*" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn get_greetings() -> &'static str {\n", + " let s = \"Hello, world!\";\n", + " s\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Lifetime annotations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Most of the time, Rust takes care of lifetimes for you, but sometimes, it needs a bit of extra help. This extra help is called a **lifetime annotation**, which means “extra lifetime information.”\n", + "* Liftetime annotations don't change the lifetimes of any references, they just describe the relationships of the lifetimes of multiple references.\n", + "* Lifetime annotations syntax: the names of lifetime parameters must start with an apostrophe (') and are usually all lowercase and very short, like this: `'a`, `'b`, and so on. But the names of lifetime parameters are arbitrary and have no meaning." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Inferring lifetimes\n", + "\n", + "* Rust has a **lifetime elision** rules, which are a set of three rules that the compiler follows to figure out what lifetimes references have when there aren't explicit annotations.\n", + "* 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.\n", + "\n", + "* **The first rule**: each parameter that is a reference gets its own lifetime parameter:\n", + " * a function with one parameter gets one lifetime parameter: `fn foo<'a>(x: &'a i32);` \n", + " * a function with two parameters gets two separate lifetime parameters:`fn foo<'a, 'b>(x: &'a i32, y: &'b i32);` and so on.\n", + "* **The second rule**: if there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters: \n", + " * `fn foo<'a>(x: &'a i32) -> &'a i32;`\n", + "* **The third rule**: 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: \n", + " * `fn foo<'a, 'b>(&'a self, x: &'b i32) -> &'a i32;`" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn print_value(v: &Value) {\n", + " println!(\"Value.content: {}\", v.content);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "can be explicitly written as:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Value.content: 600\n" + ] + } + ], + "source": [ + "fn print_value<'a>(v: &'a Value) {\n", + " println!(\"Value.content: {}\", v.content);\n", + "}\n", + "\n", + "let val = Value { content: 600 };\n", + "print_value(&val);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Static Lifetime" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "What happens when we have no input lifetimes, but we want to return a reference? In this case, we can use the `'static` lifetime, which means that the reference can live for the entire duration of the program." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The answer is: 42\n" + ] + } + ], + "source": [ + "static THE_ANSWER: Value = Value { content: 42 };\n", + "\n", + "fn the_answer() -> &'static Value {\n", + " &THE_ANSWER\n", + "}\n", + "\n", + "fn main() {\n", + " let answer = the_answer();\n", + " println!(\"The answer is: {}\", answer.content);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The Rust compiler guarantees that a `static` item always has the same address for the entire duration of the program and never moves. This means that a reference to a static item has a ``'static`` lifetime.\n", + "* Other objects with `'static` lifetime:\n", + " * String literals\n", + " * Constants - but not if they implement `Drop` trait\n", + " * Leaked objects - using `Box::leak` or similar functions\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Value.content: 665\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let on_heap = Box::new(Value{content: 665});\n", + " let static_ref: &'static Value = Box::leak(on_heap);\n", + " print_value(static_ref);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Returning references" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When a function takes a single reference as an argument, and returns a single reference, Rust assumes that the two must have the same lifetime (see *the second rule*)." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn ref_to_min(data: &mut [i32]) -> &mut i32 {\n", + " // let min = data.iter_mut().min().unwrap();\n", + " \n", + " let mut index: usize = 0;\n", + " for (i, value) in data.iter().enumerate() {\n", + " if *value < data[index] {\n", + " index = i;\n", + " }\n", + " } \n", + "\n", + " &mut data[index]\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[42, 665, 0, 44, 99]\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let mut data = [42, 665, 1, 44, 99];\n", + " let the_smallest: &mut i32 = ref_to_min(&mut data);\n", + " *the_smallest = 0;\n", + "\n", + " println!(\"{:?}\", data);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "The variable `s` contains a reference with a non-static lifetime so\ncan't be persisted. You can prevent this error by making sure that the\nvariable goes out of scope - i.e. wrapping the code in {}.", + "output_type": "error", + "traceback": [ + "\u001b[31mError:\u001b[0m The variable `s` contains a reference with a non-static lifetime so", + "can't be persisted. You can prevent this error by making sure that the", + "variable goes out of scope - i.e. wrapping the code in {}." + ] + } + ], + "source": [ + "let s;\n", + "\n", + "{\n", + " let mut data = [9, 4, 1, 0, 1, 4, 9];\n", + " s = ref_to_min(&mut data);\n", + "}\n", + "\n", + "println!(\"{}\", *s);" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "## Structs Containing References (Annotations)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "missing lifetime specifier", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0106] Error:\u001b[0m missing lifetime specifier", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_27:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249m3\u001b[0m\u001b[38;5;249m2\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m expected named lifetime parameter", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "struct View {\n", + " value: &i32\n", + "}\n", + "\n", + "fn main() {\n", + " let mut data = [42, 665, 1, 44, 99];\n", + " let the_smallest: &mut i32 = ref_to_min(&mut data);\n", + "\n", + " let view = View { value: the_smallest };\n", + " println!(\"{}\", view.value);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "Whenever a reference type appears inside another type’s definition, you must write out its lifetime." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], + "source": [ + "struct View<'a> {\n", + " value: &'a i32\n", + "}\n", + "\n", + "fn main() {\n", + " let mut data = [42, 665, 1, 44, 99];\n", + " let the_smallest: &mut i32 = ref_to_min(&mut data);\n", + " let view = View { value: the_smallest };\n", + " println!(\"{}\", view.value);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the `View` type has a lifetime, just as reference types do. Each value you create of type `Value` gets a fresh lifetime `'a`, which becomes constrained by how you use the value. The lifetime of any reference you store in `ref_value` had better enclose `'a`, and `'a` must outlast the lifetime of wherever you store the `View`." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "struct `View<'_>` has no field named `ref_value`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0560] Error:\u001b[0m struct `View<'_>` has no field named `ref_value`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_29:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m12 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mV\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mf\u001b[0m\u001b[38;5;54m_\u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `View<'_>` does not have this field", + "\u001b[38;5;246m────╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "no field `ref_value` on type `View<'_>`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0609] Error:\u001b[0m no field `ref_value` on type `View<'_>`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_29:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m13 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mf\u001b[0m\u001b[38;5;54m_\u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m unknown field", + "\u001b[38;5;246m────╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "no field `ref_value` on type `View<'_>`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0609] Error:\u001b[0m no field `ref_value` on type `View<'_>`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_29:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m17 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m[\u001b[0m\u001b[38;5;249m0\u001b[0m\u001b[38;5;249m]\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mf\u001b[0m\u001b[38;5;54m_\u001b[0m\u001b[38;5;54mv\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m unknown field", + "\u001b[38;5;246m────╯\u001b[0m" + ] + } + ], + "source": [ + "struct View<'a> {\n", + " value: &'a i32\n", + "}\n", + "\n", + "#[allow(unused_variables)]\n", + "fn main() {\n", + " let mut bag: Vec = Vec::new();\n", + " {\n", + " let mut data = [42, 665, 1, 44, 99];\n", + " let the_smallest: &mut i32 = ref_to_min(&mut data);\n", + "\n", + " let view = View { ref_value: the_smallest };\n", + " println!(\"{}\", view.ref_value);\n", + " bag.push(view);\n", + " }\n", + "\n", + " println!(\"{}\", bag[0].ref_value);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When we want to place `View` inside of another `struct`, we must specify the lifetime of the reference it contains:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "struct Record {\n", + " v: View<'static>\n", + "}\n", + "\n", + "// or\n", + "\n", + "struct MinView<'a> {\n", + " v: View<'a>\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A type’s lifetime parameters always reveal whether it contains references with interesting (that is, non-`'static`) lifetimes, and what those lifetimes can be" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn get_min<'a>(data: &'a mut [i32]) -> MinView<'a> {\n", + " let min: &'a mut i32 = data.iter_mut().min().unwrap();\n", + " MinView { v: View { value: min } }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Without looking into the definition of the `MinView` type at all, we can tell that, if we receive a `MinView` from `get_min`, whatever references it contains must point into the input slice we passed in, and nowhere else (except perhaps at 'static values)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Distinct Lifetimes\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "#[allow(unused_variables)]\n", + "struct Record<'a> {\n", + " x: &'a i32,\n", + " y: &'a i32\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "`y` does not live long enough", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0597] Error:\u001b[0m `y` does not live long enough", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_3:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m 6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68my\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m6\u001b[0m\u001b[38;5;249m6\u001b[0m\u001b[38;5;249m5\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m binding `y` declared here", + " \u001b[38;5;246m 7 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;54my\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m borrowed value does not live long enough", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m 9 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100m}\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m `y` dropped here while still borrowed", + " \u001b[38;5;246m10 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;37mr\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m borrow later used here", + "\u001b[38;5;246m────╯\u001b[0m" + ] + } + ], + "source": [ + "#[allow(unused_variables)]\n", + "fn main() {\n", + " let x = 42;\n", + " let r: &i32;\n", + " {\n", + " let y = 665;\n", + " let local = Record { x: &x, y: &y };\n", + " r = local.x;\n", + " }\n", + " println!(\"{}\", r);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This code doesn’t create any dangling pointers. The reference to `y` stays in `local`, which goes out of scope before `y` does. The reference to `x` ends up in `r`, which doesn’t outlive `x`.\n", + "\n", + "If you try to compile this, however, Rust will complain that `y` does not live long enough, even though it clearly does. Why is Rust worried? If you work through the code carefully, you can follow its reasoning:\n", + "\n", + "* Both fields of `Record` are references with the same lifetime `'a`, so Rust must find a single lifetime that works for both `local.x` and `local.y`.\n", + "* We assign `r = local.x`, requiring `'a` to enclose `r’s` lifetime.\n", + "* We initialized `local.y` with `&y`, requiring `'a` to be no longer than `y`’s lifetime.\n", + "\n", + "These constraints are impossible to satisfy: no lifetime is shorter than `y`’s scope, but longer than `r`’s." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The problem arises because both references in `Record` have the same lifetime `'a`. Changing the definition of `Record` to let each reference have a distinct lifetime fixes everything:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "struct Record<'a, 'b> {\n", + " x: &'a i32,\n", + " y: &'b i32,\n", + "}\n", + "\n", + "impl<'a, 'b> Record<'a, 'b> {\n", + " fn print(&self) {\n", + " println!(\"x: {}, y: {}\", self.x, self.y);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x: 42, y: 665\n", + "42\n" + ] + } + ], + "source": [ + "#[allow(unused_variables)]\n", + "fn main() {\n", + " let x = 42;\n", + " let r;\n", + " {\n", + " let y = 665;\n", + " let local = Record { x: &x, y: &y };\n", + " r = local.x;\n", + " local.print();\n", + " }\n", + " println!(\"{}\", r);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With this definition, `local.x` and `local.y` have independent lifetimes. What we do with `local.x` has no effect on what we store in `local.y`, so it’s easy to satisfy the constraints now: `'a` can simply be `r`’s lifetime, and `'b` can be `local`’s. (`y`’s lifetime would work too for `'b`, but Rust tries to choose the smallest lifetime that works). " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Function signatures can have similar effects. Suppose we have a function like this:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn foobar_tight<'a>(x: &'a i32, y: &'a i32) -> &'a i32 { // perhaps too restrictive\n", + " x\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn foobar<'a, 'b>(x: &'a i32, y: &'b i32) -> &'a i32 { // looser\n", + " x\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "Try the simplest possible definition first, and then loosen restrictions until the code compiles. Since Rust won’t permit the code to run unless it’s safe, simply waiting to be told when there’s a problem is a perfectly acceptable tactic." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Anonymous Lifetimes\n", + "\n", + "The anonymous lifetime '_ allows you to mark an elided lifetime as being present, without having to fully restore all of the lifetime names:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Minimum value is: 1 at index 2\n" + ] + } + ], + "source": [ + "struct MinElement<'a> {\n", + " index: usize,\n", + " value: &'a i32\n", + "}\n", + "\n", + "fn find_min(data: &[i32]) -> MinElement<'_> {\n", + " let mut index: usize = 0;\n", + " for (i, value) in data.iter().enumerate() {\n", + " if *value < data[index] {\n", + " index = i;\n", + " }\n", + " } \n", + "\n", + " MinElement { index, value: &data[index] }\n", + "}\n", + "\n", + "fn main() {\n", + " let data = [42, 665, 1, 44, 99];\n", + " let min_view = find_min(&data);\n", + " println!(\"Minimum value is: {} at index {}\", min_view.value, min_view.index);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Borrow Rules\n", + "\n", + "* There are three ways to have access to data in Rust:\n", + " \n", + " * No references (**ownership**) - CRUD - Create, Read, Update, Drop\n", + " * only the item's owner can **move** the item\n", + " \n", + " * A **mutable reference** can read from underlying data and also modify it \n", + " * A reference that can only read from underlying data (**shared reference**)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Moving Ownership" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* You cannot move out an item that is behind a mutable reference!!!" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "#[derive(Debug)]\n", + "struct Item {\n", + " content: i32\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot move out of `*item` which is behind a mutable reference", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0507] Error:\u001b[0m cannot move out of `*item` which is behind a mutable reference", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_8:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100m*\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider removing the dereference here: ``", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m move occurs because `*item` has type `Option`, which does not implement the `Copy` trait", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "\n", + "fn replace(item: &mut Option, new_item: Item) -> Option {\n", + " let previous_value = *item;\n", + " *item = Some(new_item);\n", + " previous_value\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* We can, however, replace the item behind a mutable reference with a new item, and take ownership of the previous item. This is done using the `std::mem::replace` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Old item: Some(Item { content: 10 }), New item: Some(Item { content: 20 })\n" + ] + } + ], + "source": [ + "fn replace(item: &mut Option, new_item: Item) -> Option {\n", + " std::mem::replace(item, Some(new_item))\n", + "}\n", + "\n", + "fn main() {\n", + " let mut item_option: Option = Some(Item { content: 10 });\n", + "\n", + " let old_item = replace(&mut item_option, Item { content: 20 });\n", + " println!(\"Old item: {:?}, New item: {:?}\", old_item, item_option);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Borrow Rules" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are two key rules to remember about borrowing references in Rust:\n", + "\n", + "1. References must always be valid. This means they cannot outlive the data they point to.\n", + " * scope of reference ≤ lifetime of data it points to\n", + "2. You can have either:\n", + " * one mutable reference to the item or\n", + " * any number of immutable references to the item at a time.\n", + "\n", + "The borrowing rules allow the compiler to make better decisions around aliasing: tracking when two different pointers may or may not refer to the same underlying item in memory. This enables optimizations that would otherwise be unsafe if aliasing were possible." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n" + ] + } + ], + "source": [ + "fn extend(vec: &mut Vec, slice: &[i32]) {\n", + " for item in slice {\n", + " vec.push(*item);\n", + " }\n", + "}\n", + "\n", + "let mut data = vec![1, 2, 3, 4];\n", + "let exta_data_1 = vec![5, 6, 7];\n", + "let exta_data_2 = vec![8, 9, 10];\n", + "\n", + "extend(&mut data, &exta_data_1);\n", + "extend(&mut data, &exta_data_2);\n", + "\n", + "println!(\"{:?}\", data);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's try extend a vector with a slice of its own elements:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "mismatched types", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0308] Error:\u001b[0m mismatched types", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_26:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;100me\u001b[0m\u001b[38;5;100mx\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100mn\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;68m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m arguments to this function are incorrect", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m help: consider mutably borrowing here: `&mut `", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m expected `&mut Vec`, found `Vec`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "unused variable: `y`", + "output_type": "error", + "traceback": [ + "\u001b[31m[unused_variables] Error:\u001b[0m unused variable: `y`" + ] + }, + { + "ename": "Error", + "evalue": "unused variable: `y`", + "output_type": "error", + "traceback": [ + "\u001b[31m[unused_variables] Error:\u001b[0m unused variable: `y`" + ] + } + ], + "source": [ + "extend(data, &data);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Rust Rules for Mutation & Sharing\n", + "\n", + "* **Shared access** is **read-only access**. Values borrowed by shared references are read-only. Across the lifetime of a shared reference, neither its referent, nor anything reachable from that referent, can be changed by anything. There exist no live mutable references to anything in that structure; its owner is held read-only; and so on. It’s immutable\n", + "\n", + "* **Mutable access** is **exclusive access**. A value borrowed by a mutable reference is reachable exclusively via that reference. Across the lifetime of a mutable reference, there is no other usable path to its referent, or to any value reachable from there. The only references whose lifetimes may overlap with a mutable reference are those you borrow from the mutable reference itself." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot assign to `x` because it is borrowed", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0506] Error:\u001b[0m cannot assign to `x` because it is borrowed", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_2:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;54mx\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `x` is borrowed here", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mx\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100m+\u001b[0m\u001b[38;5;100m=\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100m1\u001b[0m\u001b[38;5;100m0\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m`\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m`\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249md\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m `x` is assigned to here but it was already borrowed", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m8 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68m{\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68mx\u001b[0m\u001b[38;5;68m1\u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m borrow later used here", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "{\n", + " let mut x = 10;\n", + " let rx1 = &x;\n", + " let rx2 = &x; // OK: multiple shared borrows permitted\n", + "\n", + " x += 10; // Error: cannot assign to `x` because it is borrowed\n", + "\n", + " println!(\"{x}, {rx1}, {rx2}\");\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot borrow `x` as mutable because it is also borrowed as immutable", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0502] Error:\u001b[0m cannot borrow `x` as mutable because it is also borrowed as immutable", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_3:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100m&\u001b[0m\u001b[38;5;100mx\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m immutable borrow occurs here", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54mx\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m`\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m`\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m mutable borrow occurs here", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68m{\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68mx\u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m immutable borrow later used here", + "\u001b[38;5;246m───╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "cannot borrow `x` as immutable because it is also borrowed as mutable", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0502] Error:\u001b[0m cannot borrow `x` as immutable because it is also borrowed as mutable", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_3:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100m&\u001b[0m\u001b[38;5;100mm\u001b[0m\u001b[38;5;100mu\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100mx\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m`\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m`\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m mutable borrow occurs here", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;54m{\u001b[0m\u001b[38;5;54mx\u001b[0m\u001b[38;5;54m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68m{\u001b[0m\u001b[38;5;68mm\u001b[0m\u001b[38;5;68mx\u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m immutable borrow occurs here", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m mutable borrow later used here", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "{\n", + " let mut x = 10;\n", + " let rx = &x;\n", + " \n", + " let mx = &mut x; // Error: cannot borrow `x` as mutable because it is also borrowed as immutable\n", + "\n", + " println!(\"{x}, {rx}, {mx}\");\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot borrow `y` as mutable more than once at a time", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0499] Error:\u001b[0m cannot borrow `y` as mutable more than once at a time", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_4:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54my\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m first mutable borrow occurs here", + " \u001b[38;5;246m4 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100m&\u001b[0m\u001b[38;5;100mm\u001b[0m\u001b[38;5;100mu\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100my\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m second mutable borrow occurs here", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68m{\u001b[0m\u001b[38;5;68mm\u001b[0m\u001b[38;5;68my\u001b[0m\u001b[38;5;68m1\u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m first borrow later used here", + "\u001b[38;5;246m───╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "cannot borrow `y` as immutable because it is also borrowed as mutable", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0502] Error:\u001b[0m cannot borrow `y` as immutable because it is also borrowed as mutable", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_4:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100m&\u001b[0m\u001b[38;5;100mm\u001b[0m\u001b[38;5;100mu\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100my\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m mutable borrow occurs here", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;54m{\u001b[0m\u001b[38;5;54my\u001b[0m\u001b[38;5;54m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;68m{\u001b[0m\u001b[38;5;68mm\u001b[0m\u001b[38;5;68my\u001b[0m\u001b[38;5;68m1\u001b[0m\u001b[38;5;68m}\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m2\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m immutable borrow occurs here", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m mutable borrow later used here", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "{\n", + " let mut y = 20;\n", + " let my1 = &mut y;\n", + " let my2 = &mut y; // Error: cannot borrow as mutable more than once\n", + "\n", + " println!(\"{y}, {my1}, {my2}\");\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is **OK to reborrow a shared reference from a shared reference**:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let record = (42, 665);\n", + " let r = &record;\n", + " let r0 = &r.0; // OK: reborrowing shared as shared\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "unused variable: `m1`", + "output_type": "error", + "traceback": [ + "\u001b[31m[unused_variables] Error:\u001b[0m unused variable: `m1`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_7:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m4 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54m1\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m warning: unused variable: `m1`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: if this is intentional, prefix it with an underscore: `_m1`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "cannot borrow `r.1` as mutable, as it is behind a `&` reference", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0596] Error:\u001b[0m cannot borrow `r.1` as mutable, as it is behind a `&` reference", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0mcommand_7:1:1\u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider changing this to be a mutable reference: `mut `", + " \u001b[38;5;246m4 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m&\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54m.\u001b[0m\u001b[38;5;54m1\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `r` is a `&` reference, so the data it refers to cannot be borrowed as mutable", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;115mNote\u001b[0m: You can change an existing variable to mutable like: `let mut x = x;`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "{\n", + " let record = (42, 665);\n", + " let r = &record;\n", + " let m1 = &mut r.1; // Error: cannot reborrow as mutable because it is also borrowed as immutable\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can reborrow from a mutable reference:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "123, 665\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let mut record = (42, 665);\n", + " let m = &mut record;\n", + " let m0 = &mut m.0; \n", + " *m0 = 123; // OK: mutable borrow is unique\n", + "\n", + " let r1 = &m.1; // Ok: reborrowing mutable as shared\n", + " println!(\"{m0}, {r1}\");\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/07_enums_patterns.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/07_enums_patterns.ipynb new file mode 100644 index 0000000..a06342d --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/07_enums_patterns.ipynb @@ -0,0 +1,1784 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Enums" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Enums are a powerful feature in Rust that allow you to define a type by enumerating its possible variants.\n", + "\n", + "* Enums can have data associated with each variant, making them more flexible than simple enums in other languages.\n", + "\n", + "* Pattern matching with enums allows you to concisely handle different cases, making your code more expressive and safe." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## C-Style Enums" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "* The simplest enums are just a list of variants without any associated data.\n", + "* They are also called \"fieldless\" or \"unit\" variants.\n", + "* They work similar to enums in C/C++" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "direction = North\n" + ] + } + ], + "source": [ + "#[derive(Debug)]\n", + "enum Compass {\n", + " North,\n", + " East,\n", + " South,\n", + " West\n", + "}\n", + "\n", + "let direction = Compass::North;\n", + "\n", + "println!(\"direction = {:?}\", direction);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* In memory, values of C-style enums are stored as integers. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "use std::mem::size_of;\n", + "\n", + "assert!(size_of::() == 1);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Importing constructors" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "use self::Compass::*; // self - when enum type is defined in current module; otherwise name of a module\n", + "\n", + "let direction = North;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using Integral Values" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Debug)]\n", + "enum HttpStatus {\n", + " Ok = 200,\n", + " NotModified = 304,\n", + " NotFound = 404\n", + "}\n", + "\n", + "assert!(size_of::() == 2);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Casting to Integer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* \"Fieldless\" or \"unit\" variants can be converted to integral value using the `as` keyword." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "let ok_code = HttpStatus::Ok as i32;\n", + "assert!(ok_code == 200);\n", + "\n", + "assert!(HttpStatus::NotFound as i32 == 404);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Casting from Integer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Converting from integral value requires pattern matching." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "fn http_status_from_u32(status: u32) -> Option {\n", + " match status {\n", + " 200 => Some(HttpStatus::Ok),\n", + " 304 => Some(HttpStatus::NotModified),\n", + " 404 => Some(HttpStatus::NotFound),\n", + " _ => None\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Some(NotFound)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "http_status_from_u32(404)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "None" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "http_status_from_u32(1024)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deriving From Common Traits\n", + "\n", + "* As with structs, the compiler will implement features like the `==` operator for you, but you have to ask." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Copy, Clone, Debug, PartialEq)]\n", + "enum TimeUnit {\n", + " Seconds, Minutes, Hours, Days, Months, Years\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "assert!(TimeUnit::Seconds == TimeUnit::Seconds);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Implementing Methods for Enums" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* We can add methods to enums using `impl` blocks" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "impl TimeUnit {\n", + " /// Return the plural noun for this time unit.\n", + " fn plural(self) -> &'static str {\n", + " match self {\n", + " TimeUnit::Seconds => \"seconds\",\n", + " TimeUnit::Minutes => \"minutes\",\n", + " TimeUnit::Hours => \"hours\",\n", + " TimeUnit::Days => \"days\",\n", + " TimeUnit::Months => \"months\",\n", + " TimeUnit::Years => \"years\"\n", + " }\n", + " }\n", + "\n", + " /// Return the singular noun for this time unit.\n", + " fn singular(self) -> &'static str {\n", + " self.plural().trim_right_matches('s')\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"days\"" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let time_unit = TimeUnit::Days;\n", + "\n", + "time_unit.plural()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Enums With Data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The power of Rust enums lies in their ability to store additional data\n", + "* This allows to create complex data types that can hold different kinds of information" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Clone, Debug, PartialEq)]\n", + "enum Message {\n", + " Quit, // no data associated\n", + " Move{ x: i32, y: i32 }, // named fields - like a struct\n", + " Write(String), // single String\n", + " ChangeColor(i32, i32, i32) // tuple struct of three values\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "msg = Quit\n", + "msg = Move { x: 100, y: 200 }\n", + "msg = Write(\"TEXT\")\n", + "msg = ChangeColor(255, 0, 255)\n" + ] + } + ], + "source": [ + "let mut msg = Message::Quit;\n", + "println!(\"msg = {:?}\", msg);\n", + "\n", + "msg = Message::Move{ x: 100, y: 200};\n", + "println!(\"msg = {:?}\", msg);\n", + "\n", + "msg = Message::Write(String::from(\"TEXT\"));\n", + "println!(\"msg = {:?}\", msg);\n", + "\n", + "msg = Message::ChangeColor(255, 0, 255);\n", + "println!(\"msg = {:?}\", msg);" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "impl Message {\n", + " fn call(&self) {\n", + " match *self {\n", + " Message::Quit => println!(\"Quit\"),\n", + " Message::Move{ x, y } => println!(\"Move to x={}, y={}\", x, y),\n", + " Message::Write(ref text) => println!(\"Write '{}'\", text),\n", + " Message::ChangeColor(r, g, b) => println!(\"Change color to r={}, g={}, b={}\", r, g, b)\n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Change color to r=255, g=0, b=255\n" + ] + } + ], + "source": [ + "msg.call();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generic Enums" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Enums can be parametrized with type parameters to create generic enums." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `Option`" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " enum Option {\n", + " None,\n", + " Some(T)\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `Result`" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain{\n", + " enum Result { \n", + " Ok(T), \n", + " Err(E) \n", + " }\n", + "} " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Real-World Example - BinaryTree" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "// An ordered collection of `T`s.\n", + "enum BinaryTree {\n", + " Empty,\n", + " NonEmpty(Box>)\n", + "}\n", + "\n", + "// A part of a BinaryTree.\n", + "struct TreeNode {\n", + " element: T,\n", + " left: BinaryTree,\n", + " right: BinaryTree\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "use self::BinaryTree::*;\n", + "\n", + "let jupyter_tree = NonEmpty(Box::new(TreeNode {\n", + " element: \"Jupyter\",\n", + " left: Empty,\n", + " right: Empty\n", + "}));\n", + "\n", + "let mercury_tree = NonEmpty(Box::new(TreeNode {\n", + " element: \"Mercury\",\n", + " left: Empty,\n", + " right: Empty\n", + "}));\n", + "\n", + "let venus_tree = NonEmpty(Box::new(TreeNode {\n", + " element: \"Venus\",\n", + " left: Empty,\n", + " right: Empty\n", + "}));\n", + "\n", + "let mars_tree = NonEmpty(Box::new(TreeNode {\n", + " element: \"Mars\",\n", + " left: jupyter_tree,\n", + " right: mercury_tree\n", + "}));\n", + "\n", + "let uranus_tree = NonEmpty(Box::new(TreeNode {\n", + " element: \"Uranus\",\n", + " left: Empty,\n", + " right: venus_tree\n", + "}));\n", + "\n", + "// the root\n", + "let saturn_tree = NonEmpty(Box::new(TreeNode {\n", + " element: \"Saturn\",\n", + " left: mars_tree,\n", + " right: uranus_tree\n", + "}));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pattern Matching\n", + "\n", + "* Pattern matching allows to compare a value against a series of patterns and execute code based on which pattern matches.\n", + "* Patterns can include literals, variable names, wildcards, and more complex structures.\n", + "* Patterns are commonly used with the `match` expression in Rust.\n", + "* Match arms consist of a pattern and the code to execute if the pattern matches.\n", + "* `match` expression is **infallible** and must cover all possible cases." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "enum RoughTime {\n", + " InThePast(TimeUnit, u32),\n", + " JustNow,\n", + " InTheFuture(TimeUnit, u32)\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "fn rough_time_to_english(rt: RoughTime) -> String {\n", + " match rt {\n", + " RoughTime::InThePast(units, 1) => format!(\"{} ago\", units.singular()),\n", + " RoughTime::InThePast(units, count) => format!(\"{} {} ago\", count, units.plural()),\n", + " RoughTime::JustNow => format!(\"just now\"),\n", + " RoughTime::InTheFuture(units, 1) => format!(\"{} from now\", units.singular()), \n", + " RoughTime::InTheFuture(units, count) => format!(\"{} {} from now\", count, units.plural()) \n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"second ago\"" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rough_time_to_english(RoughTime::InThePast(TimeUnit::Seconds, 1))" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"42 seconds ago\"" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rough_time_to_english(RoughTime::InThePast(TimeUnit::Seconds, 42))" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"just now\"" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rough_time_to_english(RoughTime::JustNow)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"42 seconds from now\"" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "rough_time_to_english(RoughTime::InTheFuture(TimeUnit::Seconds, 42))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "| Pattern type | Example | Notes |\n", + "| ----------------------- | ---------------------- | -------------------------------------------------------------------------- |\n", + "| Literal | `100` | Matches an exact value; the name of a const is also allowed |\n", + "| | `\"name\"` | |\n", + "| Range | `0 ... 100` | Matches any value in range, including the end value |\n", + "| | `'a' ... 'k'` | |\n", + "| Wildcard | `_` | Matches any value and ignores it |\n", + "| Variable | `name` | Like _ but moves or copies the value into a new local variable |\n", + "| | `mut count` | |\n", + "| ref variable | `ref field` | Borrows a reference to the matched value instead of moving or copying it |\n", + "| | `ref mut field` | |\n", + "| Binding with subpattern | `val @ 0 ... 99` | Matches the pattern to the right of @, using the variable name to the left |\n", + "| | `ref circle @` | |\n", + "| | `Shape::Circle { .. }` | |\n", + "| Enum pattern\t | `Some(value)` | |\n", + "| | `None` | |\n", + "| | `Message::Ok` | |\n", + "| Tuple pattern\t | `(key, value)` | |\n", + "| | `(r, g, b)` | |\n", + "| Struct pattern\t | `Color(r, g, b)` | |\n", + "| | `Point { x, y }` | |\n", + "| | `Card { suit: Clubs, rank: n }` | |\n", + "| | `Account { id, name, .. }` | | \n", + "| Reference\t | `&value` | Matches only reference values |\n", + "| | `&(k, v)`\t | |\n", + "| Multiple patterns\t | `'a' \\| 'A'` | In match only (not valid in let, etc.) |\n", + "| Guard expression\t | `x if x * x <= r2` | In match only (not valid in let, etc.) |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Literals, Variables and Wildcards in Patterns" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"4 clicks\"" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let no_of_clicks = 4;\n", + "\n", + "match no_of_clicks {\n", + " 0 => format!(\"No one clicked...\"),\n", + " 1 => format!(\"Just one click...\"),\n", + " n => format!(\"{} clicks\", n)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Rust requires every match expression to handle all possible values, a wildcard is often required at the end\n", + "* This ensures that the match is exhaustive and prevents runtime errors." + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Not Ok\"" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let http_code = HttpStatus::NotFound;\n", + "\n", + "match http_code {\n", + " HttpStatus::Ok => \"Ok\",\n", + " _ => \"Not Ok\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tuple Patterns" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "fn describe_point(x: i32, y:i32) -> &'static str {\n", + " use std::cmp::Ordering::*;\n", + "\n", + " match (x.cmp(&0), y.cmp(&0)) {\n", + " (Equal, Equal) => \"at the origin\",\n", + " (_, Equal) => \"on the x axis\",\n", + " (Equal, _) => \"on the y axis\",\n", + " (Greater, Greater) => \"in the first quadrant\",\n", + " (Less, Greater) => \"in the second quadrant\",\n", + " _ => \"somewhere else\"\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"on the y axis\"" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "describe_point(0, 42)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Struct Patterns" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"straight up 100 meters\"" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#[derive(Debug, PartialEq)]\n", + "struct Point {\n", + " x: i32, \n", + " y: i32\n", + "}\n", + "\n", + "let location = Point{x:0, y:100};\n", + "\n", + "match location {\n", + " Point{x: 0, y: height} => format!(\"straight up {} meters\", height),\n", + " Point{x: x, y: y } => format!(\"at ({},{})\", x, y)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Patterns like `Point { x: x, y: y }` are common when matching structs, and the redundant names are visual clutter, so Rust has a shorthand for this: `Point {x, y}` - this pattern still stores a point’s `x` field in a new local `x` and its `y` field in a new local `y`." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"straight up 100 meters\"" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "match location {\n", + " Point{x: 0, y} => format!(\"straight up {} meters\", y),\n", + " Point{x, y} => format!(\"at ({},{})\", x, y)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Matching larger structs" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Debug)]\n", + "struct Person {\n", + " id: u64,\n", + " first_name: String,\n", + " last_name: String,\n", + " age: u8,\n", + " salary: f32,\n", + " job: String\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Jan Kowalski\"" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let person = Person{id: 665, first_name: \"Jan\".to_string(), last_name: \"Kowalski\".to_string(), age: 32, salary:9000.99, job: \"Rust Developer\".to_string() };\n", + "\n", + "match person {\n", + " Person{id: _, first_name, last_name, age: _, salary: _, job: _} => format!(\"{} {}\", first_name, last_name)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* We can skip uninteresting fields with `..`" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Jan Kowalski\"" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let person = Person{id: 665, first_name: \"Jan\".to_string(), last_name: \"Kowalski\".to_string(), age: 32, salary:9000.99, job: \"Rust Developer\".to_string() };\n", + "\n", + "match person {\n", + " Person{first_name, last_name, ..} => format!(\"{} {}\", first_name, last_name)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Reference Patterns" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "#### `ref` Patterns\n", + "\n", + "* `ref` patterns borrow parts of a matched value" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "fn print_names(person: &Person) {\n", + " print!(\"{} {}\", person.first_name, person.last_name); \n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "use of deprecated method `core::str::::trim_right_matches`: superseded by `trim_end_matches`", + "output_type": "error", + "traceback": [ + "\u001b[31m[deprecated] Error:\u001b[0m use of deprecated method `core::str::::trim_right_matches`: superseded by `trim_end_matches`" + ] + }, + { + "ename": "Error", + "evalue": "borrow of partially moved value: `person`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0382] Error:\u001b[0m borrow of partially moved value: `person`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_38:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m4 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mP\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mj\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mb\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;100m&\u001b[0m\u001b[38;5;100mp\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100mn\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m!\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mW\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mk\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m\u001b[38;5;249m}\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mj\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m}\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m┬\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m help: borrow this binding in the pattern to avoid moving the value: `ref `", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m value partially moved here", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m value borrowed here after partial move", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let person = Person{ id: 665, first_name: \"Jan\".to_string(), last_name: \"Kowalski\".to_string(), age: 32, salary:9000.99, job: \"Rust Developer\".to_string() };\n", + "\n", + "match person {\n", + " Person{ job, .. } => { print_names(&person); println!(\"Works as {}\", job) }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jan Kowalski Works as Rust Developer\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let person = Person{ id: 665, first_name: \"Jan\".to_string(), last_name: \"Kowalski\".to_string(), age: 32, salary:9000.99, job: \"Rust Developer\".to_string() };\n", + "\n", + "match person {\n", + " Person{ ref job, .. } => { print_names(&person); println!(\" Works as {}\", job) }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Person { id: 665, first_name: \"JAN\", last_name: \"KOWALSKI\", age: 32, salary: 9000.99, job: \"Rust Developer\" }" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fn to_upper(text: &mut String) {\n", + " *text = text.to_uppercase();\n", + "}\n", + "\n", + "let mut person = Person{ id: 665, first_name: \"Jan\".to_string(), last_name: \"Kowalski\".to_string(), age: 32, salary:9000.99, job: \"Rust Developer\".to_string() };\n", + "\n", + "match person {\n", + " Person{ref mut first_name, ref mut last_name, ..} => { to_upper(first_name); to_upper(last_name) }\n", + "}\n", + "\n", + "person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### & Pattern\n", + "\n", + "* A pattern starting with `&` matches a reference." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"x=100, y=200\"" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#[derive(Debug, PartialEq)]\n", + "struct ColorCircle { pos: Point, r: u32, color: (u8, u8, u8) }\n", + "\n", + "impl ColorCircle {\n", + " fn center(&mut self) -> &mut Point {\n", + " return &mut self.pos;\n", + " }\n", + "}\n", + "\n", + "let mut c = ColorCircle{pos: Point{x: 100, y: 200}, r: 50, color: (255, 0, 0)};\n", + "\n", + "match c.center() {\n", + " &mut Point{x, y} => { format!(\"x={}, y={}\", x, y) },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Point { x: 10, y: 200 }" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "match c.center() {\n", + " &mut Point{ref mut x, ..} => { *x = 10; },\n", + "}\n", + "\n", + "c.center()" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "item peeked: Item(\"one\")\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#[derive(Debug)]\n", + "struct Item(String);\n", + "\n", + "let items = [Item(\"one\".to_string()), Item(\"two\".to_string()), Item(\"three\".to_string())];\n", + "\n", + "{\n", + " let mut iter = items.iter().peekable();\n", + "\n", + " match iter.peek() {\n", + " Some(&item) => println!(\"item peeked: {:?}\", item),\n", + " None => println!(\"no item\")\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Matching Multiple Possibilities" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "true" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let text = String::from(\"\\nBC\");\n", + "\n", + "{\n", + " let mut chars = text.chars().peekable();\n", + "\n", + " let at_end = match chars.peek() {\n", + " Some(&'\\r') | Some(&'\\n') | None => true,\n", + " _ => false\n", + " };\n", + "\n", + " at_end\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lowercase letter: a\n", + "uppercase letter: B\n", + "digit: 1\n", + "digit: 2\n", + "whitespace\n", + "other character\n", + "special character: &\n", + "special character: ^\n", + "special character: %\n", + "special character: $\n", + "special character: #\n", + "whitespace\n", + "digit: 3\n", + "digit: 4\n", + "lowercase letter: c\n", + "lowercase letter: d\n", + "other character\n", + "newline\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let text = String::from(\"aB12 -&^%$# 34cd\\r\\n\");\n", + "\n", + "for c in text.chars() {\n", + " match c {\n", + " 'a'..='z' => println!(\"lowercase letter: {}\", c),\n", + " 'A'..='Z' => println!(\"uppercase letter: {}\", c),\n", + " '0'..='9' => println!(\"digit: {}\", c),\n", + " ' ' | '\\t' => println!(\"whitespace\"),\n", + " '&' | '^' | '%' | '$' | '#' => println!(\"special character: {}\", c),\n", + " '\\n' => println!(\"newline\"),\n", + " _ => println!(\"other character\")\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Pattern Guards" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Pattern guards allow to add additional conditional checks to patterns in match arms." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "\"Inside the sphere: x=0.3, y=0.4, z=0.5\"" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "struct Point3D {\n", + " x: f64,\n", + " y: f64,\n", + " z: f64\n", + "}\n", + "\n", + "impl Point3D {\n", + " fn new(x: f64, y: f64, z: f64) -> Point3D {\n", + " Point3D{x: x, y: y, z: z}\n", + " }\n", + "\n", + " fn distance_to_origin(&self) -> f64 {\n", + " (self.x.powf(2.0) + self.y.powf(2.0) + self.z.powf(2.0)).sqrt()\n", + " }\n", + "}\n", + "\n", + "let point = Point3D::new(0.3, 0.4, 0.5);\n", + "\n", + "match point {\n", + " Point3D{x, y, z} if point.distance_to_origin() <= 1.0 => format!(\"Inside the sphere: x={}, y={}, z={}\", x, y, z),\n", + " Point3D{x, y, z} => format!(\"Outside the sphere: x={}, y={}, z={}\", x, y, z)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `@` Patterns\n", + "\n", + "* Finally, `x @` pattern matches exactly like the given pattern, but on success, instead of creating variables for parts of the matched value, it creates a single variable `x` and moves or copies the whole value into it" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Matched: 5\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let number = Some(5);\n", + "\n", + "match number {\n", + " Some(n @ 1..=5) => println!(\"Matched: {}\", n),\n", + " Some(n) => println!(\"Other: {}\", n),\n", + " None => println!(\"No value\"),\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x is in range 1 to 5: 5; y is 10\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let point = Point { x: 5, y: 10 };\n", + "\n", + "match point {\n", + " Point { x: n @ 1..=5, y } => println!(\"x is in range 1 to 5: {}; y is {}\", n, y),\n", + " Point { x, y } => println!(\"x is {}; y is {}\", x, y),\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Debug, PartialEq)]\n", + "struct Circle{ center: Point, radius: u32 }\n", + "\n", + "#[derive(Debug, PartialEq)]\n", + "struct Rectangle{ top_left: Point, width: u32, height: u32 }\n", + "\n", + "#[derive(Debug, PartialEq)]\n", + "enum Shape {\n", + " Circle(Circle),\n", + " Rectangle(Rectangle)\n", + "}\n", + "\n", + "fn inflate(shape: &mut Shape, factor: u32) {\n", + " match shape {\n", + " Shape::Circle(circle @ Circle{..}) => {\n", + " circle.radius *= factor;\n", + " },\n", + " Shape::Rectangle(rect @ Rectangle{..}) => {\n", + " rect.width *= factor; \n", + " rect.height *= factor; \n", + " }\n", + " }\n", + "}\n", + "\n", + "let mut s = Shape::Circle(Circle{center: Point{x: 100, y: 200}, radius: 50});\n", + "\n", + "inflate(&mut s, 2);\n", + "\n", + "assert!(s == Shape::Circle(Circle{center: Point{x: 100, y: 200}, radius: 100}));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Where Patterns Can Be Applied\n", + "\n", + "#### Irrefutable patterns\n", + "\n", + "* Patterns that always match are special in Rust. \n", + "* They’re called **irrefutable patterns**\n", + "* They’re the only patterns allowed places shown below" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Unpack a struct into three new local variables" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "x=1, y=2\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let point = Point3D::new(1.0, 2.0, 3.0);\n", + "\n", + "let Point3D{x, y, ..} = point; // unpacking\n", + "\n", + "println!(\"x={}, y={}\", x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Unpack a function argument that's a tuple" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "5.0" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fn distance_to((x, y): (f64, f64)) -> f64 { \n", + " (x.powf(2.0) + y.powf(2.0)).sqrt() \n", + "}\n", + "\n", + "distance_to((3.0, 4.0))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Iterate over keys and values of a HashMap" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "three: 3\n", + "one: 1\n", + "two: 2\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "use std::collections::HashMap;\n", + "\n", + "let mut dictionary = HashMap::new();\n", + "dictionary.insert(\"one\", 1);\n", + "dictionary.insert(\"two\", 2);\n", + "dictionary.insert(\"three\", 3);\n", + "\n", + "for (key, value) in &dictionary {\n", + " println!(\"{}: {}\", key, value);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Refutable Patterns\n", + "\n", + "* A **refutable pattern** is one that might not match, like `Ok(x)`, which doesn’t match an error result, or `'0' ... '9'`, which doesn’t match the character `'Q'`\n", + "\n", + "* Refutable patterns are also allowed in `if let` and `while let` expressions, which can be used to..." + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "32 years ago\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 53, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fn date_of_birth() -> RoughTime {\n", + " RoughTime::InThePast(TimeUnit::Years, 32)\n", + "}\n", + "\n", + "if let RoughTime::InThePast(units, count) = date_of_birth() {\n", + " println!(\"{} years ago\", count);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Real-World Example - BinaryTree - 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "// An ordered collection of `T`s.\n", + "enum BinaryTree {\n", + " Empty,\n", + " NonEmpty(Box>)\n", + "}\n", + "\n", + "// A part of a BinaryTree.\n", + "struct TreeNode {\n", + " element: T,\n", + " left: BinaryTree,\n", + " right: BinaryTree\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "impl BinaryTree {\n", + " fn add(&mut self, value: T) {\n", + " match *self {\n", + " BinaryTree::Empty => {\n", + " *self = BinaryTree::NonEmpty(Box::new(TreeNode {\n", + " element: value,\n", + " left: BinaryTree::Empty,\n", + " right: BinaryTree::Empty\n", + " }));\n", + " },\n", + " BinaryTree::NonEmpty(ref mut node) => {\n", + " if value <= node.element {\n", + " node.left.add(value);\n", + " } else {\n", + " node.right.add(value);\n", + " }\n", + " }\n", + " }\n", + " }\n", + "\n", + " fn preorder<'a>(&'a self, values: &mut Vec<&'a T>) {\n", + " match *self {\n", + " BinaryTree::Empty => {},\n", + " BinaryTree::NonEmpty(ref node) => {\n", + " values.push(&node.element);\n", + " node.left.preorder(values);\n", + " node.right.preorder(values);\n", + " }\n", + " }\n", + " }\n", + "\n", + " fn postorder<'a>(&'a self, values: &mut Vec<&'a T>) {\n", + " match *self {\n", + " BinaryTree::Empty => {},\n", + " BinaryTree::NonEmpty(ref node) => {\n", + " node.left.postorder(values);\n", + " node.right.postorder(values);\n", + " values.push(&node.element);\n", + " }\n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"Givry\", \"Beaune\", \"Mercurey\", \"Rully\", \"Pommard\"]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut tree = BinaryTree::Empty;\n", + "\n", + "{\n", + " tree.add(\"Givry\".to_string());\n", + " tree.add(\"Mercurey\".to_string());\n", + " tree.add(\"Rully\".to_string());\n", + " tree.add(\"Beaune\".to_string());\n", + " tree.add(\"Pommard\".to_string());\n", + "\n", + " let mut values: Vec<&String> = Vec::new();\n", + " tree.preorder(&mut values);\n", + "\n", + " values\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"Cabernet Sauvignon\", \"Solaris\", \"Pinot Noir\", \"Merlot\", \"Tannat\", \"Chardonnay\"]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut another_tree = BinaryTree::Empty;\n", + "\n", + "{\n", + " another_tree.add(\"Chardonnay\");\n", + " another_tree.add(\"Tannat\");\n", + " another_tree.add(\"Cabernet Sauvignon\");\n", + " another_tree.add(\"Merlot\");\n", + " another_tree.add(\"Pinot Noir\");\n", + " another_tree.add(\"Solaris\");\n", + "\n", + " let mut values = Vec::new();\n", + " another_tree.postorder(&mut values);\n", + " values\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/08_error_handling.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/08_error_handling.ipynb new file mode 100644 index 0000000..2a29d69 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/08_error_handling.ipynb @@ -0,0 +1,938 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Error handling" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Panic\n", + "\n", + "* A program panics when it encounters something so messed up that there must be a bug in the program itself. Something like:\n", + " * Out-of-bounds array access\n", + " * Integer division by zero\n", + " * Calling `.unwrap()` on an `Option` that happens to be `None`\n", + " * Assertion failure\n", + "* A panic is not a crash. It’s not undefined behavior. The behavior is well-defined!\n", + "* Macro `panic!` can trigger a panic directly" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "thread '' panicked at src\\lib.rs:6:1:\n", + "Critical error!\n", + "stack backtrace:\n", + " 0: std::panicking::begin_panic_handler\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\std\\src\\panicking.rs:697\n", + " 1: core::panicking::panic_fmt\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:75\n", + " 2: ctx::run_user_code_2::{{closure}}\n", + " 3: _rust_try.llvm.7135721178753807917\n", + " 4: std::panic::catch_unwind\n", + " 5: run_user_code_2\n", + " 6: \n", + " 7: \n", + " 8: \n", + " 9: \n", + " 10: \n", + " 11: \n", + " 12: \n", + " 13: \n", + " 14: BaseThreadInitThunk\n", + " 15: RtlUserThreadStart\n", + "note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n" + ] + } + ], + "source": [ + "panic!(\"Critical error!\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Unwinding\n", + "\n", + "* Panic typically proceeds as follows:\n", + " * An error message is printed to the terminal. If you set the `RUST_BACKTRACE` environment variable, as the messages suggests, Rust will also dump the stack at this point.\n", + " * The stack is unwound. This is a lot like C++ exception handling. Any temporary values, local variables, or arguments that the current function was using are dropped, in the reverse of the order they were created. Dropping a value simply means cleaning up after it: any `String`s or `Vec`s the program was using are freed, any open `File`s are closed, and so on. User-defined `drop` methods are called too.\n", + " * Finally, the thread exits. If the panicking thread was the main thread, then the whole process exits (with a nonzero exit code)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Aborting\n", + "\n", + "* Stack unwinding is the default panic behavior, but there are two circumstances in which Rust does not try to unwind the stack.\n", + " * If a `.drop()` method triggers a second panic while Rust is still trying to clean up after the first, this is considered fatal. Rust stops unwinding and aborts the whole process.\n", + " * Rust’s panic behavior is customizable. If you compile with `-C panic=abort`, the first panic in your program immediately aborts the process. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Result" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A `Result` represents an operation that can either succeed (returning a result) or fail (returning an error).\n", + "* It is defined as:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " pub enum Result {\n", + " Ok(T),\n", + " Err(E),\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Debug)]\n", + "struct ReadFileError {\n", + " message: String\n", + "}\n", + "\n", + "impl std::fmt::Display for ReadFileError {\n", + " fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n", + " write!(f, \"ReadFileError: {}\", self.message)\n", + " }\n", + "}\n", + "\n", + "impl std::error::Error for ReadFileError {}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "failed to resolve: use of unresolved module or unlinked crate `io`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0433] Error:\u001b[0m failed to resolve: use of unresolved module or unlinked crate `io`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_5:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m<\u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m use of unresolved module or unlinked crate `io`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: a builtin type with a similar name exists: `i8`", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;115mNote\u001b[0m: help: consider importing this module: `use std::io;", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m `", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "fn read_file(path: &str) -> Result { \n", + " let file: io::Result = std::fs::read_to_string(path);\n", + " \n", + " match file {\n", + " Ok(content) => Result::Ok(content),\n", + " Err(err) => Result::Err(ReadFileError{message: err.to_string()}),\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot find function `read_file` in this scope", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0425] Error:\u001b[0m cannot find function `read_file` in this scope", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_6:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54m_\u001b[0m\u001b[38;5;54mf\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m not found in this scope", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let file_content = read_file(\"file.txt\");\n", + "\n", + "match file_content {\n", + " Ok(content) => println!(\"{}\", content),\n", + " Err(err) => println!(\"{}\", err),\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Result methods" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "* `result.unwrap()` also returns the success value, if result is a success result. However, if result is an error result, this method panics.\n", + "* `result.expect(message)` is the same as `.unwrap()`, but lets you provide a message that it prints in case of panic.\n", + "* `result.is_ok()` and `result.is_err()` return a `bool` telling if result is a success result or an error result.\n", + "* `result.ok()` returns the success value, if any, as an `Option`. If result is a success result, this returns `Some(success_value)`; otherwise, it returns `None`, discarding the error value.\n", + "* `result.err()` returns the error value, if any, as an `Option`.\n", + "* `result.unwrap_or(fallback)` returns the success value, if result is a success result. Otherwise, it returns `fallback`, discarding the error value." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot find function `read_file` in this scope", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0425] Error:\u001b[0m cannot find function `read_file` in this scope", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_7:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54m_\u001b[0m\u001b[38;5;54mf\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mk\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mD\u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mF\u001b[0m\u001b[38;5;249mA\u001b[0m\u001b[38;5;249mU\u001b[0m\u001b[38;5;249mL\u001b[0m\u001b[38;5;249mT\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mF\u001b[0m\u001b[38;5;249mI\u001b[0m\u001b[38;5;249mL\u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mC\u001b[0m\u001b[38;5;249mO\u001b[0m\u001b[38;5;249mN\u001b[0m\u001b[38;5;249mT\u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mN\u001b[0m\u001b[38;5;249mT\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m not found in this scope", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "static DEFAULT_FILE_CONTENT: &str = \"Default content\";\n", + "\n", + "let file_content = read_file(\"unknown_file.txt\").unwrap_or(DEFAULT_FILE_CONTENT.to_string());\n", + "\n", + "file_content" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `result.unwrap_or_else(fallback_fn)` is the same, but instead of passing a fallback value directly, you pass a function or closure. This is for cases where it would be wasteful to compute a fallback value if you’re not going to use it. The `fallback_fn` is called only if we have an error result.\n", + "* `result.as_ref()` converts a `Result` to a `Result<&T, &E>`, borrowing a reference to the success or error value in the existing result.\n", + "* `result.as_mut()` is the same, but borrows a mutable reference. The return type is `Result<&mut T, &mut E>`.\n", + "\n", + "#### Mapping\n", + "\n", + "* Mapping means transforming the success or error value inside a `Result`, while leaving the other value untouched. It simplifies code that would otherwise require matching on the `Result` and handling both cases.\n", + " * `result.map(success_fn)` transforms a `Result` to a `Result` by applying the function `success_fn` to the success value, if any. The error value is left untouched.\n", + " * `result.map_err(error_fn)` is the same, but applies `error_fn` to the error value, if any, transforming a `Result` to a `Result`." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a file with some text.\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fn read_file(path: &str) -> Result { \n", + " std::fs::read_to_string(path).map_err(|err| ReadFileError{message: err.to_string()})\n", + "}\n", + "\n", + "let file_content = read_file(\"file.txt\");\n", + "\n", + "match file_content {\n", + " Ok(content) => println!(\"{}\", content),\n", + " Err(err) => println!(\"{}\", err),\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Result Type Aliases\n", + "\n", + "* Modules often define a `Result` type alias to avoid having to repeat an error type that’s used consistently by almost every function in the module" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Some(\"This is a file with some text.\")" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mod custom {\n", + " pub type Result = std::result::Result;\n", + "\n", + " #[derive(Debug)]\n", + " pub struct ReadFileError {\n", + " message: String\n", + " }\n", + "\n", + " pub fn read_file(path: &str) -> Result { \n", + " let file = std::fs::read_to_string(path);\n", + " \n", + " match file {\n", + " Ok(content) => Result::Ok(content),\n", + " Err(err) => Result::Err(ReadFileError{message: err.to_string()}),\n", + " }\n", + " }\n", + "}\n", + "\n", + "custom::read_file(\"file.txt\").ok()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Trait std::error::Error - Printing Errors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* All of standard library errors have a common interface, the `std::error::Error` trait, which means they share the following features:\n", + " * They’re all printable using `println!()`. Printing an error with the `{}` format specifier typically displays only a brief error message. Alternatively, you can print with the `{:?}` format specifier, to get a `Debug` view of the error\n", + " * `err.description()` returns an error message as a `&str`.\n", + " * `err.source()` returns an `Option<&(dyn Error + 'static)>`: the underlying error, if any, that triggered `err`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Propagating Error" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* In most places where we try something that could fail, we don’t want to catch and handle the error immediately\n", + "* Instead, if an error occurs, we usually want to let our caller deal with it. We want errors to propagate up the call stack.\n", + "* Rust has a `?` operator that does this. You can add a `?` to any expression that produces a `Result`, such as the result of a function call:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "use std::io;\n", + "\n", + "fn read_file_content(name: &str) -> io::Result {\n", + " use std::fs::File;\n", + " use std::io::prelude::*;\n", + " \n", + " let mut file = File::open(name)?; \n", + " let mut contents = String::new();\n", + " file.read_to_string(&mut contents)?;\n", + " Ok(contents)\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error: Os { code: 2, kind: NotFound, message: \"Nie można odnaleźć określonego pliku.\" }\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let file_content = read_file_content(\"unknown_file.txt\");\n", + "\n", + "match file_content {\n", + " Ok(content) => println!(\"{}\", content),\n", + " Err(err) => println!(\"Error: {:?}\", err),\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The behavior of `?` depends on whether this function returns a success result or an error result:\n", + " * on success, it unwraps the Result to get the success value inside.\n", + " * on error, it immediately returns from the enclosing function, passing the error result up the call chain. To ensure that this works, `?` can only be used in functions that have a `Result` return type." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Working with Multiple Error Types" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "`?` couldn't convert the error to `std::io::Error`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0277] Error:\u001b[0m `?` couldn't convert the error to `std::io::Error`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_14:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249mf\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mB\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m-\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mR\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100mu\u001b[0m\u001b[38;5;100ml\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100m<\u001b[0m\u001b[38;5;100mV\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100mc\u001b[0m\u001b[38;5;100m<\u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100m3\u001b[0m\u001b[38;5;100m2\u001b[0m\u001b[38;5;100m>\u001b[0m\u001b[38;5;100m,\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100mE\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m expected `std::io::Error` because of this", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54m<\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54m3\u001b[0m\u001b[38;5;54m2\u001b[0m\u001b[38;5;54m>\u001b[0m\u001b[38;5;54m(\u001b[0m\u001b[38;5;54m)\u001b[0m\u001b[38;5;68m?\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m this can't be annotated with `?` because it has type `Result<_, ParseIntError>`", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m the trait `From` is not implemented for `std::io::Error`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "use std::io::{self, BufRead};\n", + "\n", + "fn read_numbers(file: &mut dyn BufRead) -> Result, io::Error> {\n", + " let mut numbers = Vec::new();\n", + " for line_result in file.lines() {\n", + " let line = line_result?;\n", + " let number = line.parse::()?;\n", + " numbers.push(number);\n", + " }\n", + " Ok(numbers)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Case 1 - Trait `From`" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "use std::io::BufRead;\n", + "\n", + "#[derive(Debug)]\n", + "enum ReadNumbersError {\n", + " Io(io::Error),\n", + " ParseInt(std::num::ParseIntError),\n", + "}\n", + "\n", + "impl From for ReadNumbersError {\n", + " fn from(err: io::Error) -> Self {\n", + " ReadNumbersError::Io(err)\n", + " }\n", + "}\n", + "\n", + "impl From for ReadNumbersError {\n", + " fn from(err: std::num::ParseIntError) -> Self {\n", + " ReadNumbersError::ParseInt(err)\n", + " }\n", + "}\n", + "\n", + "fn read_numbers(file: &mut dyn BufRead) -> Result, ReadNumbersError> {\n", + " let mut numbers = Vec::new();\n", + " for line_result in file.lines() {\n", + " let line = line_result?;\n", + " let number = line.parse::()?;\n", + " numbers.push(number);\n", + " }\n", + " Ok(numbers)\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 42, 665, 6, 7, 8]\n" + ] + } + ], + "source": [ + "use std::io::{self, BufReader};\n", + "\n", + "fn main() {\n", + " let file = std::fs::File::open(\"numbers.txt\").unwrap();\n", + "\n", + " let mut buffer_reader = BufReader::new(file);\n", + "\n", + " let numbers_result = read_numbers(&mut buffer_reader);\n", + "\n", + " match numbers_result {\n", + " Ok(numbers) => println!(\"{:?}\", numbers),\n", + " Err(e) => println!(\"Error reading numbers: {:?}\", e),\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Case 2 - `Box`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Code does not compile - there is no conversion from `ParseIntError` to `io::Error`\n", + "* The easiest way to handle multiple error types is to define these type aliases:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "type GenericError = Box;\n", + "type GenericResult = Result;" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* and change return type from function to `GenericResult>`" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "use std::io::{self, BufRead};\n", + "\n", + "fn parse_numbers(file: &mut dyn BufRead) -> GenericResult> {\n", + " let mut numbers = Vec::new();\n", + " for line_result in file.lines() {\n", + " let line = line_result?;\n", + " let number = line.parse::()?;\n", + " numbers.push(number);\n", + " }\n", + " Ok(numbers)\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error reading numbers: ParseIntError { kind: InvalidDigit }\n" + ] + } + ], + "source": [ + "use std::io::{self, BufReader};\n", + "\n", + "fn main() {\n", + " let file = std::fs::File::open(\"invalid_numbers.txt\").unwrap();\n", + " let mut buffer_reader = BufReader::new(file);\n", + " \n", + " let numbers_result = parse_numbers(&mut buffer_reader);\n", + "\n", + " match numbers_result {\n", + " Ok(numbers) => println!(\"{:?}\", numbers),\n", + " Err(e) => println!(\"Error reading numbers: {:?}\", e),\n", + " }\n", + "}\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Declaring a Custom Error Types" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Debug, Clone)]\n", + "pub struct JsonError {\n", + " pub message: String,\n", + " pub line: usize,\n", + " pub column: usize,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "use std;\n", + "use std::fmt;\n", + "\n", + "// Errors should be printable.\n", + "impl fmt::Display for JsonError {\n", + " fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {\n", + " write!(f, \"{} ({}:{})\", self.message, self.line, self.column)\n", + " }\n", + "}\n", + "\n", + "// Errors should implement the std::error::Error trait.\n", + "impl std::error::Error for JsonError {\n", + " fn description(&self) -> &str {\n", + " &self.message\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + ":dep serde_json = \"1.0.64\"\n", + ":dep serde = { version=\"1.0.127\", features=[\"derive\"] }\n", + "\n", + "use serde::{Deserialize, Serialize};\n", + "\n", + "#[derive(Serialize, Deserialize, Debug)]\n", + "struct Person {\n", + " name: String,\n", + " age: i32,\n", + "}\n", + "\n", + "fn load_from_json() -> Result {\n", + " let json = r#\"\n", + " {\n", + " \"name\": \"John\",\n", + " \"age\": 30\n", + " }\n", + " \"#;\n", + "\n", + " let person: Person = match serde_json::from_str(json) {\n", + " Ok(data) => data,\n", + " Err(err) => {\n", + " let line = err.line();\n", + " let column = err.column();\n", + " let message = err.to_string();\n", + " return Err(JsonError { message, line, column });\n", + " }\n", + " };\n", + "\n", + " Ok(person)\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "None" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let person = load_from_json().err();\n", + "person" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Crates for Error Handling" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## thiserror\n", + "\n", + "* The `thiserror` crate provides a convenient way to define custom error types in Rust by using procedural macros. It simplifies the process of implementing the `std::error::Error` trait and provides useful features for error handling.\n", + "* With `thiserror`, you can define your error types as enums or structs and annotate them with the `#[derive(Error)]` attribute. This automatically generates the necessary implementations for the `Error` trait, including methods like `source()` and `description()`.\n", + "* The crate also allows you to specify error messages using the `#[error(\"...\")]` attribute, which supports formatting and interpolation of fields within the error type.\n", + "* Additionally, `thiserror` supports automatic conversion from other error types using the `#[from]` attribute, making it easy to wrap and propagate errors from different sources." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Error parsing numbers: Failed to parse number: 66d5\n" + ] + } + ], + "source": [ + ":dep thiserror = \"2.0\"\n", + "\n", + "mod thiserror_example {\n", + " use thiserror::Error;\n", + "\n", + " #[derive(Error, Debug)]\n", + " pub enum ParseNumberError {\n", + " #[error(\"File not found\")]\n", + " NotFound(#[from] std::io::Error), // automatic conversion from std::io::Error\n", + " #[error(\"Failed to parse number: {0}\")]\n", + " ParseError(String),\n", + " }\n", + "\n", + " pub fn parse_numbers(path: &str) -> Result, ParseNumberError> {\n", + " use std::fs::File;\n", + " use std::io::{self, BufRead, BufReader};\n", + "\n", + " let file = File::open(path)?;\n", + " let reader = BufReader::new(file);\n", + " let mut numbers = Vec::new();\n", + "\n", + " for line_result in reader.lines() {\n", + " let line = line_result?;\n", + " match line.parse::() {\n", + " Ok(num) => numbers.push(num),\n", + " Err(_) => return Err(ParseNumberError::ParseError(line)),\n", + " }\n", + " }\n", + "\n", + " Ok(numbers)\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " match thiserror_example::parse_numbers(\"invalid_numbers.txt\") {\n", + " Ok(numbers) => println!(\"Parsed numbers: {:?}\", numbers),\n", + " Err(e) => println!(\"Error parsing numbers: {}\", e),\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## anyhow\n", + "\n", + "* The `anyhow` crate provides a simple and flexible way to handle errors in Rust applications. It allows you to create and propagate errors without having to define custom error types for every possible error scenario.\n", + "* With `anyhow`, you can use the `anyhow::Error` type to represent errors, which can encapsulate any error type that implements the `std::error::Error` trait. This makes it easy to work with different error types without having to define a separate error enum or struct for each case.\n", + "* The crate also provides convenient macros like `anyhow!` for creating errors with formatted messages, and the `Context` trait for adding additional context to errors as they propagate through your code.\n", + "* Library convinent when prototyping or building applications where you want to focus on error handling without the overhead of defining numerous custom error types." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 42, 665, 6, 7, 8]\n" + ] + } + ], + "source": [ + ":dep anyhow = \"1.0\"\n", + "\n", + "mod anyhow_example {\n", + " use anyhow::Context;\n", + " use std::io::{BufRead, BufReader};\n", + "\n", + " pub fn parse_numbers(file: &mut dyn BufRead) -> anyhow::Result> {\n", + " let mut numbers = Vec::new();\n", + " for (index, line_result) in file.lines().enumerate() {\n", + " let line = line_result.context(format!(\"Failed to read line {}\", index + 1))?;\n", + " let number = line.parse::()\n", + " .context(format!(\"Failed to parse '{}' as i32 on line {}\", line, index + 1))?;\n", + " numbers.push(number);\n", + " }\n", + " Ok(numbers)\n", + " }\n", + "\n", + " pub fn read_numbers(path: &str) -> anyhow::Result> {\n", + " let file = std::fs::File::open(path)\n", + " .with_context(|| format!(\"Failed to open file '{}'\", path))?;\n", + " \n", + " let mut buffer_reader = BufReader::new(file);\n", + " let numbers = parse_numbers(&mut buffer_reader)\n", + " .with_context(|| format!(\"Failed to read numbers from file '{}'\", path))?;\n", + " \n", + " Ok(numbers)\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let result = anyhow_example::read_numbers(\"numbers.txt\"); \n", + " match result {\n", + " Ok(numbers) => println!(\"{:?}\", numbers),\n", + " Err(err) => println!(\"Error: {:?}\", err),\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "use anyhow::{anyhow, Result};\n", + "\n", + "fn get_user_age(name: &str) -> Result {\n", + " match name {\n", + " \"Alice\" => Ok(30),\n", + " \"Bob\" => Ok(25),\n", + " _ => Err(anyhow!(\"No age data available for user '{}'\", name)),\n", + " }\n", + "}\n", + "\n", + "fn main() -> Result<()> {\n", + " let age = get_user_age(\"Charlie\")?;\n", + " println!(\"User age: {}\", age);\n", + " Ok(())\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/09_traits_generics.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/09_traits_generics.ipynb new file mode 100644 index 0000000..e0fecc1 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/09_traits_generics.ipynb @@ -0,0 +1,1466 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Traits" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining Traits" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Traits are Rust’s take on interfaces or abstract base classes\n", + "* They define shared behavior that types can implement\n", + "* They enable polymorphism and code reuse" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "trait Shape {\n", + " fn move_by(&mut self, dx: i32, dy: i32);\n", + " fn area(&self) -> f64;\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementing Traits\n", + "\n", + "* To implement a trait for a type, we use the `impl` keyword" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "#[derive(Debug, Copy, Clone)]\n", + "struct Point {\n", + " x: i32,\n", + " y: i32\n", + "}\n", + "\n", + "#[derive(Debug, Copy, Clone)]\n", + "struct Circle {\n", + " position: Point,\n", + " radius: u32\n", + "}\n", + "\n", + "#[derive(Debug, Copy, Clone)]\n", + "struct Rectangle {\n", + " position: Point,\n", + " width: u32,\n", + " height: u32\n", + "}\n", + "\n", + "impl Shape for Circle {\n", + " fn move_by(&mut self, dx: i32, dy: i32) {\n", + " self.position.x += dx;\n", + " self.position.y += dy;\n", + " }\n", + "\n", + " fn area(&self) -> f64 {\n", + " std::f64::consts::PI * (self.radius as f64) * (self.radius as f64)\n", + " }\n", + "}\n", + "\n", + "impl Shape for Rectangle {\n", + "\n", + " fn move_by(&mut self, dx: i32, dy: i32) {\n", + " self.position.x += dx;\n", + " self.position.y += dy;\n", + " }\n", + "\n", + " fn area(&self) -> f64 {\n", + " (self.width * self.height) as f64\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* We can implement many traits for a type:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Drawing the circle at Point { x: 0, y: 0 } with radius: 10\n", + "Drawing the rectangle at Point { x: 0, y: 0 } with width: 10 and height: 20\n", + "--- after move:\n", + "Drawing the circle at Point { x: 10, y: 20 } with radius: 10\n", + "Drawing the rectangle at Point { x: 10, y: 20 } with width: 10 and height: 20\n" + ] + } + ], + "source": [ + "trait Drawable {\n", + " fn draw(&self);\n", + "}\n", + "\n", + "impl Drawable for Circle {\n", + " fn draw(&self) {\n", + " println!(\"Drawing the circle at {:?} with radius: {}\", self.position, self.radius);\n", + " }\n", + "}\n", + "\n", + "impl Drawable for Rectangle {\n", + " fn draw(&self) {\n", + " println!(\"Drawing the rectangle at {:?} with width: {} and height: {}\", self.position, self.width, self.height);\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let mut c = Circle { position: Point { x: 0, y: 0 }, radius: 10 };\n", + " let mut r = Rectangle { position: Point { x: 0, y: 0 }, width: 10, height: 20 };\n", + "\n", + " c.draw();\n", + " r.draw();\n", + "\n", + " c.move_by(10, 20);\n", + " r.move_by(10, 20);\n", + "\n", + " println!(\"--- after move:\");\n", + "\n", + " c.draw();\n", + " r.draw();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Trait Bounds\n", + "\n", + "* Trait bounds specify that a generic can be any type that implements a trait. \n", + "* They work like a contracts at compile time\n", + "\n", + "### impl Trait Syntax\n", + "\n", + "* The `impl Trait` syntax is a shorthand for a longer form, called a **trait bound**" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn draw_shape(shape: &impl Drawable) {\n", + " shape.draw()\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Drawing the circle at Point { x: 10, y: 20 } with radius: 5\n", + "Drawing the rectangle at Point { x: 40, y: 50 } with width: 10 and height: 5\n" + ] + } + ], + "source": [ + "let mut circle = Circle{position: Point{x: 10, y: 20}, radius: 5};\n", + "let mut rect = Rectangle{position: Point{x: 40, y: 50}, width: 10, height: 5};\n", + "\n", + "draw_shape(&circle);\n", + "draw_shape(&rect);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Trait Bound Syntax" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Drawing the circle at Point { x: 10, y: 20 } with radius: 5\n", + "Drawing the rectangle at Point { x: 40, y: 50 } with width: 10 and height: 5\n" + ] + } + ], + "source": [ + "fn draw_shape(shape: &T) {\n", + " shape.draw()\n", + "}\n", + "\n", + "draw_shape(&circle);\n", + "draw_shape(&rect);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Many trait bounds" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* We can specify multiple trait bounds on a generic type by using the `+` syntax" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn use_shape(shape: &mut (impl Shape + Drawable)) {\n", + " shape.move_by(10, 15);\n", + " shape.draw();\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn use_shape(shape: &mut T) {\n", + " shape.move_by(10, 15);\n", + " shape.draw();\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* We can use a `where` clause to specify trait bounds on generic types" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn use_shape(shape: &mut T)\n", + "where \n", + " T: Shape + Drawable \n", + "{\n", + " shape.move_by(10, 15);\n", + " shape.draw();\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use_shape(&mut circle);\n", + "use_shape(&mut rect);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Returning Types that Implement Traits" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Drawing the circle at Point { x: 0, y: 0 } with radius: 1\n" + ] + } + ], + "source": [ + "fn create_drawable(id: &str) -> Box {\n", + " match id {\n", + " \"Circle\" => Box::new(Circle{position: Point{x: 0, y: 0}, radius: 1}),\n", + " \"Rectangle\" => Box::new(Rectangle{position: Point{x: 0, y: 0}, width: 2, height: 1}),\n", + " _ => panic!(\"Unknown shape\")\n", + " }\n", + "}\n", + "\n", + "let shape: Box = create_drawable(\"Circle\");\n", + "shape.draw();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Self in Traits\n", + "\n", + "* `Self` is an alias for the type we’re implementing the trait for" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "trait Cloneable {\n", + " fn clone(&self) -> Self; // the type of x.clone() is the same as the type of x, whatever that might be\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A trait that uses the `Self` type is incompatible with trait objects\n", + "* Trait objects require dynamic dispatch, but using `Self` in a trait implies static dispatch — the compiler must know the concrete type at compile time.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "the trait `Cloneable` is not dyn compatible", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0038] Error:\u001b[0m the trait `Cloneable` is not dyn compatible", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_12:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249mf\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54my\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54mC\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mb\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m-\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mB\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249m<\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mC\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `Cloneable` is not dyn compatible", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "fn do_clone(prototype: &dyn Cloneable) -> Box {\n", + " let cloned_object = prototype.clone();\n", + " Box::new(cloned_object)\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Trait Objects\n", + "\n", + "* A trait object allows to implement dynamic polymorphism in Rust" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "mismatched types", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0308] Error:\u001b[0m mismatched types", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_13:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100my\u001b[0m\u001b[38;5;100mn\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100mS\u001b[0m\u001b[38;5;100mh\u001b[0m\u001b[38;5;100ma\u001b[0m\u001b[38;5;100mp\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mc\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54mc\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mO\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mz\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m expected due to this", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m expected `dyn Shape`, found `Circle`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + }, + { + "ename": "Error", + "evalue": "the size for values of type `dyn Shape` cannot be known at compilation time", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0277] Error:\u001b[0m the size for values of type `dyn Shape` cannot be known at compilation time", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_13:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mh\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249mO\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mv\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mz\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m doesn't have a size known at compile-time", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider borrowing here: `&`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let circle = Circle{position: Point{x: 10, y: 20}, radius: 5};\n", + "\n", + "let shape: dyn Shape = circle; // ERROR: Shape does not have a constant size" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### References to Trait" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A reference to trait type, like `Shape` is called **trait object**. Like any other reference, a trait object points to some value, it has a lifetime, and it can be either mut or shared.\n", + "* In memory, a trait object is a **fat pointer** consisting of a pointer to the value, plus a pointer to a table representing that value’s type (virtual table).\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape area: 78.54\n", + "Shape area: 200.00\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let mut shape: &dyn Shape;\n", + " \n", + " let circle = Circle{position: Point{x: 10, y: 20}, radius: 5};\n", + " shape = &circle;\n", + " println!(\"Shape area: {:.2}\", shape.area());\n", + "\n", + " shape = &Rectangle{position: Point{x: 10, y: 20}, width: 10, height: 20};\n", + " println!(\"Shape area: {:.2}\", shape.area());\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Boxes to Trait\n", + "\n", + "* We can also use a `Box` to store a trait object\n", + "* A `Box` is a trait object that points to a value on the heap" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape area: 78.54\n", + "Shape area: 200.00\n" + ] + } + ], + "source": [ + "let box_circle: Box = Box::new(Circle{position: Point{x: 10, y: 20}, radius: 5});\n", + "let mut shape: Box = box_circle; // OK - Box is trait object\n", + "println!(\"Shape area: {:.2}\", shape.area());\n", + "\n", + "shape = Box::new(Rectangle{position: Point{x: 10, y: 20}, width: 10, height: 20});\n", + "println!(\"Shape area: {:.2}\", shape.area());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dynamic Polymorphism in Rust" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape area: 78.54\n", + "Shape area: 50.00\n", + "Shape area: 49.00\n" + ] + } + ], + "source": [ + "#[derive(Debug)]\n", + "struct Square {\n", + " position: Point,\n", + " side: u32\n", + "} \n", + "\n", + "impl Shape for Square {\n", + "\n", + " fn move_by(&mut self, dx: i32, dy: i32) {\n", + " self.position.x += dx;\n", + " self.position.y += dy;\n", + " }\n", + "\n", + " fn area(&self) -> f64 {\n", + " (self.side * self.side) as f64\n", + " }\n", + "}\n", + "\n", + "impl Drawable for Square {\n", + " fn draw(&self) {\n", + " println!(\"Drawing the square at {:?} with side: {}\", self.position, self.side);\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let mut shapes: Vec> = vec![\n", + " Box::new(Circle{position: Point{x: 10, y: 20}, radius: 5}),\n", + " Box::new(Rectangle{position: Point{x: 40, y: 50}, width: 10, height: 5}),\n", + " Box::new(Square{position: Point{x: 70, y: 80}, side: 7})\n", + " ];\n", + "\n", + " for shape in shapes.iter_mut() {\n", + " shape.move_by(5, 5);\n", + " println!(\"Shape area: {:.2}\", shape.area());\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Combining Traits\n", + "\n", + "* Trait objects can only use one trait at a time, but you can work around this by creating a supertrait:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Drawing the circle at Point { x: 20, y: 35 } with radius: 5\n", + "Drawing the rectangle at Point { x: 50, y: 65 } with width: 10 and height: 5\n", + "Drawing the square at Point { x: 80, y: 95 } with side: 7\n" + ] + } + ], + "source": [ + "trait DrawableShape: Shape + Drawable {} // Trait that combines Shape and Drawable - a supertrait\n", + "\n", + "impl DrawableShape for T \n", + " where T: Shape + Drawable \n", + "{} // Implement DrawableShape for any type that implements Shape and Drawable - blanket implementation\n", + "\n", + "fn main() {\n", + " let mut drawable_shapes: Vec> = vec![\n", + " Box::new(Circle{position: Point{x: 10, y: 20}, radius: 5}),\n", + " Box::new(Rectangle{position: Point{x: 40, y: 50}, width: 10, height: 5}),\n", + " Box::new(Square{position: Point{x: 70, y: 80}, side: 7})\n", + " ];\n", + "\n", + " for shape in drawable_shapes.iter_mut() {\n", + " shape.move_by(10, 15);\n", + " shape.draw();\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Orphan Rules\n", + "\n", + "* The **orphan rule** prevent us from implementing a foreign trait on a foreign type\n", + "* **You are allowed to implement any trait on any type, as long as either the trait or the type is local to your crate**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Extension traits" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "// Extension trait for checking if a character is an emoji\n", + "trait IsEmoji {\n", + " fn is_emoji(&self) -> bool;\n", + "}\n", + "\n", + "// Implementation of the IsEmoji trait for the char type\n", + "impl IsEmoji for char {\n", + " fn is_emoji(&self) -> bool {\n", + " let c = *self as u32;\n", + " (c >= 0x1F600 && c <= 0x1F64F) || (c >= 0x1F300 && c <= 0x1F5FF) || (c >= 0x1F680 && c <= 0x1F6FF) || (c >= 0x1F900 && c <= 0x1F9FF)\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let c = '😀';\n", + " println!(\"Is '{}' an emoji? {}\", c, c.is_emoji());\n", + " println!(\"Is '{}' an emoji? {}\", '@', '@'.is_emoji());\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `IsEmoji` is an *extension trait* - adds a method to an existing type" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generic `impl` blocks\n", + "\n", + "* A generic `impl` block for a trait allows you to implement a trait for any type that satisfies certain bounds. This is a powerful way to write reusable, composable behavior across many types." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::fmt::Debug;\n", + "\n", + "trait DrawInDebug {\n", + " fn draw_dbg(&self);\n", + "}\n", + "\n", + "impl DrawInDebug for T {\n", + " fn draw_dbg(&self) {\n", + " println!(\"Drawing a {:?}\", self);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "circle.draw_dbg();\n", + "rect.draw_dbg();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Associated Types\n", + "\n", + "* Associated types are a way to associate a type placeholder with a trait\n", + "* Each type that implements the trait can specify its own concrete type for the associated type" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "1\n", + "2\n", + "3\n", + "4\n" + ] + } + ], + "source": [ + "trait Iterator {\n", + " type Item; // associated type\n", + " fn next(&mut self) -> Option;\n", + "}\n", + "\n", + "struct Counter {\n", + " value: u32,\n", + " sentinel: u32\n", + "}\n", + "\n", + "impl Counter {\n", + " fn new(sentinel: u32) -> Counter {\n", + " Counter{value: 0, sentinel}\n", + " }\n", + "}\n", + "\n", + "impl Iterator for Counter {\n", + " type Item = u32; // definition of associated type for Counter\n", + "\n", + " fn next(&mut self) -> Option {\n", + " if self.value < self.sentinel {\n", + " let result = self.value;\n", + " self.value += 1;\n", + " Some(result)\n", + " } else {\n", + " None\n", + " }\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let mut counter = Counter::new(5);\n", + "\n", + " while let Some(value) = counter.next() {\n", + " println!(\"{}\", value);\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Generic code can use associated types:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn collect_into_vector(mut iter: I) -> Vec // I::Item is the associated type of the Iterator\n", + "{\n", + " let mut results = Vec::new();\n", + "\n", + " while let Some(value) = iter.next() {\n", + " results.push(value);\n", + " }\n", + "\n", + " results\n", + "}\n", + "\n", + "fn main() { \n", + " let numbers = collect_into_vector(Counter::new(5));\n", + " println!(\"Collected numbers: {:?}\", numbers);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Subtraits\n", + "\n", + "* A trait can be an extension of another trait" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Forward iteration:\n", + "Counter value: 0\n", + "Counter value: 1\n", + "Counter value: 2\n", + "Counter value: 3\n", + "Counter value: 4\n", + "\n", + "Backward iteration:\n", + "Counter value: 4\n", + "Counter value: 3\n", + "Counter value: 2\n", + "Counter value: 1\n", + "Counter value: 0\n" + ] + } + ], + "source": [ + "trait BidirectionalIterator: Iterator {\n", + " fn prev(&mut self) -> Option;\n", + "}\n", + "\n", + "impl BidirectionalIterator for Counter {\n", + " fn prev(&mut self) -> Option {\n", + " if self.value > 0 {\n", + " let result = self.value;\n", + " self.value -= 1;\n", + " Some(self.value)\n", + " } else {\n", + " None\n", + " }\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let mut counter = Counter::new(5);\n", + "\n", + " println!(\"Forward iteration:\");\n", + " while let Some(value) = counter.next() {\n", + " println!(\"Counter value: {}\", value);\n", + " }\n", + "\n", + " println!(\"\\nBackward iteration:\");\n", + " while let Some(value) = counter.prev() {\n", + " println!(\"Counter value: {}\", value);\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Static Methods in Traits\n", + "\n", + "* Traits can have static methods" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating set:\n", + "Set contains 'apple': true\n", + "Set contains 'grape': false\n", + "\n", + "Creating set from slice:\n", + "Set contains 'apple': true\n", + "Set contains 'grape': false\n" + ] + } + ], + "source": [ + "trait StringSet {\n", + " /// Creates a new, empty set. - static method\n", + " fn new() -> Self;\n", + "\n", + " /// Creates a set from a slice of strings. - static method\n", + " fn from_slice(values: &[&str]) -> Self;\n", + "\n", + " /// Adds a string to the set.\n", + " fn add(&mut self, value: &str);\n", + "\n", + " /// Removes a string from the set.\n", + " fn contains(&self, value: &str) -> bool;\n", + "}\n", + "\n", + "struct HashSet {\n", + " data: std::collections::HashSet\n", + "}\n", + "\n", + "impl StringSet for HashSet {\n", + " fn new() -> Self {\n", + " HashSet{data: std::collections::HashSet::new()}\n", + " }\n", + "\n", + " fn from_slice(values: &[&str]) -> Self {\n", + " let mut set = HashSet::new();\n", + " for value in values {\n", + " set.add(value);\n", + " }\n", + " set\n", + " }\n", + "\n", + " fn add(&mut self, value: &str) {\n", + " self.data.insert(value.to_string());\n", + " }\n", + "\n", + " fn contains(&self, value: &str) -> bool {\n", + " self.data.contains(value)\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " println!(\"Creating set:\");\n", + " let mut set = HashSet::new();\n", + " set.add(\"apple\");\n", + " set.add(\"banana\");\n", + " set.add(\"orange\");\n", + "\n", + " println!(\"Set contains 'apple': {}\", set.contains(\"apple\"));\n", + " println!(\"Set contains 'grape': {}\", set.contains(\"grape\"));\n", + "\n", + " println!(\"\\nCreating set from slice:\");\n", + "\n", + " let set = HashSet::from_slice(&[\"apple\", \"banana\", \"orange\"]);\n", + " println!(\"Set contains 'apple': {}\", set.contains(\"apple\"));\n", + " println!(\"Set contains 'grape': {}\", set.contains(\"grape\"));\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fully qualified methods\n", + "\n", + "* A fully qualified method call is a way to explicitly specify which trait’s method you want to call, especially when:\n", + " - Multiple traits define methods with the same name.\n", + " - You’re calling a trait method that isn’t in scope via automatic dispatch.\n", + " - You want to call a method on a trait for a type that implements it, but the method isn’t available via dot syntax.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Drawing a deck of cards on the screen\n", + "Drawing a card from the deck\n", + "Drawing a card from the deck\n" + ] + } + ], + "source": [ + "mod FullyQualifiedSyntax {\n", + " \n", + " pub trait Drawable {\n", + " fn draw(&self);\n", + " }\n", + "\n", + " pub trait Deck {\n", + " fn draw(&self) -> String;\n", + " }\n", + "\n", + " pub struct DeckOfCards;\n", + "\n", + " impl Drawable for DeckOfCards {\n", + " fn draw(&self) {\n", + " println!(\"Drawing a deck of cards on the screen\");\n", + " }\n", + " }\n", + "\n", + " impl Deck for DeckOfCards {\n", + " fn draw(&self) -> String {\n", + " println!(\"Drawing a card from the deck\");\n", + " \"Ace of Spades\".to_string()\n", + " }\n", + " }\n", + "}\n", + "\n", + "use FullyQualifiedSyntax::*;\n", + "\n", + "let deck_of_cards = DeckOfCards;\n", + "\n", + "// deck_of_cards.draw(); // ambigous call of draw() method\n", + "\n", + "FullyQualifiedSyntax::Drawable::draw(&deck_of_cards);\n", + "Deck::draw(&deck_of_cards);\n", + "::draw(&deck_of_cards);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reverse-Engineering Bounds\n", + "\n", + "* When writing generic function or structs with trait bounds, it is often useful to reverse-engineer the bounds from the function body.\n", + "* This approach helps in understanding the minimum requirements for the types used in generics.\n", + "* Compiler errors can guide the addition of necessary trait bounds." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### First attempt" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn dot(v1: &[N], v2: &[N]) -> N {\n", + " let mut total: N = N::default();\n", + " for i in 0 .. v1.len() {\n", + " total = total + v1[i] * v2[i];\n", + " }\n", + " total\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### After reading compiler errors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::ops::{Add, Mul};\n", + "\n", + "fn dot + Add + Copy>(v1: &[N], v2: &[N]) -> N \n", + "{\n", + " let mut total: N = N::default();\n", + " for i in 0 .. v1.len() {\n", + " total = total + v1[i] * v2[i];\n", + " }\n", + " total\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let v1 = [1, 2, 3];\n", + "let v2 = vec![4, 5, 6];\n", + "\n", + "dot(&v1[..], &v2[..])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Generics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generic Function" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn min(a: T, b: T) -> T {\n", + " if a < b {\n", + " a\n", + " } else {\n", + " b\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "min(1, 2)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"hello\"" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "min(\"hello\", \"world\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generic Structure\n", + "\n", + "* Generic structures allow us to define data types that can hold values of any type" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Popped: 3\n", + "Popped: 2\n", + "Popped: 1\n" + ] + } + ], + "source": [ + "struct Stack {\n", + " items: Vec\n", + "}\n", + "\n", + "impl Stack {\n", + " fn push(&mut self, item: T) {\n", + " self.items.push(item);\n", + " }\n", + "\n", + " fn pop(&mut self) -> Option {\n", + " self.items.pop()\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let mut stack = Stack{items: Vec::new()};\n", + " stack.push(1);\n", + " stack.push(2);\n", + " stack.push(3);\n", + "\n", + " while let Some(item) = stack.pop() {\n", + " println!(\"Popped: {}\", item);\n", + " }\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generic Traits\n", + "\n", + "* Traits can also be generic over types" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " /// std::ops::Mul, the trait for types that support `*`.\n", + " pub trait Mul {\n", + " /// The resulting type after applying the `*` operator\n", + " type Output;\n", + "\n", + " /// The method for the `*` operator\n", + " fn mul(self, rhs: RHS) -> Self::Output;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Result: -5 + 10i\n" + ] + } + ], + "source": [ + "#[derive(Debug)]\n", + "struct Complex {\n", + " real: f64,\n", + " imag: f64\n", + "}\n", + "\n", + "use std::ops::Mul;\n", + "\n", + "impl Mul for Complex {\n", + " type Output = Complex;\n", + "\n", + " fn mul(self, rhs: Complex) -> Complex {\n", + " Complex {\n", + " real: self.real * rhs.real - self.imag * rhs.imag,\n", + " imag: self.real * rhs.imag + self.imag * rhs.real\n", + " }\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let a = Complex{real: 1.0, imag: 2.0};\n", + " let b = Complex{real: 3.0, imag: 4.0};\n", + "\n", + " let c = a.mul(b);\n", + " println!(\"Result: {} + {}i\", c.real, c.imag);\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/10_smart_pointers.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/10_smart_pointers.ipynb new file mode 100644 index 0000000..fc98235 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/10_smart_pointers.ipynb @@ -0,0 +1,536 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Smart Pointers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `Box`\n", + "\n", + "* A smart pointer that allocates data on the heap.\n", + "* Provides ownership for the heap data.\n", + "* Dereferences to the data it points to.\n", + "* Useful for recursive data types and large data on the heap." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Storing Data on Heap" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let on_heap = Box::new(5);\n", + " println!(\"{}\", on_heap);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Recursive Data Structures" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "enum List {\n", + " Cons(i32, Box), // Box allows for recursive data structures\n", + " Nil,\n", + "}\n", + "\n", + "use List::{Cons, Nil};\n", + "\n", + "fn main() {\n", + " let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Dereferencing" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let x = 5;\n", + " let y = &x;\n", + " let z = Box::new(x);\n", + "\n", + " assert!(5 == x);\n", + " assert!(5 == *y);\n", + " assert!(5 == *z);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### How it works" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Creating MyBox with some data!\n", + "Dropping MyBox with some data!\n" + ] + } + ], + "source": [ + "struct MyBox(T);\n", + "\n", + "impl MyBox {\n", + " fn new(x: T) -> MyBox {\n", + " println!(\"Creating MyBox with some data!\");\n", + " MyBox(x)\n", + " }\n", + "}\n", + " \n", + "use std::ops::Deref;\n", + "\n", + "impl Deref for MyBox {\n", + " type Target = T;\n", + "\n", + " fn deref(&self) -> &Self::Target {\n", + " &self.0\n", + " }\n", + "}\n", + "\n", + "impl Drop for MyBox {\n", + " fn drop(&mut self) {\n", + " println!(\"Dropping MyBox with some data!\");\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let x = 5;\n", + " let y = MyBox::new(x);\n", + "\n", + " assert!(5 == x);\n", + " assert!(5 == *y);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deref coercion\n", + "\n", + "* Deref coercion converts a reference to a type that implements the `Deref` trait into a reference to another type. For example, deref coercion can convert `&String` to `&str` because `String` implements the `Deref` trait such that it returns `&str`" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hello, John!\n", + "Hello, Jane!\n", + "Creating MyBox with some data!\n", + "Hello, Rust!\n", + "Dropping MyBox with some data!\n" + ] + } + ], + "source": [ + "fn hello(name: &str) {\n", + " println!(\"Hello, {}!\", name);\n", + "}\n", + "\n", + "fn main() {\n", + " hello(\"John\");\n", + " \n", + " let name = String::from(\"Jane\");\n", + " hello(&name);\n", + "\n", + " let m = MyBox::new(String::from(\"Rust\"));\n", + " hello(&m);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `Rc` (Reference Counted Smart Pointer)\n", + "\n", + "* `Rc` enables multiple ownership of the same data; `Rc` keeps track of the number of references to a value which determines whether or not a value is still in use.\n", + "* `Rc` is only for use in single-threaded scenarios. For multi-threaded code, `Arc` should be used.\n", + "* `Rc` points to immutable data. If you need mutability, put a `RefCell` inside the `Rc`." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using Value(\"foo\"). RC: 3\n", + "Using Value(\"bar\"). RC: 2\n", + "v_inner - Value(\"bar\") - it was only one strong reference\n", + "Dropping Value(\"bar\")\n", + "Value(\"foo\")\n", + "---------------------------------\n", + "Dropping Value(\"foo\")\n", + "---------------------------------\n" + ] + } + ], + "source": [ + "use std::collections::HashMap;\n", + "use std::rc::Rc;\n", + "\n", + "#[derive(Debug)]\n", + "struct Value(String);\n", + "\n", + "impl Value {\n", + " fn value(&self) -> &String {\n", + " &self.0\n", + " }\n", + "}\n", + "\n", + "impl Drop for Value {\n", + " fn drop(&mut self) {\n", + " println!(\"Dropping {:?}\", self);\n", + " }\n", + "}\n", + "\n", + "fn use_value(data: Rc) {\n", + " println!(\"Using {:?}. RC: {}\", data, Rc::strong_count(&data));\n", + "}\n", + "\n", + "fn main() {\n", + " let mut map = HashMap::<&str, Rc>::new();\n", + " {\n", + " let v_foo = Rc::new(Value(\"foo\".to_string()));\n", + " map.insert(\"foo\", v_foo.clone());\n", + " use_value(v_foo.clone());\n", + " {\n", + " let v_bar = Rc::new(Value(\"bar\".to_string()));\n", + " use_value(v_bar.clone());\n", + " \n", + " let v_inner = Rc::into_inner(v_bar);\n", + "\n", + " match v_inner {\n", + " Some(value) => {\n", + " println!(\"v_inner - {:?} - it was only one strong reference\", value);\n", + " }\n", + " None => {\n", + " println!(\"v_inner is None - more than one reference to it\");\n", + " }\n", + " }\n", + " } \n", + " }\n", + "\n", + " println!(\"{:?}\", map.get(\"foo\").unwrap());\n", + "\n", + " println!(\"---------------------------------\");\n", + "\n", + " map.clear();\n", + "\n", + " println!(\"---------------------------------\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `Weak` - Breaking Reference Cycles\n", + "\n", + "* `Weak` is a version of `Rc` that holds a non-owning reference. The object that `Weak` points to is only valid if there is at least one `Rc` pointing to it.\n", + "* `Weak` is useful in breaking reference cycles. A reference cycle is a situation where two objects reference each other, preventing either from being cleaned up." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Memory Leak - Reference Cycles" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "John: John (30) is not married\n", + "Jane: Jane (25) is not married\n", + "Dropping Jane (25)\n", + "Dropping John (30)\n" + ] + } + ], + "source": [ + "use std::cell::RefCell;\n", + "use std::fmt::{self, Display, Formatter, Debug};\n", + "\n", + "struct Human {\n", + " name: String,\n", + " age: u32,\n", + "\n", + " partner: RefCell>>,\n", + "}\n", + "\n", + "impl Debug for Human {\n", + " fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n", + " write!(f, \"{} ({})\", self.name, self.age)\n", + " }\n", + "}\n", + "\n", + "impl Display for Human {\n", + " fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n", + " write!(f, \"{} ({})\", self.name, self.age);\n", + "\n", + " if let Some(partner) = &*self.partner.borrow() {\n", + " write!(f, \" is married to {}\", partner.name)\n", + " } else {\n", + " write!(f, \" is not married\")\n", + " }\n", + " }\n", + "}\n", + "\n", + "impl Drop for Human {\n", + " fn drop(&mut self) {\n", + " println!(\"Dropping {:?}\", self);\n", + " }\n", + "}\n", + "\n", + "impl Human {\n", + " fn new(name: &str, age: u32) -> Rc {\n", + " Rc::new(Human {\n", + " name: name.to_string(),\n", + " age,\n", + " partner: RefCell::new(None),\n", + " })\n", + " }\n", + "}\n", + "\n", + "fn marry(h1: Rc, h2: Rc) {\n", + " *h1.partner.borrow_mut() = Some(h2.clone());\n", + " *h2.partner.borrow_mut() = Some(h1.clone());\n", + "}\n", + "\n", + "fn main() {\n", + " let mut john = Human::new(\"John\", 30);\n", + " let mut jane = Human::new(\"Jane\", 25);\n", + "\n", + " //marry(john.clone(), jane.clone()); // this creates a reference cycle -> memory leak\n", + "\n", + " println!(\"John: {}\", john);\n", + " println!(\"Jane: {}\", jane);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Breaking Reference Cycles with `Weak`" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "John: John (30) is married to Jane - Rc::strong_count(1)\n", + "Jane: Jane (25) is married to John - Rc::strong_count(1)\n", + "Dropping Jane (25)\n", + "John: John (30) is widow/widower\n", + "Dropping John (30)\n" + ] + } + ], + "source": [ + "use std::rc::{Rc, Weak}\n", + "use std::cell::RefCell;\n", + "use std::fmt::{self, Display, Formatter};\n", + "\n", + "struct Human {\n", + " name: String,\n", + " age: u32,\n", + "\n", + " partner: RefCell>>,\n", + "}\n", + "\n", + "impl Debug for Human {\n", + " fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n", + " write!(f, \"{} ({})\", self.name, self.age)\n", + " }\n", + "}\n", + "\n", + "impl Display for Human {\n", + " fn fmt(&self, f: &mut Formatter) -> fmt::Result {\n", + " write!(f, \"{} ({})\", self.name, self.age);\n", + "\n", + " if let Some(partner) = &*self.partner.borrow() {\n", + " if let Some(partner) = partner.upgrade() {\n", + " write!(f, \" is married to {}\", partner.name)\n", + " } else {\n", + " write!(f, \" is widow/widower\")\n", + " }\n", + " } else {\n", + " write!(f, \" is not married\")\n", + " }\n", + " }\n", + "}\n", + "\n", + "impl Drop for Human {\n", + " fn drop(&mut self) {\n", + " println!(\"Dropping {:?}\", self);\n", + " }\n", + "}\n", + "\n", + "impl Human {\n", + " fn new(name: &str, age: u32) -> Rc {\n", + " Rc::new(Human {\n", + " name: name.to_string(),\n", + " age,\n", + " partner: RefCell::new(None),\n", + " })\n", + " }\n", + "}\n", + "\n", + "fn marry(h1: Rc, h2: Rc) {\n", + " *h1.partner.borrow_mut() = Some(Rc::downgrade(&h2));\n", + " *h2.partner.borrow_mut() = Some(Rc::downgrade(&h1));\n", + "}\n", + "\n", + "fn main() {\n", + " let mut john: Rc = Human::new(\"John\", 30);\n", + " let mut jane: Rc = Human::new(\"Jane\", 25);\n", + "\n", + " marry(john.clone(), jane.clone());\n", + "\n", + " println!(\"John: {} - Rc::strong_count({})\", john, Rc::strong_count(&john));\n", + " println!(\"Jane: {} - Rc::strong_count({})\", jane, Rc::strong_count(&jane));\n", + "\n", + " std::mem::drop(jane);\n", + " println!(\"John: {}\", john);\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/11_internal_mutability.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/11_internal_mutability.ipynb new file mode 100644 index 0000000..db01653 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/11_internal_mutability.ipynb @@ -0,0 +1,453 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Interior Mutability" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::collections::HashMap;\n", + "\n", + "struct DnsService {\n", + " lookup_table: HashMap\n", + "}\n", + "\n", + "impl DnsService {\n", + " fn lookup(&self, hostname: &str) -> Option<&str> {\n", + " self.lookup_table.get(hostname).map(|s| s.as_str())\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some(\"1.22.33.1\")\n" + ] + } + ], + "source": [ + "let dns_service = DnsService {\n", + " lookup_table: [(\"www.example.com\".to_string(), \"143.33.44.4\".to_string()),\n", + " (\"www.rust-lang.org\".to_string(), \"1.22.33.1\".to_string()),\n", + " (\"www.mozilla.org\".to_string(), \"2.43.44.3\".to_string())\n", + " ].iter().cloned().collect()\n", + "};\n", + "\n", + "println!(\"{:?}\", dns_service.lookup(\"www.rust-lang.org\"));" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "cannot assign to `self.counter`, which is behind a `&` reference", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0594] Error:\u001b[0m cannot assign to `self.counter`, which is behind a `&` reference", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_4:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m 9 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mk\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m-\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mO\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m<\u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m help: consider changing this to be a mutable reference: `mut `", + " \u001b[38;5;246m10 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54mf\u001b[0m\u001b[38;5;54m.\u001b[0m\u001b[38;5;54mc\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mu\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m+\u001b[0m\u001b[38;5;54m=\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m1\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m`\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m`\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249md\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `self` is a `&` reference, so the data it refers to cannot be written", + "\u001b[38;5;246m────╯\u001b[0m" + ] + } + ], + "source": [ + "use std::collections::HashMap;\n", + "\n", + "struct DnsService {\n", + " lookup_table: HashMap,\n", + " counter: i32\n", + "}\n", + "\n", + "impl DnsService {\n", + " fn lookup(&self, hostname: &str) -> Option<&str> {\n", + " self.counter += 1; // Error: cannot assign to `self.counter` because it is borrowed\n", + " self.lookup_table.get(hostname).map(|s| s.as_str())\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## `Cell` - for `Copy` types" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::collections::HashMap;\n", + "use std::cell::Cell;\n", + "\n", + "struct DnsService {\n", + " lookup_table: HashMap,\n", + " counter: Cell\n", + "}\n", + "\n", + "impl DnsService {\n", + " fn lookup(&self, hostname: &str) -> Option<&str> {\n", + " let value = self.counter.get();\n", + " self.counter.set(value + 1);\n", + " self.lookup_table.get(hostname).map(|s| s.as_str())\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "The type of the variable dns_service was redefined, so was lost.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some(\"1.22.33.1\")\n", + "Some(\"143.33.44.4\")\n", + "None\n", + "Dns used 3 times\n" + ] + } + ], + "source": [ + "let dns_service = DnsService {\n", + " lookup_table: [(\"www.example.com\".to_string(), \"143.33.44.4\".to_string()),\n", + " (\"www.rust-lang.org\".to_string(), \"1.22.33.1\".to_string()),\n", + " (\"www.mozilla.org\".to_string(), \"2.43.44.3\".to_string())\n", + " ].iter().cloned().collect(), \n", + " counter: Cell::new(0)\n", + "};\n", + "\n", + "println!(\"{:?}\", dns_service.lookup(\"www.rust-lang.org\"));\n", + "println!(\"{:?}\", dns_service.lookup(\"www.example.com\"));\n", + "println!(\"{:?}\", dns_service.lookup(\"www.infotraining.pl\"));\n", + "\n", + "println!(\"Dns used {} times\", dns_service.counter.get());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## RefCell - for non-`Copy` types" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::collections::HashMap;\n", + "use std::cell::RefCell;\n", + "\n", + "type LookupLog = Vec;\n", + "\n", + "struct DnsService {\n", + " lookup_table: HashMap,\n", + " lookup_log: RefCell,\n", + "}\n", + "\n", + "impl DnsService {\n", + " fn lookup(&self, hostname: &str) -> Option<&str> {\n", + " self.lookup_log.borrow_mut().push(hostname.to_string());\n", + " self.lookup_table.get(hostname).map(|s| s.as_str())\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some(\"1.22.33.1\")\n", + "Some(\"143.33.44.4\")\n", + "None\n", + "Dns looked up for [\"www.rust-lang.org\", \"www.example.com\", \"www.infotraining.pl\"]\n" + ] + } + ], + "source": [ + "let dns_service = DnsService {\n", + " lookup_table: [(\"www.example.com\".to_string(), \"143.33.44.4\".to_string()),\n", + " (\"www.rust-lang.org\".to_string(), \"1.22.33.1\".to_string()),\n", + " (\"www.mozilla.org\".to_string(), \"2.43.44.3\".to_string())\n", + " ].iter().cloned().collect(), \n", + " lookup_log: RefCell::new(Vec::new())\n", + "};\n", + "\n", + "println!(\"{:?}\", dns_service.lookup(\"www.rust-lang.org\"));\n", + "println!(\"{:?}\", dns_service.lookup(\"www.example.com\"));\n", + "println!(\"{:?}\", dns_service.lookup(\"www.infotraining.pl\"));\n", + "\n", + "println!(\"Dns looked up for {:?}\", dns_service.lookup_log.borrow());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## RwLock - for thread-safe shared access" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::collections::HashMap;\n", + "use std::cell::RefCell;\n", + "\n", + "type LookupLog = Vec;\n", + "\n", + "struct DnsService {\n", + " lookup_table: HashMap,\n", + " lookup_log: RefCell,\n", + "}\n", + "\n", + "impl DnsService {\n", + " fn from_slice(data: &[(&str, &str)]) -> DnsService {\n", + " DnsService {\n", + " lookup_table: data.iter().cloned().map(|(k, v)| (k.to_string(), v.to_string())).collect(),\n", + " lookup_log: RefCell::new(Vec::new())\n", + " }\n", + " }\n", + "\n", + " fn lookup(&self, hostname: &str) -> Option<&str> {\n", + " self.lookup_log.borrow_mut().push(hostname.to_string());\n", + " self.lookup_table.get(hostname).map(|s| s.as_str())\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "`RefCell>` cannot be shared between threads safely", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0277] Error:\u001b[0m `RefCell>` cannot be shared between threads safely", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_11:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;54m╭\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249m1\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100mh\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100ma\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100mp\u001b[0m\u001b[38;5;100ma\u001b[0m\u001b[38;5;100mw\u001b[0m\u001b[38;5;100mn\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;68m|\u001b[0m\u001b[38;5;68m|\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m{\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m required by a bound introduced by this call", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m note: required because it's used within this closure", + " \u001b[38;5;240m ┆\u001b[0m \u001b[38;5;54m┆\u001b[0m ", + " \u001b[38;5;246m8 │\u001b[0m \u001b[38;5;54m├\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m▶\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m}\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `RefCell>` cannot be shared between threads safely", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;115mNote\u001b[0m: note: required because it appears within the type `DnsService`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "fn main() {\n", + " let dns_service = DnsService::from_slice(&[(\"www.example.com\", \"122.33.22.33\"), (\"www.rust-lang.org\", \"1.2.3.4\"), (\"www.mozilla.org\", \"9.4.3.4\")]);\n", + "\n", + " println!(\"{:?}\", dns_service.lookup(\"www.rust-lang.org\"));\n", + "\n", + " let thd_1 = std::thread::spawn(|| {\n", + " println!(\"{:?}\", dns_service.lookup(\"www.example.com\"));\n", + " });\n", + "\n", + " thd_1.join().unwrap();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Arc + RwLock - for shared access across threads" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::collections::HashMap;\n", + "use std::sync::RwLock;\n", + "use std::borrow::BorrowMut;\n", + "\n", + "type LookupLog = Vec;\n", + "\n", + "struct DnsService {\n", + " lookup_table: HashMap,\n", + " lookup_log: RwLock,\n", + "}\n", + "\n", + "impl DnsService {\n", + " fn from_slice(data: &[(&str, &str)]) -> DnsService {\n", + " DnsService {\n", + " lookup_table: data.iter().cloned().map(|(k, v)| (k.to_string(), v.to_string())).collect(),\n", + " lookup_log: RwLock::new(Vec::new())\n", + " }\n", + " }\n", + "\n", + " fn lookup(&self, hostname: &str) -> Option<&str> {\n", + " let mut log = self.lookup_log.write().unwrap();\n", + " log.push(hostname.to_string());\n", + " self.lookup_table.get(hostname).map(|s| s.as_str())\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some(\"1.2.3.4\")\n", + "Some(\"122.33.22.33\")\n", + "Some(\"9.4.3.4\")\n", + "Dns looked up for [\"www.rust-lang.org\", \"www.example.com\", \"www.mozilla.org\"]\n" + ] + } + ], + "source": [ + "use std::sync::Arc;\n", + "\n", + "fn main() {\n", + " let dns_service = Arc::new(\n", + " DnsService::from_slice(&[(\"www.example.com\", \"122.33.22.33\"), (\"www.rust-lang.org\", \"1.2.3.4\"), (\"www.mozilla.org\", \"9.4.3.4\")])\n", + " );\n", + "\n", + " println!(\"{:?}\", dns_service.lookup(\"www.rust-lang.org\"));\n", + "\n", + " let mut dns = dns_service.clone();\n", + " let thd_1 = std::thread::spawn(move || {\n", + " println!(\"{:?}\", dns.lookup(\"www.example.com\"));\n", + " });\n", + "\n", + " let mut dns = dns_service.clone();\n", + " let thd_2 = std::thread::spawn(move || {\n", + " println!(\"{:?}\", dns.lookup(\"www.mozilla.org\"));\n", + " });\n", + "\n", + " thd_1.join().unwrap();\n", + " thd_2.join().unwrap();\n", + "\n", + " println!(\"Dns looked up for {:?}\", dns_service.lookup_log.read().unwrap());\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/12_operator_overloading.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/12_operator_overloading.ipynb new file mode 100644 index 0000000..cd2001c --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/12_operator_overloading.ipynb @@ -0,0 +1,1055 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Operator overloading\n", + "\n", + "Rust allows you to overload operators by implementing specific traits from the `std::ops` module." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "| Category | Trait | Operator |\n", + "| -------------------- | -------------------------- | --------------------------------- |\n", + "| Unary operators | `std::ops::Neg` | `-x ` |\n", + "| | `std::ops::Not` | `!x ` |\n", + "| Arithmetic operators | `std::ops::Add` | `x + y` |\n", + "| | `std::ops::Sub` | `x - y` |\n", + "| | `std::ops::Mul` | `x * y` |\n", + "| | `std::ops::Div` | `x / y` |\n", + "| | `std::ops::Rem` | `x % y` |\n", + "| Bitwise operators | `std::ops::BitAnd` | `x & y` |\n", + "| | `std::ops::BitOr ` | `x \\| y` |\n", + "| | `std::ops::BitXor` | `x ^ y` |\n", + "| | `std::ops::Shl ` | `x << y` |\n", + "| | `std::ops::Shr ` | `x >> y` |\n", + "| Compound assignment arithmetic operators | `std::ops::AddAssign` | `x += y` |\n", + "| | `std::ops::MulAssign` | `x *= y` |\n", + "| | `std::ops::SubAssign` | `x -= y` |\n", + "| | `std::ops::DivAssign` | `x /= y` |\n", + "| | `std::ops::RemAssign` | `x %= y` |\n", + "| Compound assignment bitwise operators | `std::ops::BitAndAssign` | `x &= y` |\n", + "| | `std::ops::BitOrAssign ` | `x \\| = y` |\n", + "| | `std::ops::BitXorAssign` | `x ^= y` |\n", + "| | `std::ops::ShlAssign ` | `x <<= y` |\n", + "| | `std::ops::ShrAssign ` | `x >>= y` |\n", + "| Comparison | `std::cmp::PartialEq ` | `x == y`, `x != y` |\n", + "| | `std::cmp::PartialOrd` | `x < y,  x <= y,  x > y,  x >= y` |\n", + "| Indexing | `std::ops::Index` | `x[y],  &x[y]` |\n", + "| | `std::ops::IndexMut` | `x[y] = z,  &mut x[y]` |" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "#[derive(Debug, Clone, Copy)]\n", + "struct Complex {\n", + " re: T,\n", + " im: T,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let z: Complex = Complex { re: 1.0, im: 2.0 };" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "an implementation of `Add` might be missing for `Complex`", + "output_type": "error", + "traceback": [ + "\u001b[31mError:\u001b[0m an implementation of `Add` might be missing for `Complex`" + ] + } + ], + "source": [ + "z + z" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementing `Add` trait" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Add {\n", + " type Output;\n", + " fn add(self, rhs: RHS) -> Self::Output;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::ops::Add;\n", + "\n", + "impl Add for Complex \n", + "where T: Add {\n", + " type Output = Self; \n", + " \n", + " fn add(self, rhs: Complex) -> Self::Output {\n", + " Complex {\n", + " re: self.re + rhs.re,\n", + " im: self.im + rhs.im,\n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Complex { re: 2.0, im: 4.0 }" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z + z" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementing `Neg` trait" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Neg {\n", + " type Output;\n", + " fn neg(self) -> Self::Output;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "an implementation of `std::ops::Neg` might be missing for `Complex`", + "output_type": "error", + "traceback": [ + "\u001b[31mError:\u001b[0m an implementation of `std::ops::Neg` might be missing for `Complex`" + ] + } + ], + "source": [ + "-z" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::ops::Neg;\n", + "\n", + "impl Neg for Complex\n", + "where T: Neg {\n", + " type Output = Self;\n", + " \n", + " fn neg(self) -> Self::Output {\n", + " Complex {\n", + " re: -self.re,\n", + " im: -self.im,\n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Complex { re: -1.0, im: -2.0 }" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "-z" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compound Assignment Operators: `+=`, `-=`, `*=`, `/=`, `%=`" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " trait AddAssign {\n", + " fn add_assign(&mut self, rhs: RHS);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::ops::AddAssign;\n", + "\n", + "impl AddAssign for Complex\n", + "where T: AddAssign {\n", + " fn add_assign(&mut self, rhs: Complex) {\n", + " self.re += rhs.re;\n", + " self.im += rhs.im;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Complex { re: 4.0, im: 6.0 }" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut c = Complex { re: 1.0, im: 2.0 };\n", + "\n", + "c += Complex { re: 3.0, im: 4.0 };\n", + "\n", + "c" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Equality and Ordering\n", + "\n", + "### PartialEq\n", + "\n", + "* in order to use `==` and `!=` operators, implement `PartialEq` trait" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " trait PartialEq \n", + " where RHS: ?Sized\n", + " {\n", + " fn eq(&self, other: &RHS) -> bool; \n", + " fn ne(&self, other: &RHS) -> bool { !self.eq(other) }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::cmp::PartialEq;\n", + "\n", + "impl PartialEq for Complex {\n", + " fn eq(&self, other: &Complex) -> bool {\n", + " self.re == other.re && self.im == other.im\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* now we can check for equality using `==` and `!=` operators" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "true" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z == z" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "true" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "z != c" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### PartialEq & f64\n", + "\n", + " * Mathematical definition of an equivalence relation, of which equality is one instance, imposes three requirements. For any values `x` and `y`:\n", + " * If `x == y` is true, then `y == x` must be true as well. In other words, swapping the two sides of an equality comparison doesn’t affect the result.\n", + " * If `x == y` and `y == z`, then it must be the case that `x == z`. Given any chain of values, each equal to the next, each value in the chain is directly equal to every other. Equality is contagious.\n", + " * It must always be true that `x == x`.\n", + "\n", + " Types `f32` and `f64` are IEEE standard floating-point values. According to that standard, expressions like `0.0/0.0` and others with no appropriate value must produce special not-a-number values, usually referred to as `NaN` values. The standard further requires that a `NaN` value be treated as unequal to every other value—including itself." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "assert!(f64::is_nan(0.0 / 0.0));\n", + "\n", + "assert_eq!(0.0/0.0 == 0.0/0.0, false);\n", + "assert_eq!(0.0/0.0 != 0.0/0.0, true);" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NaN == NaN yields false\n" + ] + } + ], + "source": [ + "println!(\"{:?} == {:?} yields {:?}\", 0.0/0.0, f64::NAN, 0.0/0.0 == f64::NAN);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While Rust’s `==` operator meets the first two requirements for equivalence relations, it clearly doesn’t meet the third when used on IEEE floating-point values. This is called a partial equivalence relation, so Rust uses the name `PartialEq` for the `==` operator’s built-in trait." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Eq - Full equivalence relation\n", + "\n", + "If you’d prefer your generic code to require a full equivalence relation, you can instead use the `std::cmp::Eq` trait as a bound, which represents a full equivalence relation: if a type implements `Eq`, then `x == x` must be `true` for every value `x` of that type. \n", + "\n", + "In practice, almost every type that implements `PartialEq` should implement `Eq` as well; `f32` and `f64` are the only types in the standard library that are `PartialEq` but not `Eq`.\n", + "\n", + "The standard library defines `Eq` as an extension of `PartialEq`, adding no new methods:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Eq : PartialEq {}\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Implementing `Eq` for `Complex` is straightforward:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "impl Eq for Complex {}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We could also derive `Eq` for generic types that implement `PartialEq`:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "#[derive(Copy, Clone, Debug, PartialEq, Eq)]\n", + "struct Value {\n", + " value: T\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Derived implementations on a generic type may depend on the type parameters. With the derive attribute, `Value` would implement `Eq`, because `i32` does, but `Value` would implement only `PartialEq`, since `f32` doesn’t implement `Eq`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### PartialOrd" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Any comparison for `f64` or `f32` involving `NaN` will return `false`:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "assert_eq!(0.0/0.0 < 0.0/0.0, false);\n", + "assert_eq!(0.0/0.0 > 0.0/0.0, false);\n", + "assert_eq!(0.0/0.0 <= 0.0/0.0, false);\n", + "assert_eq!(0.0/0.0 >= 0.0/0.0, false);" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " enum Ordering {\n", + " Less,\n", + " Equal,\n", + " Greater,\n", + " }\n", + "\n", + " trait PartialOrd: PartialEq \n", + " where \n", + " RHS: ?Sized \n", + " {\n", + " fn partial_cmp(&self, other: &RHS) -> Option;\n", + " \n", + " fn lt(&self, other: &RHS) -> bool { \n", + " match self.partial_cmp(other) {\n", + " Some(Ordering::Less) => true,\n", + " _ => false,\n", + " }\n", + " }\n", + " \n", + " fn le(&self, other: &RHS) -> bool { \n", + " match self.partial_cmp(other) {\n", + " Some(Ordering::Less) | Some(Ordering::Equal) => true,\n", + " _ => false,\n", + " }\n", + " }\n", + " \n", + " fn gt(&self, other: &RHS) -> bool { \n", + " match self.partial_cmp(other) {\n", + " Some(Ordering::Greater) => true,\n", + " _ => false,\n", + " }\n", + " }\n", + " \n", + " fn ge(&self, other: &RHS) -> bool { \n", + " match self.partial_cmp(other) {\n", + " Some(Ordering::Greater) | Some(Ordering::Equal) => true,\n", + " _ => false,\n", + " }\n", + " }\n", + " }\n", + "\n", + " // trait for types that can be totally ordered\n", + " trait Ord: Eq + PartialOrd {\n", + " fn cmp(&self, other: &Self) -> Ordering;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "#[derive(PartialEq, Eq, Debug)]\n", + "struct Interval {\n", + " lower: T, // inclusive\n", + " upper: T // exclusive \n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::cmp::{Ordering, PartialOrd};\n", + "\n", + "impl PartialOrd for Interval {\n", + " fn partial_cmp(&self, other: &Interval) -> Option {\n", + " if self == other {\n", + " Some(Ordering::Equal)\n", + " } else if self.lower >= other.upper {\n", + " Some(Ordering::Greater)\n", + " } else if self.upper <= other.lower {\n", + " Some(Ordering::Less)\n", + " } else {\n", + " None\n", + " } \n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "assert!(Interval { lower: 10, upper: 20 } < Interval { lower: 20, upper: 40 });\n", + "assert!(Interval { lower: 7, upper: 8 } >= Interval { lower: 0, upper: 1 });\n", + "assert!(Interval { lower: 7, upper: 8 } <= Interval { lower: 7, upper: 8 });\n", + "\n", + "// Overlapping intervals aren't ordered with respect to each other.\n", + "let left = Interval { lower: 10, upper: 30 };\n", + "let right = Interval { lower: 20, upper: 40 };\n", + "assert!(!(left < right));\n", + "assert!(!(left >= right));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### PartialOrd & f64\n", + "\n", + "Floating-point numbers have a `PartialOrd` implementation, because `NaN` is neither greater than, less than, nor equal to any other value." + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "None" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "3.14.partial_cmp(&f64::NAN)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "None" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f64::NAN.partial_cmp(&f64::NAN)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Indexing\n", + "\n", + "* In order to use indexing expression `obj[i]` for a given type we have to implement `Index` (and optionally `IndexMut`) trait:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " \n", + " trait Index {\n", + " type Output: ?Sized;\n", + " fn index(&self, index: Idx) -> &Self::Output;\n", + " }\n", + "\n", + " trait IndexMut: Index {\n", + " fn index_mut(&mut self, index: Idx) -> &mut Self::Output;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "struct Image

{\n", + " width: usize,\n", + " pixels: Vec

,\n", + "}\n", + "\n", + "impl Image

{\n", + "\n", + " /// Create a new image with the given dimensions.\n", + " fn new(width: usize, height: usize) -> Self {\n", + " Image {\n", + " width: width,\n", + " pixels: vec![P::default(); width * height],\n", + " }\n", + " }\n", + "}\n", + "\n", + "impl

std::ops::Index for Image

{\n", + " type Output = [P];\n", + " \n", + " fn index(&self, row: usize) -> &[P] {\n", + " let start = row * self.width;\n", + " &self.pixels[start..start + self.width]\n", + " }\n", + "}\n", + "\n", + "impl

std::ops::IndexMut for Image

{\n", + " fn index_mut(&mut self, row: usize) -> &mut [P] {\n", + " let start = row * self.width;\n", + " &mut self.pixels[start..start + self.width]\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let mut bitmap = Image::new(2, 2);\n", + "\n", + "bitmap[0][0] = 1;\n", + "bitmap[0][1] = 2;\n", + "\n", + "assert!(bitmap[0][0] == 1);\n", + "assert!(bitmap[0][1] == 2);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Sorting floats" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Common `sort()` method from `std::slice::SliceExt` trait doesn't work for floats because of the `NaN` values:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "the trait bound `{float}: Ord` is not satisfied", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0277] Error:\u001b[0m the trait bound `{float}: Ord` is not satisfied", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_30:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249md\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m the trait `Ord` is not implemented for `{float}`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let mut data = vec![2.1, 3.14, 0.3, 0.271, 0.0, 5.34, 1.0, 0.0];\n", + "\n", + "data.sort();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* When we assume that `NaN` is not present in the slice, we can use `sort_by()` method from `std::slice::SliceExt` trait combined with `partial_cmp()` method from `std::cmp::PartialOrd` trait:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[-inf, NaN, NaN, 0.0, 0.271, 0.3, 1.0, 2.1, 3.14, 3.141592653589793, 5.34, inf]\n" + ] + } + ], + "source": [ + "let mut data = vec![2.1, 3.14, 0.3, 0.271, 0.0, std::f64::NAN, 5.34, 1.0, 0.0/0.0, std::f64::consts::PI, std::f64::INFINITY, std::f64::NEG_INFINITY];\n", + "\n", + "data.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Less));\n", + "\n", + "println!(\"{:?}\", data);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Total ordering for floats\n", + "\n", + "* Since Rust 1.64 we can use `f64::total_cmp()` method. It returns `Ordering` enum which can be used to sort floats.\n", + "\n", + "* The floating point values are totally ordered in the following sequence:\n", + "\n", + " * negative quiet NaN\n", + " * negative signaling NaN\n", + " * negative infinity\n", + " * negative numbers\n", + " * negative subnormal numbers\n", + " * negative zero\n", + " * positive zero\n", + " * positive subnormal numbers\n", + " * positive numbers\n", + " * positive infinity\n", + " * positive signaling NaN\n", + " * positive quiet NaN." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Greater" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "f64::NAN.total_cmp(&std::f64::consts::PI)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[-inf, 0.0, 0.271, 0.3, 1.0, 2.1, 3.14, 3.141592653589793, 5.34, inf, NaN, NaN]\n" + ] + } + ], + "source": [ + "let mut data = vec![2.1, 3.14, 0.3, 0.271, 0.0, std::f64::NAN, 5.34, 1.0, 0.0/0.0, std::f64::consts::PI, std::f64::INFINITY, std::f64::NEG_INFINITY];\n", + "\n", + "// data.sort_by(|a, b| f64::total_cmp(a, b));\n", + "data.sort_by(f64::total_cmp);\n", + "\n", + "println!(\"{:?}\", data);" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/13_utility_traits.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/13_utility_traits.ipynb new file mode 100644 index 0000000..d52f854 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/13_utility_traits.ipynb @@ -0,0 +1,1177 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Utility Traits " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Drop\n", + "\n", + "The `Drop` trait is used to run some code when a value goes out of scope. It is similar to the destructor in C++." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + ":dep libc" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening file - FileDescriptor { fd: 43, file_name: \"file_descriptor_test1.txt\" }\n", + "Writing \"Hello, world!\" to file FileDescriptor { fd: 43, file_name: \"file_descriptor_test1.txt\" }\n", + "Opening file - FileDescriptor { fd: 44, file_name: \"file_descriptor_test2.txt\" }\n", + "Writing \"Rust is awesome!\" to file FileDescriptor { fd: 44, file_name: \"file_descriptor_test2.txt\" }\n", + "Closing file - FileDescriptor { fd: 43, file_name: \"file_descriptor_test1.txt\" }\n", + "Writing \"Goodbye, world!\" to file FileDescriptor { fd: 44, file_name: \"file_descriptor_test2.txt\" }\n", + "Closing file - FileDescriptor { fd: 44, file_name: \"file_descriptor_test2.txt\" }\n" + ] + }, + { + "data": { + "text/plain": [ + "Ok(())" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "//use std::os::unix::io::{RawFd, FromRawFd, AsRawFd};\n", + "//use std::fs::File;\n", + "use std::io::{self};\n", + "use std::os::raw::c_int;\n", + "use std::str;\n", + "\n", + "static mut FD: i32 = 42;\n", + "\n", + "#[derive(Debug)]\n", + "pub struct FileDescriptor {\n", + " fd: c_int,\n", + " file_name: String\n", + "}\n", + "\n", + "impl FileDescriptor {\n", + " // Create a new FileDescriptor from a RawFd\n", + " pub fn new(path: &str) -> Self {\n", + " // real implementation under unix\n", + " // let fd = unsafe { libc::open(path.as_ptr() as *const i8, libc::O_RDWR) };\n", + " // FileDescriptor { fd }\n", + "\n", + " let fd: FileDescriptor;\n", + " unsafe {\n", + " FD += 1;\n", + " fd = FileDescriptor{ fd: FD, file_name: path.to_string() };\n", + " }\n", + " println!(\"Opening file - {:?}\", fd);\n", + " fd\n", + " }\n", + "\n", + " // Write to the file descriptor\n", + " pub fn write(&self, buf: &[u8]) -> io::Result {\n", + " // real implementation\n", + " // let mut file = unsafe { File::from_raw_fd(self.fd) };\n", + " // let result = file.write(buf);\n", + " // std::mem::forget(file); // Prevent the file from being closed\n", + " // result\n", + "\n", + " println!(r###\"Writing \"{}\" to file {:?}\"###, str::from_utf8(buf).unwrap(), self);\n", + " return Ok(buf.len());\n", + " }\n", + "}\n", + "\n", + "impl Drop for FileDescriptor {\n", + " fn drop(&mut self) {\n", + " // real implementation\n", + " // unsafe {\n", + " // libc::close(self.fd);\n", + " // }\n", + "\n", + " println!(\"Closing file - {:?}\", self);\n", + " }\n", + "}\n", + "\n", + "fn main() -> io::Result<()> {\n", + " let mut fd1 = FileDescriptor::new(\"file_descriptor_test1.txt\"); // 1 is the file descriptor for stdout\n", + " {\n", + " fd1.write(b\"Hello, world!\")?;\n", + " \n", + " let fd2 = FileDescriptor::new(\"file_descriptor_test2.txt\");\n", + " fd2.write(\"Rust is awesome!\".as_bytes())?;\n", + "\n", + " fd1 = fd2;\n", + " }\n", + "\n", + " fd1.write(\"Goodbye, world!\".as_bytes())?;\n", + "\n", + " Ok(())\n", + "}\n", + "\n", + "main()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Sized Trait" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sized Types\n", + "\n", + "* A sized type is one whose values all have the same size in memory. \n", + " * Almost all types in Rust are sized:\n", + " * `u64` takes eight bytes\n", + " * `(f32, f32, f32)` tuple takes twelve bytes \n", + " * Structs & Enums are sized\n", + " * `String` and `Vec` own a heap-allocated buffer whose size can vary, the `Vec` value itself is a pointer to the buffer, its capacity, and its length, so `Vec` is a sized type.\n", + "* All sized types implement the `std::marker::Sized` trait, which has no methods or associated types. \n", + " * Rust implements it automatically for all types to which it applies; you can’t implement it yourself\n", + " * Traits of this sort are called *marker traits*, because the Rust language itself uses them to mark certain types as having characteristics of interest.\n", + "* The only use for `Sized` is as a bound for type variables: a bound like `T: Sized` requires `T` to be a type whose size is known at compile time.\n", + "\n", + "* Most generic type variables should be restricted to `Sized` types. Therefore it is the implicit default trait in Rust:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "struct AStruct {\n", + " a: T,\n", + "}\n", + "\n", + "// is equivalent to\n", + "\n", + "struct AStruct {\n", + " a: T,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Unsized Types\n", + "\n", + "* However, Rust also has a few unsized types whose values are not all the same size. \n", + " * The string slice type `str` (without an `&`) is unsized. The string literals `\"diminutive\"` and `\"big\"` are `str` objects that occupy ten and three bytes.\n", + " * Array slice types like `[T]` (again, without an `&`) are unsized. A shared reference like `&[u8]` can point to a `[u8]` slice of any size. Because the `str` and `[T]` types denote sets of values of varying sizes, they are unsized types.\n", + " * `dyn Type` is unsized - but `&dyn std::io::Write` and `Box` are fat pointers (two words wide: a pointer to a slice also carries the slice’s length, and a trait object also carries a pointer to a vtable of method implementations) and are sized\n", + "\n", + "If you plan to use unsized type in a generic context, you need to use the `?Sized` bound. This is a special trait bound that relaxes the requirement that the type must be sized. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "struct BoxedValue {\n", + " value: Box,\n", + "}\n", + "\n", + "let boxed: BoxedValue<[i32]> = BoxedValue {\n", + " value: Box::new([1, 2, 3]),\n", + "};" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clone" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The `std::clone::Clone` trait is for types that can make copies of themselves. `Clone` is defined as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Clone: Sized {\n", + " fn clone(&self) -> Self;\n", + " fn clone_from(&mut self, source: &Self) { \n", + " *self = source.clone(); \n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The `clone()` method should construct an independent copy of `self` and return it. Since this method’s return type is `Self`, and functions may not return unsized values, the `Clone` trait itself extends the `Sized` trait: this has the effect of bounding implementations’ `Self` types to be `Sized`.\n", + "* The `clone_from()` method modifies `self` into a copy of source. The default definition of `clone_from()` simply clones source, and then moves that into `*self`. This is implemented by default as `*self = source.clone()`, but may be more efficient for types that can reuse allocated resources." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Deriving `Clone`\n", + "\n", + "If your `Clone` implementation simply applies clone to each field or element of your type, and then constructs a new value from those clones, and the default definition of `clone_from` is good enough, then put `#[derive(Clone)]` above your type definition:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Debug, Clone, PartialEq)]\n", + "struct Data {\n", + " name: String,\n", + " id: i32,\n", + " data: Vec,\n", + "}\n", + "\n", + "let user_data = Data { name: \"John\".to_string(), id: 42, data: vec![1, 2, 3] };\n", + "let mut backup_data = user_data.clone();\n", + "\n", + "assert_eq!(user_data, backup_data);\n", + "\n", + "backup_data.name = \"Jane\".to_string();\n", + "\n", + "assert_ne!(user_data, backup_data);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Copy\n", + "\n", + "* A type is `Copy` if it implements the `std::marker::Copy` marker trait, which is defined as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Copy: Clone { }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Implementing the `Copy` trait is a marker that a type’s values can be duplicated simply by copying bits. This trait has no methods, and the compiler enforces the property that `Copy` types cannot implement the `Drop` trait." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Clone)]\n", + "struct ValueData {\n", + " id: u64,\n", + " data: f64\n", + "}\n", + "\n", + "impl Copy for ValueData { }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The simplest way to make a type `Copy` is to derive it:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Copy, Clone)]\n", + "struct ValueData {\n", + " id: u64,\n", + " data: f64\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Deref & DerefMut" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* You can specify how dereferencing operators like `*` and `.` behave on your types by implementing the `std::ops::Deref` and `std::ops::DerefMut` traits. \n", + "* Pointer types like `Box` and `Rc` implement these traits so that they can behave as Rust’s built-in pointer types do." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "The type of the variable user_data was redefined, so was lost.\n", + "The type of the variable backup_data was redefined, so was lost.\n" + ] + } + ], + "source": [ + ":dep num-complex\n", + "\n", + "use num_complex::Complex;\n", + "\n", + "let z: Box> = Box::new(Complex{re: 1.0, im: 2.0});\n", + "\n", + "assert_eq!((*z).re, 1.0);\n", + "assert_eq!(z.re, 1.0); // deref coercion" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Deref {\n", + " type Target: ?Sized;\n", + " fn deref(&self) -> &Self::Target;\n", + " }\n", + " \n", + " trait DerefMut: Deref {\n", + " fn deref_mut(&mut self) -> &mut Self::Target;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The `Deref` and `DerefMut` are used for **deref coercions**. \n", + " * Since deref takes a `&Self` reference and returns a `&Self::Target` reference, Rust uses this to **automatically convert references of the former type into the latter**. \n", + " * In other words, if inserting a deref call would prevent a type mismatch, Rust inserts one for you. Implementing `DerefMut` enables the corresponding conversion for mutable references. These are called the **deref coercions: one type is being “coerced” into behaving as another**.\n", + "* Examples of *deref coersions*:\n", + " * if you have a `Rc` value `r`, you can call `r.len()` even though `Rc` does not have a `len` method. This is because `Rc` implements `Deref`, so Rust automatically inserts a `*` dereference to convert `r` into a `&String` reference, and then calls `len()` on that reference.\n", + " * you can call `split_at()` on `String` values, even though `String` does not have a `split_at()` method. This is because `String` implements `Deref`, so Rust automatically inserts a `*` dereference to convert the `&String` into a `&str` reference, and then calls `split_at()` on that reference.\n", + " * if you have some `Rc` value r, and want to apply `String::find` to it, you can simply write `r.find('?')`, instead of `(*r).find('?')`: the method call implicitly borrows `r`, and `&Rc` coerces to `&String`, because `Rc` implements `Deref`" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "10" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "use std::rc::Rc;\n", + "\n", + "let r: Rc = Rc::new(\"HelloWorld\".to_string());\n", + "\n", + "r.len() // r will coerce to &String" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(\"Hello\", \"World\")" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "r.split_at(5) // r will coerce to &String, then &String will coerce to &str which has the method split_at" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Implementing `Deref` & `DerefMut`" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "struct Selector {\n", + " /// Elements available in this `Selector`.\n", + " elements: Vec,\n", + "\n", + " /// The index of the \"current\" element in `elements`. A `Selector`\n", + " /// behaves like a pointer to the current element.\n", + " current: usize\n", + "}\n", + "\n", + "use std::ops::{Deref, DerefMut};\n", + "\n", + "impl Deref for Selector {\n", + " type Target = T;\n", + " fn deref(&self) -> &T {\n", + " &self.elements[self.current]\n", + " }\n", + "}\n", + "\n", + "impl DerefMut for Selector {\n", + " fn deref_mut(&mut self) -> &mut T {\n", + " &mut self.elements[self.current]\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "let mut s = Selector { elements: vec!['x', 'y', 'z'], current: 2 };\n", + "\n", + "// Because `Selector` implements `Deref`, we can use the `*` operator to\n", + "// refer to its current element.\n", + "assert_eq!(*s, 'z');\n", + "\n", + "// Assert that 'z' is alphabetic, using a method of `char` directly on a\n", + "// `Selector`, via deref coercion.\n", + "assert!(s.is_alphabetic());\n", + "\n", + "// Change the 'z' to a 'w', by assigning to the `Selector`'s referent.\n", + "*s = 'w';\n", + "\n", + "assert_eq!(s.elements, ['x', 'y', 'w']);" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ugly\n" + ] + } + ], + "source": [ + "let s = Selector { \n", + " elements: vec![\"good\", \"bad\", \"ugly\"],\n", + " current: 2 \n", + "};\n", + "\n", + "fn show_it(thing: &str) { \n", + " println!(\"{}\", thing); \n", + "}\n", + "\n", + "show_it(&s); // OK because `&Selector` can be coerced to `&T`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Rust do not apply coercion when checking whether the bound `T: Trait` is satisfied" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "use std::fmt::Display;\n", + "\n", + "fn show_it_generic(thing: T) { println!(\"{}\", thing); }\n", + "\n", + "//show_it_generic(&s); // error[E0277]: the trait bound `&Selector<&str>: Display` is not satisfied" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ugly\n" + ] + } + ], + "source": [ + "show_it_generic(&s as &str); // OK" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Default\n", + "\n", + "* Some types have a reasonably obvious default value: \n", + " * the default vector or string is empty, \n", + " * the default number is zero, \n", + " * the default `Option` is `None`, and so on. \n", + "* Types like this can implement the `std::default::Default` trait:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Default {\n", + " fn default() -> Self;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Explicit implementaiton of `Default`:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Complex { re: 0.0, im: 0.0 }" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#[derive(Debug)]\n", + "struct Complex {\n", + " re: T,\n", + " im: T,\n", + "}\n", + "\n", + "impl Default for Complex where T: Default {\n", + " fn default() -> Self {\n", + " Complex { re: Default::default(), im: Default::default() }\n", + " }\n", + "}\n", + "\n", + "Complex::::default()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* If a type `T` implements `Default`, then the standard library implements `Default` automatically for `Rc`, `Arc`, `Box`, `Cell`, `RefCell`, `Cow`, `Mutex`, and `RwLock`. The default value for the type `Rc`, for example, is an `Rc` pointing to the default value for type `T`.\n", + "\n", + "* If all the element types of a tuple type implement `Default`, then the tuple type does too, defaulting to a tuple holding each element’s default value.\n", + "\n", + "* Rust does not implicitly implement `Default` for struct types, but if all of a struct’s fields implement `Default`, you can implement `Default` for the struct automatically using `#[derive(Default)]`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Usage of common use of `Default` is to produce default values for structs that represent a large collection of parameters, most of which you won’t usually need to change" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Player { name: \"John\", age: 42, level: 0, score: 0, record_score: 0 }" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#[derive(Default, Debug)]\n", + "struct Player {\n", + " name: String,\n", + " age: u8,\n", + " level: u8,\n", + " score: u32,\n", + " record_score: u32\n", + "}\n", + "\n", + "let player = Player { \n", + " name: \"John\".to_string(),\n", + " age: 42,\n", + " ..Default::default()\n", + "};\n", + "\n", + "player" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## AsRef and AsMut\n", + "\n", + "* When a type implements `AsRef`, that means you can borrow a `&T` from it efficiently. `AsMut` is the analogue for mutable references. " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " trait AsRef {\n", + " fn as_ref(&self) -> &T;\n", + " }\n", + " \n", + " trait AsMut {\n", + " fn as_mut(&mut self) -> &mut T;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* For example:\n", + " * `Vec` implements `AsRef<[T]>` \n", + " * `String` implements `AsRef` and you can also borrow a `String`’s contents as an array of bytes, so `String` implements `AsRef<[u8]>` as well." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `AsRef` is typically used **to make functions more flexible in the argument types they accept**." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing data: SomeData { values: [1, 2, 3] }\n", + "Processing data: [1, 2, 3]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing data: [1, 2, 3, 4, 5, 6]\n" + ] + } + ], + "source": [ + "use std::fmt::Debug;\n", + "\n", + "#[derive(Debug)]\n", + "struct SomeData {\n", + " values: Vec\n", + "}\n", + "\n", + "impl AsRef<[i32]> for SomeData {\n", + " fn as_ref(&self) -> &[i32] {\n", + " &self.values\n", + " }\n", + "}\n", + "\n", + "fn process_data + Debug>(data: T) {\n", + " println!(\"Processing data: {:?}\", data);\n", + "}\n", + "\n", + "let data = SomeData { values: vec![1, 2, 3] };\n", + "process_data(&data);\n", + "\n", + "let v = vec![1, 2, 3];\n", + "process_data(&v);\n", + "\n", + "let a = [1, 2, 3, 4, 5, 6];\n", + "process_data(&a);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Borrow and BorrowMut\n", + "\n", + "* The `std::borrow::Borrow` trait is similar to `AsRef`: if a type implements `Borrow`, then its borrow method efficiently borrows a `&T` from it. \n", + "* But `Borrow` imposes more restrictions: a type should implement `Borrow` only when a `&T` hashes and compares the same way as the value it’s borrowed from. (Rust doesn’t enforce this; it’s just the documented intent of the trait.) This makes Borrow valuable in dealing with keys in hash tables and trees or when dealing with values that will be hashed or compared for some other reason.\n", + " * `String` implements `AsRef`, `AsRef<[u8]>`, and `AsRef`, but those three target types will generally have different hash values. Only the `&str` slice is guaranteed to hash like the equivalent `String`, so `String` implements only `Borrow`." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Borrow {\n", + " fn borrow(&self) -> &Borrowed;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## From and Into" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The `std::convert::From` and `std::convert::Into` traits represent **conversions that consume a value of one type and return a value of another**. \n", + "* Whereas the `AsRef` and `AsMut` traits borrow a reference of one type from another, `From` and `Into` take ownership of their argument, transform it, and then return ownership of the result back to the caller." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " trait From: Sized {\n", + " fn from(other: T) -> Self;\n", + " }\n", + "\n", + " trait Into: Sized {\n", + " fn into(self) -> T;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The standard library automatically implements the trivial conversion from each type to itself: every type `T` implements `From` and `Into`\n", + "\n", + "* Although the traits simply provide two ways to do the same thing, they lend themselves to different uses." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "### Using Into\n", + "* You generally use `Into` to make your functions more flexible in the arguments they accept. For example, if you write:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pinging address: 23.21.68.141\n", + "Ok(true)\n", + "Pinging address: 66.146.219.98\n", + "Ok(true)\n", + "Pinging address: 208.118.235.148\n", + "Ok(true)\n" + ] + } + ], + "source": [ + "use std::net::Ipv4Addr;\n", + "fn ping(address: A) -> std::io::Result\n", + " where A: Into\n", + "{\n", + " let ipv4_address = address.into();\n", + " // rest implementation\n", + " println!(\"Pinging address: {}\", ipv4_address);\n", + " Ok(true)\n", + "}\n", + "\n", + "println!(\"{:?}\", ping(Ipv4Addr::new(23, 21, 68, 141))); // pass an Ipv4Addr\n", + "println!(\"{:?}\", ping([66, 146, 219, 98])); // pass a [u8; 4]\n", + "println!(\"{:?}\", ping(0xd076eb94_u32)); // pass a u32" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Using From" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " * The `from` method serves as a generic constructor for producing an instance of a type from some other single value. \n", + " * For example, rather than `Ipv4Addr` having two methods named `from_array` and `from_u32`, it simply implements `From<[u8;4]>` and `From`, allowing us to write:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "let addr1 = Ipv4Addr::from([66, 146, 219, 98]);\n", + "let addr2 = Ipv4Addr::from(0xd076eb94_u32);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Given an appropriate `From` implementation, the standard library automatically implements the corresponding `Into` trait. \n", + "* When you define your own type, if it has single-argument constructors, you should write them as implementations of `From` for the appropriate types; you’ll get the corresponding `Into` implementations for free.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `from` and `into` methods take ownership of their arguments (conversion can reuse the original values)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[72, 101, 108, 108, 111, 32, 82, 117, 115, 116, 121, 33]\n" + ] + } + ], + "source": [ + "let text = \"Hello Rusty!\".to_string();\n", + "let bytes: Vec = text.into();\n", + "println!(\"{:?}\", bytes);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `From` and `Into` are infallible traits—their API requires that conversions will not fail" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-1454759936\n" + ] + } + ], + "source": [ + "let huge = 2_000_000_000_000i64;\n", + "let smaller: i32 = huge as i32;\n", + "println!(\"{:?}\", smaller);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## TryFrom & TryInto\n", + "\n", + "* `TryFrom` and `TryInto` are the fallible cousins of `From` and `Into` and are similarly reciprocal; implementing `TryFrom` means that `TryInto` is implemented as well." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " pub trait TryFrom: Sized {\n", + " type Error;\n", + " fn try_from(value: T) -> Result;\n", + " }\n", + "\n", + " pub trait TryInto: Sized {\n", + " type Error;\n", + " fn try_into(self) -> Result;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2147483647" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let huge = 2_000_000_000_000i64;\n", + "let smaller = huge.try_into().unwrap_or(i32::MAX);\n", + "smaller" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2147483647" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let smaller: i32= huge.try_into().unwrap_or_else(|_| {\n", + " if huge >= 0 {\n", + " i32::MAX\n", + " } else {\n", + " i32::MIN\n", + " }\n", + "});\n", + "smaller" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ToOwned" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `The std::borrow::ToOwned` trait provides a slightly looser (than `Clone` trait) way to convert a reference to an owned value:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " trait ToOwned {\n", + " type Owned: std::borrow::Borrow;\n", + " fn to_owned(&self) -> Self::Owned;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Unlike `clone`, which must return exactly `Self`, `to_owned` can return anything you could borrow a `&Self` from: the `Owned` type must implement `Borrow`. You can borrow a `&[T]` from a `Vec`, so `[T]` can implement `ToOwned>`, as long as `T` implements `Clone`, so that we can copy the slice’s elements into the vector" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Clone on Write (COW)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `Cow` either borrows a shared reference to a `B`, or owns a value which we could borrow such a reference. This can be useful when you have a value that is sometimes owned and sometimes borrowed." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "mod explain {\n", + " enum Cow<'a, B: ?Sized + 'a> \n", + " where B: ToOwned \n", + " {\n", + " Borrowed(&'a B),\n", + " Owned(::Owned),\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The requested item could not be found.\n", + "An error occurred: Disk full\n" + ] + } + ], + "source": [ + "use std::borrow::Cow;\n", + "\n", + "enum Error {\n", + " NotFound,\n", + " PermissionDenied,\n", + " OutOfMemory,\n", + " Other(String),\n", + "}\n", + "\n", + "fn describe_error(error: &Error) -> Cow<'static, str> {\n", + " match error {\n", + " Error::NotFound => Cow::Borrowed(\"The requested item could not be found.\"),\n", + " Error::PermissionDenied => Cow::Borrowed(\"You do not have permission to access the requested item.\"),\n", + " Error::OutOfMemory => Cow::Borrowed(\"The system is out of memory.\"),\n", + " Error::Other(message) => Cow::Owned(format!(\"An error occurred: {}\", message)),\n", + " }\n", + "}\n", + "\n", + "let error_not_found = Error::NotFound;\n", + "let error_other = Error::Other(\"Disk full\".to_string());\n", + "\n", + "println!(\"{}\", describe_error(&error_not_found));\n", + "println!(\"{}\", describe_error(&error_other));" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/14_closures.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/14_closures.ipynb new file mode 100644 index 0000000..df1005e --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/14_closures.ipynb @@ -0,0 +1,718 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Functions & Closures" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "People ordered by age: [Person { name: \"Marry\", age: 11, height: 1.5 }, Person { name: \"Adam\", age: 16, height: 1.6 }, Person { name: \"Alice\", age: 20, height: 1.7 }, Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }, Person { name: \"David\", age: 35, height: 1.75 }, Person { name: \"Eve\", age: 40, height: 1.75 }]\n", + "People ordered by height: [Person { name: \"Marry\", age: 11, height: 1.5 }, Person { name: \"Adam\", age: 16, height: 1.6 }, Person { name: \"Alice\", age: 20, height: 1.7 }, Person { name: \"David\", age: 35, height: 1.75 }, Person { name: \"Eve\", age: 40, height: 1.75 }, Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }]\n" + ] + } + ], + "source": [ + "#[derive(Debug, PartialEq, Clone)]\n", + "struct Person {\n", + " name: String,\n", + " age: u8,\n", + " height: f32\n", + "}\n", + "\n", + "fn create_people() -> Vec {\n", + " vec![\n", + " Person {\n", + " name: \"Alice\".to_string(),\n", + " age: 20,\n", + " height: 1.7\n", + " },\n", + " Person {\n", + " name: \"Bob\".to_string(),\n", + " age: 25,\n", + " height: 1.8\n", + " },\n", + " Person {\n", + " name: \"Charlie\".to_string(),\n", + " age: 30,\n", + " height: 1.9\n", + " },\n", + " Person {\n", + " name: \"David\".to_string(),\n", + " age: 35,\n", + " height: 1.75\n", + " },\n", + " Person {\n", + " name: \"Eve\".to_string(),\n", + " age: 40,\n", + " height: 1.75\n", + " },\n", + " Person {\n", + " name: \"Adam\".to_string(),\n", + " age: 16,\n", + " height: 1.6\n", + " },\n", + " Person {\n", + " name: \"Marry\".to_string(),\n", + " age: 11,\n", + " height: 1.5\n", + " }\n", + " ]\n", + "}\n", + "\n", + "let people = create_people();\n", + "\n", + "let people_sorted_by_age = {\n", + " let mut people = people.clone();\n", + " people.sort_by_key(|person| person.age);\n", + " people\n", + "};\n", + "\n", + "let people_sorted_by_height = {\n", + " let mut people = people.clone();\n", + " people.sort_by(|person, other| f32::total_cmp(&person.height, &other.height));\n", + " people\n", + "};\n", + "\n", + "println!(\"People ordered by age: {:?}\", people_sorted_by_age);\n", + "println!(\"People ordered by height: {:?}\", people_sorted_by_height);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Closures Borrow by Default" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "People with translated names: [Person { name: \"Alicja\", age: 20, height: 1.7 }, Person { name: \"Robert\", age: 25, height: 1.8 }, Person { name: \"Karol\", age: 30, height: 1.9 }, Person { name: \"Dawid\", age: 35, height: 1.75 }, Person { name: \"Ewa\", age: 40, height: 1.75 }, Person { name: \"Adam\", age: 16, height: 1.6 }, Person { name: \"Maria\", age: 11, height: 1.5 }]\n" + ] + } + ], + "source": [ + "use std::collections::HashMap;\n", + "\n", + "fn create_name_translator() -> HashMap {\n", + " [\n", + " (\"Alice\".to_string(), \"Alicja\".to_string()),\n", + " (\"Bob\".to_string(), \"Robert\".to_string()),\n", + " (\"Charlie\".to_string(), \"Karol\".to_string()),\n", + " (\"David\".to_string(), \"Dawid\".to_string()),\n", + " (\"Eve\".to_string(), \"Ewa\".to_string()),\n", + " (\"Adam\".to_string(), \"Adam\".to_string()),\n", + " (\"Marry\".to_string(), \"Maria\".to_string())\n", + " ].iter().cloned().collect()\n", + "}\n", + "\n", + "fn main() {\n", + " let people = create_people();\n", + "\n", + " let name_translator: HashMap<_, _> = create_name_translator();\n", + "\n", + " let person_name_translator = |person: &Person| {\n", + " let translated_name = name_translator.get(&person.name).unwrap_or(&person.name); // name_translator is borrowed\n", + " Person {\n", + " name: translated_name.to_string(),\n", + " age: person.age,\n", + " height: person.height\n", + " }\n", + " };\n", + "\n", + " let people_translated: Vec = people.iter().map(person_name_translator).collect();\n", + "\n", + " println!(\"People with translated names: {:?}\", people_translated);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Closures That Move" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "People with translated names: [Person { name: \"Alicja\", age: 20, height: 1.7 }, Person { name: \"Robert\", age: 25, height: 1.8 }, Person { name: \"Karol\", age: 30, height: 1.9 }, Person { name: \"Dawid\", age: 35, height: 1.75 }, Person { name: \"Ewa\", age: 40, height: 1.75 }, Person { name: \"Adam\", age: 16, height: 1.6 }, Person { name: \"Maria\", age: 11, height: 1.5 }]\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let people: Vec = create_people();\n", + " let name_translator: HashMap = create_name_translator();\n", + "\n", + " let person_name_translator = move |person: &Person| {\n", + " let translated_name = name_translator.get(&person.name).unwrap_or(&person.name); // name_translator is moved\n", + " Person {\n", + " name: translated_name.to_string(),\n", + " age: person.age,\n", + " height: person.height\n", + " }\n", + " };\n", + "\n", + " let thd = std::thread::spawn(move || { \n", + " let people_translated: Vec = people.iter().map(person_name_translator).collect(); // people & person_name_translator are moved\n", + " \n", + " println!(\"People with translated names: {:?}\", people_translated);\n", + " });\n", + "\n", + " thd.join().unwrap();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Function & Closure Types" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Function Type" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "fn is_adult(person: &Person) -> bool {\n", + " person.age >= 18\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The function `is_adult` has a type: `fn(&Person) -> bool`" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adults: [Person { name: \"Alice\", age: 20, height: 1.7 }, Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }, Person { name: \"David\", age: 35, height: 1.75 }, Person { name: \"Eve\", age: 40, height: 1.75 }]\n" + ] + } + ], + "source": [ + "let f_predicate: fn(&Person) -> bool = is_adult;\n", + "\n", + "fn filter_by_predicate(people: &Vec, predicate: fn(&Person) -> bool) -> Vec {\n", + " let mut qualified: Vec = Vec::new();\n", + " for person in people {\n", + " if predicate(person) {\n", + " qualified.push(person.clone());\n", + " }\n", + " }\n", + " qualified\n", + "}\n", + "\n", + "let adults = filter_by_predicate(&people, is_adult);\n", + "\n", + "println!(\"Adults: {:?}\", adults);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When function expects a function as an argument, it accepts a closure that captures nothing:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tall people: [Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }]\n" + ] + } + ], + "source": [ + "let tall_people = filter_by_predicate(&people, |person| person.height > 1.75);\n", + "\n", + "println!(\"Tall people: {:?}\", tall_people);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A closure that captures variables from the enclosing scope cannot be passed to a function that accepts a function type:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "mismatched types", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0308] Error:\u001b[0m mismatched types", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_10:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mf\u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100ml\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100m_\u001b[0m\u001b[38;5;100mb\u001b[0m\u001b[38;5;100my\u001b[0m\u001b[38;5;100m_\u001b[0m\u001b[38;5;100mp\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100md\u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100mc\u001b[0m\u001b[38;5;100ma\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m,\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54m|\u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54m|\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;54m.\u001b[0m\u001b[38;5;54mh\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mg\u001b[0m\u001b[38;5;54mh\u001b[0m\u001b[38;5;54mt\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;54m>\u001b[0m\u001b[38;5;54m \u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68me\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mg\u001b[0m\u001b[38;5;68mh\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68m_\u001b[0m\u001b[38;5;68ml\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mm\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m arguments to this function are incorrect", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m expected fn pointer, found closure", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m `height_limit` captured here", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let height_limit = 1.75;\n", + "let tall_people = filter_by_predicate(&people, |person| person.height > height_limit);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `Fn` Trait" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Functions and closures implement the `Fn` trait, which is the trait of things that can be called like functions." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "fn filter_by_predicate(people: &Vec, predicate: F) -> Vec\n", + " where F: Fn(&Person) -> bool \n", + "{\n", + " let mut qualified: Vec = Vec::new();\n", + " for person in people {\n", + " if predicate(person) {\n", + " qualified.push(person.clone());\n", + " }\n", + " }\n", + " qualified\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adults: [Person { name: \"Alice\", age: 20, height: 1.7 }, Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }, Person { name: \"David\", age: 35, height: 1.75 }, Person { name: \"Eve\", age: 40, height: 1.75 }]\n", + "Tall people: [Person { name: \"Bob\", age: 25, height: 1.8 }, Person { name: \"Charlie\", age: 30, height: 1.9 }]\n" + ] + } + ], + "source": [ + "let adults = filter_by_predicate(&people, is_adult); // using function pointer\n", + "println!(\"Adults: {:?}\", adults);\n", + "\n", + "let height_limit = 1.75;\n", + "let tall_people = filter_by_predicate(&people, |person| person.height > height_limit); // using closure that captures (borrows) height_limit\n", + "println!(\"Tall people: {:?}\", tall_people);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Now the function `filter_by_predicate` is generic. It accepts any function or closure `F` that implements the special trait `Fn(&Person) -> bool`.\n", + "* The trait `Fn(&Person) -> bool` automatically implemented by all functions and closures that can be called with a `&Person` argument and return a `bool`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `FnMut` Trait\n", + "\n", + "* The `FnMut` trait is a subtrait of `Fn`. It allows the closure to mutate the captured variables." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Generated number: 11\n", + "Generated number: 12\n", + "Generated number: 13\n" + ] + } + ], + "source": [ + "fn create_generator(seed: i32) -> impl FnMut() -> i32 {\n", + " let mut state = seed;\n", + " move || {\n", + " state += 1; // mutating captured state\n", + " state\n", + " }\n", + "}\n", + "\n", + "fn main() {\n", + " let mut generator = create_generator(10);\n", + " println!(\"Generated number: {}\", generator());\n", + " println!(\"Generated number: {}\", generator());\n", + " println!(\"Generated number: {}\", generator());\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "fn main() {\n", + " let mut state = 10;\n", + "\n", + " let mut f = || {\n", + " state += 1; // mutating captured state\n", + " state\n", + " };\n", + "\n", + " let mut boxed_f: Boxi32> = Box::new(f);\n", + "\n", + " boxed_f();\n", + " boxed_f();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### `FnOnce` Trait\n", + "\n", + "* Some functions and closures can be called only once. They are not allowed to be called multiple times.\n", + "* The trait `FnOnce` is the trait of things that can be called once. It is implemented by functions and closures that can be called only once.\n", + " * The `FnOnce` trait is a subtrait of the `FnMut` trait, which is a subtrait of the `Fn` trait." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "ename": "Error", + "evalue": "use of moved value: `f`", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0382] Error:\u001b[0m use of moved value: `f`", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_15:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m|\u001b[0m\u001b[38;5;249m|\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;68mm\u001b[0m\u001b[38;5;68my\u001b[0m\u001b[38;5;68m_\u001b[0m\u001b[38;5;68ms\u001b[0m\u001b[38;5;68mt\u001b[0m\u001b[38;5;68mr\u001b[0m\u001b[38;5;68mi\u001b[0m\u001b[38;5;68mn\u001b[0m\u001b[38;5;68mg\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m/\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249me\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m┬\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m note: closure cannot be invoked more than once because it moves the variable `my_string` out of its environment", + " \u001b[38;5;240m │\u001b[0m ", + " \u001b[38;5;246m5 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;37mf\u001b[0m\u001b[38;5;54m(\u001b[0m\u001b[38;5;54m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m┬\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;37m╰\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m\u001b[38;5;37m─\u001b[0m note: this value implements `FnOnce`, which causes it to be moved when called", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m│\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m `f` moved due to this call", + " \u001b[38;5;246m6 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mf\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m┬\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m value used here after move", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "fn main() {\n", + " let my_string = \"Hello, World!\".to_string();\n", + " let f = || drop(my_string); // this closure should be called only once\n", + "\n", + " f();\n", + " f();\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Data: [1, 2, 3, 4, 5, 665]\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let mut data = vec![1, 2, 3, 4, 5];\n", + "\n", + " let mut f_once = || {\n", + " data.push(665); // mutating captured data\n", + " data // moving the data out of the closure\n", + " };\n", + "\n", + " let mut f_once_boxed: Box Vec> = Box::new(f_once);\n", + "\n", + " let data = f_once_boxed();\n", + " println!(\"Data: {:?}\", data);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `Fn` is the family of closures and functions that you can call multiple times without restriction. This highest category also includes all fn functions.\n", + "\n", + "* `FnMut` is the family of closures that can be called multiple times if the closure itself is declared mut.\n", + "\n", + "* `FnOnce` is the family of closures that can be called once, if the caller owns the closure.\n", + "\n", + "* Every `Fn` meets the requirements for `FnMut`, and every `FnMut` meets the requirements for `FnOnce`" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [], + "source": [ + "// mod explain {\n", + "// // Pseudocode for `Fn`, `FnMut`, and `FnOnce` traits.\n", + "// trait Fn() -> R {\n", + "// fn call(&self) -> R;\n", + "// }\n", + "\n", + "// trait FnMut() -> R {\n", + "// fn call_mut(&mut self) -> R;\n", + "// }\n", + "\n", + "// trait FnOnce() -> R {\n", + "// fn call_once(self) -> R;\n", + "// }\n", + "// }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Copy and Clone for Closures\n", + "\n", + "* Just as Rust automatically figures out which closures can be called only once, it can figure out which closures can implement `Copy` and `Clone`, and which cannot." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "fn main() {\n", + " let y = 4;\n", + " let add_y = move |x| x + y;\n", + " let copy_of_add_y = add_y; // closure is copied\n", + " assert_eq!(add_y(copy_of_add_y(3)), 11);\n", + "\n", + " let word: String = \"Hello\".to_string();\n", + " let append_word = move |mut s: String| {\n", + " s.push_str(&word); \n", + " s\n", + " };\n", + " let copy_of_append_word = append_word.clone(); // closure is cloned\n", + " assert_eq!(append_word(copy_of_append_word(\" World\".to_string())), \" WorldHelloHello\");\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Callbacks" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "* Callbacks are functions that are passed as arguments to other functions.\n", + "* Callbacks are used to implement event handlers, asynchronous programming, and other patterns." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "type Callback = Box;\n", + "\n", + "struct TemperaturMonitor {\n", + " current_temp: f32,\n", + " on_temperature_changed: Option,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "impl TemperaturMonitor {\n", + " fn new() -> Self {\n", + " Self {\n", + " current_temp: 0.0,\n", + " on_temperature_changed: None,\n", + " }\n", + " }\n", + "\n", + " fn set_on_temperature_changed(&mut self, callback: F) {\n", + " self.on_temperature_changed = Some(Box::new(callback));\n", + " }\n", + "\n", + " fn set_current_temp(&mut self, temp: f32) {\n", + " self.current_temp = temp;\n", + " if let Some(ref on_temperature_changed) = self.on_temperature_changed {\n", + " on_temperature_changed(self.current_temp);\n", + " }\n", + " } \n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Rust makes us add this `'static bound`. Without it, the call to `Box::new(callback)` would be an error, because it’s not safe to store a closure if it contains borrowed references to variables that are about to go out of scope.)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Temperature changed to: 22.5\n", + "Temperature changed to: 23.5\n", + "Current temperature is: 24.7°C\n", + "Current temperature is: 27.1°C\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let mut monitor = TemperaturMonitor::new();\n", + " monitor.set_on_temperature_changed(|temp| println!(\"Temperature changed to: {}\", temp));\n", + " monitor.set_current_temp(22.5);\n", + " monitor.set_current_temp(23.5);\n", + "\n", + " monitor.set_on_temperature_changed(|temp| println!(\"Current temperature is: {}°C\", temp));\n", + " monitor.set_current_temp(24.7);\n", + " monitor.set_current_temp(27.1);\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/15_iterators.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/15_iterators.ipynb new file mode 100644 index 0000000..415b524 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/15_iterators.ipynb @@ -0,0 +1,2832 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Iterators" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Iterator & Iterable\n", + "\n", + "* An iterator is any value that implements `std::iter::Iterator` trait:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " trait Iterator {\n", + " type Item;\n", + " fn next(&mut self) -> Option;\n", + " //... many default methods\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* If there’s a natural way to iterate over some type, that type can implement `std::iter::IntoIterator`, whose `into_iter` method takes a value and returns an iterator over it:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " trait IntoIterator \n", + " //where Self::IntoIter::Item == Self::Item \n", + " {\n", + " type Item;\n", + " type IntoIter: Iterator;\n", + " \n", + " fn into_iter(self) -> Self::IntoIter;\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* An **iterator** is any type that implements `Iterator`.\n", + "\n", + "* An **iterable** is any type that implements `IntoIterator`: you can get an iterator over it by calling its `into_iter` method. The vector reference `&data` is the iterable in this case.\n", + "\n", + "* An iterator **produces** values.\n", + "\n", + "* The values an iterator produces are **items**.\n", + "\n", + "* The code that receives the items an iterator produces is the **consumer** (the for loop is the consumer)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## for loop" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fender\n", + "Gibson\n", + "PRS\n", + "Yamaha\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let data = vec![\"Fender\".to_string(), \"Gibson\".to_string(), \"PRS\".to_string(), \"Yamaha\".to_string()];\n", + "\n", + "for element in data {\n", + " println!(\"{}\", element);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* for loop is a shorthand for:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Fender\n", + "Gibson\n", + "PRS\n", + "Yamaha\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let data = vec![\"Fender\".to_string(), \"Gibson\".to_string(), \"PRS\".to_string(), \"Yamaha\".to_string()];\n", + "\n", + " let mut iterator = data.into_iter();\n", + " while let Some(element) = iterator.next() {\n", + " println!(\"{}\", element);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating iterators" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### iter() and iter_mut()\n", + "\n", + "* Most collection types and array slices like `&[T]` and `&mut [T]` provide `iter()` and `iter_mut()` methods that return the natural iterators over the type, producing a shared or mutable reference to each item" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{ \n", + " let data = vec![1, 1, 2, 3, 5, 8, 13];\n", + "\n", + " let mut iterator = data.iter();\n", + " assert_eq!(iterator.next(), Some(&1));\n", + " assert_eq!(iterator.next(), Some(&1));\n", + " assert_eq!(iterator.next(), Some(&2));\n", + " assert_eq!(iterator.next(), Some(&3));\n", + " assert_eq!(iterator.next(), Some(&5));\n", + " assert_eq!(iterator.next(), Some(&8));\n", + " assert_eq!(iterator.next(), Some(&13));\n", + " assert_eq!(iterator.next(), None);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* This iterator’s item type is `&i32`: each call to next produces a reference to the next element, until we reach the end of the vector." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iteration over &str\n", + "\n", + "* There is no `iter()` method on the `&str` string slice type. Instead, if `s` is a `&str`, then `s.bytes()` returns an iterator that produces each byte of s, whereas `s.chars()` interprets the contents as UTF-8 and produces each Unicode character." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### IntoIterator implementations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* When a type implements `IntoIterator`, you can call its `into_iter()` method yourself, just as a for loop would:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::collections::BTreeSet;\n", + "\n", + "let mut guitars = BTreeSet::new();\n", + "guitars.insert(\"Yamaha\".to_string());\n", + "guitars.insert(\"Fender\".to_string());\n", + "guitars.insert(\"Gibson\".to_string());\n", + "\n", + "let mut iterator = guitars.into_iter();\n", + "assert_eq!(iterator.next(), Some(\"Fender\".to_string()));\n", + "assert_eq!(iterator.next(), Some(\"Gibson\".to_string()));\n", + "assert_eq!(iterator.next(), Some(\"Yamaha\".to_string()));\n", + "assert_eq!(iterator.next(), None);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Most collections provide several implementations of `IntoIterator`, for shared references `(&T)`, mutable references `(&mut T)`, and owned values`(T)`:\n", + " * Given a **shared reference to the collection**, `into_iter()` returns an iterator that produces shared references to its items. For example, in the preceding code, `(&favorites).into_iter()` would return an iterator whose `Item` type is `&String`.\n", + " * Given a **mutable reference to the collection**, `into_iter()` returns an iterator that produces mutable references to the items. For example, if vector is some `Vec`, the call `(&mut vector).into_iter()` returns an iterator whose `Item` type is `&mut String`.\n", + " * When passed the **collection by value**, `into_iter()` returns an iterator that takes ownership of the collection and returns items by value; \n", + " * The items’ ownership moves from the collection to the consumer, and the original collection is consumed in the process. For example, the call `guitars.into_iter()` in the preceding code returns an iterator that produces each string by value; the consumer receives ownership of each string. When the iterator is dropped, any elements remaining in the `BTreeSet` are dropped too, and the set’s now-empty husk is disposed of.\n", + "\n", + "* Since a for loop applies `IntoIterator::into_iter()` to its operand, these three implementations are what create the following idioms for iterating over shared or mutable references to a collection, or consuming the collection and taking ownership of its elements:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Yamaha\n", + "Fender\n", + "Gibson\n", + "Yamaha!\n", + "Fender!\n", + "Gibson!\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "use std::collections::BTreeSet;\n", + "\n", + "let mut guitars = vec![\"Yamaha\".to_string(), \"Fender\".to_string(), \"Gibson\".to_string()];\n", + "\n", + "for guitar in &guitars {\n", + " println!(\"{}\", guitar);\n", + "}\n", + "\n", + "for guitar in &mut guitars {\n", + " *guitar += \"!\";\n", + "}\n", + "\n", + "for guitar in guitars {\n", + " println!(\"{}\", guitar);\n", + "}\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Not every type provides all three implementations. \n", + " * `HashSet`, `BTreeSet` and `BinaryHeap` don’t implement `IntoIterator` on mutable references, since modifying their elements would probably violate the type’s invariants\n", + " * `HashMap` and `BTreeMap` produce mutable reference to their entries’ values, but only shared references to their keys\n", + " * Slices implement two of the three `IntoIterator` variants; since they don’t own their elements, there is no “by value” case" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### IntoIterator in Generics\n", + "\n", + "* You can use a bound like `T: IntoIterator` to restrict the type variable `T` to types that can be iterated over. \n", + "* Or, you can write `T: IntoIterator` to further require the iteration to produce a particular type `U`" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn dump(t: T)\n", + " where T: IntoIterator,\n", + " U: std::fmt::Debug \n", + "{\n", + " for u in t {\n", + " println!(\"{:?}\", u);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\"Yamaha\"\n", + "\"Fender\"\n", + "\"Gibson\"\n" + ] + } + ], + "source": [ + "let mut guitars = vec![\"Yamaha\".to_string(), \"Fender\".to_string(), \"Gibson\".to_string()];\n", + "dump(guitars);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### from_fn\n", + "\n", + "* `std::iter::from_fn` creates an iterator from a closure that returns an `Option`. Each call to the closure produces the next item, and iteration ends when the closure returns `None`." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + ":dep rand = \"0.7\"" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use rand::random;\n", + "use std::iter::from_fn;\n", + "\n", + "let lengths: Vec = from_fn(|| Some((random::() - random::()).abs()))\n", + " .take(10)\n", + " .collect();" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0.1460489564142653, 0.05699591261484327, 0.31470490145547103, 0.48051853856776183, 0.2768923821261209, 0.34452084700737795, 0.45857317951144794, 0.4249180902549178, 0.6220745268714714, 0.08394659480329847]" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lengths" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn fibonacci() -> impl Iterator {\n", + " let mut state = (0, 1);\n", + " \n", + " from_fn(move || {\n", + " let next = state.0;\n", + " state = (state.1, state.0 + state.1);\n", + " Some(next)\n", + " })\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fibonacci().take(10).collect::>()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### drain methods" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* The `drain()` method removes the specified range from the vector in bulk, returning all removed elements as an iterator. If the iterator is dropped before being fully consumed, it drops the remaining removed elements." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3, 8, 9, 10]\n", + "[4, 5, 6, 7]\n", + "[1]\n" + ] + } + ], + "source": [ + "let mut data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n", + "let inner = data.drain(3..7).collect::>();\n", + "\n", + "println!(\"{:?}\", data);\n", + "println!(\"{:?}\", inner);\n", + "\n", + "data.drain(1..);\n", + "println!(\"{:?}\", data);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Other iterator sources" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "#### `std::ops::Range` - `1..10`\n", + "\n", + "* Endpoints must be an integer type to be iterable" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(1..10).collect::>()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 3, 5, 7, 9]" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(1..10).step_by(2).collect::>()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `std::ops::RangeFrom` - `1..`\n", + "\n", + "* Unbounded iteration. Start must be an integer. May panic or overflow if the value reaches the limit of the type." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "(1..).take(10).collect::>()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `Option` - `Some(5).iter()` or `None.iter()`" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let opt = Some(5);\n", + "for v in opt {\n", + " println!(\"{}\", v);\n", + "}\n", + "\n", + "let opt: Option = None;\n", + "for v in opt {\n", + " println!(\"{}\", v);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `Result`\n", + "\n", + "* `iter()` and `iter_mut()` methods are available for `Result`\n", + "* Returns an iterator over the possibly contained value. The iterator yields one value if the result is `Result::Ok`, otherwise none." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn load_data(path: &str) -> Result {\n", + " if path != \"not-found\" {\n", + " Ok(42)\n", + " } else {\n", + " Err(\"File not found\")\n", + " }\n", + "}\n", + "\n", + "let data = load_data(\"found\");\n", + "assert_eq!(data.iter().next(), Some(&42));\n", + "\n", + "let data = load_data(\"not-found\");\n", + "assert_eq!(data.iter().next(), None);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `Vec[T]`, `&[T]`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `v.windows(3)` - Returns an iterator over contiguous windows of length 3 within the vector." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3]\n", + "[2, 3, 4]\n", + "[3, 4, 5]\n", + "[4, 5, 6]\n", + "[5, 6, 7]\n", + "[6, 7, 8]\n", + "[7, 8, 9]\n", + "[8, 9, 10]\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n", + "\n", + "for wnd in v.windows(3) {\n", + " println!(\"{:?}\", wnd);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `v.chunks(3)` & `v.chunks_mut(3)` - Return iterators over contiguous chunks of length 3 within the vector, with `chunks_mut` providing mutable references." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2, 3]\n", + "[4, 5, 6]\n", + "[7, 8, 9]\n", + "[10]\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for chunk in v.chunks(3) {\n", + " println!(\"{:?}\", chunk);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `v.split(predicate)` & `v.split_mut(predicate)` - Return iterators over subslices separated by elements that match the predicate, with `split_mut` providing mutable references." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1, 2]\n", + "[4, 5]\n", + "[7, 8]\n", + "[10]\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for slice in v.split(|&n| n % 3 == 0) {\n", + " println!(\"{:?}\", slice);\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[10]\n", + "[7, 8]\n", + "[4, 5]\n", + "[1, 2]\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for slice in v.rsplit(|&n| n % 3 == 0) {\n", + " println!(\"{:?}\", slice);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `String`, `&str`" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let text = \"Nobody expects the Spanish Inquisition!\\n Amongst our weaponry are such diverse elements as fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms.\".to_string();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `text.bytes()` - produces bytes of the UTF-8 form" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Bytes(Copied { it: Iter([78, 111, 98, 111, 100, 121, 32, 101, 120, 112, 101, 99, 116, 115, 32, 116, 104, 101, 32, 83, 112, 97, 110, 105, 115, 104, 32, 73, 110, 113, 117, 105, 115, 105, 116, 105, 111, 110, 33, 10, 32, 65, 109, 111, 110, 103, 115, 116, 32, 111, 117, 114, 32, 119, 101, 97, 112, 111, 110, 114, 121, 32, 97, 114, 101, 32, 115, 117, 99, 104, 32, 100, 105, 118, 101, 114, 115, 101, 32, 101, 108, 101, 109, 101, 110, 116, 115, 32, 97, 115, 32, 102, 101, 97, 114, 44, 32, 115, 117, 114, 112, 114, 105, 115, 101, 44, 32, 114, 117, 116, 104, 108, 101, 115, 115, 32, 101, 102, 102, 105, 99, 105, 101, 110, 99, 121, 44, 32, 97, 110, 32, 97, 108, 109, 111, 115, 116, 32, 102, 97, 110, 97, 116, 105, 99, 97, 108, 32, 100, 101, 118, 111, 116, 105, 111, 110, 32, 116, 111, 32, 116, 104, 101, 32, 80, 111, 112, 101, 44, 32, 97, 110, 100, 32, 110, 105, 99, 101, 32, 114, 101, 100, 32, 117, 110, 105, 102, 111, 114, 109, 115, 46]) })" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.bytes()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `text.chars()` - produces `char`s of UTF-8 represents" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Chars(['N', 'o', 'b', 'o', 'd', 'y', ' ', 'e', 'x', 'p', 'e', 'c', 't', 's', ' ', 't', 'h', 'e', ' ', 'S', 'p', 'a', 'n', 'i', 's', 'h', ' ', 'I', 'n', 'q', 'u', 'i', 's', 'i', 't', 'i', 'o', 'n', '!', '\\n', ' ', 'A', 'm', 'o', 'n', 'g', 's', 't', ' ', 'o', 'u', 'r', ' ', 'w', 'e', 'a', 'p', 'o', 'n', 'r', 'y', ' ', 'a', 'r', 'e', ' ', 's', 'u', 'c', 'h', ' ', 'd', 'i', 'v', 'e', 'r', 's', 'e', ' ', 'e', 'l', 'e', 'm', 'e', 'n', 't', 's', ' ', 'a', 's', ' ', 'f', 'e', 'a', 'r', ',', ' ', 's', 'u', 'r', 'p', 'r', 'i', 's', 'e', ',', ' ', 'r', 'u', 't', 'h', 'l', 'e', 's', 's', ' ', 'e', 'f', 'f', 'i', 'c', 'i', 'e', 'n', 'c', 'y', ',', ' ', 'a', 'n', ' ', 'a', 'l', 'm', 'o', 's', 't', ' ', 'f', 'a', 'n', 'a', 't', 'i', 'c', 'a', 'l', ' ', 'd', 'e', 'v', 'o', 't', 'i', 'o', 'n', ' ', 't', 'o', ' ', 't', 'h', 'e', ' ', 'P', 'o', 'p', 'e', ',', ' ', 'a', 'n', 'd', ' ', 'n', 'i', 'c', 'e', ' ', 'r', 'e', 'd', ' ', 'u', 'n', 'i', 'f', 'o', 'r', 'm', 's', '.'])" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "text.chars()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `text.split_whitespace()` - Returns an iterator over the non-whitespace substrings of the string slice." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nobody\n", + "expects\n", + "the\n", + "Spanish\n", + "Inquisition!\n", + "Amongst\n", + "our\n", + "weaponry\n", + "are\n", + "such\n", + "diverse\n", + "elements\n", + "as\n", + "fear,\n", + "surprise,\n", + "ruthless\n", + "efficiency,\n", + "an\n", + "almost\n", + "fanatical\n", + "devotion\n", + "to\n", + "the\n", + "Pope,\n", + "and\n", + "nice\n", + "red\n", + "uniforms.\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for text_chunk in text.split_whitespace() {\n", + " println!(\"{}\", text_chunk);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `text.lines()` - Returns an iterator over the lines of the string slice." + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Nobody expects the Spanish Inquisition!\n", + "Amongst our weaponry are such diverse elements as fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms.\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "for line in text.lines() {\n", + " println!(\"{}\", line.trim());\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `text.matches` - Returns an iterator over all non-overlapping matches of a pattern in the string slice." + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "3\n", + "4\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let text = \"A1B2C3D4\".to_string();\n", + "\n", + "for numeric_chunks in text.matches(char::is_numeric) {\n", + " println!(\"{}\", numeric_chunks);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `HashMap`, `BTreeMap`\n", + "\n", + "* `HashMap` - A hash table-based implementation of a map, providing fast lookups and insertions on average.\n", + "* `BTreeMap` - A tree-based implementation of a map, providing ordered key-value pairs and efficient range queries." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let mut dictionary = std::collections::HashMap::new();\n", + "\n", + "dictionary.insert(\"apple\", 3);\n", + "dictionary.insert(\"banana\", 2);\n", + "dictionary.insert(\"cherry\", 5);" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"apple\", \"banana\", \"cherry\"]" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dictionary.keys()" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[3, 2, 5]" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dictionary.values()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### `HashSet`, `BTreeSet`\n", + "\n", + "* `HashSet` - A hash table-based implementation of a set, providing fast lookups and insertions on average.\n", + "* `BTreeSet` - A tree-based implementation of a set, providing ordered elements and efficient range queries." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "union: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]\n", + "intersection: [5, 6, 7, 8, 9, 10]\n", + "difference: [1, 2, 3, 4]\n", + "symmetric_difference: [1, 2, 3, 4, 11, 12, 13, 14, 15]\n" + ] + } + ], + "source": [ + "use std::collections::BTreeSet;\n", + "\n", + "let set_a = (1..=10).collect::>();\n", + "let set_b = (5..=15).collect::>();\n", + "\n", + "println!(\"union: {:?}\", set_a.union(&set_b).collect::>());\n", + "\n", + "println!(\"intersection: {:?}\", set_a.intersection(&set_b).collect::>());\n", + "\n", + "println!(\"difference: {:?}\", set_a.difference(&set_b).collect::>());\n", + "\n", + "println!(\"symmetric_difference: {:?}\", set_a.symmetric_difference(&set_b).collect::>());" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Free functions" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let empty_iterable = std::iter::empty::();\n", + "\n", + "let mut iter = empty_iterable.into_iter();\n", + "\n", + "assert_eq!(iter.next(), None);" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let one_element_iterable = std::iter::once(42);\n", + "\n", + "let mut iter = one_element_iterable.into_iter();\n", + "\n", + "assert_eq!(iter.next(), Some(42));\n", + "assert_eq!(iter.next(), None);" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"*-@*-@*-@*-@*-@*-@*-@*-@*-@\"" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let pattern = std::iter::repeat(\"*-@\").take(9).collect::();\n", + "pattern" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Iterator Adaptors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* the `Iterator` trait provides a broad selection of **adapter methods**, or simply adapters, that consume one iterator and build a new one with useful behaviors" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### map & filter\n", + "\n", + "* `map` - Transforms each element of an iterator using a closure.\n", + "* `filter` - Retains only the elements of an iterator that satisfy a predicate." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"Nobody expects the Spanish Inquisition!\", \"Amongst our weaponry are such diverse elements as\", \"fear,\", \"surprise,\", \"ruthless efficiency,\", \"an almost fanatical devotion to the Pope, and nice red uniforms.\"]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let text = \"Nobody expects the Spanish Inquisition!\\n Amongst our weaponry are such diverse elements as \\n fear, \\n surprise, \\n ruthless efficiency, \\n an almost fanatical devotion to the Pope, and nice red uniforms.\".to_string();\n", + "\n", + "let text_lines = text.lines()\n", + " .map(|s| s.trim().to_owned())\n", + " .collect::>();\n", + "\n", + "text_lines" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"fear,\", \"surprise,\"]" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let short_lines = text.lines()\n", + " .map(|s| s.trim().to_owned())\n", + " .filter(|s| s.len() < 20)\n", + " .collect::>();\n", + "\n", + "short_lines" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Adapters are lazy" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "[\"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"ten\"]\n", + " .iter()\n", + " .map(|s| println!(\"{}\", s));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### filter_map\n", + "\n", + "* `filter_map` - Combines the functionality of `filter` and `map`, allowing you to both filter and transform elements of an iterator in a single pass.\n", + "* Closure passed to `filter_map` takes an element and returns an `Option` type, which determines if the element should be included in the output and how it should be transformed." + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6, 7, 9, 10]" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let input_data = \"1\\n2\\n3\\n4\\n5\\nabc\\n6\\n7\\ndefn8\\n9\\n10\";\n", + "\n", + "let numbers = input_data.lines()\n", + " .filter_map(|line| line.parse::().ok())\n", + " .collect::>();\n", + "\n", + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### flat_map\n", + "\n", + "* `flat_map` - Transforms each element of an iterator and flattens the result into a single iterator." + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\"Tokyo\", \"Kyoto\", \"São Paulo\", \"Brasília\", \"Nairobi\", \"Mombasa\"]\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "use std::collections::HashMap;\n", + "\n", + "fn create_major_cities_map() -> std::collections::HashMap<&'static str, Vec<&'static str>> {\n", + " let mut major_cities = std::collections::HashMap::new();\n", + " major_cities.insert(\"Japan\", vec![\"Tokyo\", \"Kyoto\"]);\n", + " major_cities.insert(\"The United States\", vec![\"Portland\", \"Nashville\"]);\n", + " major_cities.insert(\"Brazil\", vec![\"São Paulo\", \"Brasília\"]);\n", + " major_cities.insert(\"Kenya\", vec![\"Nairobi\", \"Mombasa\"]);\n", + " major_cities.insert(\"The Netherlands\", vec![\"Amsterdam\", \"Utrecht\"]);\n", + " major_cities.insert(\"Poland\", vec![\"Warsaw\", \"Kraków\"]);\n", + "\n", + " major_cities\n", + "}\n", + "\n", + "{\n", + " let mut major_cities = create_major_cities_map();\n", + " \n", + " let countries = [\"Japan\", \"Brazil\", \"Kenya\"];\n", + " \n", + " let selected_cities = countries.iter()\n", + " .flat_map(|country| &major_cities[country])\n", + " .collect::>();\n", + "\n", + " println!(\"{:?}\", selected_cities);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### flatten\n", + "\n", + "* `flatten` - Flattens a nested iterator into a single iterator by concatenating all the inner iterators." + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[\"Amsterdam\", \"Utrecht\", \"Warsaw\", \"Kraków\", \"Portland\", \"Nashville\", \"Nairobi\", \"Mombasa\", \"São Paulo\", \"Brasília\", \"Tokyo\", \"Kyoto\"]\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let mut major_cities = create_major_cities_map();\n", + "\n", + " let all_cities: Vec<&&str> = major_cities\n", + " .values()\n", + " .flatten()\n", + " .collect();\n", + "\n", + " println!(\"{:?}\", all_cities);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### take & take_while\n", + "\n", + "* `take(n)` - Returns an iterator that yields the first `n` elements of the original iterator.\n", + "* `take_while(predicate)` - Returns an iterator that yields elements from the original iterator as long as the predicate is satisfied." + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"To: jimb\", \"From: superego \"]" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let message = \"To: jimb\\r\\n\\\n", + " From: superego \\r\\n\\\n", + " \\r\\n\\\n", + " Did you get any writing done today?\\r\\n\\\n", + " When will you stop wasting time plotting fractals?\\r\\n\";\n", + "\n", + "message.lines().take_while(|l| !l.is_empty()).collect::>()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### skip & skip_while\n", + "\n", + "* `skip(n)` - Returns an iterator that skips the first `n` elements of the original iterator.\n", + "* `skip_while(predicate)` - Returns an iterator that skips elements from the original iterator as long as the predicate is satisfied." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[\"Did you get any writing done today?\", \"When will you stop wasting time plotting fractals?\"]" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let message = \"To: jimb\\r\\n\\\n", + " From: superego \\r\\n\\\n", + " \\r\\n\\\n", + " Did you get any writing done today?\\r\\n\\\n", + " When will you stop wasting time plotting fractals?\\r\\n\";\n", + "\n", + "message\n", + " .lines()\n", + " .skip_while(|l| !l.is_empty())\n", + " .skip(1)\n", + " .collect::>()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### peekable\n", + "\n", + "* A peekable iterator lets you peek at the next item that will be produced without actually consuming it." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::iter::Peekable;\n", + "\n", + "fn parse_number(tokens: &mut Peekable) -> i32\n", + " where I: Iterator \n", + "{ \n", + " let mut number = 0;\n", + "\n", + " loop {\n", + " match tokens.peek() {\n", + " Some(c) if c.is_digit(10) => {\n", + " number = number * 10 + c.to_digit(10).unwrap() as i32; \n", + " }\n", + " _ => return number\n", + " }\n", + " tokens.next();\n", + " }\n", + "}\n", + "\n", + "let mut chars = \"423143,353454\".chars().peekable();\n", + "assert_eq!(parse_number(&mut chars), 423143);\n", + "assert_eq!(chars.next(), Some(','));\n", + "assert_eq!(parse_number(&mut chars), 353454);\n", + "assert_eq!(chars.next(), None);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### fuse\n", + "\n", + "* Once an `Iterator` has returned `None`, the trait doesn’t specify how it ought to behave if you call its next method again. Most iterators just return `None` again, but not all. If your code counts on that behavior, you may be in for a surprise.\n", + "* The fuse adapter takes any iterator and produces one that will definitely continue to return `None` once it has done so the first time:" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "struct Flaky(bool);\n", + "\n", + "impl Iterator for Flaky {\n", + " type Item = &'static str;\n", + " fn next(&mut self) -> Option {\n", + " if self.0 {\n", + " self.0 = false;\n", + " Some(\"totally the last item\")\n", + " } else {\n", + " self.0 = true; // D'oh!\n", + " None\n", + " }\n", + " }\n", + "}\n", + "\n", + "let mut flaky = Flaky(true);\n", + "assert_eq!(flaky.next(), Some(\"totally the last item\"));\n", + "assert_eq!(flaky.next(), None);\n", + "assert_eq!(flaky.next(), Some(\"totally the last item\"));\n", + "\n", + "let mut not_flaky = Flaky(true).fuse();\n", + "assert_eq!(not_flaky.next(), Some(\"totally the last item\"));\n", + "assert_eq!(not_flaky.next(), None);\n", + "assert_eq!(not_flaky.next(), None);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Reversible Iterators & rev\n", + "\n", + "* The `rev` adapter takes any iterator and produces one that produces the same items in reverse order\n", + "* The `DoubleEndedIterator` trait extends `Iterator` with a `next_back` method that returns the last item of the iterator, or `None` if the iterator is empty." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "mod explain {\n", + " trait DoubleEndedIterator: Iterator {\n", + " fn next_back(&mut self) -> Option;\n", + " //... many default methods\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let mut data = vec![1, 2, 3, 4];\n", + " \n", + " let mut iterator = data.iter().rev();\n", + " assert_eq!(iterator.next(), Some(&4));\n", + " assert_eq!(iterator.next(), Some(&3));\n", + " assert_eq!(iterator.next(), Some(&2));\n", + " assert_eq!(iterator.next(), Some(&1));\n", + " assert_eq!(iterator.next(), None);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### chain\n", + "\n", + "The chain adapter appends one iterator to another. More precisely, `i1.chain(i2)` returns an iterator that draws items from `i1` until it’s exhausted, and then draws items from `i2`.|" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let v: Vec = (1..4).chain(vec![4, 5, 6]).collect();\n", + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A `chain` iterator is reversible, if both of its underlying iterators are:" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[6, 5, 4, 3, 2, 1]" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let v_reversed: Vec = (1..4).chain(vec![4, 5, 6]).rev().collect();\n", + "v_reversed" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### enumerate\n", + "\n", + "* `enumerate` - Adds a counter to an iterator and returns it as an iterator of tuples, where each tuple contains the index and the element." + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0: zero\n", + "1: one\n", + "2: two\n", + "3: three\n", + "4: four\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let words = vec![\"zero\", \"one\", \"two\", \"three\", \"four\"];\n", + "\n", + "for (index, item) in words.into_iter().enumerate() {\n", + " println!(\"{}: {}\", index, item);\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D'), (4, 'E')]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let v: Vec<(i32, char)> = (0..5).zip(\"ABCDE\".chars()).collect();\n", + "v" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### by_ref\n", + "\n", + "An iterator’s by_ref method borrows a mutable reference to the iterator, so that you can apply adaptors to the reference. When you’re done consuming items from these adaptors, you drop them, the borrow ends, and you regain access to your original iterator." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "To: SuperUser\n", + "From: HelpDesk\n", + "Hello!\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let message = \"To: SuperUser\\r\\nFrom: HelpDesk\\r\\n\\r\\nHello!\\r\\n\";\n", + "\n", + "let mut lines = message.lines();\n", + "\n", + "for headers in lines.by_ref().take_while(|l| !l.is_empty()) {\n", + " println!(\"{}\", headers);\n", + "}\n", + "\n", + "for body in lines {\n", + " println!(\"{}\", body);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### cloned\n", + "\n", + "The cloned adapter takes an iterator that produces references, and returns an iterator that produces values cloned from those references. Naturally, the referent type must implement `Clone`" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let words = vec![\"zero\".to_string(), \"one\".to_string(), \"two\".to_string(), \"three\".to_string(), \"four\".to_string()];\n", + "\n", + "assert_eq!(words.iter().next(), Some(&\"zero\".to_string()));\n", + "assert_eq!(words.iter().cloned().next(), Some(\"zero\".to_string()));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### cycle\n", + "\n", + "The cycle adapter returns an iterator that endlessly repeats the sequence produced by the underlying iterator. The underlying iterator must implement `std::clone::Clone`, so that cycle can save its initial state and reuse it each time the cycle starts again" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "North\n", + "East\n", + "South\n", + "West\n", + "North\n", + "East\n", + "South\n", + "West\n", + "North\n", + "East\n", + "South\n", + "West\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let directions = [\"North\", \"East\", \"South\", \"West\"];\n", + "\n", + "for direction in directions.iter().cycle().take(12) {\n", + " println!(\"{}\", direction);\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "2\n", + "fizz\n", + "4\n", + "buzz\n", + "fizz\n", + "7\n", + "8\n", + "fizz\n", + "buzz\n", + "11\n", + "fizz\n", + "13\n", + "14\n", + "fizzbuzz\n", + "16\n", + "17\n", + "fizz\n", + "19\n" + ] + } + ], + "source": [ + "use std::iter::{once, repeat};\n", + "\n", + "fn main() {\n", + "let fizzes = repeat(\"\").take(2).chain(once(\"fizz\")).cycle();\n", + "let buzzes = repeat(\"\").take(4).chain(once(\"buzz\")).cycle();\n", + "let fizzes_buzzes = fizzes.zip(buzzes);\n", + "\n", + "let fizz_buzz = (1..20).zip(fizzes_buzzes)\n", + " .map(|tuple|\n", + " match tuple {\n", + " (i, (\"\", \"\")) => i.to_string(),\n", + " (_, (fizz, buzz)) => format!(\"{}{}\", fizz, buzz)\n", + " });\n", + "\n", + "for line in fizz_buzz {\n", + " println!(\"{}\", line);\n", + "}\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### count, sum, product\n", + "\n", + "The count method draws items from an iterator until it returns `None`, and tells you how many it got." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "49" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let evens_count = (1..100).filter(|&x| x % 2 == 0).count();\n", + "evens_count" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn triangle(n: u64) -> u64 {\n", + " (1..n+1).sum()\n", + "}\n", + "assert_eq!(triangle(20), 210);\n", + "\n", + "fn factorial(n: u64) -> u64 {\n", + " (1..n+1).product()\n", + "}\n", + "assert_eq!(factorial(20), 2432902008176640000);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### max, min\n", + "\n", + "* `max` - Returns the maximum element of an iterator, if it exists.\n", + "* `min` - Returns the minimum element of an iterator, if it exists." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "assert_eq!([-2, 0, 1, 0, -2, -5].iter().max(), Some(&1));\n", + "assert_eq!([-2, 0, 1, 0, -2, -5].iter().min(), Some(&-5));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Rust’s floating-point types `f32` and `f64` implement only `std::cmp::PartialOrd`, `not std::cmp::Ord`, so you can’t use the `min` and `max` methods to compute the least or greatest of a sequence of floating-point numbers. This is not a popular aspect of Rust’s design, but it is deliberate: it’s not clear what such functions should do with IEEE NaN values. Simply ignoring them would risk masking more serious problems in the code." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "ename": "Error", + "evalue": "the trait bound `{float}: Ord` is not satisfied", + "output_type": "error", + "traceback": [ + "\u001b[31m[E0277] Error:\u001b[0m the trait bound `{float}: Ord` is not satisfied", + " \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_64:1:1 \u001b[38;5;246m]\u001b[0m", + " \u001b[38;5;246m│\u001b[0m", + " \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ", + " \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m the trait `Ord` is not implemented for `{float}`", + "\u001b[38;5;246m───╯\u001b[0m" + ] + } + ], + "source": [ + "let real_numbers = vec![1.3, 2.4, 3.5, 4.6, 5.7];\n", + "let min_number =real_numbers.iter().min();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### min_by, max_by\n", + "\n", + "The `max_by` and `min_by` methods return the maximum or minimum item the iterator produces, as determined by a comparison function you provide:" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n", + "thread '' panicked at src\\lib.rs:26:26:\n", + "called `Option::unwrap()` on a `None` value\n", + "stack backtrace:\n", + " 0: std::panicking::begin_panic_handler\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\std\\src\\panicking.rs:697\n", + " 1: core::panicking::panic_fmt\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:75\n", + " 2: core::panicking::panic\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:145\n", + " 3: core::option::unwrap_failed\n", + " at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\option.rs:2130\n", + " 4: std::panic::catch_unwind\n", + " 5: run_user_code_61\n", + " 6: \n", + " 7: \n", + " 8: \n", + " 9: \n", + " 10: \n", + " 11: \n", + " 12: \n", + " 13: \n", + " 14: BaseThreadInitThunk\n", + " 15: RtlUserThreadStart\n", + "note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n" + ] + } + ], + "source": [ + "use std::cmp::{PartialOrd, Ordering};\n", + "\n", + "// Compare two f64 values. Panic if given a NaN.\n", + "fn cmp(lhs: &&f64, rhs: &&f64) -> Ordering {\n", + " lhs.partial_cmp(rhs).unwrap()\n", + "}\n", + "\n", + "let numbers = [1.0, 4.0, 2.0];\n", + "assert_eq!(numbers.iter().max_by(cmp), Some(&4.0));\n", + "assert_eq!(numbers.iter().min_by(cmp), Some(&1.0));\n", + "\n", + "let numbers = [1.0, 4.0, std::f64::NAN, 2.0];\n", + "assert_eq!(numbers.iter().max_by(cmp), Some(&4.0)); // panics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### min_by_key, max_by_key\n", + "\n", + "The `max_by_key` and `min_by_key` methods are similar to `max_by` and `min_by`, but they take a key extraction function that computes the value to compare:" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::collections::HashMap;\n", + "\n", + "let mut populations = HashMap::new();\n", + "populations.insert(\"Portland\", 583_776);\n", + "populations.insert(\"Fossil\", 449);\n", + "populations.insert(\"Greenhorn\", 2);\n", + "populations.insert(\"Boring\", 7_762);\n", + "populations.insert(\"The Dalles\", 15_340);\n", + "\n", + "assert_eq!(populations.iter().max_by_key(|&(_name, pop)| pop),\n", + " Some((&\"Portland\", &583_776)));\n", + "assert_eq!(populations.iter().min_by_key(|&(_name, pop)| pop),\n", + " Some((&\"Greenhorn\", &2)));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Comparing Item Sequences\n", + "\n", + "You can use `<` and `==` operators to compare strings, vectors and slices. However, you can’t compare iterators directly.\n", + "Iterators provide functions `eq` and `lt` that do comparisons." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let packed = \"John Doe,123 Main St.,Hometown,CA,12345\";\n", + "let spaced = \"John Doe, 123 Main St., Hometown, CA, 12345\";\n", + "\n", + "assert!(packed.split(',').eq(spaced.split(',').map(str::trim)));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### any, all" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let sentence = \"The quick brown fox jumps over the lazy dog\";\n", + "\n", + "assert!(sentence.chars().any(char::is_uppercase));\n", + "assert!(sentence.chars().all(|c| c.is_ascii_alphabetic() || c.is_whitespace()));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### position, rposition\n", + "\n", + "The position method applies a closure to each item from the iterator and returns the index of the first item for which the closure returns `true`. More precisely, it returns an `Option` of the index: if the closure returns `true` for no item, position returns `None`. It stops drawing items as soon as the closure returns `true`." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let text = \"The quick brown fox jumps over the lazy dog\";\n", + "assert_eq!(text.chars().position(|c| c.is_whitespace()), Some(3));\n", + "\n", + "let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11];\n", + "assert_eq!(numbers.iter().rposition(|&n| n % 2 == 0), Some(7));" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### fold\n", + "\n", + "The fold method is a very general tool for accumulating some sort of result over the entire sequence of items an iterator produces. Given an initial value, which we’ll call the accumulator, and a closure, fold repeatedly applies the closure to the current accumulator and the next item from the iterator. The value the closure returns is taken as the new accumulator, to be passed to the closure with the next item. The final accumulator value is what fold itself returns. If the sequence is empty, fold simply returns the initial accumulator." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n", + "\n", + "assert_eq!(v.iter().fold(0, |acc, _| acc + 1), 10); // count\n", + "assert_eq!(v.iter().fold(0, |acc, n| acc + n), 55); // sum\n", + "assert_eq!(v.iter().fold(1, |acc, n| acc * n), 3_628_800); // product" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "\"And now for something completely different\"" + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let words = [\"And\", \"now\", \"for\", \"something\", \"completely\", \"different\"];\n", + "\n", + "let sentence = words.iter().fold(String::new(), |mut acc, &word| {\n", + " acc.push_str(word);\n", + " acc.push(' ');\n", + " acc\n", + "});\n", + "\n", + "sentence.trim()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### nth\n", + "\n", + "The `nth` method takes an index `n`, skips that many items from the iterator, and returns the next item, or `None` if the sequence ends before that point. Calling `.nth(0)` is equivalent to `.next()`.\n", + "\n", + "It doesn’t take ownership of the iterator the way an adapter would, so you can call it many times." + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let mut squares = (0..10).map(|n| n * n);\n", + "\n", + " assert_eq!(squares.nth(4), Some(16));\n", + " assert_eq!(squares.nth(0), Some(25));\n", + " assert_eq!(squares.nth(3), Some(81));\n", + " assert_eq!(squares.nth(2), None);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### last\n", + "\n", + "The last method consumes items until the iterator returns `None`, and then returns the last item. If the iterator produces no items, then last returns `None`" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn main() {\n", + " let squares = (0..10).map(|n| n * n);\n", + " assert_eq!(squares.last(), Some(81));\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This consumes all the iterator’s items starting from the front, even if the iterator is reversible. If you have a reversible iterator and don’t need to consume all its items, you should instead just write `iter.rev().next()`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### find\n", + "\n", + "The find method draws items from an iterator, returning the first item for which the given closure returns true, or None if the sequence ends before a suitable item is found" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some(529)\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let mut squares = (0..100).map(|n| n * n);\n", + "\n", + " let first_gt_500 = squares.find(|&n| n > 500);\n", + " println!(\"{:?}\", first_gt_500);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### partition\n", + "\n", + "The partition method divides an iterator’s items among two collections, using a closure to decide where each item belongs:" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "even numbers: [2, 4, 6, 8, 10]\n", + "odd numbers: [1, 3, 5, 7, 9]\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n", + "\n", + " let (evens, odds): (Vec, Vec) = numbers.iter().partition(|&n| n % 2 == 0);\n", + "\n", + " println!(\"even numbers: {:?}\", evens);\n", + " println!(\"odd numbers: {:?}\", odds);\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Implementing an Iterator" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "struct I32Range {\n", + " start: i32,\n", + " end: i32,\n", + "}\n", + "\n", + "impl Iterator for I32Range {\n", + " type Item = i32;\n", + "\n", + " fn next(&mut self) -> Option {\n", + " if self.start < self.end {\n", + " let result = Some(self.start);\n", + " self.start += 1;\n", + " result\n", + " } else {\n", + " None\n", + " }\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 75, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "let mut pi = 0.0;\n", + "let mut numerator = 1.0;\n", + "\n", + "for k in (I32Range { start: 0, end: 14 }) {\n", + " pi += numerator / (2*k + 1) as f64;\n", + " numerator /= -3.0;\n", + "}\n", + "pi *= f64::sqrt(12.0);\n", + "\n", + "// IEEE 754 specifies this result exactly.\n", + "assert_eq!(pi as f32, std::f32::consts::PI);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Case Study - BinaryTree" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "enum BinaryTree {\n", + " Empty,\n", + " NonEmpty(Box>),\n", + "}\n", + "\n", + "struct TreeNode {\n", + " element: T,\n", + " left: BinaryTree,\n", + " right: BinaryTree,\n", + "}\n", + "\n", + "struct TreeIter<'a, T> {\n", + " unvisited: Vec<&'a TreeNode>,\n", + "}\n", + "\n", + "impl<'a, T> TreeIter<'a, T> {\n", + " fn push_left_edge(&mut self, mut tree: &'a BinaryTree) {\n", + " while let BinaryTree::NonEmpty(ref node) = *tree {\n", + " self.unvisited.push(node);\n", + " tree = &node.left;\n", + " }\n", + " }\n", + "}\n", + "\n", + "impl BinaryTree {\n", + " fn iter(&self) -> TreeIter {\n", + " let mut iter = TreeIter { unvisited: Vec::new() };\n", + " iter.push_left_edge(self);\n", + " iter\n", + " }\n", + "}\n", + "\n", + "impl<'a, T: 'a> IntoIterator for &'a BinaryTree {\n", + " type Item = &'a T;\n", + " type IntoIter = TreeIter<'a, T>;\n", + "\n", + " fn into_iter(self) -> Self::IntoIter {\n", + " self.iter()\n", + " }\n", + "}\n", + "\n", + "impl<'a, T> Iterator for TreeIter<'a, T> {\n", + " type Item = &'a T;\n", + "\n", + " fn next(&mut self) -> Option {\n", + " let node = match self.unvisited.pop() {\n", + " None => return None,\n", + " Some(n) => n,\n", + " };\n", + "\n", + " self.push_left_edge(&node.right);\n", + " Some(&node.element)\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use self::BinaryTree::*;\n", + "\n", + "fn make_node(left: BinaryTree, element: T, right: BinaryTree) -> BinaryTree {\n", + " NonEmpty(Box::new(TreeNode { left, element, right }))\n", + "}\n", + "\n", + "fn main() {\n", + " // Build a small tree.\n", + " let subtree_l = make_node(Empty, \"mecha\", Empty);\n", + " let subtree_rl = make_node(Empty, \"droid\", Empty);\n", + " let subtree_r = make_node(subtree_rl, \"robot\", Empty);\n", + " let tree = make_node(subtree_l, \"Jaeger\", subtree_r);\n", + "\n", + " // Iterate over it.\n", + " let mut v = Vec::new();\n", + " for kind in &tree {\n", + " v.push(*kind);\n", + " }\n", + " assert_eq!(v, [\"mecha\", \"Jaeger\", \"droid\", \"robot\"]);\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/16_collections.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/16_collections.ipynb new file mode 100644 index 0000000..7687b21 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/16_collections.ipynb @@ -0,0 +1,1104 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Collections" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Types of Collections in Rust\n", + "\n", + "| Collection Type | Description | C++ Equivalent |\n", + "| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------- |\n", + "| `Vec` | A resizable array type. It allows you to store multiple values of the same type in a single data structure. | `std::vector` |\n", + "| `String` | A growable, mutable, UTF-8 encoded string type. | `std::string` |\n", + "| `VecDeque` | A double-ended queue. Like `Vec` but better for use as FIFO queue. | `std::deque` |\n", + "| `LinkedList` | A doubly-linked list. Like `VecDeque` and adds fast concatenation. Slower than `Vec` and `VecDeque`. | `std::list` |\n", + "| `HashSet where T: Eq + Hash` | A set of values of type `T`. Adding and removing values is fast, and it’s fast to ask whether a given value is in the set or not. | `std::unordered_set` |\n", + "| `HashMap where K: Eq + Hash` | A table of key-value pairs. Looking up a value by its key is fast. The entries are stored in an arbitrary order. | `std::unordered_map` |\n", + "| `BTreeSet where T: Ord` | A set implemented as a B-Tree. It allows you to store unique values in sorted order. | `std::set` |\n", + "| `BTreeMap where K: Ord` | A map implemented as a B-Tree. It allows you to store key-value pairs in sorted order. | `std::map` |\n", + "| `BinaryHeap where T: Ord` | A binary heap, which is a priority queue implemented with a binary heap. | `std::priority_queue` |\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Vec\\" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating a vector" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [], + "source": [ + "let mut numbers = Vec::::new(); // empty vector\n", + "\n", + "let words = vec![\"hello\", \"world\"]; // vector with two elements\n", + "let mut buffer = vec![0u8; 1024]; // vector with 1024 elements, all initialized to 0" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98]" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut numbers: Vec = (1..100).filter(|&x| x & 1 == 0).collect(); // vector with numbers from 1 to 99\n", + "numbers" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let head = numbers[..10].to_vec(); // first 10 elements\n", + "head" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Accessing elements\n", + "\n", + "* `.first()`\n", + "* `.first_mut()`\n", + "* `.last()`\n", + "* `.last_mut()`\n", + "* `.get(index)`\n", + "* `.get_mut(index)`\n" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[2, 4, 6, 8, 20, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98]" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "assert_eq!(numbers.first(), Some(&2));\n", + "assert_eq!(numbers.last(), Some(&98));\n", + "assert_eq!(numbers.get(4), Some(&10));\n", + "\n", + "{\n", + " let item: &mut i32 = numbers.get_mut(4).unwrap();\n", + " *item *= 2;\n", + "}\n", + "\n", + "numbers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Iteration\n", + "\n", + "* Iterating over a `Vec` produces items of type `T`. The elements are moved out of the vector one by one, consuming it.\n", + "* Iterating over a value of type `&[T; N]`, `&[T]`, or `&Vec` - that is, a reference to an array, slice, or vector—produces items of type `&T`, references to the individual elements, which are not moved.\n", + "* Iterating over a value of type `&mut [T; N]`,` &mut [T]`, or` &mut Vec` produces items of type `&mut T`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Growing & Shrinking" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Length\n", + "\n", + "The length of an array, slice, or vector is the number of elements it contains.\n", + "\n", + "* `slice.len()` returns a slice’s length, as a `usize`.\n", + "\n", + "* `slice.is_empty()` is `true` if slice contains no elements (that is, `slice.len() == 0`)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Managing the capacity\n", + "\n", + "* `Vec::with_capacity(n)` creates a new, empty vector with capacity `n`.\n", + "\n", + "* `vec.capacity()` returns vec’s capacity, as a `usize`. It’s always true that `vec.capacity() >= vec.len()`.\n", + "\n", + "* `vec.reserve(n)` makes sure the vector has at least enough spare capacity for n more elements: that is, `vec.capacity()` is at least `vec.len() + n`. If there’s already enough room, this does nothing. If not, this \n", + "allocates a larger buffer and moves the vector’s contents into it.\n", + "\n", + "* `vec.reserve_exact(n)` is like` vec.reserve(n)`, but tells vec not to allocate any extra capacity for future growth, beyond `n`. Afterward, `vec.capacity()` is exactly `vec.len() + n`.\n", + "\n", + "* `vec.shrink_to_fit()` tries to free up the extra memory if `vec.capacity()` is greater than `vec.len()`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adding & removing elements" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "source": [ + "* `vec.push(value)` adds the given `value` to the end of `vec`.\n", + "\n", + "* `vec.pop()` removes and returns the last element. The return type is `Option`. This returns `Some(x)` if the popped element is x and None if the vector was already empty.\n", + "\n", + "* `vec.insert(index, value)` inserts the given value at `vec[index]`, sliding any existing values in `vec[index..]` one spot to the right to make room. Panics if `index > vec.len()`.\n", + "\n", + "* `vec.remove(index)` removes and returns `vec[index]`, sliding any existing values in `vec[index+1..]` one spot to the left to close the gap. Panics if `index >= vec.len()`, since in that case there is no element `vec[index]` to remove. The longer the vector, the slower this operation gets. If you find yourself doing `vec.remove(0)` a lot, consider using a `VecDeque` instead of a `Vec`.\n", + "\n", + "* `vec.resize(new_len, value)` changes the length of `vec` to `new_len`. If `new_len > vec.len()`, this appends `new_len - vec.len()` copies of `value` to the end of `vec`. If `new_len < vec.len()`, this truncates `vec` to `new_len`.\n", + "\n", + "* `vec.truncate(new_len)` truncates `vec` to `new_len`. If `new_len > vec.len()`, this does nothing. If `new_len < vec.len()`, this drops the last `vec.len() - new_len` elements.\n", + "\n", + "* `vec.split_off(index)` is like `vec.truncate(index)`, except that it returns a `Vec` containing the values removed from the end of `vec`. It’s like a multivalue version of `.pop()`.\n", + "\n", + "* `vec.clear()` removes all elements from `vec`. This is equivalent to `vec.truncate(0)`.\n", + "\n", + "* `vec.extend(iter)` adds all elements from `iter` to the end of `vec`. The iterator’s element type must be `T`.\n", + "\n", + "* `vec.append(&mut other)` moves all elements from `other` to the end of `vec`. Afterward, `other` is empty.\n", + "\n", + "* `vec.drain(range)` removes all elements in `range` from `vec` and returns an iterator over the removed elements. The range can be a `Range`, `RangeFrom`, `RangeTo`, or `RangeFull`. The iterator’s element type is `T`." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "vec = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\n" + ] + } + ], + "source": [ + "let mut vec = vec![1, 2, 3, 4, 5];\n", + "vec.push(6);\n", + "vec.insert(0, 0);\n", + "vec.append(&mut vec![7, 8, 9, 10]);\n", + "vec.extend(11..20);\n", + "vec.drain(0..5);\n", + "println!(\"vec = {vec:?}\");" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "removed tail = [15, 16, 17, 18, 19]\n", + "vec = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]\n" + ] + } + ], + "source": [ + "let removed_tail = vec.split_off(10);\n", + "println!(\"removed tail = {removed_tail:?}\");\n", + "println!(\"vec = {vec:?}\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `vec.retain(predicate)` removes all elements from `vec` for which `predicate` returns `false`. The predicate’s argument type is `&T`." + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "vec = [6, 8, 10, 12, 14]\n" + ] + } + ], + "source": [ + "vec.retain(|x| x & 1 == 0);\n", + "println!(\"vec = {vec:?}\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `vec.dedup()` - drops repeated elements\n", + "* `vec.dedup_by(same)` - drops repeated elements based on a comparison function\n", + "* `vec.dedup_by_key(key_fn)` - drops repeated elements based on a key function\n" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5]" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut data = vec![1, 2, 2, 3, 3, 3, 4, 4, 4, 5];\n", + "data.dedup();\n", + "data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Joining" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Two methods work on *arrays of arrays* (array, slice or vector whose elements are themselves arrays, slices, or vectors):\n", + " * `slices.concat()` concatenates the elements of the outer array, producing a single vector.\n", + " * `array.join(&separator)` concatenates the elements of the outer array, producing a single vector" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 5, 6]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[[1, 2], [3, 4], [5, 6]].concat()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[[1, 2, 3], [4, 5, 6], [7, 8, 9]].join(&0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Splitting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `slice.split_at(index)` and `slice.split_at_mut(index)` break a slice in two, returning a pair. `slice.split_at(index)` is equivalent to `(&slice[..index], &slice[index..])`. These methods panic if index is out of bounds.\n", + "\n", + "* `slice.split_first()` and `slice.split_first_mut()` also return a pair: a reference to the first element `(slice[0])` and a slice reference to all the rest `(slice[1..])`.\n", + " * The return type of `.split_first()` is `Option<(&T, &[T])>`; the result is `None` if slice is empty.\n", + "\n", + "* `slice.split_last()` and `slice.split_last_mut()` are analogous but split off the last element rather than the first.\n", + " * The return type of `.split_last()` is `Option<(&T, &[T])>`.\n", + "\n", + "* `slice.split(is_sep)` and `slice.split_mut(is_sep)` split slice into one or more subslices, using the function or closure `is_sep` to figure out where to split. They return an iterator over the subslices.\n", + " * As you consume the iterator, it calls `is_sep(&element)` for each element in the slice. If `is_sep(&element)` is true, the element is a separator. Separators are not included in any output subslice.\n", + "\n", + "* `slice.splitn(n, is_sep)` and `slice.splitn_mut(n, is_sep)` are the same, but they produce at most `n` subslices. After the first `n-1` slices are found, `is_sep` is not called again. The last subslice contains all the remaining elements.\n", + "\n", + "* `slice.rsplitn(n, is_sep)` and `slice.rsplitn_mut(n, is_sep)` are just like `.splitn()` and `.splitn_mut()` except that the slice is scanned in reverse order. That is, these methods split on the `last n-1` separators in the slice, rather than the first, and the subslices are produced starting from the end.\n", + "\n", + "* `slice.chunks(n)` and `slice.chunks_mut(n)` return an iterator over non-overlapping subslices of length `n`.\n", + " * If `slice.len()` is not a multiple of `n`, then the last subslice will have a length less than `n`.\n", + "\n", + "* `slice.windows(n)` and `slice.windows_mut(n)` return an iterator over overlapping subslices of length `n`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "data = [5, 4, 3, 2, 1, 10, 9, 8, 7, 6, 15, 14, 13, 12, 11, 20, 19, 18, 17, 16]\n" + ] + } + ], + "source": [ + "let mut data = (1..=20).collect::>();\n", + "\n", + "data.chunks_mut(5).for_each(|chunk| {\n", + " chunk.reverse();\n", + "});\n", + "\n", + "println!(\"data = {data:?}\");" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "data.windows(3) = [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9], [8, 9, 10]]\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " let data = (1..=10).collect::>();\n", + "\n", + " let windows = data.windows(3).collect::>();\n", + "\n", + " println!(\"data.windows(3) = {:?}\", windows);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Swapping" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `slice.swap(i, j)` swaps the two elements `slice[i]` and `slice[j]`.\n", + "\n", + "Vectors have a related method for efficiently removing any element:\n", + "* `vec.swap_remove(i)` removes and returns `vec[i]`. This is like `vec.remove(i)` except that instead of sliding the rest of the vector’s elements over to close the gap, it simply moves vec’s last element into the gap. It’s useful when you don’t care about the order of the items left in the vector." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3, 4, 10, 6, 7, 8, 9]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let mut data = (1..=10).collect::>();\n", + "data.swap_remove(4);\n", + "data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Sorting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `slice.sort()` sorts the elements into increasing order. This method is present only when the element type implements `Ord`.\n", + "\n", + "* `slice.sort_by(cmp)` sorts the elements of slice using a function or closure cmp to specify the sort order. `cmp` must implement `Fn(&T, &T) -> std::cmp::Ordering`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Debug)]\n", + "struct Person {\n", + " first_name: &'static str,\n", + " last_name: &'static str,\n", + " age: u8,\n", + "}\n", + "\n", + "let mut people = vec![\n", + " Person { first_name: \"John\", last_name: \"Doe\", age: 30 },\n", + " Person { first_name: \"Alice\", last_name: \"Smith\", age: 45 },\n", + " Person { first_name: \"Jane\", last_name: \"Doe\", age: 25 },\n", + " Person { first_name: \"Bob\", last_name: \"Smith\", age: 50 },\n", + "];" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Person { first_name: \"Jane\", last_name: \"Doe\", age: 25 }, Person { first_name: \"John\", last_name: \"Doe\", age: 30 }, Person { first_name: \"Alice\", last_name: \"Smith\", age: 45 }, Person { first_name: \"Bob\", last_name: \"Smith\", age: 50 }]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people.sort_by(|a, b| a.age.cmp(&b.age));\n", + "people" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Person { first_name: \"Jane\", last_name: \"Doe\", age: 25 }, Person { first_name: \"John\", last_name: \"Doe\", age: 30 }, Person { first_name: \"Alice\", last_name: \"Smith\", age: 45 }, Person { first_name: \"Bob\", last_name: \"Smith\", age: 50 }]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people.sort_by(|a, b| {\n", + " let a_key = (a.last_name, a.first_name);\n", + " let b_key = (b.last_name, b.first_name);\n", + " a_key.cmp(&b_key) // use tuple comparison\n", + "});\n", + "people" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `slice.sort_by_key(key_fn)` sorts the elements of slice into increasing order by a sort key, given by the function or closure key. The type of key must implement `Fn(&T) -> K where K: Ord`." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Person { first_name: \"Jane\", last_name: \"Doe\", age: 25 }, Person { first_name: \"John\", last_name: \"Doe\", age: 30 }, Person { first_name: \"Alice\", last_name: \"Smith\", age: 45 }, Person { first_name: \"Bob\", last_name: \"Smith\", age: 50 }]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "people.sort_by_key(|p| p.age);\n", + "people" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `slice.reverse()` reverses a slice in place" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `slice.binary_search(&value)`, `slice.binary_search_by(&value, cmp)`, and `slice.binary_search_by_key(&value, key)` all search for value in the given sorted slice. Note that value is passed by reference.\n", + " * The return type of these methods is `Result`. They return `Ok(index)` if `slice[index]` equals value under the specified sort order. If there is no such index, then they return `Err(insertion_point)` such that inserting value at insertion_point would preserve the order.\n", + " * Requires sorted order. If the slice is not sorted, the result is unspecified.\n", + "\n", + "* `slice.contains(&value)` returns `true` if any element of slice is equal to value. This simply checks each element of the slice until a match is found. Slice may be in any order." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Comparing slices\n", + "\n", + "If a type `T` supports the `==` and `!=` operators (the `PartialEq` trait, described in “Equality Tests”), then arrays `[T; N]`, slices `[T]`, and vectors `Vec` support them too. Two slices are equal if they’re the same length and their corresponding elements are equal. The same goes for arrays and vectors." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `slice.starts_with(other)` returns `true` if slice starts with a sequence of values that are equal to the elements of the slice other:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "assert_eq!([1, 2, 3, 4].starts_with(&[1, 2]), true);\n", + "assert_eq!([1, 2, 3, 4].starts_with(&[2, 3]), false);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `slice.ends_with(other)` is similar but checks the end of slice:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "assert_eq!([1, 2, 3, 4].ends_with(&[3, 4]), true);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## VecDeque\\\n", + "\n", + "Rust’s `std::collections::VecDeque` is a deque (pronounced “deck”), a double-ended queue. It supports efficient add and remove operations at both the front and the back." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `deque.push_front(value)` adds a value at the front of the queue.\n", + "\n", + "* `deque.push_back(value)` adds a value at the end. (This method is used much more than `.push_front()`, because the usual convention for queues is that values are added at the back and removed at the front, like people waiting in a line.)\n", + "\n", + "* `deque.pop_front()` removes and returns the front value of the queue, returning an `Option` that is `None` if the queue is empty, like `vec.pop()`.\n", + "\n", + "* `deque.pop_back()` removes and returns the value at the back, again returning an `Option`.\n", + "\n", + "* `deque.front()` and `deque.back()` work like `vec.first()` and `vec.last()`. They return a reference to the front or back element of the queue. The return value is an `Option<&T>` that is `None` if the queue is empty.\n", + "\n", + "* `deque.front_mut()` and `deque.back_mut()` work like `vec.first_mut()` and `vec.last_mut()`, returning `Option<&mut T>`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Deques don’t store their elements contiguously in memory, they don’t inherit all the methods of slices. One way to perform vector and slice operations on deque data is to convert the `VecDeque` to a `Vec`, do the operation, and then change it back:\n", + "\n", + "* `Vec` implements `From>`, so `Vec::from(deque)` turns a deque into a vector. This costs `O(n)` time, since it may require rearranging the elements.\n", + "\n", + "* `VecDeque` implements `From>`, so `VecDeque::from(vec)` turns a vector into a deque. This is also `O(n)`, but it’s usually fast, even if the vector is large, because the vector’s heap allocation can simply be moved to the new deque.\n", + "\n", + "This method makes it easy to create a deque with specified elements, even though there is no standard `vec_deque![]` macro:" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "v = [0, 1, 2, 3, 4, 5]\n" + ] + } + ], + "source": [ + "use std::collections::VecDeque;\n", + "\n", + "let mut v = VecDeque::from(vec![1, 2, 3, 4]);\n", + "\n", + "v.push_front(0);\n", + "v.push_back(5);\n", + "\n", + "println!(\"v = {:?}\", v);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## LinkedList\\" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "LinkedList is a doubly-linked list. It’s like VecDeque but with some differences:\n", + "\n", + "* each element is stored in a separate heap allocation, so it’s slower to access elements by index\n", + "* it’s faster to concatenate two `LinkedList`s" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "lst_1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]\n", + "lst_2 = []\n" + ] + } + ], + "source": [ + "use std::collections::LinkedList;\n", + "\n", + "let mut lst_1 = (1..=5).collect::>();\n", + "let mut lst_2 = (6..=10).collect::>();\n", + "\n", + "lst_1.append(&mut lst_2);\n", + "\n", + "println!(\"lst_1 = {:?}\", lst_1);\n", + "println!(\"lst_2 = {:?}\", lst_2);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## HashMap\\ & BTreeMap\\" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A **map** is a collection of **key-value** pairs (called entries). No two entries have the same key, and the entries are kept organized so that if you have a key, you can efficiently look up the corresponding value in a map. \n", + "* A map is a lookup table." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* A `HashMap` stores the keys and values in a hash table, so it requires a key type `K` that implements `Hash` and `Eq`, the standard traits for hashing and equality.\n", + "* A `BTreeMap` stores the keys and values in a B-Tree, so it requires a key type `K` that implements `Ord`, the standard trait for ordering." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Creating a map" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `HashMap::new()` and `BTreeMap::new()` create new, empty maps.\n", + "\n", + "* `iter.collect()` can be used to create and populate a new `HashMap` or `BTreeMap` from key-value pairs. iter must be an `Iterator`.\n", + "\n", + "* `HashMap::with_capacity(n)` creates a new, empty hash map with room for at least n entries. `HashMaps`, like vectors, store their data in a single heap allocation, so they have a capacity and the related methods `hash_map.capacity()`, `hash_map.reserve(additional)`, and `hash_map.shrink_to_fit()`. `BTreeMaps` do not." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Working with keys & values" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `map.len()` returns the number of entries.\n", + "\n", + "* `map.is_empty()` returns true if map has no entries.\n", + "\n", + "* `map.contains_key(&key)` returns true if the map has an entry for the given key.\n", + "\n", + "* `map.get(&key)` searches map for an entry with the given key. If a matching entry is found, this returns Some(r), where r is a reference to the corresponding value. Otherwise, this returns None.\n", + "\n", + "* `map.get_mut(&key)` is similar, but it returns a mut reference to the value.\n", + "\n", + "* `map.insert(key, value)` inserts the entry `(key, value)` into map. If there’s already an entry for key in the map, the newly inserted value overwrites the old one.\n", + " * Returns the old value, if any. The return type is `Option`.\n", + "\n", + "* `map.extend(iterable)` iterates over the `(K, V)` items of iterable and inserts each of those key-value pairs into map.\n", + "\n", + "* `map.append(&mut map2)` moves all entries from `map2` into map. Afterward, `map2` is empty.\n", + "\n", + "* `map.remove(&key)` finds and removes any entry with the given key from map.\n", + " * Returns the removed value, if any. The return type is `Option`.\n", + "\n", + "* `map.clear()` removes all entries.\n", + "\n", + "* `map[&key]` - maps implement the `Index` built-in trait. However, this panics if there is not already an entry for the given key, like an out-of-bounds array access, so use this syntax only if the entry you’re looking up is sure to be populated." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Because a `BTreeMap` keeps its entries sorted by key, it supports an additional operation:\n", + "\n", + "* `btree_map.split_at(&key)` splits `btree_map` in two. Entries with keys less than key are left in `btree_map`. Returns a new `BTreeMap` containing the other entries." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Entries" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Both `HashMap` and `BTreeMap` have a corresponding `Entry` type. The point of entries is to eliminate redundant map lookups\n", + "\n", + "* All `Entry` values are created by the same method:\n", + " * `map.entry(key)` returns an `Entry` for the given key. If there’s no such key in the map, this returns a vacant `Entry`." + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dict_entry = \"three\"\n", + "dictionary = {3: \"three\", 2: \"two\", 1: \"one\", 4: \"four\"}\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{\n", + " use std::collections::HashMap;\n", + "\n", + " let mut dictionary: HashMap = vec![(1, \"one\".to_string()), (2, \"two\".to_string()), (4, \"four\".to_string())].into_iter().collect();\n", + "\n", + " {\n", + " let dict_entry = dictionary.entry(3).or_insert(\"three\".to_string());\n", + " println!(\"dict_entry = {:?}\", dict_entry);\n", + " }\n", + " \n", + " println!(\"dictionary = {:?}\", dictionary);\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Entry values provide two methods for filling in vacant entries:\n", + "\n", + "* `map.entry(key).or_insert(value)` ensures that map contains an entry with the given key, inserting a new entry with the given default value if needed. It returns a `mut` reference to the new or existing value." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "vote_counts: {\"John\": 3, \"Alice\": 2, \"Jane\": 2}\n" + ] + } + ], + "source": [ + "let ballots = vec![\"John\", \"Jane\", \"Alice\", \"Jane\", \"John\", \"Alice\", \"John\"];\n", + "\n", + "let mut vote_counts: HashMap = HashMap::new();\n", + "\n", + "for name in ballots {\n", + " let count = vote_counts.entry(name.to_string()).or_insert(0);\n", + " *count += 1;\n", + "}\n", + "\n", + "println!(\"vote_counts: {:?}\", vote_counts);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `map.entry(key).or_insert_with(default_fn)` is the same, except that if it needs to create a new entry, it calls `default_fn()` to produce the default value. If there’s already an entry for key in the map, then `default_fn` is not used." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Map Iteration" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* Iterating by value `for (k, v) in map` produces `(K, V)` pairs. This consumes the map.\n", + "\n", + "* Iterating over a shared reference `for (k, v) in &map` produces `(&K, &V)` pairs.\n", + "\n", + "* Iterating over a mut reference `for (k, v) in &mut map` produces `(&K, &mut V)` pairs.\n", + "\n", + "Like vectors, maps have `.iter()` and `.iter_mut()` methods which return by-reference iterators, just like iterating over `&map` or `&mut map`. In addition:\n", + "\n", + "* `map.keys()` returns an iterator over just the keys, by reference.\n", + "\n", + "* `map.values()` returns an iterator over the values, by reference.\n", + "\n", + "* `map.values_mut()` returns an iterator over the values, by mut reference." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Hashing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `std::hash::Hash` is the standard library trait for hashable types. `HashMap` keys and `HashSet` elements must implement both `Hash` and `Eq`.\n", + "\n", + "* Most built-in types that implement `Eq` also implement `Hash`. The integer types, `char`, and `String` are all hashable; so are tuples, arrays, slices, and vectors, as long as their elements are hashable.\n", + "\n", + "* One principle of the standard library is that a value should have the same hash code regardless of where you store it or how you point to it. Therefore, a reference has the same hash code as the value it refers to, and a `Box` has the same hash code as the boxed value. A vector vec has the same hash code as the slice containing all its data, `&vec[..]`. A `String` has the same hash code as a `&str` with the same characters.\n", + "\n", + "* Structs and enums don’t implement `Hash` by default, but an implementation can be derived (all fields must implement `Hash`):" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "metadata": {}, + "outputs": [], + "source": [ + "#[derive(Debug, Eq, PartialEq, Hash)]\n", + "struct GadgetId(u64);" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you implement `PartialEq` by hand for a type, you should also implement `Hash` by hand:" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "struct Gadget {\n", + " id: GadgetId,\n", + " name: String,\n", + " description: String \n", + "}\n", + "\n", + "impl PartialEq for Gadget {\n", + " fn eq(&self, other: &Self) -> bool {\n", + " self.id == other.id && self.name == other.name\n", + " }\n", + "}\n", + "\n", + "impl Eq for Gadget {}\n", + "\n", + "impl std::hash::Hash for Gadget {\n", + " fn hash(&self, hasher: &mut H) {\n", + " self.id.hash(hasher);\n", + " self.name.hash(hasher);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "* `std::hash::BuildHasher`, is the trait for types that represent the initial state of a hashing algorithm. Each `Hasher` is single-use, like an iterator: you use it once and throw it away. A `BuildHasher` is reusable." + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [], + "source": [ + "use std::hash::{Hash, Hasher, BuildHasher};\n", + "\n", + "fn compute_hash(builder: &B, value: &T) -> u64\n", + " where B: BuildHasher, T: Hash\n", + "{\n", + " let mut hasher = builder.build_hasher(); // 1. start the algorithm\n", + " value.hash(&mut hasher); // 2. feed it data\n", + " hasher.finish() // 3. finish, producing a u64\n", + "}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/17_multithreading.ipynb b/Rust_2026/rust-bs-2026-09-14/notebooks/17_multithreading.ipynb new file mode 100644 index 0000000..b57b07f --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/17_multithreading.ipynb @@ -0,0 +1,650 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4e3ae322", + "metadata": {}, + "source": [ + "# Multithreading in Rust" + ] + }, + { + "cell_type": "markdown", + "id": "947ff014", + "metadata": {}, + "source": [ + "## Managing Threads\n", + "\n", + "### Creating and joining threads\n", + "\n", + "* Creating a thread using `std::thread::spawn`\n", + "* Joining threads to wait for their completion using `join()`" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e8d84fec", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Counting 1 in background ThreadId(2)\n", + "Counting 1 in spawned ThreadId(3)\n", + "Counting 2 in background ThreadId(2)\n", + "Counting 2 in spawned ThreadId(3)\n", + "Counting 3 in background ThreadId(2)\n", + "Counting 3 in spawned ThreadId(3)\n", + "Counting 4 in background ThreadId(2)\n", + "Counting 4 in spawned ThreadId(3)\n", + "Counting 5 in spawned ThreadId(3)\n", + "Counting 5 in background ThreadId(2)\n", + "Counting 6 in spawned ThreadId(3)\n", + "Counting 6 in background ThreadId(2)\n", + "Counting 7 in background ThreadId(2)\n", + "Counting 7 in spawned ThreadId(3)\n", + "Counting 8 in background ThreadId(2)\n", + "Counting 8 in spawned ThreadId(3)\n", + "Counting 9 in spawned ThreadId(3)\n", + "Counting 9 in background ThreadId(2)\n", + "Counting 10 in spawned ThreadId(3)\n", + "Counting 10 in background ThreadId(2)\n" + ] + } + ], + "source": [ + "use std::thread;\n", + "\n", + "fn background_counting() {\n", + " for i in 1..=10 {\n", + " println!(\"Counting {} in background {:?}\", i, thread::current().id());\n", + " thread::sleep(std::time::Duration::from_millis(50));\n", + " }\n", + "}\n", + "\n", + "let thd_1 = thread::spawn(background_counting);\n", + "\n", + "let thd_2 = thread::spawn(|| {\n", + " for i in 1..=10 {\n", + " println!(\"Counting {} in spawned {:?}\", i, thread::current().id());\n", + " thread::sleep(std::time::Duration::from_millis(50));\n", + " }\n", + "});\n", + "\n", + "thd_1.join().unwrap();\n", + "thd_2.join().unwrap();" + ] + }, + { + "cell_type": "markdown", + "id": "d64ca60d", + "metadata": {}, + "source": [ + "## Sharing Data Between Threads\n", + "\n", + "\n", + "### Transfer Ownership\n", + "\n", + "* Safely transfer ownership of data to threads\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "80c5a805", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Processing data_vec in thread ThreadId(4)\n", + "Sum of data_vec: 500500\n" + ] + }, + { + "data": { + "text/plain": [ + "()" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "let data_vec = (1..=1000).collect::>();\n", + "\n", + "let thd_process = thread::spawn(move || {\n", + " println!(\"Processing data_vec in thread {:?}\", thread::current().id());\n", + " thread::sleep(std::time::Duration::from_millis(500));\n", + " let sum: i32 = data_vec.iter().sum();\n", + " // panic!(\"Intentional panic after processing data_vec with sum {}\", sum);\n", + " sum\n", + "});\n", + "\n", + "match thd_process.join() {\n", + " Ok(sum) => println!(\"Sum of data_vec: {}\", sum),\n", + " Err(e) => println!(\"Thread panicked: {:?}\", e),\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "72eaed46", + "metadata": {}, + "source": [ + "### Shared Ownership - Immutable Data\n", + "\n", + "* Using `Arc` for shared ownership across threads" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "01bd107d", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Min in thread ThreadId(6): 1\n", + "Max in thread ThreadId(6): 100\n", + "Sum in thread ThreadId(5): 5025\n", + "Final Results - Total Sum: 5025, Min: 1, Max: 100\n" + ] + } + ], + "source": [ + ":dep rand = \"0.9\"\n", + "\n", + "use rand::random_range;\n", + "use std::iter::from_fn;\n", + "use std::sync::Arc;\n", + "\n", + "let data: Vec = from_fn(|| Some(random_range(1..=100))).take(100).collect();\n", + "let shared_data = Arc::new(data);\n", + "\n", + "let thd_sum = {\n", + " let data_clone = Arc::clone(&shared_data);\n", + " thread::spawn(move || {\n", + " let sum: i32 = data_clone.iter().sum();\n", + " println!(\"Sum in thread {:?}: {}\", thread::current().id(), sum);\n", + " sum\n", + " })\n", + "}; \n", + "\n", + "let thd_max = {\n", + " let data_clone = Arc::clone(&shared_data);\n", + " thread::spawn(move || {\n", + " let min = data_clone.iter().min().cloned().unwrap();\n", + " println!(\"Min in thread {:?}: {}\", thread::current().id(), min);\n", + " let max = data_clone.iter().max().cloned().unwrap();\n", + " println!(\"Max in thread {:?}: {}\", thread::current().id(), max);\n", + " (min, max)\n", + " })\n", + "};\n", + "\n", + "let total_sum = thd_sum.join().unwrap();\n", + "let (min_value, max_value) = thd_max.join().unwrap();\n", + "\n", + "println!(\"Final Results - Total Sum: {}, Min: {}, Max: {}\", total_sum, min_value, max_value);" + ] + }, + { + "cell_type": "markdown", + "id": "85f2b2a1", + "metadata": {}, + "source": [ + "### Shared Mutable State Between Threads" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "3c499020", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "use std::sync::{Arc, Mutex};\n", + "\n", + "pub struct BankAccount {\n", + " // balance: f32\n", + " balance: Mutex,\n", + "}\n", + "\n", + "impl BankAccount {\n", + " pub fn new(initial_balance: f32) -> Self {\n", + " BankAccount { balance: Mutex::new(initial_balance) }\n", + " }\n", + " \n", + " pub fn deposit(&self, amount: f32) {\n", + " *self.balance.lock().unwrap() += amount;\n", + " }\n", + " \n", + " pub fn withdraw(&self, amount: f32) -> Result<(), String> {\n", + " let mut balance = self.balance.lock().unwrap();\n", + " if amount > *balance {\n", + " Err(\"Insufficient funds\".to_string())\n", + " } else {\n", + " *balance -= amount;\n", + " Ok(())\n", + " }\n", + " }\n", + "\n", + " fn transfer(&self, to: &BankAccount, amount: f32) -> Result<(), String> {\n", + " let mut from_balance = self.balance.lock().unwrap();\n", + " if amount > *from_balance {\n", + " return Err(\"Insufficient funds\".to_string());\n", + " }\n", + " let mut to_balance = to.balance.lock().unwrap();\n", + "\n", + " *from_balance -= amount;\n", + " *to_balance += amount;\n", + " \n", + " Ok(())\n", + " }\n", + " \n", + " pub fn balance(&self) -> f32 {\n", + " *self.balance.lock().unwrap()\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "aa2240cd", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [], + "source": [ + "fn pay_in(account: Arc, amount: f32, count: usize) {\n", + " for _ in 0..count {\n", + " if account.balance() < 200.0 {\n", + " println!(\"Balance low: ${}, depositing ${}\", account.balance(), amount);\n", + " account.deposit(amount);\n", + " } \n", + "\n", + " std::thread::sleep(std::time::Duration::from_millis(10));\n", + " }\n", + "}\n", + "\n", + "fn take_out(account: Arc, amount: f32, count: usize) {\n", + " for _ in 0..count {\n", + " match account.withdraw(amount) {\n", + " Ok(_) => println!(\"Withdrew ${}, new balance: ${}\", amount, account.balance()),\n", + " Err(e) => println!(\"Failed to withdraw ${}: {}\", amount, e),\n", + " }\n", + "\n", + " std::thread::sleep(std::time::Duration::from_millis(20));\n", + " }\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a22be054", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Withdrew $30, new balance: $70\n", + "Balance low: $70, depositing $50\n", + "Balance low: $120, depositing $50\n", + "Withdrew $30, new balance: $140\n", + "Balance low: $140, depositing $50\n", + "Balance low: $190, depositing $50\n", + "Withdrew $30, new balance: $210\n", + "Withdrew $30, new balance: $180\n", + "Balance low: $180, depositing $50\n", + "Withdrew $30, new balance: $200\n", + "Withdrew $30, new balance: $170\n", + "Withdrew $30, new balance: $140\n", + "Withdrew $30, new balance: $110\n", + "Withdrew $30, new balance: $80\n", + "Withdrew $30, new balance: $50\n" + ] + } + ], + "source": [ + "fn main() {\n", + " let mut account = Arc::new(BankAccount::new(100.0));\n", + "\n", + " let mut account_for_pay_in = Arc::clone(&account);\n", + " let pay_in_thread = std::thread::spawn(move || pay_in(account_for_pay_in, 50.0, 10));\n", + "\n", + " let mut account_for_take_out = Arc::clone(&account);\n", + " let take_out_thread = std::thread::spawn(move || take_out(account_for_take_out, 30.0, 10));\n", + "\n", + " pay_in_thread.join().unwrap();\n", + " take_out_thread.join().unwrap();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "id": "7f7968af", + "metadata": {}, + "source": [ + "## Send and Sync" + ] + }, + { + "cell_type": "markdown", + "id": "e02fb112", + "metadata": {}, + "source": [ + "* The `Send` and `Sync` traits affect the ability to transfer and share data between threads\n", + "* Types that implement `Send` can be transferred to another thread\n", + " * Most primitive types in Rust implement `Send`\n", + " * Owned types like `String` and `Vec` also implement `Send`\n", + " * Types that do not implement `Send` include raw pointers and types that manage non-thread-safe resources: `Rc`, `RefCell`\n", + "* Types that implement `Sync` can be referenced from multiple threads safely\n", + " * Immutable references (`&T`) are `Sync` if `T` is `Sync`\n", + " * Mutable references (`&mut T`) are not `Sync`\n", + " * Types that do not implement `Sync` include `Cell` and `RefCell`\n", + " * Types like `Mutex` and `RwLock` provide safe shared mutable access across threads" + ] + }, + { + "cell_type": "markdown", + "id": "7c6393dc", + "metadata": {}, + "source": [ + "## Deadlocks" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5ae7b3aa", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $450, to $350\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $320, to $480\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $290, to $510\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $460, to $340\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $310, to $490\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $440, to $360\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $330, to $470\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $420, to $380\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $350, to $450\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $400, to $400\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $370, to $430\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $380, to $420\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $390, to $410\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $360, to $440\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $410, to $390\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $340, to $460\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $430, to $370\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $320, to $480\n", + "Transferred $30 from BankAccount#2 to BankAccount#1. New balances: from $450, to $350\n", + "Transferred $50 from BankAccount#1 to BankAccount#2. New balances: from $300, to $500\n", + "Final Balance of Account A: $300\n", + "Final Balance of Account B: $500\n" + ] + } + ], + "source": [ + "use std::sync::{Arc, Mutex};\n", + "\n", + "mod Bank {\n", + " use std::sync::{Arc, Mutex};\n", + "\n", + " pub struct BankAccount {\n", + " id: u32,\n", + " balance: Mutex,\n", + " }\n", + "\n", + " impl BankAccount {\n", + " pub fn new(id: u32, initial_balance: f32) -> Self {\n", + " BankAccount { id, balance: std::sync::Mutex::new(initial_balance) }\n", + " }\n", + "\n", + " pub fn transfer(&self, to: &BankAccount, amount: f32) -> Result<(), String> {\n", + "\n", + " let (mut from_balance, mut to_balance) = if self.id < to.id {\n", + " let from_balance = self.balance.lock().unwrap();\n", + " let to_balance = to.balance.lock().unwrap();\n", + " (from_balance, to_balance)\n", + " } else {\n", + " let to_balance = to.balance.lock().unwrap();\n", + " let from_balance = self.balance.lock().unwrap();\n", + " (from_balance, to_balance)\n", + " };\n", + "\n", + " //let mut from_balance = self.balance.lock().unwrap();\n", + " //let mut to_balance = to.balance.lock().unwrap();\n", + "\n", + " *from_balance -= amount;\n", + " *to_balance += amount;\n", + " \n", + " Ok(())\n", + " }\n", + " \n", + " pub fn balance(&self) -> f32 {\n", + " *self.balance.lock().unwrap()\n", + " }\n", + "\n", + " pub fn id(&self) -> u32 {\n", + " self.id\n", + " }\n", + " }\n", + "\n", + " pub fn transfer_funds(from: Arc, to: Arc, amount: f32, count: usize) {\n", + " for _ in 0..count {\n", + " match from.transfer(&to, amount) {\n", + " Ok(_) => println!(\"Transferred ${} from BankAccount#{} to BankAccount#{}. New balances: from ${}, to ${}\", \n", + " amount, from.id(), to.id(), from.balance(), to.balance()),\n", + " Err(e) => println!(\"Failed to transfer ${} from BankAccount#{} to BankAccount#{}: {}\", \n", + " amount, from.id(), to.id(), e),\n", + " }\n", + "\n", + " std::thread::sleep(std::time::Duration::from_millis(15));\n", + " }\n", + " }\n", + "\n", + "}\n", + "\n", + "fn main() {\n", + " let account_a = Arc::new(Bank::BankAccount::new(1, 500.0));\n", + " let account_b = Arc::new(Bank::BankAccount::new(2, 300.0));\n", + "\n", + " let account_a_for_transfer_1 = Arc::clone(&account_a);\n", + " let account_b_for_transfer_1 = Arc::clone(&account_b);\n", + "\n", + " let thd_transfer_from_a_to_b = std::thread::spawn(move || {\n", + " Bank::transfer_funds(account_a_for_transfer_1, account_b_for_transfer_1, 50.0, 10);\n", + " });\n", + "\n", + " let account_a_for_transfer_2 = Arc::clone(&account_a);\n", + " let account_b_for_transfe_2 = Arc::clone(&account_b);\n", + " let thd_transfer_from_b_to_a = std::thread::spawn(move || {\n", + " Bank::transfer_funds(account_b_for_transfe_2, account_a_for_transfer_2, 30.0, 10);\n", + " });\n", + "\n", + " thd_transfer_from_a_to_b.join().unwrap();\n", + "\n", + " println!(\"Final Balance of Account A: ${}\", account_a.balance());\n", + " println!(\"Final Balance of Account B: ${}\", account_b.balance());\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "id": "52220484", + "metadata": {}, + "source": [ + "## Thread Communication With Channels" + ] + }, + { + "cell_type": "markdown", + "id": "b3653ab8", + "metadata": {}, + "source": [ + "* Using channels (`std::sync::mpsc`) for thread communication simplifies data transfer between threads\n", + "* Channels provide a way to send data from one thread to another safely\n", + " * Create a channel with `mpsc::channel()` - returns a sender and a receiver\n", + " * Send data between threads using the `send()` method on the sender\n", + " * Receive data in the main thread using the `recv()` method or iterating over the receiver" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "de2665bc", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Received: Hello from the sender thread\n" + ] + } + ], + "source": [ + "use std::thread;\n", + "use std::sync::mpsc;\n", + "\n", + "fn main() {\n", + " let (sender, receiver) = mpsc::channel();\n", + "\n", + " let sender_clone = sender.clone();\n", + " let thd_sender = thread::spawn(move || {\n", + " let message = \"Hello from the sender thread\".to_string();\n", + " sender_clone.send(message).unwrap();\n", + " });\n", + "\n", + " let received_data = receiver.recv().unwrap();\n", + " println!(\"Received: {}\", received_data);\n", + "\n", + " thd_sender.join().unwrap();\n", + "}\n", + "\n", + "main();" + ] + }, + { + "cell_type": "markdown", + "id": "74bd9f3b", + "metadata": {}, + "source": [ + "### Message Passing with Structs" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "73c689b8", + "metadata": { + "vscode": { + "languageId": "rust" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Received Message - ID: 42, Content: Important data\n" + ] + } + ], + "source": [ + "use std::thread;\n", + "use std::sync::mpsc;\n", + "\n", + "struct CustomMessage {\n", + " id: u32,\n", + " content: String,\n", + "}\n", + "\n", + "fn main() {\n", + " let (sender, receiver) = mpsc::channel();\n", + "\n", + " let sender_clone = sender.clone();\n", + " let handle = thread::spawn(move || {\n", + " let message = CustomMessage {\n", + " id: 42,\n", + " content: \"Important data\".to_string(),\n", + " };\n", + " sender_clone.send(message).unwrap();\n", + " });\n", + "\n", + " let received_message = receiver.recv().unwrap();\n", + " println!(\"Received Message - ID: {}, Content: {}\", received_message.id, received_message.content);\n", + "\n", + " handle.join().unwrap();\n", + "}\n", + "\n", + "main();" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Rust", + "language": "rust", + "name": "rust" + }, + "language_info": { + "codemirror_mode": "rust", + "file_extension": ".rs", + "mimetype": "text/rust", + "name": "Rust", + "pygment_lexer": "rust", + "version": "" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/file.txt b/Rust_2026/rust-bs-2026-09-14/notebooks/file.txt new file mode 100644 index 0000000..a866152 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/file.txt @@ -0,0 +1 @@ +This is a file with some text. \ No newline at end of file diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-clone.png b/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-clone.png new file mode 100644 index 0000000..98af80e Binary files /dev/null and b/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-clone.png differ diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-move.png b/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-move.png new file mode 100644 index 0000000..bfa335a Binary files /dev/null and b/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-move.png differ diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-slices.png b/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-slices.png new file mode 100644 index 0000000..cd84754 Binary files /dev/null and b/Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-slices.png differ diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/invalid_numbers.txt b/Rust_2026/rust-bs-2026-09-14/notebooks/invalid_numbers.txt new file mode 100644 index 0000000..293bcc4 --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/invalid_numbers.txt @@ -0,0 +1,8 @@ +1 +2 +3 +42 +66d5 +6 +7 +8 \ No newline at end of file diff --git a/Rust_2026/rust-bs-2026-09-14/notebooks/numbers.txt b/Rust_2026/rust-bs-2026-09-14/notebooks/numbers.txt new file mode 100644 index 0000000..7e05afe --- /dev/null +++ b/Rust_2026/rust-bs-2026-09-14/notebooks/numbers.txt @@ -0,0 +1,8 @@ +1 +2 +3 +42 +665 +6 +7 +8 \ No newline at end of file