From 0b2b0a65cd3ea7ccf72820f0573dffefe6717299 Mon Sep 17 00:00:00 2001 From: Sasza Stanczew Date: Mon, 14 Sep 2026 07:27:59 +0200 Subject: [PATCH] RUst - initial commit --- README.md | 4 + .../.devcontainer/Dockerfile | 7 + .../.devcontainer/devcontainer.json | 37 + Rust_2026/rust-bs-2026-09-14/Cargo.toml | 2 + Rust_2026/rust-bs-2026-09-14/README.md | 47 + .../code/hello-world/Cargo.toml | 9 + .../code/hello-world/src/main.rs | 5 + .../exercises/primes/Cargo.toml | 6 + .../exercises/primes/src/main.rs | 55 + .../rust-exercises/10_iterators.ipynb | 178 ++ .../rust-exercises/1_basic_types.ipynb | 469 +++ .../2_ownership_borrowing.ipynb | 313 ++ .../3_arrays_vectors_strings.ipynb | 282 ++ .../rust-exercises/4_struct-enum.ipynb | 442 +++ .../rust-exercises/5_pattern-match.ipynb | 213 ++ .../exercises/rust-exercises/6_errors.ipynb | 118 + .../exercises/rust-exercises/7_traits.ipynb | 421 +++ .../exercises/rust-exercises/8_generics.ipynb | 202 ++ .../exercises/rust-exercises/9_closures.ipynb | 187 ++ .../exercises/vector/Cargo.toml | 6 + .../exercises/vector/src/main.rs | 11 + .../exercises/vector/src/vector.rs | 143 + .../notebooks/01_variables_data_types.ipynb | 1573 +++++++++ .../notebooks/02_control_flow.ipynb | 674 ++++ .../notebooks/03_ownership.ipynb | 912 ++++++ .../notebooks/04_arrays_vectors_slices.ipynb | 2377 ++++++++++++++ .../notebooks/05_structs.ipynb | 564 ++++ .../notebooks/06_lifetimes-annotations.ipynb | 1742 ++++++++++ .../notebooks/07_enums_patterns.ipynb | 1784 +++++++++++ .../notebooks/08_error_handling.ipynb | 938 ++++++ .../notebooks/09_traits_generics.ipynb | 1466 +++++++++ .../notebooks/10_smart_pointers.ipynb | 536 ++++ .../notebooks/11_internal_mutability.ipynb | 453 +++ .../notebooks/12_operator_overloading.ipynb | 1055 ++++++ .../notebooks/13_utility_traits.ipynb | 1177 +++++++ .../notebooks/14_closures.ipynb | 718 +++++ .../notebooks/15_iterators.ipynb | 2832 +++++++++++++++++ .../notebooks/16_collections.ipynb | 1104 +++++++ .../notebooks/17_multithreading.ipynb | 650 ++++ .../rust-bs-2026-09-14/notebooks/file.txt | 1 + .../notebooks/images/rust-clone.png | Bin 0 -> 178222 bytes .../notebooks/images/rust-move.png | Bin 0 -> 252952 bytes .../notebooks/images/rust-slices.png | Bin 0 -> 69066 bytes .../notebooks/invalid_numbers.txt | 8 + .../rust-bs-2026-09-14/notebooks/numbers.txt | 8 + 45 files changed, 23729 insertions(+) create mode 100644 Rust_2026/rust-bs-2026-09-14/.devcontainer/Dockerfile create mode 100644 Rust_2026/rust-bs-2026-09-14/.devcontainer/devcontainer.json create mode 100644 Rust_2026/rust-bs-2026-09-14/Cargo.toml create mode 100644 Rust_2026/rust-bs-2026-09-14/README.md create mode 100644 Rust_2026/rust-bs-2026-09-14/code/hello-world/Cargo.toml create mode 100644 Rust_2026/rust-bs-2026-09-14/code/hello-world/src/main.rs create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/primes/Cargo.toml create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/primes/src/main.rs create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/10_iterators.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/1_basic_types.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/2_ownership_borrowing.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/3_arrays_vectors_strings.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/4_struct-enum.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/5_pattern-match.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/6_errors.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/7_traits.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/8_generics.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/rust-exercises/9_closures.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/vector/Cargo.toml create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/vector/src/main.rs create mode 100644 Rust_2026/rust-bs-2026-09-14/exercises/vector/src/vector.rs create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/01_variables_data_types.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/02_control_flow.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/03_ownership.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/04_arrays_vectors_slices.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/05_structs.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/06_lifetimes-annotations.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/07_enums_patterns.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/08_error_handling.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/09_traits_generics.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/10_smart_pointers.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/11_internal_mutability.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/12_operator_overloading.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/13_utility_traits.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/14_closures.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/15_iterators.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/16_collections.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/17_multithreading.ipynb create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/file.txt create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-clone.png create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-move.png create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/images/rust-slices.png create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/invalid_numbers.txt create mode 100644 Rust_2026/rust-bs-2026-09-14/notebooks/numbers.txt 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 0000000000000000000000000000000000000000..98af80e40c5855729f6e9bb7115eaee5ad488352 GIT binary patch literal 178222 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

-kCpO$%NvZelX1X&%;xDV1-!Y%(v&dk!d;Jnj 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 z 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

0A# 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 z�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ⅇp)5;1|~?iEr+f4O=V_aQ_UX$P1sR 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

T{EKB@EU&9 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 tct7Q4zd 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%0Ai93orljxU`?b+$Kn%B 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

bEiiXlyjmk7IXF)#>~LSEZo#0* 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 z