diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml deleted file mode 100644 index c4100271..00000000 --- a/.github/workflows/gh-pages.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: GitHub Pages - -on: - push: - branches: - - main - pull_request: - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Clone repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: true - - - name: Setup Hugo - uses: peaceiris/actions-hugo@v2 - with: - hugo-version: 'latest' - extended: true - - - name: Build - run: hugo --minify - - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - if: ${{ github.ref == 'refs/heads/main' }} - with: - deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} - external_repository: learning-rust/learning-rust.github.io - publish_branch: main - publish_dir: ./public diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..27d2a182 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,37 @@ +name: GitHub Pages + +on: + push: + branches: + - main + paths: + - docs/** + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + deploy: + runs-on: ubuntu-latest + + permissions: + pages: write + id-token: write + + environment: + name: github-pages + url: ${{steps.deployment.outputs.page_url}} + + steps: + - name: Configure Pages + uses: actions/configure-pages@v5 + - name: Checkout + uses: actions/checkout@v6 + - name: Upload Artifact + uses: actions/upload-pages-artifact@v4 + with: + path: docs + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index a5e3acef..84c5c196 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ -/.idea -/.vscode -/public +.hugo_build.lock .DS_Store -/resources/ -.hugo_build.lock \ No newline at end of file + +.zed +.idea +.vscode \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 57f8bf1e..79654ede 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "themes/docura"] - path = themes/docura - url = https://github.com/docura/docura.git +[submodule "themes/E25DX"] + path = themes/E25DX + url = https://github.com/dumindu/E25DX.git diff --git a/LICENSE b/LICENSE index ca311f8e..3de4146f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -MIT License +MIT NON-AI License -Copyright (c) 2015-2022 Dumindu Madunuwan +Copyright (c) 2015-2026 Dumindu Madunuwan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -9,7 +9,12 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +1. The Software and any derivative works may not be used for the +purposes of training, fine-tuning, or validating artificial intelligence +models or machine learning algorithms without prior written permission from +the copyright holders. + +2. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR diff --git a/README.md b/README.md index 951f8ca5..1420f0b9 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,10 @@ -# Learning Rust - -This website is built using [Hugo](https://gohugo.io/) and [Docura](https://docura.github.io/). +[![buymeacoffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-dumindu-FFDD00?style=for-the-badge&logo=buymeacoffee&logoColor=ffffff&labelColor=333333)](https://www.buymeacoffee.com/dumindu) -## Local setup -- [Install the extended version of Hugo](https://gohugo.io/getting-started/installing/) -- Clone the repository and start Hugo server - ``` - $ git clone --depth 1 https://github.com/learning-rust/site.git - $ cd site - $ git submodule update --init --recursive - $ hugo server - ``` +# Learning Rust -## Content +> Build with [Hugo](https://gohugo.io/installation/) and [E25DX](https://themes.gohugo.io/themes/e25dx/) theme. ❤️ -### Basics +## Basics * [Why Rust](content/en/docs/a1.why-rust.md) * [Installation](content/en/docs/a2.installation.md) * [Hello World](content/en/docs/a3.hello-world.md) @@ -26,19 +16,20 @@ This website is built using [Hugo](https://gohugo.io/) and [Docura](https://docu * [Operators](content/en/docs/a9.operators.md) * [Control flows](content/en/docs/a10.control-flows.md) -### Beyond The Basics +## Beyond The Basics * [Vectors](content/en/docs/b1.vectors.md) * [Structs](content/en/docs/b2.structs.md) * [Enums](content/en/docs/b3.enums.md) * [Generics](content/en/docs/b4.generics.md) -* [Impls and traits](content/en/docs/b5.impls-and-traits.md) +* [Impls](content/en/docs/b5.impls.md) +* [Traits](content/en/docs/b6.traits.md) -### The Tough Part +## The Tough Part * [Ownership](content/en/docs/c1.ownership.md) * [Borrowing](content/en/docs/c2.borrowing.md) * [Lifetimes](content/en/docs/c3.lifetimes.md) -### Lets Get It Started +## Lets Get It Started * [Code organization](content/en/docs/d1.code-organization.md) * [Functions](content/en/docs/d2.functions.md) * [Modules](content/en/docs/d3.modules.md) @@ -47,7 +38,7 @@ This website is built using [Hugo](https://gohugo.io/) and [Docura](https://docu * [use](content/en/docs/d6.use.md) * [std, primitives and preludes](content/en/docs/d7.std-primitives-and-preludes.md) -### Error Handling +## Error Handling * [Smart Compiler](content/en/docs/e1.smart-compiler.md) * [Panicking](content/en/docs/e2.panicking.md) * [Option and Result](content/en/docs/e3.option-and-result.md) @@ -55,9 +46,3 @@ This website is built using [Hugo](https://gohugo.io/) and [Docura](https://docu * [Error and None Propagation](content/en/docs/e5.error-and-none-propagation.md) * [Combinators](content/en/docs/e6.combinators.md) * [Custom Error Types](content/en/docs/e7.custom-error-types.md) - ---- -* Site : http://learning-rust.github.io -* Medium: https://medium.com/learning-rust - -> 🐣 I am a **Sri Lankan**🇱🇰 Web Developer who lives in **Singapore**🇸🇬. I am not a native English speaker and just practicing Rust in my leisure time, while working as a Golang/Devops developer. So, if you found any mistake or something I need to be changed, even a spelling/ grammar mistake, feel free to buzz me. diff --git a/assets/hero.svg b/assets/hero.svg new file mode 100644 index 00000000..4fc02f59 --- /dev/null +++ b/assets/hero.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/scss/icon/default.scss b/assets/scss/icon/default.scss deleted file mode 100644 index 82d9ec9d..00000000 --- a/assets/scss/icon/default.scss +++ /dev/null @@ -1,89 +0,0 @@ -.icon { - display: block; - width: 18px; - height: 18px; -} - -/* -- social icons: add `.icon-colored` with `.icon` -- */ -.icon-github { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 16 16' fill='%2324292f' %3E%3Cpath d='M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z'%3E%3C/path%3E%3C/svg%3E"); -} - -:root[data-color="dark"] .icon-github, :root[data-color="night"] .icon-github { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 16 16' fill='%236e7681' %3E%3Cpath d='M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z'%3E%3C/path%3E%3C/svg%3E"); -} - - -/* -- template icons -- */ -.icon-menu { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24'%3E%3Cpath d='M0,0h24v24H0V0z' fill='none'/%3E%3Cpath d='M4,18h11c0.55,0,1-0.45,1-1v0c0-0.55-0.45-1-1-1H4c-0.55,0-1,0.45-1,1v0C3,17.55,3.45,18,4,18z M4,13h8c0.55,0,1-0.45,1-1v0 c0-0.55-0.45-1-1-1H4c-0.55,0-1,0.45-1,1v0C3,12.55,3.45,13,4,13z M3,7L3,7c0,0.55,0.45,1,1,1h11c0.55,0,1-0.45,1-1v0 c0-0.55-0.45-1-1-1H4C3.45,6,3,6.45,3,7z M20.3,14.88L17.42,12l2.88-2.88c0.39-0.39,0.39-1.02,0-1.41l0,0 c-0.39-0.39-1.02-0.39-1.41,0l-3.59,3.59c-0.39,0.39-0.39,1.02,0,1.41l3.59,3.59c0.39,0.39,1.02,0.39,1.41,0l0,0 C20.68,15.91,20.69,15.27,20.3,14.88z'/%3E%3Cpath d='M0,0h24v24H0V0z' fill='none'/%3E%3C/svg%3E"); -} - -.icon-toc { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' fill='%23000000'%3E%3Cpath d='M0 0h24v24H0V0zm0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M3 9h14V7H3v2zm0 4h14v-2H3v2zm0 4h14v-2H3v2zm16 0h2v-2h-2v2zm0-10v2h2V7h-2zm0 6h2v-2h-2v2z'/%3E%3C/svg%3E"); -} - -.icon-close { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z'/%3E%3C/svg%3E"); -} - -.icon-home { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24'%3E%3Crect fill='none' height='24' width='24'/%3E%3Cpolygon opacity='.3' points='18,19 13,19 13,15 11,15 11,19 6,19 6,10.1 12,5.52 18,10.1'/%3E%3Cpath d='M12,3L6,7.58V6H4v3.11L1,11.4l1.21,1.59L4,11.62V21h16v-9.38l1.79,1.36L23,11.4L12,3z M18,19h-5v-4h-2v4H6v-8.9l6-4.58 l6,4.58V19z M10,1c0,1.66-1.34,3-3,3C6.45,4,6,4.45,6,5H4c0-1.66,1.34-3,3-3c0.55,0,1-0.45,1-1H10z'/%3E%3C/svg%3E"); -} - -.icon-book { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24'%3E%3Cg%3E%3Crect fill='none' height='24' width='24'/%3E%3C/g%3E%3Cg%3E%3Cg/%3E%3Cg%3E%3Cpath d='M21,5c-1.11-0.35-2.33-0.5-3.5-0.5c-1.95,0-4.05,0.4-5.5,1.5c-1.45-1.1-3.55-1.5-5.5-1.5S2.45,4.9,1,6v14.65 c0,0.25,0.25,0.5,0.5,0.5c0.1,0,0.15-0.05,0.25-0.05C3.1,20.45,5.05,20,6.5,20c1.95,0,4.05,0.4,5.5,1.5c1.35-0.85,3.8-1.5,5.5-1.5 c1.65,0,3.35,0.3,4.75,1.05c0.1,0.05,0.15,0.05,0.25,0.05c0.25,0,0.5-0.25,0.5-0.5V6C22.4,5.55,21.75,5.25,21,5z M3,18.5V7 c1.1-0.35,2.3-0.5,3.5-0.5c1.34,0,3.13,0.41,4.5,0.99v11.5C9.63,18.41,7.84,18,6.5,18C5.3,18,4.1,18.15,3,18.5z M21,18.5 c-1.1-0.35-2.3-0.5-3.5-0.5c-1.34,0-3.13,0.41-4.5,0.99V7.49c1.37-0.59,3.16-0.99,4.5-0.99c1.2,0,2.4,0.15,3.5,0.5V18.5z'/%3E%3Cpath d='M11,7.49C9.63,6.91,7.84,6.5,6.5,6.5C5.3,6.5,4.1,6.65,3,7v11.5C4.1,18.15,5.3,18,6.5,18 c1.34,0,3.13,0.41,4.5,0.99V7.49z' opacity='.3'/%3E%3C/g%3E%3Cg%3E%3Cpath d='M17.5,10.5c0.88,0,1.73,0.09,2.5,0.26V9.24C19.21,9.09,18.36,9,17.5,9c-1.28,0-2.46,0.16-3.5,0.47v1.57 C14.99,10.69,16.18,10.5,17.5,10.5z'/%3E%3Cpath d='M17.5,13.16c0.88,0,1.73,0.09,2.5,0.26V11.9c-0.79-0.15-1.64-0.24-2.5-0.24c-1.28,0-2.46,0.16-3.5,0.47v1.57 C14.99,13.36,16.18,13.16,17.5,13.16z'/%3E%3Cpath d='M17.5,15.83c0.88,0,1.73,0.09,2.5,0.26v-1.52c-0.79-0.15-1.64-0.24-2.5-0.24c-1.28,0-2.46,0.16-3.5,0.47v1.57 C14.99,16.02,16.18,15.83,17.5,15.83z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); -} - -.icon-theme { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c2.76 0 5-2.24 5-5 0-4.42-4.03-8-9-8zm-5.5 9c-.83 0-1.5-.67-1.5-1.5S5.67 9 6.5 9 8 9.67 8 10.5 7.33 12 6.5 12zm3-4C8.67 8 8 7.33 8 6.5S8.67 5 9.5 5s1.5.67 1.5 1.5S10.33 8 9.5 8zm5 0c-.83 0-1.5-.67-1.5-1.5S13.67 5 14.5 5s1.5.67 1.5 1.5S15.33 8 14.5 8zm3 4c-.83 0-1.5-.67-1.5-1.5S16.67 9 17.5 9s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z'/%3E%3C/svg%3E"); -} - -.icon-brightness { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M18 9.52V6h-3.52L12 3.52 9.52 6H6v3.52L3.52 12 6 14.48V18h3.52L12 20.48 14.48 18H18v-3.52L20.48 12 18 9.52zm-6 7.98v-11c3.03 0 5.5 2.47 5.5 5.5s-2.47 5.5-5.5 5.5z' opacity='.3'/%3E%3Cpath d='M20 8.69V4h-4.69L12 .69 8.69 4H4v4.69L.69 12 4 15.31V20h4.69L12 23.31 15.31 20H20v-4.69L23.31 12 20 8.69zm-2 5.79V18h-3.52L12 20.48 9.52 18H6v-3.52L3.52 12 6 9.52V6h3.52L12 3.52 14.48 6H18v3.52L20.48 12 18 14.48zM12 6.5v11c3.03 0 5.5-2.47 5.5-5.5S15.03 6.5 12 6.5z'/%3E%3C/svg%3E"); -} - -.icon-light-mode { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Crect fill='none' height='24' width='24'/%3E%3Ccircle cx='12' cy='12' opacity='.3' r='3'/%3E%3Cpath d='M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z'/%3E%3C/svg%3E"); -} - -.icon-dark-mode { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Crect fill='none' height='24' width='24'/%3E%3Cpath d='M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z' opacity='.3'/%3E%3Cpath d='M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z'/%3E%3C/svg%3E"); -} - -.icon-night-mode { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Cg%3E%3Crect fill='none' height='24' width='24'/%3E%3C/g%3E%3Cg%3E%3Cg%3E%3Cpath d='M8.1,14.15C9.77,14.63,11,16.17,11,18c0,0.68-0.19,1.31-0.48,1.87c0.48,0.09,0.97,0.14,1.48,0.14 c1.48,0,2.9-0.41,4.13-1.15c-2.62-0.92-5.23-2.82-6.8-5.86C7.74,9.94,7.78,7.09,8.29,4.9c-2.57,1.33-4.3,4.01-4.3,7.1c0,0,0,0,0,0 c0.01,0,0.01,0,0.02,0C5.66,12,7.18,12.83,8.1,14.15z' opacity='.3'/%3E%3Cpath d='M19.78,17.51c-2.47,0-6.57-1.33-8.68-5.43C8.77,7.57,10.6,3.6,11.63,2.01C6.27,2.2,1.98,6.59,1.98,12 c0,0.14,0.02,0.28,0.02,0.42C2.61,12.16,3.28,12,3.98,12c0,0,0,0,0,0c0-3.09,1.73-5.77,4.3-7.1C7.78,7.09,7.74,9.94,9.32,13 c1.57,3.04,4.18,4.95,6.8,5.86c-1.23,0.74-2.65,1.15-4.13,1.15c-0.5,0-1-0.05-1.48-0.14c-0.37,0.7-0.94,1.27-1.64,1.64 c0.98,0.32,2.03,0.5,3.11,0.5c3.5,0,6.58-1.8,8.37-4.52C20.18,17.5,19.98,17.51,19.78,17.51z'/%3E%3Cpath d='M7,16l-0.18,0C6.4,14.84,5.3,14,4,14c-1.66,0-3,1.34-3,3s1.34,3,3,3c0.62,0,2.49,0,3,0c1.1,0,2-0.9,2-2 C9,16.9,8.1,16,7,16z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); -} - -.icon-translate { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M12.65 15.67c.14-.36.05-.77-.23-1.05l-2.09-2.06.03-.03c1.74-1.94 2.98-4.17 3.71-6.53h1.94c.54 0 .99-.45.99-.99v-.02c0-.54-.45-.99-.99-.99H10V3c0-.55-.45-1-1-1s-1 .45-1 1v1H1.99c-.54 0-.99.45-.99.99 0 .55.45.99.99.99h10.18C11.5 7.92 10.44 9.75 9 11.35c-.81-.89-1.49-1.86-2.06-2.88-.16-.29-.45-.47-.78-.47-.69 0-1.13.75-.79 1.35.63 1.13 1.4 2.21 2.3 3.21L3.3 16.87c-.4.39-.4 1.03 0 1.42.39.39 1.02.39 1.42 0L9 14l2.02 2.02c.51.51 1.38.32 1.63-.35zM17.5 10c-.6 0-1.14.37-1.35.94l-3.67 9.8c-.24.61.22 1.26.87 1.26.39 0 .74-.24.88-.61l.89-2.39h4.75l.9 2.39c.14.36.49.61.88.61.65 0 1.11-.65.88-1.26l-3.67-9.8c-.22-.57-.76-.94-1.36-.94zm-1.62 7l1.62-4.33L19.12 17h-3.24z'/%3E%3C/svg%3E"); -} - -.icon-search { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M15.5 14h-.79l-.28-.27c1.2-1.4 1.82-3.31 1.48-5.34-.47-2.78-2.79-5-5.59-5.34-4.23-.52-7.79 3.04-7.27 7.27.34 2.8 2.56 5.12 5.34 5.59 2.03.34 3.94-.28 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E"); -} - -.icon-select { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M12 5.83L15.17 9l1.41-1.41L12 3 7.41 7.59 8.83 9 12 5.83zm0 12.34L8.83 15l-1.41 1.41L12 21l4.59-4.59L15.17 15 12 18.17z'/%3E%3C/svg%3E"); -} - -.icon-calendar { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24'%3E%3Cg%3E%3Crect fill='none' height='24' width='24'/%3E%3C/g%3E%3Cg%3E%3Crect height='2' opacity='.3' width='14' x='5' y='6'/%3E%3Cpath d='M19,4h-1V2h-2v2H8V2H6v2H5C3.89,4,3.01,4.9,3.01,6L3,20c0,1.1,0.89,2,2,2h14c1.1,0,2-0.9,2-2V6C21,4.9,20.1,4,19,4z M19,20 H5V10h14V20z M19,8H5V6h14V8z M9,14H7v-2h2V14z M13,14h-2v-2h2V14z M17,14h-2v-2h2V14z M9,18H7v-2h2V18z M13,18h-2v-2h2V18z M17,18 h-2v-2h2V18z'/%3E%3C/g%3E%3C/svg%3E"); -} - -.icon-next { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Cpath d='M24 24H0V0h24v24z' fill='none' opacity='.87'/%3E%3Cpath d='M7.38 21.01c.49.49 1.28.49 1.77 0l8.31-8.31c.39-.39.39-1.02 0-1.41L9.15 2.98c-.49-.49-1.28-.49-1.77 0s-.49 1.28 0 1.77L14.62 12l-7.25 7.25c-.48.48-.48 1.28.01 1.76z' fill='%23328ac1'/%3E%3C/svg%3E"); -} - -.icon-prev { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Crect fill='none' height='24' width='24'/%3E%3Cg%3E%3Cpath d='M16.88,2.88L16.88,2.88c-0.49-0.49-1.28-0.49-1.77,0l-8.41,8.41c-0.39,0.39-0.39,1.02,0,1.41l8.41,8.41 c0.49,0.49,1.28,0.49,1.77,0l0,0c0.49-0.49,0.49-1.28,0-1.77L9.54,12l7.35-7.35C17.37,4.16,17.37,3.37,16.88,2.88z' fill='%23328ac1'/%3E%3C/g%3E%3C/svg%3E"); -} - -.icon-copyright { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M10.08 10.86c.05-.33.16-.62.3-.87s.34-.46.59-.62c.24-.15.54-.22.91-.23.23.01.44.05.63.13.2.09.38.21.52.36s.25.33.34.53.13.42.14.64h1.79c-.02-.47-.11-.9-.28-1.29s-.4-.73-.7-1.01-.66-.5-1.08-.66-.88-.23-1.39-.23c-.65 0-1.22.11-1.7.34s-.88.53-1.2.92-.56.84-.71 1.36S8 11.29 8 11.87v.27c0 .58.08 1.12.23 1.64s.39.97.71 1.35.72.69 1.2.91c.48.22 1.05.34 1.7.34.47 0 .91-.08 1.32-.23s.77-.36 1.08-.63.56-.58.74-.94.29-.74.3-1.15h-1.79c-.01.21-.06.4-.15.58s-.21.33-.36.46-.32.23-.52.3c-.19.07-.39.09-.6.1-.36-.01-.66-.08-.89-.23-.25-.16-.45-.37-.59-.62s-.25-.55-.3-.88-.08-.67-.08-1v-.27c0-.35.03-.68.08-1.01zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z'/%3E%3C/svg%3E"); -} - -/* -- add `.icon-colored` -- */ -.icon-love { - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18px' height='18px' viewBox='0 0 24 24' fill='%23ff4d4d' %3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M13.35 20.13c-.76.69-1.93.69-2.69-.01l-.11-.1C5.3 15.27 1.87 12.16 2 8.28c.06-1.7.93-3.33 2.34-4.29 2.64-1.8 5.9-.96 7.66 1.1 1.76-2.06 5.02-2.91 7.66-1.1 1.41.96 2.28 2.59 2.34 4.29.14 3.88-3.3 6.99-8.55 11.76l-.1.09z'/%3E%3C/svg%3E"); -} diff --git a/config/_default/config.toml b/config/_default/config.toml deleted file mode 100644 index 38be1fa1..00000000 --- a/config/_default/config.toml +++ /dev/null @@ -1,15 +0,0 @@ -title = 'Learning Rust' -baseURL = 'https://learning-rust.github.io/' - -defaultContentLanguage = 'en' -languageCode = 'en-us' - -enableGitInfo = true - -theme = 'docura' - -markup.highlight.noClasses = false - -[services] -[services.googleAnalytics] -ID = 'G-FZHQCXSZ89' \ No newline at end of file diff --git a/config/_default/languages.toml b/config/_default/languages.toml deleted file mode 100644 index 7f7ab210..00000000 --- a/config/_default/languages.toml +++ /dev/null @@ -1,4 +0,0 @@ -[en] -contentDir = 'content/en' -languageName = 'English' -weight = 1 \ No newline at end of file diff --git a/config/_default/menu.toml b/config/_default/menu.toml deleted file mode 100644 index 9b6a6096..00000000 --- a/config/_default/menu.toml +++ /dev/null @@ -1,13 +0,0 @@ -[[main]] -identifier = 'home' -name = 'Home' -pre = "" -url = '/' -weight = 1 - -[[main]] -identifier = 'docs' -name = 'Documentation' -pre = "" -url = '/docs/' -weight = 2 diff --git a/config/_default/params.toml b/config/_default/params.toml deleted file mode 100644 index ac5939e7..00000000 --- a/config/_default/params.toml +++ /dev/null @@ -1,25 +0,0 @@ -description = 'Rust Programming Language Tutorials' - -currentYear = 2023 -projectStartYear = 2016 - -facebookURL = '' -twitterURL = '' -githubURL = 'https://github.com/learning-rust' -youtubeURL = '' - -githubRepo = 'learning-rust/site' - -buyMeACoffee = 'dumindu' -githubSponsor = 'dumindu' - -[author] -name= 'Dumindu Madunuwan' -URL = 'https://github.com/dumindu' - -[algolia] -[algolia.en] -container = '#site-header-search' -appId = 'QEN78N5RTO' -indexName = 'learning_rust' -apiKey = '07315fdb3221618273bdaffb1ab6f388' diff --git a/content/en/docs/_overview.md b/content/en/docs/_overview.md index daba0323..cd5d7445 100644 --- a/content/en/docs/_overview.md +++ b/content/en/docs/_overview.md @@ -5,8 +5,18 @@ aliases: - "/docs" --- -This is based on the posts I wrote on **Medium**, https://medium.com/learning-rust +## About me -[![Rust Playground](/docs/learning_rust_medium.png)](https://medium.com/learning-rust) +> 🧑‍💻 I am an expat working in Singapore as a Go Backend and DevOps Engineer. Feel free to reach out if you find any mistakes or anything that needs to be changed, including spelling or grammar errors. Alternatively, you can create a pull request, open an issue, or [share your awesome ideas in this gist](https://gist.github.com/dumindu/00a0be2d175ed5ff3bc3c17bbf1ca5b6). Good luck with learning Rust! -> 🐣 I am a **Sri Lankan** 🇱🇰 Web Developer who lives in **Singapore** 🇸🇬. So I am not a native English speaker and just learning Rust, If you found any mistake or something need to be changed, even a spelling or a grammar mistake, feel free to create a pull request. Thanks. \ No newline at end of file +[![learning-rust.github.io](https://img.shields.io/github/stars/learning-rust/learning-rust.github.io?style=for-the-badge&logo=rust&label=learning-rust.github.io&logoColor=333333&labelColor=f9f9f9&color=F46623)](https://github.com/learning-rust/learning-rust.github.io) +[![learning-cloud-native-go.github.io](https://img.shields.io/github/stars/learning-cloud-native-go/learning-cloud-native-go.github.io?style=for-the-badge&logo=go&logoColor=333333&label=learning-cloud-native-go.github.io&labelColor=f9f9f9&color=00ADD8)](https://learning-cloud-native-go.github.io) + +[![github.com](https://img.shields.io/badge/dumindu-866ee7?style=for-the-badge&logo=GitHub&logoColor=333333&labelColor=f9f9f9)](https://github.com/dumindu) +[![buymeacoffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-dumindu-FFDD00?style=for-the-badge&logo=buymeacoffee&logoColor=333333&labelColor=f9f9f9)](https://www.buymeacoffee.com/dumindu) + +## Overview + +This publication has its origins in the posts I authored on Medium at https://medium.com/learning-rust. However, please note that I have ceased updating the Medium posts. All current and future updates, new content, code, and grammar fixes will be exclusively maintained and released here, https://learning-rust.github.io. + +[![Learning Rust @Medium](/docs/learning_rust_medium.png)](https://medium.com/learning-rust) diff --git a/content/en/docs/a1.why-rust.md b/content/en/docs/a1.why-rust.md index 68ad75dd..b9b6e109 100755 --- a/content/en/docs/a1.why-rust.md +++ b/content/en/docs/a1.why-rust.md @@ -4,45 +4,52 @@ slug: why-rust --- ## History of Rust -Rust was initially designed and developed by former Mozilla employee **[Graydon Hoare](https://github.com/graydon)** as a personal project. Mozilla began sponsoring the project in 2009 and announced it in 2010. But the first stable release, Rust 1.0 was released on May 15, 2015. + +Rust was initially designed and developed by former Mozilla employee **[Graydon Hoare](https://github.com/graydon)** as a personal project. Mozilla began sponsoring the project in 2009 and announced it in 2010. But the first stable release, Rust 1.0, was released on May 15, 2015. + +Since Rust 1.0, major updates have been released as [`Editions`](/docs/cargo-crates-and-basic-project-structure/#rust-editions) approximately every three years: Rust 2015 (with the release of Rust 1.0) , Rust 2018, Rust 2021, and Rust 2024, all maintaining backward compatibility. ## Initial Goals -The goal of Rust is to be a good programming language for creating highly concurrent, safe and performant systems. -> **"Rust is a systems programming language focused on three goals: safety, speed, and concurrency."** -> \_\_ Rust Documentation +> Rust is a systems programming language focused on three goals: safety, speed, and concurrency. +>
~ Rust Documentation + +**Rust is a modern, [multi-platform](https://doc.rust-lang.org/rustc/platform-support.html), [multi-paradigm](https://en.wikipedia.org/wiki/Comparison_of_multi-paradigm_programming_languages), [statically compiled](https://en.wikipedia.org/wiki/Compiled_language), memory & thread safety–focused, systems programming language.** -Rust is very young and very modern language. It is a **[compiled programming language](https://en.wikipedia.org/wiki/Compiled_language)** and it uses [LLVM](https://en.wikipedia.org/wiki/LLVM) on the backend. Also, Rust is a **[multi-paradigm programming language](https://en.wikipedia.org/wiki/Comparison_of_multi-paradigm_programming_languages)**, which supports imperative procedural, concurrent actor, object-oriented and pure functional styles. It also supports generic programming and metaprogramming, in both static and dynamic styles. +- It uses [LLVM](https://en.wikipedia.org/wiki/LLVM) on the backend and supports many different operating systems, architectures, targets, and cross-compiling. +- It supports imperative procedural, concurrent actor, object-oriented, and pure functional styles. Rust also supports generic programming and metaprogramming, in both static and dynamic styles. +- It doesn't use a built-in runtime or an automated garbage collection system \(GC\). -> 🔎 One of Rust’s most unique and compelling features is [Ownership](c1.ownership.html), which is used to achieve memory safety. Rust creates memory pointers optimistically, checks memory pointers’ limited accesses at compile-time with the usage of [References and Borrowing](c2.borrowing.html). And it does automatic compile-time memory management by checking the [Lifetimes](c3.lifetimes.html). + > [!recap] + > However, async Rust requires an async runtime, which is provided by community-maintained ecosystems like [Compio](https://compio.rs), [Embassy](https://embassy.dev), [`smol`](https://github.com/smol-rs/smol), [`tokio`](https://tokio.rs) etc. The async runtime will be bundled into the final executable. + +- One of Rust’s most unique and compelling features is [Ownership](/docs/ownership), which is used to achieve memory safety. Rust creates memory pointers optimistically, checks memory pointers’ limited accesses at compile-time with the usage of [References and Borrowing](/docs/borrowing). And it does automatic compile-time memory management by checking the [Lifetimes](/docs/lifetimes). ## Influences + Its design elements came from a wide range of sources. -- Abstract Machine Model: **C** -- Data types: **C, SML, OCaml, Lisp, Limbo** -- Optional Bindings: **Swift** -- Hygienic Macros: **Scheme** -- Functional Programming: **Haskell, OCaml, F\#** -- Attributes: **ECMA**-335 -- Memory Model and Memory Management: **C++, ML Kit, Cyclone** -- Type Classes: **Haskell** -- Crate: Assembly in the **ECMA**-335 CLI model -- Channels and Concurrency: **Newsqueak, Alef, Limbo** -- Message passing and Thread failure: **Erlang** +- Abstract Machine Model: C +- Data types: C, SML, OCaml, Lisp, Limbo +- Optional Bindings: Swift +- Hygienic Macros: Scheme +- Functional Programming: Haskell, OCaml, F\# +- Attributes: ECMA-335 +- Memory Model and Memory Management: C++, ML Kit, Cyclone +- Type Classes: Haskell +- Crate: Assembly in the ECMA-335 CLI model +- Channels and Concurrency: Newsqueak, Alef, Limbo +- Message passing and Thread failure: Erlang and etc. - -Rust **doesn't use an automated garbage collection** system\(GC\) by default. - -Rust compiler observes the code **at compile-time** and helps to [**prevent many types of errors**](https://doc.rust-lang.org/error-index.html) that are possible to write in C, C++ like programming languages. +Rust compiler observes the code at compile-time and helps to [prevent many types of errors](https://doc.rust-lang.org/error-index.html) that are possible to write in C, C++ like programming languages. ## 👨‍🏫 Before going to the next... - The following guides will be helpful for you to understand the maturity of the Rust ecosystem and the tools you need to choose, according to the area you want to master. + - [Are we async yet?](https://areweasyncyet.rs/) - [Are we web yet?](http://www.arewewebyet.org/) - [Are we game yet?](http://arewegameyet.com/) - - [Are we learning yet?](http://www.arewelearningyet.com/) - [Are we GUI yet?](https://areweguiyet.com/) - - [Are we audio yet?](https://areweaudioyet.com/) + - [Are we learning yet?](http://www.arewelearningyet.com/) diff --git a/content/en/docs/a10.control-flows.md b/content/en/docs/a10.control-flows.md index d54e5c9d..02edcb0c 100755 --- a/content/en/docs/a10.control-flows.md +++ b/content/en/docs/a10.control-flows.md @@ -5,7 +5,7 @@ slug: control-flows ## if - else if - else -- Using only `if` block. +### `if` ```rust let age = 13; @@ -15,7 +15,7 @@ if age < 18 { } ``` -- Using only `if` and `else` blocks. +### `if` `else` ```rust let i = 7; @@ -27,17 +27,18 @@ if i % 2 == 0 { } ``` -- Using with `let` statement. +### With `let` Statements ```rust let age: u8 = 13; let is_below_eighteen = if age < 18 { true } else { false }; // true ``` -- More examples, +### `if` `else if` `else` + +i. A simple example, ```rust -// i. A simple example let team_size = 7; if team_size < 5 { @@ -49,8 +50,9 @@ if team_size < 5 { } ``` +ii. Let's refactor the above code, + ```rust -// ii. Let's refactor above code let team_size = 7; let team_size_in_text; @@ -65,8 +67,9 @@ if team_size < 5 { println!("Current team size : {}", team_size_in_text); // Current team size : Medium ``` +iii. Let's refactor further (variable shadowing), + ```rust -// iii. Let's refactor further let team_size = 7; let team_size = if team_size < 5 { "Small" // ⭐️ no ; @@ -81,22 +84,29 @@ println!("Current team size : {}", team_size); // Current team size : Medium ⭐️ **Return data type should be the same on each block when using this as an expression.** - ## match +### With Multiple Patterns + ```rust let tshirt_width = 20; let tshirt_size = match tshirt_width { - 16 => "S", // check 16 - 17 | 18 => "M", // check 17 and 18 - 19 ..= 21 => "L", // check from 19 to 21 (19,20,21) - 22 => "XL", - _ => "Not Available", + 13 => "XS", // check 13 + 14 | 15 => "S", // check 14 and 15 + 16..18 => "M", // check from 16 to 17 / 18 exclusive (16,17) + 18..=20 => "L", // check from 18 to 20 (18,19,20) + + x if x > 20 && x < 26 => "XL", // check 21 to 25 (21,22,23,24,25) + // >,>=,<,<= via assigning the value to a variable (x) + if + + _ => "Not Available", // default behavior, if none of conditions matches }; println!("{}", tshirt_size); // L ``` +### Without Default Behavior + ```rust let is_allowed = false; let list_type = match is_allowed { @@ -109,6 +119,8 @@ let list_type = match is_allowed { println!("{}", list_type); // Restricted ``` +### With Multiple Variable Matchings + ```rust let marks_paper_a: u8 = 25; let marks_paper_b: u8 = 30; @@ -124,158 +136,220 @@ let output = match (marks_paper_a, marks_paper_b) { println!("{}", output); // Work hard ``` - ## loop +### Infinite `loop` + ```rust +// ⚠️ This will run forever without terminating. Termininate manually (Ctrl+C) loop { - println!("Loop forever!"); + println!("Loop forever!"); } ``` +### With `break` and `continue` + ```rust -// Usage of break and continue let mut a = 0; loop { - if a == 0 { - println!("Skip Value : {}", a); - a += 1; - continue; - } else if a == 2 { - println!("Break At : {}", a); - break; - } - - println!("Current Value : {}", a); - a += 1; + if a == 0 { + println!("Skip Value : {}", a); + a += 1; + continue; + } else if a == 2 { + println!("Break At : {}", a); + break; + } + + println!("Current Value : {}", a); + a += 1; } ``` +### Returning a Value with `break` + +```rust +let (mut x, y) = (1, 10); + +let z = loop { + x *= 2; // x = 2,4,8,16... + + if x >= y { // 16 >= 10, so return 16 + break x; + } +}; + +println!("{z}"); // 16 +``` + +### Labeling and `break` the Outer `loop` + ```rust -// Outer break let mut b1 = 1; -'outer_loop: loop { //set label outer_loop - let mut b2 = 1; +'outer: loop { // set label outer (or any other snake_case name) + let mut b2 = 1; - 'inner_loop: loop { - println!("Current Value : [{}][{}]", b1, b2); + 'inner: loop { // set label inner + println!("Current Value : [{}][{}]", b1, b2); - if b1 == 2 && b2 == 2 { - break 'outer_loop; // kill outer_loop - } else if b2 == 5 { - break; - } + if b1 == 2 && b2 == 2 { + break 'outer; // kill outer loop + } else if b2 == 5 { + break; + } - b2 += 1; - } + b2 += 1; + } - b1 += 1; + b1 += 1; } ``` - ## while +### Infinite `while` + +```rust +// ⚠️ This will run forever without terminating. Termininate manually (Ctrl+C) +while true { + println!("While forever!"); +} +``` + +### A simple `while` + ```rust let mut a = 1; while a <= 10 { - println!("Current value : {}", a); - a += 1; //no ++ or -- on Rust + println!("Current value : {}", a); + a += 1; //no ++ or -- on Rust } ``` +### With `break` and `continue` + ```rust -// Usage of break and continue let mut b = 0; while b < 5 { - if b == 0 { - println!("Skip value : {}", b); - b += 1; - continue; - } else if b == 2 { - println!("Break At : {}", b); - break; - } - - println!("Current value : {}", b); - b += 1; + if b == 0 { + println!("Skip value : {}", b); + b += 1; + continue; + } else if b == 2 { + println!("Break At : {}", b); + break; + } + + println!("Current value : {}", b); + b += 1; } + +// 💡 You can't break with a value in a while ``` +### Labeling and `break` the Outer `while` + ```rust -// Outer break let mut c1 = 1; -'outer_while: while c1 < 6 { //set label outer_while - let mut c2 = 1; +'outer: while c1 < 6 { // set label outer (or any other snake_case name) + let mut c2 = 1; - 'inner_while: while c2 < 6 { - println!("Current Value : [{}][{}]", c1, c2); - if c1 == 2 && c2 == 2 { break 'outer_while; } //kill outer_while - c2 += 1; - } + 'inner: while c2 < 6 { // set label inner + println!("Current Value : [{}][{}]", c1, c2); - c1 += 1; + if c1 == 2 && c2 == 2 { + break 'outer; // kill outer while + } + + c2 += 1; + } + + c1 += 1; } ``` - ## for +### A simple `for` + ```rust // 0 to 10 (10 exclusive); In other languages, `for(i = 0; i < 10; i++)` for i in 0..10 { - println!("Current value : {}", i); + println!("Current value : {}", i); } ``` ```rust // 1 to 10 (10 inclusive); In other languages, `for(i = 1; i <= 10; i++)` for i in 1..=10 { - println!("Current value : {}", i); + println!("Current value : {}", i); } ``` +### With `break` and `continue` + ```rust -// Usage of break and continue +// 💡 You can't break with a value in a for for b in 0..6 { - if b == 0 { - println!("Skip Value : {}", b); - continue; - } else if b == 2 { - println!("Break At : {}", b); - break; - } - - println!("Current value : {}", b); + if b == 0 { + println!("Skip Value : {}", b); + continue; + } else if b == 2 { + println!("Break At : {}", b); + break; + } + + println!("Current value : {}", b); } ``` +### Labeling and `break` the Outer `for` + ```rust -// Outer break -'outer_for: for c1 in 1..6 { //set label outer_for +'outer: for c1 in 1..6 { // set label outer (or any other snake_case name) - 'inner_for: for c2 in 1..6 { - println!("Current Value : [{}][{}]", c1, c2); - if c1 == 2 && c2 == 2 { break 'outer_for; } //kill outer_for - } + 'inner: for c2 in 1..6 { // set label inner + println!("Current Value : [{}][{}]", c1, c2); + if c1 == 2 && c2 == 2 { + break 'outer; // kill outer for + } + } } ``` +### With Arrays and Vectors + ```rust -// Working with arrays/vectors let group : [&str; 4] = ["Mark", "Larry", "Bill", "Steve"]; -for n in 0..group.len() { // group.len() = 4 -> 0..4 👎 check group.len()on each iteration - println!("Current Person : {}", group[n]); +// 👎 group.len() = 4 check on each iteration of the for loop +for n in 0..group.len() { + println!("Current Person : {}", group[n]); +} + +// 👍 group.iter() turn the array into a simple iterator +for person in group.iter() { + println!("Current Person : {person}"); +} + +// 👍 group.iter().enumerate() helps to read both the current index (starting from zero) and the value +for (index, person) in group.iter().enumerate() { + println!("Person {index} : {person}"); } +``` + +### With a Vector of Tuples + +```rust +let list = vec![(1, "Mark"), (2, "Larry"), (3, "Steve")]; -for person in group.iter() { // 👍 group.iter() turn the array into a simple iterator - println!("Current Person : {}", person); +for (index, person) in list { + println!("Person {index} : {person}"); } ``` diff --git a/content/en/docs/a2.installation.md b/content/en/docs/a2.installation.md index c89034b9..e3419029 100755 --- a/content/en/docs/a2.installation.md +++ b/content/en/docs/a2.installation.md @@ -4,13 +4,16 @@ slug: installation --- ## Rustup + There are many ways to install Rust on your system. For the moment the official way to install Rust is using [Rustup](https://rustup.rs/). -[📖](https://rust-lang.github.io/rustup/index.html) Rustup installs The Rust Programming Language from the official release channels, enabling you to easily switch between **stable, beta, and nightly** compilers and keep them updated. It makes **cross-compiling** simpler with binary builds of the standard library for common platforms. +[📖](https://rust-lang.github.io/rustup/index.html) Rustup installs The Rust Programming Language from the official release channels, enabling you to easily switch between **stable, beta, and nightly** compilers and keep them updated. It also makes cross-compiling simpler with binary builds of the standard library for common platforms. -[📖](https://rust-lang.github.io/rustup/installation/index.html) Rustup installs **`rustc`, `cargo`, `rustup`** and other standard tools to Cargo's `bin` directory. On Unix it is located at `$HOME/.cargo/bin` and on Windows at `%USERPROFILE%\.cargo\bin`. This is the same directory that `cargo install` will install Rust programs and Cargo plugins. +[📖](https://rust-lang.github.io/rustup/installation/index.html) Rustup installs `rustc`, `cargo`, `rustup` and other standard tools to **Cargo's `bin` directory**. On Unix it is located at `$HOME/.cargo/bin` and on Windows at `%USERPROFILE%\.cargo\bin`. This is the same directory that `cargo install` will install Rust programs and Cargo plugins. -> 🔎 The main tools Rustup installs to the Cargo's `bin` directory, +> [!tip] +> The main tools Rustup installs to the Cargo's `bin` directory, +> > - `rustc`: The Rust compiler. > - `cargo`: The Rust’s built-in package manager and the build system. > - `rustup`: The Rust toolchain installer. @@ -18,23 +21,28 @@ There are many ways to install Rust on your system. For the moment the official > - `cargo-fmt`: Helps to run `rustfmt` on whole Rust projects, including multi-crate workspaces. > - `cargo-clippy`: A lint tool that provides extra checks for common mistakes and stylistic choices. > - `cargo-miri`:An experimental Rust interpreter, which can be used for checking for undefined-behavior. -> - `rls`: A language server that provides support for editors and IDEs. > - `rustdoc`: A local copy of the Rust documentation. +> - `rust-analyzer`: A language server that provides support for editors and IDEs. +> - `rust-gdb` and `rust-lldb`: Rust debuggers that wrap the GNU Debugger(GDB) and Low-Level Debugger(LLDB). ## Installation ### For Mac and Linux Users + ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` ### For Windows Users + Download **`rustup-init.exe`** from [www.rustup.rs](https://rustup.rs/) and run. -> ⭐ If you are on **Microsoft Windows**, you have to install **[Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)** 2015 or higher, which requires an additional 3–4 GBs. +> [!important] +> You may need to install [Visual C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) 2019 or higher, which requires an additional 3–4 GBs. ## 👨‍🏫 Before going to the next... -- To verify the current Rust version, use the **`rustc --version`** or **`rustc -V`** command. -- Rust ships releases on **six week cycles**. When a new Rust version released, use the **`rustup update`** command to update the Rust ecosystem. -- To open the offline Rust documentation, use the **`rustup doc`** command. For more `rustup` commands, check the **`rustup --help`** command. +- To verify the current Rust version, use the **`rustc --version`** or shorter form `rustc -V` command. +- Rust follows **six week release cycles**. Use the `rustup update` command to update the Rust ecosystem. +- You can access Rust's offline documentation via the `rustup doc` command. +- For a full list of `rustup` commands, refer to the `rustup --help` command. diff --git a/content/en/docs/a3.hello-world.md b/content/en/docs/a3.hello-world.md index 57fba806..0dddcd72 100755 --- a/content/en/docs/a3.hello-world.md +++ b/content/en/docs/a3.hello-world.md @@ -4,16 +4,18 @@ slug: hello-world --- ## Hello, World! + ```rust fn main() { println!("Hello, world!"); } ``` -`fn` means function. The `main` function is the beginning of every Rust program. +`fn` means function. The `main` function is the beginning of every Rust program. `println!()` prints text to the console and its `!` indicates that it’s a [macro](https://doc.rust-lang.org/book/ch19-06-macros.html) rather than a function. -> 💡 Rust files should have `.rs` file extension and if you’re using more than one word for the file name, follow the [snake_case](https://en.wikipedia.org/wiki/Snake_case) convention. +> [!tip] +> Rust files should have `.rs` file extension and if you’re using more than one word for the file name, follow the [snake_case](https://en.wikipedia.org/wiki/Snake_case) convention. - Save the above code in `file.rs` , but it can be any name with `.rs` extension. - Compile it with `rustc file.rs` @@ -29,36 +31,79 @@ fn main() { - These are the other usages of the `println!()` macro, -```rust -fn main() { - println!("{}, {}!", "Hello", "world"); // Hello, world! - println!("{0}, {1}!", "Hello", "world"); // Hello, world! - println!("{greeting}, {name}!", greeting = "Hello", name = "world"); // Hello, world! - - let (greeting, name) = ("Hello", "world"); // 💡 Two Variable bindings declare & initialize in one line. - println!("{greeting}, {name}!"); // Hello, world! - - println!("{:?}", [1, 2, 3]); // [1, 2, 3] - println!("{:#?}", [1, 2, 3]); - /* - [ - 1, - 2, - 3 - ] - */ - - // 🔎 The format! macro is used to store the formatted string. - let x = format!("{}, {}!", "Hello", "world"); - println!("{}", x); // Hello, world! - - // 💡 Rust has a print!() macro as well - print!("Hello, world!"); // Without new line - println!(); // A new line - - print!("Hello, world!\n"); // With new line -} -``` + ```rust + println!("{}, {}!", "Hello", "world"); // Hello, world! + + println!("{0}, {1}!", "Hello", "world"); // Hello, world! + + println!("{a}, {b}!", a = "Hello", b = "world"); // Hello, world! + + let (a, b) = ("Hello", "world"); // 💡 Two Variable bindings declare & initialize in one line. + println!("{a}, {b}!"); // Hello, world! + + println!(); // A new line + ``` + + ```rust + // Debug print and pretty-print debug print format specifiers + println!("{:?}", [1, 2, 3]); // [1, 2, 3] + + println!("{:#?}", [1, 2, 3]); + /* + [ + 1, + 2, + 3 + ] + */ + + // 💡 We can use the variable directly with the format specifier as well. + let a = [1, 2, 3]; + + println!("{a:?}"); // similar to println!("{:?}", a); + println!("{a:#?}"); // similar to println!("{:#?}", a); + ``` + +- Rust has a `print!()` macro as well. + + ```rust + print!("Hello, world!\n"); // With new line + print!("Hello, world!"); // Without new line + ``` + +- The `format!()` macro is used to store the formatted string. + + ```rust + let a = format!("{}, {}!", "Hello", "world"); + println!("{a}"); // Hello, world! + ``` + +- Let's play a bit more... + + ```rust + println!("{}", "Hello, world!".to_uppercase()); // HELLO, WORLD! + println!("{}", "Hello, world!".to_lowercase()); // hello, world! + + println!("{}", "⭐️".repeat(3)); // ⭐️⭐️⭐️ + + println!("{}", "Hello, world!".chars().count()); // 13 + // 💡 For more accurate results, you should use a crate like unicode_segmentation that follows more accurate Unicode text segmentation standards. + ``` + +- Different format specifiers. + + ```rust + let a = 255; + println!("{a}"); // 255 + + println!("{a:b}"); // Binary 💡 11111111 + println!("{a:o}"); // Octal 💡 377 + println!("{a:x}"); // LowerHex 💡 ff + println!("{a:X}"); // UpperHex 💡 FF + + println!("{a:0>5}"); // Add leading zeros till character lengh 5 💡 00255 + println!("{a:0<5}"); // Add tailing zeros till character lengh 5 💡 25500 + ``` -- Check the [difference between macros and functions](https://doc.rust-lang.org/book/ch19-06-macros.html#the-difference-between-macros-and-functions). -- For more `rustc` commands, check the **`rustc --help`** command. +- Check the [difference between macros and functions](https://doc.rust-lang.org/book/ch20-05-macros.html#the-difference-between-macros-and-functions). +- For more `rustc` commands, check the `rustc --help` command. diff --git a/content/en/docs/a4.cargo-crates-and-basic-project-structure.md b/content/en/docs/a4.cargo-crates-and-basic-project-structure.md index bdb9aa0a..05b716c2 100755 --- a/content/en/docs/a4.cargo-crates-and-basic-project-structure.md +++ b/content/en/docs/a4.cargo-crates-and-basic-project-structure.md @@ -5,34 +5,62 @@ slug: cargo-crates-and-basic-project-structure ## Cargo -Cargo is Rust’s built-in package manager and the build system. It can be used to, - -- Create a new project: `cargo new` -- Create a new project in an existing directory: `cargo init` -- Build the project: `cargo build` -- Run the project: `cargo run` -- Update project dependencies: `cargo update` -- Run tests: `cargo test` -- Run benchmarks: `cargo bench` -- Generate the project documentation via [rustdoc](https://doc.rust-lang.org/stable/rustdoc/): `cargo doc` -- Analyze the project to see it has any errors, without building it: `cargo check` - -In addition, there are `cargo` commands to publish the project as a crate/ package to **Rust's official crate registry, [crates.io](https://crates.io/)**. - -> 💡 We need to get an API token from [crates.io](https://crates.io/) to publish a crate to it. The API token can be found in the [Account Settings page](https://crates.io/me), after login to that site. We will discuss more about this under [code organization with crates](/docs/d4.crates.html#c-Using-crates-io). - -- Login to [crates.io](https://crates.io/) with the API token: `cargo login` -- Make the local crate uploadable to [crates.io](https://crates.io/): `cargo package` -- Upload the crate to [crates.io](https://crates.io/): `cargo publish` -- Install a Rust binary: `cargo install` -- Uninstall a Rust binary: `cargo uninstall` +Cargo is Rust’s built-in package manager and build system. It also supports the following actions, + +| Command | Action | +| ------------- | ------------------------------------------------------- | +| `cargo new` | Create a new project | +| `cargo init` | Create a new project in an existing directory | +| `cargo check` | Verify the project compiles without errors | +| `cargo build` | Build the executable | +| `cargo run` | Build the executable and run | +| `cargo clean` | Remove the build system directories/ `target` directory | + +> [!tip] +> - The `cargo check` command verifies that the project compiles without errors, without producing an executable. +> Thus, it is often faster than `cargo build`. +> - Cargo places executables compiled with `cargo build` or `cargo run` in the `target/debug/` directory. +> But, while those built with **`cargo build --release`** for release purposes are stored in `target/release/` directory. +> Release builds use more optimizations and remove some runtime safety checks to increase performance, although this comes at the cost of longer compile time. + +| Command | Action | +| -------------- | ------------------------------------------------- | +| `cargo add` | Add a dependency crate to the project | +| `cargo remove` | Remove a dependency crate from the project | +| `cargo fetch` | Download the dependencies specified in Cargo.lock | +| `cargo update` | Update project dependencies | + +> [!tip] +> - A crate is a package that can be shared via [crates.io](https://crates.io), Rust community’s crate registry. +> `cargo add`, `cargo remove`, `cargo fetch`, and `cargo update` commands manage project dependencies through the crate hosted on crates.io. +> - The `cargo add` command includes a specified crate in the `[dependencies]` section of `Cargo.toml`, while `cargo add --dev` adds a crate to the `[dev-dependencies]` section. This indicates that the crate is only used for development purposes like testing and will not be included in the final compiled code. + +| Command | Action | +| ------------- | ------------------------------------------------------------------------------------------- | +| `cargo test` | Run tests | +| `cargo bench` | Run benchmarks | +| `cargo doc` | Generate the project documentation via [rustdoc](https://doc.rust-lang.org/stable/rustdoc/) | + +In addition, there are `cargo` commands to publish the project as a crate to [crates.io](https://crates.io/). + +| Command | Action | +| ----------------- | ------------------------------------------------------------------ | +| `cargo login` | Login to [crates.io](https://crates.io/) with the API token | +| `cargo package` | Make the local crate uploadable to [crates.io](https://crates.io/) | +| `cargo publish` | Upload the crate to [crates.io](https://crates.io/) | +| `cargo install` | Install a Rust binary | +| `cargo uninstall` | Uninstall a Rust binary | + +> [!tip] +> You need to get an API token from [crates.io](https://crates.io/) to publish a crate to it. The API token can be found in the [Account Settings page](https://crates.io/me), after login to that site. We will discuss more about this under [code organization with crates](/docs/crates#c-using-cratesio). ## Crate -A crate is a package, which can be shared via [crates.io](https://crates.io/). A crate can produce an executable or a library. In other words, it can be a **binary** crate or a **library** crate. +- A crate is a package, which can be shared via Rust community’s crate registry, [crates.io](https://crates.io/). -01. `cargo new crate_name --bin` or `cargo new crate_name`: Produces an executable -02. `cargo new crate_name --lib`: Produces a library +- A crate can produce an executable or a library. In other words, it can be a **binary** crate or a **library** crate. + 1. `cargo new crate_name --bin` or `cargo new crate_name`: Produces an executable + 2. `cargo new crate_name --lib`: Produces a library The first one generates, @@ -54,8 +82,6 @@ and the second one generates, - **src** folder is the place to store the source code. - Each crate has an implicit crate root/ entry point. **main.rs** is the crate root for a binary crate and **lib.rs** is the crate root for a library crate. -> 💡 When we build a binary crate via `cargo build` or `cargo run`, the executable file will be stored in the **target/debug/** folder. But when building it via **`cargo build --release`** for a release it will be stored in the **target/release/** folder. The release builds are applying more optimizations while compiling the code, to make the code run faster. But it takes more compile time. - ## Project Structure This is how [Cargo documentation describes](https://doc.rust-lang.org/cargo/guide/project-layout.html) about the recommended project layout, @@ -68,7 +94,10 @@ This is how [Cargo documentation describes](https://doc.rust-lang.org/cargo/guid │ ├── main.rs │ ├── lib.rs │ └── bin -│ └── another_executable.rs +│ ├── another_executable.rs +│ └── multi_file_executable +│ ├── main.rs +│ └── some_module.rs ├── tests │ └── some_integration_tests.rs ├── benches @@ -78,25 +107,64 @@ This is how [Cargo documentation describes](https://doc.rust-lang.org/cargo/guid ``` - The source code goes in the `src` directory. - - The default executable file is `src/main.rs`. - - The default library file is `src/lib.rs`. - - Other executables can be placed in `src/bin/*.rs`. + - The default executable file is `src/main.rs`. + - The default library file is `src/lib.rs`. + - Other executables can be placed in, + - `src/bin/*.rs` + - `src/bin/*/main.rs` - Integration tests go in the `tests` directory \(unit tests go in each file they're testing\). - Benchmarks go in the `benches` directory. - Examples go in the `examples` directory. ## Rust Editions -After the initial release in 2015, according to the feedback got from user communities, the Rust team was focusing to increase the **productivity** of the language and the ecosystem. After 3 years of hard work in 2018, a new Rust edition was released with new features, simplified syntax and better tooling. We call it **Rust 2018** edition. +Rust guarantees backward compatibility while introducing major updates to the language. To support this, the `edition` field was added to the `Cargo.toml` file in Rust 2018, marking the first major update to the language ecosystem three years after its initial release. Editions are opt-in, meaning existing crates will not experience these changes until they explicitly migrate to the new edition. + +The major editions of Rust are: + +- **Rust 2015**: The initial edition, introduced with Rust 1.0. It established the core language features like ownership, borrowing, and lifetimes, laying the foundation for Rust’s safety and concurrency guarantees. -To keep the promise of supporting backward compatibility, the new `edition = "2018"` configuration was added to the `Cargo.toml` file. For new projects, the `cargo new` command adds this configuration by default. So, you don't need to care. But on legacy crates, if you can not see any `edition` configuration, Cargo will consider it as a Rust 2015 edition crate. +- **Rust 2018**: The first major update, introduced the `edition` field in `Cargo.toml`, simplified the module system, stabilized `async`/`await`, improved error handling with the `?` operator, and made several syntactic changes. + +- **Rust 2021**: Focused on improving ergonomics and removing inconsistencies, such as disjoint closure capture, `IntoIterator` for arrays, and the introduction of or-patterns in macros. + +- **Rust 2024**: The latest edition, includes enhancements like refined `async` features, more `const` generics, better diagnostics, and improved Cargo features. + +For new projects created by `cargo new`, it will set `edition = "2024"` by default in the `Cargo.toml` file. For example, + +```toml +[package] +name = "hello_world" +version = "0.1.0" +edition = "2024" +``` ## 👨‍🏫 Before going to the next... -- The **`.cargo/bin` directory of your home directory** is the default location of Rust binaries. Not only the official binaries like `rustup`, `rustc`, `cargo`, `rustfmt`, `rustdoc`, `rls` and also the binaries you can install via `cargo install` command, will be stored in this directory. +- The `.cargo/bin` directory of your home directory is the default location of Rust binaries. Not only the official binaries like `rustc`, `cargo`, `rustup`, `rustfmt`, `rustdoc`, `rust-analyzer` and also the binaries you can install via `cargo install` command, will be stored in this directory. - Even though the initial convention for naming crates and file names is using the [`snake_case`](https://en.wikipedia.org/wiki/Snake_case), some crate developers are using `kebab-case` on both crates and file names. To make your code more consistent, use the initial convention `snake_case`; especially on file names. -- Create an executable crate via `cargo new` command and run it via `cargo run`. - -- Create a library crate via `cargo new` command and run `cargo test`. \ No newline at end of file +- Create, + 1. an executable crate via `cargo new` command, run it via `cargo run` and examine the files and project structure. + 2. a library crate via `cargo new` command, run `cargo test` and examine the files and project structure. + 3. a multiple executables project and try to run each executable. + + > - You can name executables in the `Cargo.toml` file. + > ```toml + > [[bin]] + > name = "app" + > path = "src/bin/app/main.rs" + > ``` + > - You can use the `--bin` flag to specify the executable, while running `cargo` commands. + >
Ex. `cargo build --bin app`, `cargo run --bin app` + > - You can set default executable in the `Cargo.toml` file. + > ```toml + > [package] + > name = "hello_world" + > version = "0.1.0" + > edition = "2024" + > default-run = "app" + > ``` + + 4. Run `cargo build --release` and check the files in the `target` folder. diff --git a/content/en/docs/a5.comments-and-documenting-the-code.md b/content/en/docs/a5.comments-and-documenting-the-code.md index 95837b92..c1d1f124 100755 --- a/content/en/docs/a5.comments-and-documenting-the-code.md +++ b/content/en/docs/a5.comments-and-documenting-the-code.md @@ -12,11 +12,12 @@ slug: comments-and-documenting-the-code Nested block comments are supported. -💡 **By convention, try to avoid using block comments. Use line comments instead.** +> [!tip] +> By convention, try to avoid using block comments. Use line comments instead. ## Doc Comments -[As we discussed](a4.cargo,crates_and_basic_project_structure.html#cargo), we can generate the project documentation via [rustdoc](https://doc.rust-lang.org/stable/rustdoc/) by running the **`cargo doc`** command. It uses the doc comments to generate the documentation. +[As we discussed](/docs/cargo-crates-and-basic-project-structure/#cargo), we can generate the project documentation via [rustdoc](https://doc.rust-lang.org/stable/rustdoc/) by running the **`cargo doc`** command. It uses the doc comments to generate the documentation. 💡 Usually we are adding doc comments on library crates. Also, we can use [Markdown notations](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet) inside the doc comments. @@ -33,7 +34,7 @@ For example, ```rust /// This module contains tests; Outer comment mod tests { - + } mod tests { @@ -42,9 +43,11 @@ mod tests { } ``` -> 💭 The `mod` keyword is used for [modules](d3.modules.html). Don't worry about this for now; it'll be discussed later. +> [!recap] +> The `mod` keyword is used for [modules](/docs/modules). Don't worry about this for now; it'll be discussed later. ## Doc Attributes + [Doc attributes](https://doc.rust-lang.org/rustdoc/the-doc-attribute.html) are alternatives for doc comments. Especially, we use these doc attributes while we need to set controls on [rustdoc](https://doc.rust-lang.org/stable/rustdoc/). Refer [the doc attributes section of rustdoc documentation](https://doc.rust-lang.org/stable/rustdoc/the-doc-attribute.html) for more details. In the following example, each comment is equivalent to relevant doc attribute. @@ -57,7 +60,9 @@ In the following example, each comment is equivalent to relevant doc attribute. #![doc = "Inner comment"] ``` -> 🔎 An [attribute](https://doc.rust-lang.org/reference/attributes.html) is a general, free-form **metadatum** that is interpreted according to the name, convention, language and compiler version. Any item declaration may have an attribute applied to it. Syntax: +> [!tip] +> An [attribute](https://doc.rust-lang.org/reference/attributes.html) is a general, free-form **metadatum** that is interpreted according to the name, convention, language and compiler version. Any item declaration may have an attribute applied to it. Syntax: +> > - Outer attribute: `#[attr]` > - Inner attribute: `#![attr]` @@ -67,21 +72,21 @@ In the following example, each comment is equivalent to relevant doc attribute. - Run `cargo new hello_lib --lib` command to create a sample crate and replace its `src/lib.rs` file with the following code. Then run `cd hello_lib && cargo doc --open` to generate the documentation and open it from your web browser. -```rust -//! A Simple Hello World Crate + ```rust + //! A Simple Hello World Crate -/// This function returns the greeting; Hello, world! -pub fn hello() -> String { - ("Hello, world!").to_string() -} + /// This function returns the greeting; Hello, world! + pub fn hello() -> String { + ("Hello, world!").to_string() + } -#[cfg(test)] -mod tests { - use super::hello; + #[cfg(test)] + mod tests { + use super::hello; - #[test] - fn test_hello() { - assert_eq!(hello(), "Hello, world!"); - } -} -``` + #[test] + fn test_hello() { + assert_eq!(hello(), "Hello, world!"); + } + } + ``` diff --git a/content/en/docs/a6.variable-bindings-constants-and-statics.md b/content/en/docs/a6.variable-bindings-constants-and-statics.md index 01787dad..3449e220 100755 --- a/content/en/docs/a6.variable-bindings-constants-and-statics.md +++ b/content/en/docs/a6.variable-bindings-constants-and-statics.md @@ -3,87 +3,146 @@ title: Variable bindings, Constants & Statics slug: variable-bindings-constants-and-statics --- -## Variable bindings, Constants & Statics +- Rust is a statically typed language; it checks data types at compile-time. But it doesn’t require you to actually type data types when declaring variable bindings. In that case, the compiler checks the usage and sets a better data type for it. +- ⭐️ For **constants and statics, we must annotate the data type**. +- Types come after a `:` (colon) sign. +- The naming convention for the variable bindings is using the [`snake_case`](https://en.wikipedia.org/wiki/Snake_case). But, for constants and statics, we should follow the [`SCREAMING_SNAKE_CASE`](https://en.wikipedia.org/wiki/Snake_case). -⭐️ In Rust, variables are **immutable by default**, so we call them **Variable bindings**. To make them mutable, the `mut` keyword is used. +> [!recap] +> In the following examples, we will use [data types](/docs/primitive-data-types) like `bool`, `i32`, `i64` and `f64`. Don't worry about them for now; they'll be discussed later. -⭐️ Rust is a **statically typed** language; it checks data types at compile-time. But it **doesn’t require you to actually type it when declaring variable bindings**. In that case, the compiler checks the usage and sets a better data type for it. But for **constants and statics, you must annotate the type**. Types come after a colon(`:`). +## Variable Bindings -> 💭 In the following examples, we will use [data types](a8.primitive_data_types.html) like `bool`, `i32`, `i64` and `f64`. Don't worry about them for now; they'll be discussed later. +- The **`let`** keyword is used in binding expressions. We can bind a name to a value or a function. Also, because the left-hand side of a let expression is a _"pattern"_, you can bind multiple names to a set of values or function values. -- **Variable bindings** +- In Rust, variables are **immutable by default**, so we call them Variable bindings. To make them mutable, the **`mut`** keyword is used. -The **`let`** keyword is used in binding expressions. We can bind a name to a value or a function. Also, because the left-hand side of a let expression is a _"pattern"_, you can bind multiple names to a set of values or function values. +### Declaration & Assignment ```rust let a; // Declaration; without data type a = 5; // Assignment -let b: i8; // Declaration; with data type +let b: i8; // Declaration; with data type b = 5; -let t = true; // Declaration + assignment; without data type -let f: bool = false; // Declaration + assignment; with data type +let c = true; // Declaration + assignment; without data type +let d: bool = false; // Declaration + assignment; with data type + +let e = 4 + 2; // e = 6 +``` + +### Mutability + +As mentioned, variable bindings are immutable by default. We need to add the `mut` keyword to make them mutable. + +```rust +let mut a = 5; // a = 5 +a = a + 5; // a = 10 +``` + +### Multiple Declarations & Assignments + +```rust +let (a, b); // Declaration +(a, b) = (1, 2); // Assignment +``` + +```rust +// Declaration + assignment +let (a, b) = (1, 2); // a = 1 and b = 2 +``` + +```rust +// Declaration + assignment + mutability +let (mut a, mut b) = (3, 4); // a = 3 and b = 4 +(a, b) = (a-b, a+b); // a = -1 and b = 7 +``` + +### Scope -let (x, y) = (1, 2); // x = 1 and y = 2 +```rust +let (a, b) = (1, 2); // a = 1 and b = 2 -let mut z = 5; -z = 6; +let c = { + let a = 4; // affects inside wrapping {} only + let b = 6; // affects inside wrapping {} only -let z = { x + y }; // z = 3 + a + b +}; // c = 10 -let z = { - let x = 1; - let y = 2; +let d = { a + b }; // d = 3 - x + y -}; // z = 3 +println!("{a} {b} {c} {d}"); // 1 2 10 3 ``` -- **Constants** +## Constants The **`const`** keyword is used to define constants and after the assignment their values are not allowed to change. They live for the entire lifetime of a program but has no fixed address in the memory. ```rust const N: i32 = 5; + +const DB_PORT: u16 = 5432; + +const SERVER_TIMEOUT: u32 = 60 * 5; ``` -- **Statics** +## Statics The **`static`** keyword is used to define a _"global variable"_ type facility. There is only one instance for each value, and it’s at a **fixed location in memory**. ```rust static N: i32 = 5; + +static DB_PORT: u16 = 5432; + +static SERVER_TIMEOUT: u32 = 60 * 5; ``` -> 💭 While you need constants, always use `const`, instead of `static`. It’s pretty rare that you actually want a memory location associated with your constant, and using a const allows for optimizations like [constant propagation](https://en.wikipedia.org/wiki/Constant_folding#Constant_propagation), not only in your crate but also in downstream crates. +> [!important] +> While you need constants, always use `const`, instead of `static`. It’s pretty rare that you actually want a memory location associated with your constant, and using a const allows for optimizations like [constant propagation](https://en.wikipedia.org/wiki/Constant_folding#Constant_propagation), not only in your crate but also in downstream crates. ## Variable Shadowing Sometimes, while dealing with data, initially we get them in one unit but need to transform them into another unit for further processing. In this situation, instead of using different variable names, Rust allows us to **redeclare the same variable with a different data type and/ or with a different mutability setting**. We call this Shadowing. +```rust +let s: &str = "hello"; // &str +let s: String = s.to_uppercase(); // String +println!("{s}"); // HELLO +``` + +```rust +let (a, b) = (1, 2); +let (a, b) = (b, a); // swap variables via shadowing +println!("{a} {b}"); // 2 1 +``` + ```rust fn main() { - let x: f64 = -20.48; // float - let x: i64 = x.floor() as i64; // int - println!("{}", x); // -21 + let a: f64 = -20.48; // float + let a: i64 = a.floor() as i64; // int + + println!("{a}"); // -21 + + { + let a = a + 26; // affects inside wrapping {} scope only + println!("{a}"); // 5 💡 -21 + 26 + } - let s: &str = "hello"; // &str - let s: String = s.to_uppercase(); // String - println!("{}", s) // HELLO + println!("{a}"); // -21 💡 outer a } ``` ## 👨‍🏫 Before going to the next... -- The naming convention for the variable bindings is using the [`snake_case`](https://en.wikipedia.org/wiki/Snake_case). But, for constants and statics, we should follow the [`SCREAMING_SNAKE_CASE`](https://en.wikipedia.org/wiki/Snake_case). +- Usually, constants and statics are placed at the top of the code file, outside the functions (after module imports/ [`use` declarations](/docs/use)). -- Usually, constants and statics are placed at the top of the code file, outside the functions (after module imports/ [`use` declarations](/docs/d6.use.html)). + ```rust + const PI: f64 = 3.14159265359; -```rust -const PI: f64 = 3.14159265359; - -fn main() { - println!("π value is {}", PI); -} -``` + fn main() { + println!("π value is {}", PI); + } + ``` diff --git a/content/en/docs/a7.functions.md b/content/en/docs/a7.functions.md index 539cf4b0..d3ee14a4 100755 --- a/content/en/docs/a7.functions.md +++ b/content/en/docs/a7.functions.md @@ -3,13 +3,13 @@ title: Functions slug: functions --- -## Named functions +## Named Functions - Named functions are declared with the keyword **`fn`** -- When using **arguments**, you **must declare the data types**. -- By default, functions **return an empty [tuple](/docs/a8.primitive_data_types.html#tuples)/ `()`**. If you want to return a value, the **return type must be specified** after **`->`** +- When using **arguments**, we **must declare the data types**. +- By default, functions **return an empty [tuple](/docs/primitive-data-types/#tuple)/ `()`**. If you want to return a value, the **return type must be specified** after **`->`** -### i. Hello world +### Hello world ```rust fn main() { @@ -17,7 +17,7 @@ fn main() { } ``` -### ii. Passing arguments +### Passing Arguments ```rust fn print_sum(a: i8, b: i8) { @@ -25,55 +25,55 @@ fn print_sum(a: i8, b: i8) { } ``` -### iii. Returning values +### Returning Values -```rust -// 01. Without the return keyword. Only the last expression returns. -fn plus_one(a: i32) -> i32 { - a + 1 - // There is no ending ; in the above line. - // It means this is an expression which equals to `return a + 1;`. -} -``` +- Without the `return` keyword. Only the last expression returns. -```rust -// 02. With the return keyword. -fn plus_two(a: i32) -> i32 { - return a + 2; - // Should use return keyword only on conditional/ early returns. - // Using return keyword in the last expression is a bad practice. -} -``` + ```rust + fn plus_one(a: i32) -> i32 { + a + 1 + // There is no ending ; in the above line. + // It means this is an expression which equals to `return a + 1;`. + } + ``` -### iv. Function pointers, Usage as a Data Type +- With the `return` keyword. + + ```rust + fn plus_two(a: i32) -> i32 { + return a + 2; + // Should use return keyword only on conditional/ early returns. + // Using return keyword in the last expression is a bad practice. + } + ``` + +### Function Pointers as a Data Type ```rust fn main() { - // 01. Without type declarations. - let p1 = plus_one; - let x = p1(5); // 6 + let p1 = plus_one; // Without type declarations + let a = p1(5); // 6 - // 02. With type declarations. - let p1: fn(i32) -> i32 = plus_one; - let x = p1(5); // 6 + let p1: fn(i32) -> i32 = plus_one; // With the type declarations + let b = p1(5); // 6 } -fn plus_one(a: i32) -> i32 { - a + 1 +fn plus_one(i: i32) -> i32 { + i + 1 } ``` - ## Closures - Also known as **anonymous functions** or **lambda functions**. -- The **data types of arguments and returns are optional [ ⃰ⁱᵛ](#iv-Without-optional-type-declarations-Creating-and-calling-together)**. +- The **data types of arguments and returns are optional [ ⃰ⁱᵛ](#iv-without-optional-type-declarations-creating-and-calling-together)**. Example with a named function, before using closures. + ```rust fn main() { - let x = 2; - println!("{}", get_square_value(x)); + let x = 2; + println!("{}", get_square_value(x)); } fn get_square_value(i: i32) -> i32 { @@ -81,7 +81,8 @@ fn get_square_value(i: i32) -> i32 { } ``` -### i. With optional type declarations of input and return types +### With Optional Type Annotations + ```rust fn main() { let x = 2; @@ -92,7 +93,8 @@ fn main() { } ``` -### ii. Without type declarations of input and return types +### Without Type Annotations + ```rust fn main() { let x = 2; @@ -101,7 +103,8 @@ fn main() { } ``` -### iii. With optional type declarations; Creating and calling together +### With Optional Type Annotations; Define and Call Together + ```rust fn main() { let x = 2; @@ -110,7 +113,8 @@ fn main() { } ``` -### iv. Without optional type declarations; Creating and calling together +### Without Type Annotations; Define and Call Together + ```rust fn main() { let x = 2; @@ -118,3 +122,90 @@ fn main() { println!("{}", x_square); } ``` + +## Test Functions + +- Start the function name with the `test_` prefix. +- Add with the `#[test]` attribute, inside a `tests` module with the `#[cfg(test)]` attribute. + +```rust +fn greet() -> String { + "Hello, world!".to_string() +} + +#[cfg(test)] +mod tests { + use super::greet; // 💡 Reimport the greet() function from the parent module. + + #[test] + fn test_greet() { // The test function of greet() + assert_eq!("Hello, world!", greet()); + } +} +``` + +## 👨‍🏫 Before going to the next... + +- 💯 Usage of `::` and `.` to call functions in different modules, + + > [!recap] + > This is a quick reference about the usage of `::` and `.` operators while calling functions. So, please don’t worry about [structs](/docs/structs), [enums](/docs/enums), [traits](/docs/impls-and-traits), or [impls](/docs/impls-and-traits) for now. We will discuss them later. + - **Functions** are standalone blocks of code, declare with the `fn` keyword. + + - **Associated functions** are functions that are associated with a particular data type such as structs, enums, or traits via an **`impl`** block. + + - **Methods** are associated functions with a receiver of `self`, `&self`, `&mut self`, `self: Box` etc. + + > [!tip] + > - To call methods: use the `.` operator from an instance. ex. `steve.intro_name()` + > - To call associated functions that are not methods: use the `::` operator from the data type. ex. `Person::new()`, `String::from()` + + ```rust + struct Person { + name: String, + company_name: String, + } + + impl Person { // 💡 impls are used to define functions in Rust structs, enums, etc. + // 💡 The constructor (new` is a conventional name, not a keyword) + fn new(name: String, company_name: String) -> Person { // an associated function and not a method + Person { name, company_name } + } + + fn intro_name(&self) -> String { // 💡 a method + format!("I'm {}", self.name) // 💡 access fields via `.` operator + } + + fn intro_company(&self) -> String { // 💡 a method + format!("I'm from {}", self.company_name) + } + } + + fn main() { + // 💡 calling associated functions with `::` operator + let steve = Person::new(String::from("Steve Jobs"), String::from("Apple")); + + // 💡 calling methods with `.` operator + println!("{}. {}.", steve.intro_name(), steve.intro_company()); // I'm Steve Jobs. I'm from Apple. + + // ⭐️ methods are also associated functions. So, we can call them with `::` operator as well but need to pass the instance as a parameter. + println!("{}. {}.", Person::intro_name(&steve), Person::intro_company(&steve)); // I'm Steve Jobs. I'm from Apple. + } + ``` + + - Other than that, `::` operator is used to call functions in different modules. + + ```rust + mod my_mod { + pub fn greet(name: &str) { + println!("Hello, {name}!") + } + } + + fn main() { + my_mod::greet("Steve Jobs"); // Hello, Steve Jobs! + } + ``` + + > [!assignment] + > Refer [path separator and member access operators](/docs/operators/#path-separator-and-member-access-operators) for more information about the usage of the `::` and `.` operators. diff --git a/content/en/docs/a8.primitive-data-types.md b/content/en/docs/a8.primitive-data-types.md index 0525815e..499745cd 100755 --- a/content/en/docs/a8.primitive-data-types.md +++ b/content/en/docs/a8.primitive-data-types.md @@ -3,30 +3,34 @@ title: Primitive Data Types slug: primitive-data-types --- -- ## bool +## bool + true or false ```rust -let x = true; -let y: bool = false; +let a = true; +let b: bool = false; // ⭐️ no TRUE, FALSE, 1, 0 ``` +bool is a single byte(8 bits) in size. + +## char -- ## char A single Unicode scalar value ```rust -let x = 'x'; -let y: char = '😎'; +let a = 'x'; +let b: char = '😎'; // ⭐️ no "x", only single quotes ``` + Because of Unicode support, char is not a single byte, but four(32 bits). +## i8, i16, i32, i64, i128 -- ## i8, i16, i32, i64, i128 8, 16, 32, 64 and 128 bit fixed sized signed(+/-) integer types | DATA TYPE | MIN | MAX | @@ -37,15 +41,16 @@ Because of Unicode support, char is not a single byte, but four(32 bits). | i64 | -9223372036854775808 | 9223372036854775807 | | i128 | -170141183460469231731687303715884105728 | 170141183460469231731687303715884105727 | -💡 The min and max values are based on the following equation; **from -(2ⁿ⁻¹) to 2ⁿ⁻¹-1**. You can use **`min_value()`** and **`max_value()`** functions to find min and max of each integer type. ex.`i8::min_value();` +> [!tip] +> The min and max values are based on the following equation; **from -(2ⁿ⁻¹) to 2ⁿ⁻¹-1**. You can use **`MIN`** and **`MAX`** constants to find min and max of each integer type. ex.`i8::MIN;` ```rust -let x = 10; // ⭐️ The default integer type in Rust is i32 -let y: i8 = -128; +let a = 10; // ⭐️ The default integer type in Rust is i32 +let b: i8 = -128; ``` +## u8, u16, u32, u64, u128 -- ## u8, u16, u32, u64, u128 8, 16, 32, 64 and 128 bit fixed sized unsigned(0/+) integer types | DATA TYPE | MIN | MAX | @@ -56,90 +61,124 @@ let y: i8 = -128; | u64 | 0 | 18446744073709551615 | | u128 | 0 | 340282366920938463463374607431768211455 | -💡 The min and max values are based on the following equation; **from 0 to 2ⁿ-1**. Same way you can use **`min_value()`** and **`max_value()`** functions to find min and max of each integer type. ex.`u8::max_value();` +> [!tip] +> The min and max values are based on the following equation; **from 0 to 2ⁿ-1**. Same way you can use **`MIN`** and **`MAX`** constants to find min and max of each integer type. ex.`u8::MAX` +## isize, usize -- ## isize, usize Pointer sized signed and unsigned integer types -The actual bit size depends on the computer architecture you are compiling your program for. By default, the sizes are equal to 32 bits on 32-bit platforms and 64 bits on 64-bit platforms. +The actual bit size depends on the computer architecture you are compiling your program for. By default, the sizes are equal to 32 bits on 32-bit platforms and 64 bits on 64-bit platforms. You can use **`MIN`** and **`MAX`** constants to find min and max of each integer type. ex.`isize::MAX`. -> 🔎 Search more about [cross-compiling](https://github.com/rust-lang/rustup.rs#cross-compilation) and [supported tiers](https://forge.rust-lang.org/release/platform-support.html) of Rust programs. +> [!search] +> Search more about [cross-compiling](https://rust-lang.github.io/rustup/cross-compilation.html) and [supported tiers](https://doc.rust-lang.org/nightly/rustc/platform-support.html) of Rust programs. +## f32, f64 -- ## f32, f64 32 and 64 bit sized floating point numbers(numbers with decimal points) Rust follows IEEE Standard for Binary Floating-Point Arithmetic. The `f32` type is similar to float(**Single precision**) in other languages, while `f64` is similar to double(**Double precision**) in other languages. ```rust -let x = 1.5; // ⭐️ The default float type in Rust is f64 -let y: f64 = 2.0; +let a = 1.5; // ⭐️ The default float type in Rust is f64 +let b: f64 = 2.0; ``` -💡 Should avoid using `f32`, unless you need to reduce memory consumption badly or if you are doing low-level optimization, when targeted hardware does not support for double-precision or when single-precision is faster than double-precision on it. +> [!important] +> We should avoid using `f32`, unless you need to reduce memory consumption badly or if you are doing low-level optimization, when targeted hardware does not support for double-precision or when single-precision is faster than double-precision on it. +## Array -- ## Array Fixed size list of elements of same data type ```rust let a = [1, 2, 3]; -let a: [i32; 3] = [1, 2, 3]; // [Type; NO of elements] -let b: [i32; 0] = []; // An empty array +let b: [i32; 3] = [1, 2, 3]; // with the data type 💡 [Type; NO of elements] +// 💡let b: [i32; 3] = [1, 2]; // Compiling error : mismatched types : expected an array with a size of 3, found one with a size of 2 -let mut c: [i32; 3] = [1, 2, 3]; -c[0] = 2; -c[1] = 4; -c[2] = 6; +let c: [i32; 0] = []; // An empty array +``` -println!("{:?}", c); // [2, 4, 6] -println!("{:#?}", c); +```rust +// Accessing and changing elements +let mut a: [i32; 3] = [1, 2, 3]; +a[0] = 2; +a[1] = 4; +a[2] = 6; + +// Printing with debug and pretty-print debug specifiers +println!("{a:?}"); // [2, 4, 6] +println!("{a:#?}"); // [ // 2, // 4, // 6, // ] +``` -let d = [0; 5]; // [0, 0, 0, 0, 0] -let e = ["x"; 5]; // ["x", "x", "x", "x", "x"] +```rust +let a = [0; 5]; // [0, 0, 0, 0, 0] +let b = ["x"; 5]; // ["x", "x", "x", "x", "x"] ``` ⭐️ Arrays are **immutable** by default and **even with `mut`, its element count cannot be changed**. -> 🔎 If you are looking for a dynamic/ growable array, you can use [vectors](b1.vectors.html). Vectors can contain any type of elements but all elements must be in the same data type. +> [!search] +> If you are looking for a dynamic/ growable array, you can use [vectors](/docs/vectors). Vectors can contain any type of elements but all elements must be in the same data type. +## Tuple -- ## Tuple Fixed size ordered list of elements of different(or same) data types ```rust let a = (1, 1.5, true, 'a'); -let a: (i32, f64, bool, char) = (1, 1.5, true, 'a'); -let mut b = (1, 1.5); -b.0 = 2; -b.1 = 3.0; +let b: (i32, f64, bool, char) = (1, 1.5, true, 'a'); // With the data types -println!("{:?}", b); // (2, 3.0) -println!("{:#?}", b); +let c = (0,); // single-element tuple +``` + +```rust +// Accessing and changing elements +let mut a = (1, 1.5); +a.0 = 2; +a.1 = 3.0; + +// Printing with debug and pretty-print debug specifiers +println!("{a:?}"); // (2, 3.0) +println!("{a:#?}"); // ( // 2, // 3.0, // ) +``` + +```rust +// Destructuring +let a = (1, 1.5); +let (b, c) = a; // b = 1, c = 1.5 + +let a = (1, 1.5, true, 'a'); +let (b, _, _, c) = a; // b = 1, c = 'a' +let (b, .., c) = a; // b = 1, c = 'a' -let (c, d) = b; // c = 2, d = 3.0 -let (e, _, _, f) = a; // e = 1, f = 'a' +let a = (1, 2, 3, true, false, 'a', 'b', 'c'); +let (b, ..) = a; // b = 1 +let (.., c) = a; // c = 'c' +let (e, f, g, .., h, i, j) = a; // 1 2 3 'a' 'b' 'c' +``` -let g = (0,); // single-element tuple -let h = (b, (2, 4), 5); // ((2, 3.0), (2, 4), 5) +```rust +// Nesting +let a = (1, 1.5); +let b = (a, (2, 4), 6); // ((1, 1.5), (2, 4), 6) ``` ⭐️ Tuples are also **immutable** by default and **even with `mut`, its element count cannot be changed. Also, if you want to change an element’s value, the new value should have the same data type of previous value**. +## Slice -- ## Slice Dynamically-sized reference to another data structure Imagine you want to get/ pass a part of an array or any other data structure. Instead of copying it to another array (or same data structure), Rust allows for creating a view/ reference to access only that part of the data. This view/ reference can be mutable or immutable. @@ -147,17 +186,25 @@ Imagine you want to get/ pass a part of an array or any other data structure. In ```rust let a: [i32; 4] = [1, 2, 3, 4]; // Parent Array -let b: &[i32] = &a; // Slicing whole array +// Slice whole array +let b: &[i32] = &a; // data type is optional let c = &a[0..4]; // From 0th position to 4th(excluding) -let d = &a[..]; // Slicing whole array +let d = &a[..]; // high or low bounds are optional +// Slicing part of the array let e = &a[1..3]; // [2, 3] let f = &a[1..]; // [2, 3, 4] let g = &a[..3]; // [1, 2, 3] ``` +```rust +// directly creating a slice +let h = &[1, 2]; +let h: &[i32] = &[1, 2]; // with the datatype +``` + +## str -- ## str Unsized UTF-8 sequence of Unicode string slices ```rust @@ -165,54 +212,99 @@ let a = "Hello, world."; // a: &'static str let b: &str = "こんにちは, 世界!"; ``` -⭐️ It's an **immutable/ statically allocated slice** holding an **unknown sized sequence of UTF-8** code points stored in somewhere in memory. **`&str`** is used to borrow and assign the whole array to the given variable binding. +`str` is a UTF-8 sequence of Unicode string slice; It's unsized. You can’t create a variable of type `str` directly because Rust needs to know sizes at compile time. So, it must always be used behind a pointer — like `&str`, `Box`, or `Rc`. +`&str` is a fat pointer (a pointer to str and a length). So, it's a sized reference that carries both a pointer and a length. -- ## Function -[As we discussed in the functions section as well](a7.functions.html#iv-Function-pointers-Usage-as-a-Data-Type), `p1` is a function pointer to `plus_one()` in the following code. +`&'static str` is an `&str` that is statically allocated directly to the read-only data segment in the program binary. The `'static` lifetime means it lives for the entire program duration. + +> [!tip] String +> +> - String: Capital "S" as [it's a struct](https://doc.rust-lang.org/std/string/struct.String.html#representation). +> - [`String`](https://doc.rust-lang.org/std/string/struct.String.html) type is a growable, heap-allocated, UTF-8 encoded string. It is a sized type. +> - String is the most common string type. In general, you should use `String` when you need [`ownership`](/docs/ownership), and `&str` when you just need to borrow a string. +> - Can be generated from a `&str` type, via [`to_string()`](https://doc.rust-lang.org/std/string/trait.ToString.html), [`to_owned()`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html) or [`String::from()`](https://doc.rust-lang.org/std/string/struct.String.html#method.from) methods. With [`as_str()`](https://doc.rust-lang.org/std/string/struct.String.html#method.as_str) method, a `String` type can be converted to a `&str` type. +> +> ```rust +> let s: &str = "Hello"; // &str +> +> let a = s.to_string(); // String +> let b = s.to_owned(); // String +> let c = String::from(s); // String +> ``` +> +> ```rust +> let a = String::from("Hello"); // String +> let b = a.as_str(); // &str +> ``` + +## Function + +`p1` is a function pointer to `plus_one()` in the following code. ```rust fn main() { - let p1: fn(i32) -> i32 = plus_one; - let x = p1(5); // 6 + let p1 = plus_one; // Without type declarations + let a = p1(5); // 6 + + let p1: fn(i32) -> i32 = plus_one; // With the type declarations + let b = p1(5); // 6 } -fn plus_one(a: i32) -> i32 { - a + 1 +fn plus_one(i: i32) -> i32 { + i + 1 } ``` +--- + +## Type Inference -## 👨‍🏫 Before going to the next... -- In Rust, the default integer type is `i32` and the default float type is `f64`. +The Rust compiler can infer the data type when we don't explicitly specify it. For example, in Rust, the default integer type is `i32` and the default float type is `f64`. However, the Rust compiler checks the usage and determines the best data type based on the context. ```rust -let i = 10; // Equals to `let i: i32 = 10;` -let f = 3.14; // Equals to `let f: f64 = 3.14;` +let a = 10; // type inference: i32 +let b = 3.14; // type inference: f64 +let c = true; // type inference: bool ``` -- Other than adding the type annotations to the variables, **for numeric types**, we can append the data type directly to the value as the suffix. Also, to improve the readability of long numbers, we can use `_` as a divider. - ```rust -let a = 5i8; // Equals to `let a: i8 = 5;` +fn main() { + let a = 8; // type inference: i8 + println!("{}", plus_one(a)); +} -let b = 100_000_000; // Equals to `let b = 100000000;` -// 💡 The placements of _s are not strict. ex. 10000_0000 is also valid. +fn plus_one(i: i8) -> i8 { + i + 1 +} +``` -let pi = 3.141_592_653_59f64; // Equals to `let pi: f64 = 3.14159265359` +## Default Byte Sizes of Data Types -const PI: f64 = 3.141_592_653_59; // In the constants and statics, the data type must be annotated in the beginning. -``` +We can check the default byte sizes of data types via `std::mem::size_of`. For example: `size_of::()`, `size_of::()`, `size_of::<&str>()`, etc. + +| Data type | bytes | bits | +| ------------------- | -------------- | ------------------ | +| bool | 1 | 8 | +| char | 4 | 32 | +| i8 i16 i32 i64 i128 | 1, 2, 4, 8, 16 | 8, 16, 32, 64, 128 | +| u8 u16 u32 u64 u128 | 1, 2, 4, 8, 16 | 8, 16, 32, 64, 128 | +| isize usize | 8, 8 | 64, 64 | +| f32 f64 | 4, 8 | 32, 64 | +| &str | 16 | 128 | -- There are several string types in Rust. The [**`String`**](https://doc.rust-lang.org/std/string/struct.String.html) type is a **heap-allocated string**. This string is growable and is also guaranteed to be UTF-8. In general, you should use `String` when you need [**`ownership`**](/docs/c1.ownership.html), and `&str` when you just need to borrow a string. +## 👨‍🏫 Before going to the next... -- A [`String`](https://doc.rust-lang.org/std/string/struct.String.html) type can be generated from a `&str` type, via the **[`to_string()`](https://doc.rust-lang.org/std/string/trait.ToString.html)** or **[`String::from()`](https://doc.rust-lang.org/std/string/struct.String.html#method.from)** methods. With [**`as_str()`**]() method, a `String` type can be converted to a `&str` type. +- Other than adding the type annotations to the variables, **for numeric types**, we can append the data type directly to the value as the suffix. Also, to improve the readability of long numbers, we can use `_` as a divider. -```rust -let s: &str = "Hello"; // &str + ```rust + let a = 5i8; // Equals to `let a: i8 = 5;` -let s = s.to_string(); // String -let s = String::from(s); // String + let b = 100_000_000; // Equals to `let b = 100000000;` + // 💡 The placements of _s are not strict. ex. 10000_0000 is also valid. -let s = s.as_str(); // &str -``` + let pi = 3.141_592_653_59_f64; // Equals to `let pi: f64 = 3.14159265359` + // 💡 make sure group digits consistently by underscores avoid warnings + + const PI: f64 = 3.141_592_653_59; // In the constants and statics, the data type must be annotated in the beginning. + ``` diff --git a/content/en/docs/a9.operators.md b/content/en/docs/a9.operators.md index d7f12de0..8f4a6f8c 100755 --- a/content/en/docs/a9.operators.md +++ b/content/en/docs/a9.operators.md @@ -3,128 +3,188 @@ title: Operators slug: operators --- -- ## Arithmetic Operators +## Arithmetic + `+ - * / %` ```rust let a = 5; -let b = a + 1; //6 -let c = a - 1; //4 -let d = a * 2; //10 +let b = a + 1; // 6 +let c = a - 1; // 4 +let d = a * 2; // 10 let e = a / 2; // ⭐️ 2 not 2.5 -let f = a % 2; //1 +let f = a % 2; // 1 -let g = 5.0 / 2.0; //2.5 +let g = 5.0 / 2.0; // 2.5 ``` +## Comparison -- ## Comparison Operators `== != < > <= >=` ```rust let a = 1; let b = 2; -let c = a == b; //false -let d = a != b; //true -let e = a < b; //true -let f = a > b; //false -let g = a <= a; //true -let h = a >= a; //true +let c = a == b; // false +let d = a != b; // true +let e = a < b; // true +let f = a > b; // false +let g = a <= a; // true +let h = a >= a; // true // 🔎 -let i = true > false; //true -let j = 'a' > 'A'; //true +let i = true > false; // true +let j = 'a' > 'A'; // true ``` +## Logical -- ## Logical Operators `! && ||` ```rust let a = true; let b = false; -let c = !a; //false -let d = a && b; //false -let e = a || b; //true +let c = !a; // false +let d = a && b; // false +let e = a || b; // true ``` -> 🔎 On integer types,`!` inverts the individual bits in the two’s complement representation of the value. +> [!assignment] +> On integer types,`!` inverts the individual bits in the two’s complement representation of the value. > > ```rust -> let a = !-2; //1 -> let b = !-1; //0 -> let c = !0; //-1 -> let d = !1; //-2 +> let a = !-2; // 1 +> let b = !-1; // 0 +> let c = !0; // -1 +> let d = !1; // -2 > ``` +## Bitwise -- ## Bitwise Operators `& | ^ << >>` ```rust let a = 1; let b = 2; -let c = a & b; //0 (01 && 10 -> 00) -let d = a | b; //3 (01 || 10 -> 11) -let e = a ^ b; //3 (01 != 10 -> 11) -let f = a << b; //4 (Add b number of 0s to the end of a -> '01'+'00' -> 100) -let g = a >> b; //0 (Remove b number of bits from the end of a -> o̶1̶ -> 0) +let c = a & b; // 0 (01 && 10 -> 00) +let d = a | b; // 3 (01 || 10 -> 11) +let e = a ^ b; // 3 (01 != 10 -> 11) +let f = a << b; // 4 (Add b number of 0s to the end of a -> '01'+'00' -> 100) +let g = a >> b; // 0 (Remove b number of bits from the end of a -> o̶1̶ -> 0) ``` - -- ## Assignment and Compound Assignment Operators +## Assignment & Compound Assignment The `=` operator is used to assign a name to a value or a function. Compound Assignment Operators are created by composing one of `+ - * / % & | ^ << >>` operators with `=` operator. ```rust let mut a = 2; -a += 5; //2 + 5 = 7 -a -= 2; //7 - 2 = 5 -a *= 5; //5 * 5 = 25 -a /= 2; //25 / 2 = 12 not 12.5 -a %= 5; //12 % 5 = 2 - -a &= 2; //10 && 10 -> 10 -> 2 -a |= 5; //010 || 101 -> 111 -> 7 -a ^= 2; //111 != 010 -> 101 -> 5 -a <<= 1; //'101'+'0' -> 1010 -> 10 -a >>= 2; //101̶0̶ -> 10 -> 2 +a += 5; // 2 + 5 = 7 +a -= 2; // 7 - 2 = 5 +a *= 5; // 5 * 5 = 25 +a /= 2; // 25 / 2 = 12 not 12.5 +a %= 5; // 12 % 5 = 2 + +a &= 2; // 10 && 10 -> 10 -> 2 +a |= 5; // 010 || 101 -> 111 -> 7 +a ^= 2; // 111 != 010 -> 101 -> 5 +a <<= 1; // '101'+'0' -> 1010 -> 10 +a >>= 2; // 101̶0̶ -> 10 -> 2 ``` +## Type Casting -- ## Type Casting Operator `as` ```rust let a = 15; -let b = (a as f64) / 2.0; //7.5 +let b = (a as f64) / 2.0; // 7.5 ``` +## Borrowing & Dereference -- ## Borrowing and Dereference Operators `& &mut *` -🔎 The `&` or `&mut` operators are used for **borrowing** and `*` operator for **dereferencing**. For more information, refer [Ownership](c1.ownership.html), [Borrowing](c2.borrowing.html) & [Lifetimes](c3.lifetimes.html) sections. +```rust +fn main() { + let a = 3; + let mut b = 4; + + let c = &a; // 💡 A new shared reference + { + let d = &mut b; // 💡 A new mutable reference + *d += 1; // 💡 Dereference to modify the value at this memory address + } + + println!("{c} {b}"); // 3 5 +} +``` + +> [!recap] +> The `&` or `&mut` operators are used for borrowing and `*` operator for dereferencing. We will discuss them with +> [Ownership](/docs/ownership), [Borrowing](/docs/borrowing) & [Lifetimes](/docs/lifetimes). Please focus on operators first. + +## Path Separator & Member Access + +`:: .` + +- The `::` operator is used to access modules, constants, structs, enums, functions (excluding methods) in std modules, crates or nested/ parent modules and to access variants of enums. +- The `.` operator is used to access fields of tuples and structs and to call methods in datatypes. + +> [!recap] +> - We discussed the usage of the `::` and `.` operators when calling [associated functions and methods](/docs/functions/#-before-going-to-the-next) under [Functions](/docs/functions). +> - Also, we will discuss with more examples under [`impl`](/docs/impls) and [`trait`](/docs/traits). + +```rust +use std::collections::HashMap; // 💡load HashMap struct from the std::collections module + +fn main() { + // 💡 using `HashMap::from()` associated function to create a HashMap + let best_movies = HashMap::from([ + ("01", "The Shawshank Redemption"), + ("02", "The Godfather"), + ("03", "The Dark Knight"), + ]); + + println!("{:?}", best_movies); +} +``` + +```rust +println!("{}", std::primitive::u8::MAX); // 💡 access MAX constant from the std::primitive::u8 +``` +```rust +let mut a = (1, "Steve"); // 💡 a is a tuple + +// 💡 access fields via dot operator +a.0 = 2; +a.1 = "Tim"; +println!("{} {}", a.0, a.1); // 2 Tim +``` ## 👨‍🏫 Before going to the next... - About **string concatenation**, -```rust -let (s1, s2) = ("some", "thing"); // both &str -// All bellow codes return `String`; something + ```rust + let (s1, s2) = ("abc", "123"); // both &str + // All bellow codes return abc123 (in String data type) -let s = String::from(s1) + s2; // String + &str + // 1. via + operator + let s = String::from(s1) + s2; // String + &str -let mut s = String::from(s1); // String -s.push_str(s2); // + &str + // 2. via push_str method + let mut s = String::from(s1); + s.push_str(s2); // String + &str -let s = format!("{}{}", s1, s2); // &str/String + &str/String + // 3. via format! macro + let s = format!("{s1}{s2}"); // &str/String + &str/String -let s = [s1, s2].concat(); // &str or String array -``` + // 4. via concat method + let s = [s1, s2].concat(); // &str or String array + ``` diff --git a/content/en/docs/b1.vectors.md b/content/en/docs/b1.vectors.md index 2f17be51..5c776129 100755 --- a/content/en/docs/b1.vectors.md +++ b/content/en/docs/b1.vectors.md @@ -3,83 +3,175 @@ title: Vectors slug: vectors --- -If you remember, the array is a fixed-size list of elements, of the same data type. Even with mut, its element count cannot be changed. A vector is kind of **a re-sizable array** but **all elements must be in the same type**. +If you remember, the array is a fixed-size list of elements, of the same data type. Even with mut, its element count cannot be changed. A vector is **kind of a re-sizable array** but **all elements must be in the same type**. -⭐️ It’s a generic type, written as **`Vec`** . T can have any type, ex. The type of a Vec of i32s is `Vec`. Also, Vectors always allocate their data in a dynamically allocated heap. +> [!tip] +> - `Vec`: capital “V” as [it’s a struct](https://doc.rust-lang.org/std/vec/struct.Vec.html). +> - It’s a generic type, written as **`Vec`**. T can have any type, ex. A vector of i32s is `Vec`. Also, Vectors always allocate their data in a dynamically allocated heap. -## Create empty vector +## Creation + +### Empty Vector ```rust -let mut a = Vec::new(); //1.With new() keyword -let mut b = vec![]; //2.Using the vec! macro +let mut a = Vec::new(); // 1. With new() keyword +let mut b = vec![]; // 2. Using the vec! macro (💡 usually create with values same time) + +// ⭐️ If you need an immutable empty vector, you must have to specify the data type. +let a: Vec = Vec::new(); +let b: Vec = vec![]; ``` -## Create with data types +### With Type Annotations ```rust -let mut a2: Vec = Vec::new(); -let mut b2: Vec = vec![]; -let mut b3 = vec![1i32, 2, 3];//Suffixing 1st value with data type - -let mut b4 = vec![1, 2, 3]; -let mut b5: Vec = vec![1, 2, 3]; -let mut b6 = vec![1i32, 2, 3]; -let mut b7 = vec![0; 10]; //Ten zeroes +let a: Vec = Vec::new(); +let b: Vec = vec![]; + +let c = vec![1i32, 2, 3]; // Suffixing 1st value with data type ``` -## Access and change data +### With Values ```rust -//Accessing and changing existing data -let mut c = vec![5, 4, 3, 2, 1]; -c[0] = 1; -c[1] = 2; -//c[6] = 2; Cannot assign values this way, index out of bounds -println!("{:?}", c); //[1, 2, 3, 2, 1] +let a = vec![1, 2, 3]; +let b: Vec = vec![1, 2, 3]; +let c = vec![1i32, 2, 3]; +``` + +```rust +let a = vec![0; 10]; // Ten zeroes +let b = vec![""; 10]; // Ten "" str +``` + +### With a Capacity + +```rust +let mut a: Vec = Vec::with_capacity(10); +println!("Length: {}, Capacity : {}", a.len(), a.capacity()); // Length: 0, Capacity : 10 +``` + +> [!recap] +> We'll discuss this in the [Length and Capacity](/docs/vectors/#length-and-capacity). + +## Accessing and Changing Elements -//push and pop -let mut d: Vec = Vec::new(); -d.push(1); //[1] : Add an element to the end -d.push(2); //[1, 2] -d.pop(); //[1] : : Remove an element from the end +### By Index +We can access and change elements of a vector, via the index (like we access/ change elements of an array). -// 🔎 Capacity and reallocation -let mut e: Vec = Vec::with_capacity(10); -println!("Length: {}, Capacity : {}", e.len(), e.capacity()); //Length: 0, Capacity : 10 +```rust +let mut a = vec![1, 2, 3]; + +println!("{} {} {}", a[0], a[1], a[2]); // 1 2 3 + +a[0] = 4; // [4, 2, 3] +(a[1], a[2]) = (a[2], a[1]); // Shuffle + +println!("{:?}", a); // [4, 3, 2] + +a[5] = 2; // 💥 panics at runtime; index out of bounds: the len is 3 but the index is 5 +``` + +### By the `get` Method + +Similar to accessing elements via the index, but safer, as it always returns an `Option`/ optional value. + +```rust +let mut a = vec![1, 2, 3]; + +let x = a.get(0); // Some(1) +let y = a.get(5); // None +``` + +> [!recap] +> `Option` can be either `Some` value or `None` (no value). It is also a generic type, like `Vec`. We’ll discuss more details in the [Generics: Option](/docs/generics/#usages-of-option). For the moment, focus on vectors. + +### By the `push` and `pop` Methods + +```rust +let mut a: Vec = Vec::new(); + +a.push(1); // Add 1 to the end; a = [1] +a.push(2); // Add 2 to the end; a = [1, 2] + +a.pop(); // Remove 2 from the end; a = [1] + +let x = a.pop(); // Remove 1 from the end and assign it to x as Option; a = [] +// x = Some(1) + +let y = a.pop(); // Remove nothing as a is empty; a = [] ⭐️ No panics +// y = None +``` + +## Length and Capacity + +In Rust, most types have a fixed size known at compile time and implement the trait [Sized](https://doc.rust-lang.org/core/marker/trait.Sized.html). `Vec` is also a sized type; A struct that internally stores, + +1. A pointer: points to the heap-allocated memory storing the elements contiguously, like a slice `[T]` +2. Length: NO of elements currently have +3. Capacity: Amount of space allocated for any future elements + +> [!tip] +> If the length of a vector exceeds its capacity, its capacity will be increased automatically. But its elements will be reallocated(which can be slow). So, always use `Vec::with_capacity` whenever it’s possible. + +```rust +let mut e: Vec = Vec::with_capacity(10); // Length: 0, Capacity : 10 // These are all done without reallocating... for i in 0..10 { e.push(i); } -// ...but this may make the vector reallocate + +// ...but this may make the vector reallocate as exceeded current capacity e.push(11); ``` -⭐️ Mainly a vector represent 3 things, -- A **pointer** to the data -- **No of elements** currently have(**length**) -- **Capacity** (Amount of space allocated for any future elements). +> [!search] +> Dynamically sized types (DSTs)/ unsized types don’t have a fixed size at compile time, and the size is known only at run-time. Slices and trait objects are two examples of DSTs. -If the length of a vector exceeds its capacity, its capacity will be increased automatically. But its elements will be reallocated(which can be slow). So always use Vec::**with_capacity** whenever it’s possible. +## 👨‍🏫 Before going to the next... -> 💡 The **String** data type is a UTF-8 encoded vector. But you can not index into a String because of encoding. +- Vectors can be used with iterators in three ways, + ```rust + let mut a = vec![1, 2, 3, 4, 5]; -💯 Vectors can be used with iterators in three ways, + for i in a { + println!("Take ownership of the vector and its element {}", i); + } -```rust -let mut v = vec![1, 2, 3, 4, 5]; + for i in &a { + println!("A reference to {}", i); + } -for i in &v { - println!("A reference to {}", i); -} + for i in &mut a { + println!("A mutable reference to {}", i); + } + ``` -for i in &mut v { - println!("A mutable reference to {}", i); -} +- The `String`/ `&str` data types are UTF-8 encoded vectors. But you can not index into a String because of encoding. -for i in v { - println!("Take ownership of the vector and its element {}", i); -} -``` + ```rust + let a = String::from("Hello!"); + print!("{}", a.chars().count()); // 6 + // 💡 H e l l o ! + ``` + + ```rust + let a = "a̐éö̲\r\n"; + for (i, v) in a.chars().enumerate() { + println!("{i}: {v}"); + } + + // 0: a + // 1: ̐ + // 2: é + // 3: ö + // 4: ̲ + // 5: + // 6: + ``` + + > [!important] + > We can iterate over the characters of a string via the `chars()` method. But for more accurate results, you should use a crate like [`unicode_segmentation`](https://crates.io/crates/unicode-segmentation) that follows more accurate Unicode text segmentation standards. diff --git a/content/en/docs/b2.structs.md b/content/en/docs/b2.structs.md index 26800c46..1e44f4a1 100755 --- a/content/en/docs/b2.structs.md +++ b/content/en/docs/b2.structs.md @@ -3,109 +3,247 @@ title: Structs slug: structs --- -⭐️ Structs are used to **encapsulate related properties** into one unified data type. +- Used to **encapsulate related properties into one unified data type**. +- By convention, the name should follow [`PascalCase`](https://en.wikipedia.org/wiki/Camel_case). +- 3 variants, + - C-like structs: One or more `,` separated `name: value pairs` enclosed in `{}` -💡 By convention, the name of the struct should follow [PascalCase](https://en.wikipedia.org/wiki/Camel_case). + ```rust + struct Color { + red: u8, + green: u8, + blue: u8, + } + ``` -There are 3 variants of structs, -1. **C-like structs** - * One or more comma-separated name:value pairs - * Brace-enclosed list - * Similar to classes \(without its methods\) in OOP languages - * Because fields have names, we can access them through dot notation + - Tuple structs: One or more `,` separated `values` enclosed in `()` -2. **Tuple structs** - * One or more comma-separated values - * A parenthesized list like tuples - * Looks like a named tuples + ```rust + struct Color(u8, u8, u8); + ``` -3. **Unit structs** - * A struct with no members at all - * It defines a new type but it resembles an empty tuple, \(\) - * Rarely in use, useful with generics + - Unit structs: A struct with no fields/ members -⭐️ When regarding OOP in Rust, attributes and methods are placed separately on **structs** and **traits**. Structs contain only attributes, traits contain only methods. They are getting connected via **impls**. + ```rust + struct Black; + ``` ->💡More complex examples can be found on [impls & traits](b5.impls_and_traits.html), [lifetimes](c3.lifetimes.html) and [modules](d3.modules.html) sections. +> [!recap] +> - In Rust, data (attributes) and behavior (associated functions and methods) are placed separately. Structs and Enums are used to group related data, and impls and traits are used to add associated and shared behavior to that data. +> - In Rust, the term "instantiation" is used to describe the act of creating a concrete instance of a type (struct or enum). +> - In Rust, the term "field" is used to describe a named component in a C-like struct & struct-like enum variant, and the term "element" is used to describe an unnamed component in a tuple struct & tuple-like enum variant. The term "member" is used to describe both. +> - More complex examples can be found on [Impls and Traits](/docs/impls-and-traits), [Lifetimes](/docs/lifetimes) and [Modules](/docs/modules) sections. -## C-like structs +## C-like Structs + +- Similar to classes \(without its methods\) in OOP languages. +- Can access fields using the `.`/ dot notation and the field name. + +### Definition + +```rust +struct Color { + red: u8, + green: u8, + blue: u8, +} +``` + +### Instantiation & Accessing Fields ```rust -// Struct Declaration struct Color { red: u8, green: u8, - blue: u8 + blue: u8, } fn main() { - // Creating an instance - let black = Color {red: 0, green: 0, blue: 0}; + // 1. Instantiation + let white = Color { + red: 255, + green: 255, + blue: 255, + }; + + // 2. Instantiation without redundant field names, when using the same variable names + let (red, green, blue) = (0, 0, 0); + let black = Color { red, green, blue }; + + // 3. Instantiation + copy fields' values from another instance + let red = Color { red: 255, .. black }; // 💡 Copy green and blue from black + let green = Color { green: 255, .. black }; // 💡 Copy red and blue from black + let mut blue = Color { .. black }; // 💡 Copy all fields' values from black + blue.blue = 255; + + println!("RGB({}, {}, {})", white.red, white.green, white.blue); // RGB(255, 255, 255) + println!("RGB({}, {}, {})", black.red, black.green, black.blue); // RGB(0, 0, 0) + + println!("RGB({}, {}, {})", red.red, red.green, red.blue); // RGB(255, 0, 0) + println!("RGB({}, {}, {})", green.red, green.green, green.blue); // RGB(0, 255, 0) + println!("RGB({}, {}, {})", blue.red, blue.green, blue.blue); // RGB(0, 0, 255) +} +``` - // Accessing its fields using dot notation - println!("Black = rgb({}, {}, {})", black.red, black.green, black.blue); //Black = rgb(0, 0, 0) +```rust +// 4. Instantiation with default values - // Structs are immutable by default, use `mut` to make it mutable but doesn't support field level mutability - let mut link_color = Color {red: 0,green: 0,blue: 255}; - link_color.blue = 238; - println!("Link Color = rgb({}, {}, {})", link_color.red, link_color.green, link_color.blue); //Link Color = rgb(0, 0, 238) +#[derive(Default)] +struct Person { + name: String, + age: f32, +} - // Copy elements from another instance - let blue = Color {blue: 255, .. link_color}; - println!("Blue = rgb({}, {}, {})", blue.red, blue.green, blue.blue); //Blue = rgb(0, 0, 255) +fn main() { + let a = Person::default(); // Instantiation with default values - // Destructure the instance using a `let` binding, this will not destruct blue instance - let Color {red: r, green: g, blue: b} = blue; - println!("Blue = rgb({}, {}, {})", r, g, b); //Blue = rgb(0, 0, 255) + assert_eq!(a.name, ""); // String default value "" + assert_eq!(a.age, 0.0); // f32 default value 0.0 +} +``` + +> [!tip] +> In Rust, the `#[derive()]` attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The [`std::default::Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait allows us to create a new instance of a type with the `Type::default()` method. - // Creating an instance via functions & accessing its fields - let midnightblue = get_midnightblue_color(); - println!("Midnight Blue = rgb({}, {}, {})", midnightblue.red, midnightblue.green, midnightblue.blue); //Midnight Blue = rgb(25, 25, 112) +💯 5. We can also use a constructor function inside an `impl` block to initialize a struct. - // Destructure the instance using a `let` binding - let Color {red: r, green: g, blue: b} = get_midnightblue_color(); - println!("Midnight Blue = rgb({}, {}, {})", r, g, b); //Midnight Blue = rgb(25, 25, 112) +### Destructuring + +```rust +struct Person { + name: String, + company_name: String, } -fn get_midnightblue_color() -> Color { - Color {red: 25, green: 25, blue: 112} +fn get_steve() -> Person { + Person { + name: "Steve Jobs".to_string(), + company_name: "Apple".to_string(), + } } + +fn main() { + let steve = Person { + name: "Steve Jobs".to_string(), + company_name: "Apple".to_string(), + }; + + let Person {name: a, company_name: b} = steve; // 1. Destructuring fields' values to a and b + println!("{a} {b}"); // Steve Jobs Apple + + let Person {company_name: c, .. } = get_steve(); // 2. Destructuring only selected fields' values; directly from the function call + println!("{c}"); // Apple +} + +// 💯 let Person {name: ref a, company_name: ref b} = steve; // add ref keyword, to pass a field's value as a reference +``` + +## Tuple Structs + +- Looks like a named tuples. +- Can access fields using the `.`/ dot notation and the index number of the field, like on tuples. +- ⭐️ When a tuple struct has only one element, we call it **newtype pattern**. Because it helps to create a new type. + +### Definition + +```rust +struct Color(u8, u8, u8); + +struct Department(String); ``` -## Tuple structs +### Instantiation & Accessing Elements + +```rust +struct Color(u8, u8, u8); + +struct Department(String); + +fn main() { + let white = Color(255, 255, 255); + println!("RGB({}, {}, {})", white.0, white.1, white.2); // RGB(255, 255, 255) + + let eng_department = Department("Engineering".to_string()); + println!("{}", eng_department.0); // Engineering +} +``` -⭐️ When a tuple struct has only one element, we call it **newtype pattern**. Because it helps to create a new type. +### Destructuring ```rust struct Color(u8, u8, u8); -struct Kilometers(i32); + +struct Department(String); + +fn get_department() -> Department { + Department("Engineering".to_string()) +} fn main() { - // Creating an instance - let black = Color(0, 0, 0); - - // Destructure the instance using a `let` binding, this will not destruct black instance - let Color(r, g, b) = black; - println!("Black = rgb({}, {}, {})", r, g, b); //black = rgb(0, 0, 0); - - // Newtype pattern - let distance = Kilometers(20); - // Destructure the instance using a `let` binding - let Kilometers(distance_in_km) = distance; - println!("The distance: {} km", distance_in_km); //The distance: 20 km + let white = Color(255, 255, 255); + + let Color(red, green, blue) = white; // 💡 let Color(red, blue, .. ) = white; // Destructuring only selected field's value + println!("RGB({}, {}, {})", red, green, blue); // RGB(255, 255, 255) + + let Department(name) = get_department(); + println!("{}", name); // Engineering } ``` -## Unit structs +## Unit Structs + +- It defines a new type, but it resembles an empty tuple, `()` +- This is rarely useful on its own. But in combination with other features (such as generics), it can become useful. -This is rarely useful on its own. But in combination with other features, it can become useful. +### Definition & Instantiation + +```rust +struct Electron; +fn main() { + let x = Electron; +} +``` + +> [!tip] > [📖](https://doc.rust-lang.org/book/first-edition/structs.html) ex: A library may ask you to create a structure that implements a certain trait to handle events. If you don’t have any data you need to store in the structure, you can create a unit-like struct. +## Debug Printing and Pretty Debug Printing + +In Rust, the `#[derive()]` attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The [`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) trait allows us to format a value with `{:?}` or `{:#?}` in `println!` and similar macros. + ```rust +#![allow(unused)] // 💡 skip unused warnings, as we don't read fields in the structs + +#[derive(Debug)] struct Electron; +#[derive(Debug)] +struct Department(String); + +#[derive(Debug)] +struct Person { + name: String, + company_name: String, +} + fn main() { - let x = Electron; + let a = Electron; + println!("{a:?}"); // Electron // 💡{a:#?} prints the same + + let b = Department("Engineering".to_string()); + println!("{b:?}"); // Department("Engineering") + println!("{b:#?}"); + // Department( + // "Engineering", + // ) + + let c = Person { name: "Steve Jobs".to_string(), company_name: "Apple".to_string() }; + println!("{c:?}"); // Person { name: "Steve Jobs", company_name: "Apple" } + println!("{c:#?}"); + // Person { + // name: "Steve Jobs", + // company_name: "Apple", + // } } ``` diff --git a/content/en/docs/b3.enums.md b/content/en/docs/b3.enums.md index 6b43c075..6749febd 100755 --- a/content/en/docs/b3.enums.md +++ b/content/en/docs/b3.enums.md @@ -3,7 +3,9 @@ title: Enums slug: enums --- -⭐️ An **enum** is a single type. It contains **variants**, which are possible values of the enum at a given time. For example, +- An enum is a single type that contains variants, which represent the possible values of the enum at any given time. +- By convention, the enum name and its variants' names should follow [`PascalCase`](https://en.wikipedia.org/wiki/Camel_case). +- Can access the variants using the `::` notation and the variant name. ex. Day::Sunday ```rust enum Day { @@ -13,48 +15,189 @@ enum Day { Wednesday, Thursday, Friday, - Saturday + Saturday, } -// The `Day` is the enum -// Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday are the variants +// 💡 Day is the enum. Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday are its variants. ``` -⭐️ Variants can be accessed through :: notation, ex. Day::Sunday +- An enum variant can have either, + - No data (a unit variant) + - Unnamed ordered data (a tuple variant) + - Named data/ fields (a struct variant) + ```rust + enum FlashMessage { + Success, // 💡 A unit variant (no data) + Error(u8, String), // 💡 A tuple variant (one or more , separated data) + Warning { field: String, message: String }, // 💡 A struct variant (one or more , separated name: value data) + } + + // 💡 FlashMessage is the emnum, Success, Error, Warning are its variants. + ``` -⭐️ Each enum **variant** can have, -* No data (unit variant) -* Unnamed ordered data (tuple variant) -* Named data (struct variant) +> [!recap] +> - In Rust, the term "instantiation" is used to describe the act of creating a concrete instance of a type (struct or enum). +> - In Rust, the term "field" is used to describe a named component in a C-like struct & struct-like enum variant, and the term "element" is used to describe an unnamed component in a tuple struct & tuple-like enum variant. The term "member" is used to describe both. +> - More complex examples can be found on [Generics](/docs/generics/), [Impls and Traits](/docs/impls-and-traits), [Lifetimes](/docs/lifetimes) and [Modules](/docs/modules) sections. +## Instantiation ```rust +#![allow(unused)] // 💡 skip unused warnings, as we don't read fields in the enums + +#[derive(Debug)] +enum FlashMessage { // Definition + Success, + Error(u32, String), + Warning { field: String, message: String }, +} + +fn main() { + // 1. Instantiation with separate variable declaration and assignment + let x: FlashMessage; // Declaration with the data type + x = FlashMessage::Success; + println!("{x:?}"); // Success + + // 2. Instantiation with a direct variable initialization + let a = FlashMessage::Success; + let b = FlashMessage::Error(401, "Unauthorized".to_string()); + let c = FlashMessage::Warning { field: "email".to_string(), message: "This is required".to_string() }; + + println!("{a:?}"); // Success + println!("{b:?}"); // Error(401, "Unauthorized") + println!("{c:?}"); // Warning { field: "email", message: "This is required" } +} +``` + +```rust +// 3. Instantiation with a default variant +#![allow(unused)] // 💡 skip unused warnings, as we don't use the all variants of the enum + +#[derive(Debug, Default)] +enum Hand { + Left, + #[default] // 💡Set Right as the default variant + Right, +} + +fn main() { + let a = Hand::default(); // Instantiation with the default variant + println!("{a:?}"); // Right +} +``` + +> [!tip] +> In Rust, the `#[derive()]` attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The [`std::fmt::Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) trait allows us to format a value with `{:?}` or `{:#?}` in `println!` and similar macros. The [`std::default::Default`](https://doc.rust-lang.org/std/default/trait.Default.html) trait allows us to create a new instance of a type with the `Type::default()` method. + +## Pattern Matching + +### With `match` + +```rust +#![allow(unused)] // 💡 skip unused warnings, as we don't use the all variants of the enum + +enum Season { + Spring, + Summer, + Autumn, + Winter, +} + +fn main() { + let a = Season::Winter; + let result = match a { + Season::Spring => "☀️", + Season::Summer => "🍁", + Season::Autumn => "🍂", + Season::Winter => "❄️", + }; + + println!("{result}"); // ❄️ +} +``` + +### With `if let`, `else if let`, `else` + +`if let` is useful when we only care about handling one (or few) specific patterns and don’t need to explicitly match every possible case. + +```rust +#![allow(unused)] // 💡 skip unused warnings, as we don't use the all variants of the enum + +enum Season { + Spring, + Summer, + Autumn, + Winter, +} + +fn main() { + let a = Season::Winter; + let result = if let Season::Spring = a { + "☀️" + } else if let Season::Summer = a { + "🍁" + } else if let Season::Autumn = a { + "🍂" + } else if let Season::Winter = a { + "❄️" + } else { + unreachable!() + }; + + println!("{result}"); // ❄️ +} +``` + +## Destructuring & Accessing Variants' Members + +In Rust, directly accessing an enum variant's fields without any form of pattern matching is not possible. We need to use pattern matching to access the fields by using a `match` expression or `if let` expression. + +### With `match` + +```rust +#![allow(unused)] // 💡 skip unused warnings, as we don't use the all variants of the enum + enum FlashMessage { - Success, // A unit variant - Warning{ category: i32, message: String }, // A struct variant - Error(String) // A tuple variant + Success, + Error(u32, String), + Warning { field: String, message: String }, } fn main() { - let mut form_status = FlashMessage::Success; - print_flash_message(form_status); + let a = FlashMessage::Error(401, "Unauthorized".to_string()); - form_status = FlashMessage::Warning {category: 2, message: String::from("Field X is required")}; - print_flash_message(form_status); + let result = match a { + FlashMessage::Success => "We'll get back to you.".to_string(), + FlashMessage::Error(_, msg) => msg, // 💡 Destructuring only the second element of the tuple variant. + FlashMessage::Warning { message, .. } => message, // 💡 Destructuring only the second field of the struct variant. + }; - form_status = FlashMessage::Error(String::from("Connection Error")); - print_flash_message(form_status); + println!("{result}"); // Unauthorized } +``` -fn print_flash_message(m : FlashMessage) { - // Pattern matching with enum - match m { - FlashMessage::Success => - println!("Form Submitted correctly"), - FlashMessage::Warning {category, message} => // Destructure, should use same field names - println!("Warning : {} - {}", category, message), - FlashMessage::Error(msg) => - println!("Error : {}", msg) - } +### With `if let`, `else if let`, `else` + +`if let` is useful when we only care about handling one (or few) specific patterns and don’t need to explicitly match every possible case. + +```rust +#![allow(dead_code)] // 💡 Remove dead_code warnings, as we don't access the all elements of variants. + +enum FlashMessage { + Success, + Error(u32, String), + Warning { field: String, message: String }, +} + +fn main() { + let a = FlashMessage::Error(401, "Unauthorized".to_string()); + + if let FlashMessage::Error(_, msg) = a { + println!("{msg}"); // Unauthorized + } else if let FlashMessage::Warning { message, .. } = a { + println!("{message}"); + } else { + println!("We'll get back to you."); + } } ``` diff --git a/content/en/docs/b4.generics.md b/content/en/docs/b4.generics.md index d465c372..3a401a0a 100755 --- a/content/en/docs/b4.generics.md +++ b/content/en/docs/b4.generics.md @@ -3,128 +3,142 @@ title: Generics slug: generics --- -> [📖](https://doc.rust-lang.org/beta/book/first-edition/generics.html) Sometimes, when writing a function or data type, we may want it to work for multiple types of arguments. In Rust, we can do this with generics. +- The core concept of generics is abstraction over types. They let us write one piece of code to operate with any data type without repeating ourselves to write separate versions for each type. At the compile time, Rust ensures the type safety and generates an optimized code for each concrete type used in the program. +- Use an uppercase letter (`T`, `U`, ...) or a [`PascalCase`](https://en.wikipedia.org/wiki/Camel_case) identifier for the data type. + - Instead of `x: u8` we use `x: T`. + - Inform the compiler that `T` is a generic type by adding `` at first. -💭 The concept is, instead of declaring a specific data type we use an uppercase letter(or [PascalCase](https://en.wikipedia.org/wiki/Camel_case) identifier). ex, **instead of x : u8** we use **x : T** . but we have to inform to the compiler that T is a generic type(can be any type) by adding `` at first. - -## Generalizing functions - -```rust -fn takes_anything(x: T) { // x has type T, T is a generic type -} - -fn takes_two_of_the_same_things(x: T, y: T) { // Both x and y has the same type -} - -fn takes_two_things(x: T, y: U) { // Multiple types -} -``` - -## Generalizing structs +## With One Generic Type ```rust struct Point { - x: T, - y: T, + x: T, + y: T, } -fn main() { - let point_a = Point { x: 0, y: 0 }; // T is a int type - let point_b = Point { x: 0.0, y: 0.0 }; // T is a float type +fn to_tuple(x: T, y: T) -> (T, T) { + (x, y) } -// 🔎 When adding an implementation for a generic struct, the type parameters should be declared after the impl as well -// impl Point { -``` - -## Generalizing enums - -```rust -enum Option { - Some(T), - None, -} +fn main() { + let a = Point { x: 0, y: 1 }; // a: Point + let b = to_tuple(a.x, a.y); // (i32, i32) + println!("{b:?}"); // (0, 1) -enum Result { - Ok(T), - Err(E), + let c = Point { x: false, y: true }; // a: Point + let d = to_tuple(c.x, c.y); // (bool, bool) + println!("{d:?}"); // (false, true) } ``` -> ⭐️ Above [Option](https://doc.rust-lang.org/std/option/index.html) and [Result](https://doc.rust-lang.org/std/result/index.html) types are kind of special generic types which are already defined in Rust’s standard library.  -> - An **optional value** can have either **Some** value or no value/ **None**. -> - A **result** can represent either success/ **Ok** or failure/ **Err** +## With Multiple Generic Types -### Usages of Option - ```rust -// 01 - - - - - - - - - - - - - - - - - - - - - - -fn get_id_by_username(username: &str) -> Option { - // if username can be found in the system, set userId - return Some(userId); - // else - None +struct Point { + x: T, + y: U, } -// 💭 So, on the above function, instead of setting return type as usize -// set return type as Option -// Instead of return userId, return Some(userId) -// else None (💡remember? last return statement no need return keyword and ending ;) - -// 02 - - - - - - - - - - - - - - - - - - - - - - -struct Task { - title: String, - assignee: Option, +fn to_shuffled_tuple(x: T, y: U) -> (U, T) { + (y, x) } -// 💭 Instead of assignee: Person, we use Option -// because the task has not been assigned to a specific person - -// - - - - - - - - - - - - - - - - - - - - - - - -// When using Option types as return types on functions -// we can use pattern matching to catch the relevant return type(Some/None) when calling them - fn main() { - let username = "anonymous"; - match get_id_by_username(username) { - None => println!("User not found"), - Some(i) => println!("User Id: {}", i) - } + let a = Point { x: 1u8, y: true }; // a: Point + let b = to_shuffled_tuple(a.x, a.y); // (bool, u8) + println!("{b:?}"); // (true, 1) } ``` -### Usages of Result - -> [📖](https://doc.rust-lang.org/book/first-edition/error-handling.html) The Option type is a way to use Rust’s type system to express the possibility of absence. Result expresses the possibility of error. +On some occasions, the compiler cannot inter the type, and we have to specify the type when using the generic type. By the way, it's good practice to specify the type on variables when using a generic implementation. ```rust -// - - - - - - - - - - - - - - - - - - - - - - -fn get_word_count_from_file(file_name: &str) -> Result { - // if the file is not found on the system, return error - return Err("File can not be found!") - // else, count and return the word count - // let mut word_count: u32; .... - Ok(word_count) +#[derive(Debug)] +enum Data { + Value(V), + KeyValue(K, V), } -// 💭 On the above function, -// instead panic(break) the app, when the file can not be found; return Err(something) -// or when it could get the relevant data; return Ok(data) - - -// - - - - - - - - - - - - - - - - - - - - - - - -// We can use pattern matching to catch the relevant return type(Ok/Err) when calling it - fn main() { - let mut file_name = "file_a"; - match get_word_count_from_file(file_name) { - Ok(i) => println!("Word Count: {}", i), - Err(e) => println!("Error: {}", e) - } + let a: Data<(), bool> = Data::Value(true); // ⭐️ The compiler can not inter the type here. We have to specify the type. + let b = Data::KeyValue(1, true); // The compiler can infer the type; i32, bool + + println!("{a:?}"); // Value(true) + println!("{b:?}"); // KeyValue(1, true) } ``` - -> 🔎 Many useful methods have been implemented around Option and Result types. More information can be found on [std::option::Option](https://doc.rust-lang.org/std/option/enum.Option.html) and [std::result::Result](https://doc.rust-lang.org/std/result/enum.Result.html) pages on Rust doc. - -⭐️ Also **more practical examples** of options & results can be found on [Error Handling](https://doc.rust-lang.org/book/first-edition/error-handling.html) section in Rust doc. +## 👨‍🏫 Before going to the next... + +- Option and Result + + > [!recap] + > This is a quick reference to `Option` and `Result` as enums. Please don’t worry too much about them for now, as we will discuss them in detail later in [Error Handling—Option & Result](/docs/option-and-result/). + + Many languages use `null`\ `nil`\ `undefined` types to represent empty outputs, and Exceptions to handle errors. Rust skips using both, especially to prevent issues like null pointer exceptions, sensitive data leakages through exceptions, etc. [Option](https://doc.rust-lang.org/std/option/index.html) and [Result](https://doc.rust-lang.org/std/result/index.html) types are two special generic enums defined in Rust’s standard library to deal with these cases. + + ```rust + // An output can have either Some value or no value/ None. + enum Option { // T is a generic and it can contain any type of value. + Some(T), + None, + } + + // A result can represent either success/ Ok or failure/ Err. + enum Result { // T and E are generics. T can contain any type of value, E can be any error. + Ok(T), + Err(E), + } + ``` + + > [!tip] + > - An optional value can have either **Some** value or no value/ **None** ⇒ possibility of absence + > - A result can represent either success/ **Ok** or failure/ **Err** ⇒ possibility of failure + + - Option + + ```rust + struct Task { + title: String, + assignee: Option, // 💡 Instead of `assignee: Person`, we use `assignee: Option` as the assignee can be `None`. + } + ``` + + ```rust + fn get_id_by_username(username: &str) -> Option { // 💡 Instead of setting return type as `usize`, set it `Option` + // if username can be found in the system, return userId + return Some(userId); // 💡 Instead of return userId, return Some(userId) + + // else + None // 💡 The last return statement no need `return` keyword and ending `;` + } + + fn main() { + let username = "anonymous"; + match get_id_by_username(username) { // 💡 We can use pattern matching to catch the relevant return type (Some/None) + None => println!("User not found"), + Some(i) => println!("User Id: {}", i), + } + } + ``` + + - Result + + ```rust + fn get_word_count_from_file(file_name: &str) -> Result { // 💡 Instead of setting return type as `u32`, set it `Result` + // if the file is not found on the system, return error + return Err("File can not be found!"); // 💡 Instead panic/ break when the file can not be found; return Err(something) + + // else, count and return the word count + Ok(word_count) // 💡 Instead of return `word_count`, return `Ok(word_count)` + // 💡 The last return statement no need `return` keyword and ending `;` + } + + fn main() { + let mut file_name = "file_a"; + match get_word_count_from_file(file_name) { // 💡 We can use pattern matching to catch the relevant return type (Ok/Err) + Ok(i) => println!("Word Count: {}", i), + Err(e) => println!("Error: {}", e) + } + } + ``` diff --git a/content/en/docs/b5.impls-and-traits.md b/content/en/docs/b5.impls-and-traits.md deleted file mode 100755 index 85bafaa9..00000000 --- a/content/en/docs/b5.impls-and-traits.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: Impls & Traits -slug: impls-and-traits ---- - -💡 When we discussed about **C-like structs**, I mentioned that those are **similar to classes** in OOP languages **but without their methods**. **impls** are **used to define methods** for Rust structs and enums. - -💡 **Traits** are kind of **similar to interfaces** in OOP languages. They are used to define the functionality a type must provide. Multiple traits can be implemented for a single type. - -⭐️️ But traits **can also include default implementations of methods**. Default methods can be overridden when implementing types. - -## Impls without traits - -```rust -struct Player { - first_name: String, - last_name: String, -} - -impl Player { - fn full_name(&self) -> String { - format!("{} {}", self.first_name, self.last_name) - } -} - -fn main() { - let player_1 = Player { - first_name: "Rafael".to_string(), - last_name: "Nadal".to_string(), - }; - - println!("Player 01: {}", player_1.full_name()); -} - -// ⭐️ Implementation must appear in the same crate as the self type - -// 💡 And also in Rust, new traits can be implemented for existing types even for types like i8, f64 and etc. -// Same way existing traits can be implemented for new types you are creating. -// But we can not implement existing traits into existing types. -``` - -## Impls & traits, without default methods - -```rust -struct Player { - first_name: String, - last_name: String, -} - -trait FullName { - fn full_name(&self) -> String; -} - -impl FullName for Player { - fn full_name(&self) -> String { - format!("{} {}", self.first_name, self.last_name) - } -} - -fn main() { - let player_2 = Player { - first_name: "Roger".to_string(), - last_name: "Federer".to_string(), - }; - - println!("Player 02: {}", player_2.full_name()); -} - -// 🔎 Other than functions, traits can contain constants and types. -``` - -## Impls, traits & default methods - -```rust -trait Foo { - fn bar(&self); - fn baz(&self) { println!("We called baz."); } -} -``` - -⭐️ As you can see methods take a **special first parameter**, the type itself. It can be **either self, &self, or &mut self**; self if it’s a value on the stack (taking ownership), &self if it’s a reference, and &mut self if it’s a mutable reference. - -## Impls with Associated functions - -Some other languages support **static methods**. At such times, we **call a function directly** through the class without creating an object. In Rust, we call them Associated Functions. we use **::** instead of . when calling them from the struct. -ex. `Person::new(“Elon Musk Jr”);` - - -```rust -struct Player { - first_name: String, - last_name: String, -} - -impl Player { - fn new(first_name: String, last_name: String) -> Player { - Player { - first_name: first_name, - last_name: last_name, - } - } - - fn full_name(&self) -> String { - format!("{} {}", self.first_name, self.last_name) - } -} - -fn main() { - let player_name = Player::new("Serena".to_string(), "Williams".to_string()).full_name(); - println!("Player: {}", player_name); -} - -// We have used :: notation for `new()` and . notation for `full_name()` - -// 🔎 Also in here, instead of using new() and full_name() separately as two expressions, -// we can use Method Chaining. ex. `player.add_points(2).get_point_count();` -``` - -## Traits with generics - -```rust -trait From { - fn from(T) -> Self; -} - impl From for u16 { - //... - } - impl From for u32{ - //... - } - -// Should specify after the trait name like generic functions -``` - -## Traits inheritance - -```rust -trait Person { - fn full_name(&self) -> String; -} - - trait Employee : Person { // Employee inherits from person trait - fn job_title(&self) -> String; - } - - trait ExpatEmployee : Employee + Expat { // ExpatEmployee inherits from Employee and Expat traits - fn additional_tax(&self) -> f64; - } -``` - -## Trait objects - -🔎 While Rust favors static dispatch, it also supports dynamic dispatch through a mechanism called ‘trait objects.’ - -> [🅆](https://en.wikipedia.org/wiki/Dynamic_dispatch) **Dynamic dispatch** is the process of selecting which implementation of a polymorphic operation (method or function) to call at run time. - - -```rust -trait GetSound { - fn get_sound(&self) -> String; -} - -struct Cat { - sound: String, -} - impl GetSound for Cat { - fn get_sound(&self) -> String { - self.sound.clone() - } - } - -struct Bell { - sound: String, -} - impl GetSound for Bell { - fn get_sound(&self) -> String { - self.sound.clone() - } - } - - -fn make_sound(t: &T) { - println!("{}!", t.get_sound()) -} - -fn main() { - let kitty = Cat { sound: "Meow".to_string() }; - let the_bell = Bell { sound: "Ding Dong".to_string() }; - - make_sound(&kitty); // Meow! - make_sound(&the_bell); // Ding Dong! -} -``` diff --git a/content/en/docs/b5.impls.md b/content/en/docs/b5.impls.md new file mode 100644 index 00000000..c35ee149 --- /dev/null +++ b/content/en/docs/b5.impls.md @@ -0,0 +1,140 @@ +--- +title: Impls +slug: impls +--- + +- Earlier, we discussed that structs and enums group related data, while impl blocks and traits add associated and shared behavior to the data. +- Usage of `Self` vs `self` keywords: + - `Self`: Refers to the type itself (the blueprint). + - `self`: Refers to the instance of the type (the actual data). + + > [!tip] + > This can be any form of `self`, `&self`, `&mut self`, `self: Box`, `self: Pin<&mut Self>`, etc. +- There are multiple ways to implement a behavior for a type. We discuss only about the `impl` blocks with this article. The patterns involving traits are discussed under [Traits](/docs/traits). + +> [!important] +> The implementation must be in the same crate as the type. + +## Inherent impls + +Implement associated functions, methods, and constants directly for a type. + +### `impl Type` + +```rust +struct Person { + name: String, +} + +impl Person { + const GREET: &str = "Hello!"; + + fn greet(&self) -> String { // &self` is shorthand for self: &Self + format!("{} I am {}.", Self::GREET, self.name) // 💡Self to access type; self to access instance + } +} + +fn main() { + let steve = Person { name: "Steve".to_string() }; + println!("{}", steve.greet()) // Hello! I am Steve. +} +``` + +- Inside the `impl` block, + - Constants belong to the type itself. + - To access them, use the `Self` keyword or the type name. + - Example: `Self::GREET` (preferred in Rust) or `Person::GREET`. + - Methods (functions that access instance fields) must send the instance/ `self` as the first parameter. + - This can be `self`, `&self`, `&mut self`, `self: Box`, `self: Pin<&mut Self>`, etc.) as the first parameter. + +### `impl Type` + +```rust +struct Point { + x: T, + y: T, +} + +impl Point { + fn into_tuple(self) -> (T, T) { + (self.x, self.y) + } +} + +fn main() { + let a = Point { x: 0, y: 1 }; // a: Point + let b = a.into_tuple(); // (0, 1) + println!("{b:?}") +} + +// 💡 into_tuple() take self (not &self) and consumes the self / instance. +// 💡 we can pass &self (as a reference) and use as_ prefix to borrow the instance. +// fn as_tuple(&self) -> (&T, &T) { (&self.x, &self.y) } +``` + +## Associated Functions & Methods + +- Associated functions: + - Functions that are associated with a particular data type via the `impl` block. + - Can call from the type with `::` operator. + - `Person::new()`, `Vec::new()`, `String::from()` +- Methods: + - Associated functions with a receiver of `self`, `&self`, `&mut self`, `self: Box`, `self: Pin<&mut Self>`, etc. + - Can call from the instance with `.` operator or from the type (as methods are also associated functions) with `::` operator, but we need to pass the instance as the first parameter always. + - `steve.greet()` or `Person::greet(&steve)` + - `"hello".to_string()` or `String::to_string("hello")` + +```rust +struct Person { + name: String, + company_name: String, +} + +impl Person { + // 💡 The constructor (new` is a conventional name, not a keyword) + fn new(name: String, company_name: String) -> Self { // an associated function and not a method + Self { name, company_name } + } + + fn intro_name(&self) -> String { // a method + format!("I'm {}", self.name) + } + + fn intro_company(&self) -> String { // a method + format!("I'm from {}", self.company_name) + } +} + +fn main() { + // Call from the type with `::` operator + let steve = Person::new(String::from("Steve Jobs"), String::from("Apple")); + + // Call from the instance with `.` operator + println!("{}. {}.", steve.intro_name(), steve.intro_company()); // I'm Steve Jobs. I'm from Apple. + + // As methods are also associated functions; Call from the type with `::` operator and pass the instance as the first parameter + println!("{}. {}.", Person::intro_name(&steve), Person::intro_company(&steve)); // I'm Steve Jobs. I'm from Apple. +} +``` + +We can use the type/ `Person` instead `Self` keyword in the `new` function. By the way, using the `Self` keyword is considered idiomatic in Rust. + +```rust +fn new(name: String, company_name: String) -> Person { + Person { name, company_name } +} +``` + +## 👨‍🏫 Before going to the next... + +- Familiarize with the conventional prefixes and suffixes used in methods names. + + | Prefix | Postfix | self taken | self type | + |--------|----------|----------------------------|-----------| + | as_ | none | &self or &mut self | any | + | from_ | none | none | any | + | into_ | none | self | any | + | is_ | none | &mut self or &self or none | any | + | to_ | _mut | &mut self | any | + | to_ | not _mut | self | Copy | + | to_ | not _mut | &self | not Copy | diff --git a/content/en/docs/b6.traits.md b/content/en/docs/b6.traits.md new file mode 100644 index 00000000..687151d0 --- /dev/null +++ b/content/en/docs/b6.traits.md @@ -0,0 +1,632 @@ +--- +title: Traits +slug: traits +--- + +A trait is a contract that defines a set of behaviors or properties that a type must implement. It can contain associated types, constants, function or method signatures, and overridable default implementations. + +## Definition + +### With No Associates + +```rust +pub trait Sized { } +``` + +> [!tip] +> Mostly used to mark a type as having certain properties to allow in certain operations. Known as Marker Traits. + +### With the Declarations of Associates + +```rust +trait Greet { + const PREFIX: &'static str; + type Item; + fn greet(&self) -> String; +} +``` + +### With the Default Implementations of Associates + +```rust +trait Greet { + const PREFIX: &'static str = "Hello"; + + fn greet(&self) -> String { + format!("{}!", String::from(Self::PREFIX)) + } +} +``` + +### With Supertraits + +A trait must have to be implemented first before implementing the current trait. + +```rust +pub trait Copy: Clone { } // 💡 Any type that copyable should be clonable + +// 💡 Any type that clonable should be sized +pub trait Clone: Sized { + fn clone(&self) -> Self; + fn clone_from(&mut self, source: &Self) { ... } +} +``` + +> [!tip] +> `trait Subtrait: Super` or `trait Subtrait: SupertraitA + SupertraitB` + + +### With Generic Types + +```rust +// 💡 Convert from one type to another. If successful, retrun Type; else return associated Error +pub trait TryFrom: Sized { + type Error; + + fn try_from(value: T) -> Result; +} +``` + +## Trait Impls (Manual Implementation) + +Manually implementing a shared behavior defined in a trait for a type via an `impl` block. + +### With Required Components + +When a trait has only declarations of associated items, it's required to implement all of them. + +```rust +struct Person { + name: String +} + +trait Greet { + const PREFIX: &'static str; // 💡 Required constant + fn greet(&self) -> String; // 💡 Required method +} + +impl Greet for Person { + const PREFIX: &'static str = "Hello"; + + fn greet(&self) -> String { + format!("{} {}!", Self::PREFIX.to_owned(), self.name) + } +} + +fn main() { + let steve = Person { name: "Steve".to_string() }; + println!("{}", steve.greet()); // Hello Steve! +} +``` + +### With Provided Components + +When a trait has default values and default implementations, it's possible to implement only some of them or override them. + +```rust +struct Person { + name: String +} + +trait Greet { + // 💡 Provided constant + const PREFIX: &'static str = "Hello"; + + // 💡 Provided method + fn greet(&self) -> String { + format!("{}!", String::from(Self::PREFIX)) + } +} + +impl Greet for Person { + // 💡 Overridden constant + const PREFIX: &'static str = "Good morning"; + + // 💡 Overridden method + fn greet(&self) -> String { + format!("{} {}!", Self::PREFIX.to_owned(), self.name) + } +} + +fn main() { + let steve = Person { name: "Steve".to_string() }; + println!("{}", steve.greet()); // Good morning Steve! +} +``` + +### For Enum Types + +```rust +#![allow(unused)] +enum Shape { + Circle(f64), // radius + Rectangle(u32, u32), // width, height +} + +trait Area { + fn area(&self) -> f64; +} + +impl Area for Shape { + fn area(&self) -> f64 { + match *self { + Shape::Circle(radius) => std::f64::consts::PI * radius * radius, + Shape::Rectangle(width, height) => (width * height) as f64, + } + } +} + +fn main() { + let circle = Shape::Circle(7.0); + let rect = Shape::Rectangle(5, 5); + + println!("{:?}", circle.area()); + println!("{:?}", rect.area()); +} +``` + +### For Generic Types + +1. `impl Trait for Type` + + ```rust + #![allow(unused)] + struct Person { + name: String, + age: T, + } + + trait Greet { + fn greet(&self) -> String; + } + + impl Greet for Person { + fn greet(&self) -> String { + format!("Hello {}!", self.name) + } + } + + fn main() { + let steve = Person { name: "Steve".to_string(), age: 65 }; // 💡 age: i32 + let bill = Person { name: "Bill".to_string(), age: 7.5 }; // 💡 age: f64 + + println!("{:?}", steve.greet()); // Hello Steve! + println!("{:?}", bill.greet()); // Hello Bill! + } + ``` + +2. `impl Trait for Type` + + ```rust + struct Point { + x: T, + y: T, + } + + trait IntoTuple { + fn into_tuple(self) -> (T, T); + } + + impl IntoTuple for Point { + fn into_tuple(self) -> (T, T) { + (self.x, self.y) + } + } + + fn main() { + let a = Point { x: 0, y: 1 }; // Point + let b = Point { x: "2.0", y: "2.2" }; // Point<&str> + + println!("{:?}", a.into_tuple()); // (0, 1) + println!("{:?}", b.into_tuple()); // ("2.0", "2.2") + } + ``` + + > [!assignment] + > Update the above implementation to `impl Trait for Type`. + +## Derive and Auto Traits (Autogenerate Implementations) + +### Derive Traits + +The compiler generates the trait implementation automatically for you, based on the derived attributes. + +```rust +#[derive(Debug, Clone, PartialEq)] +struct Person { + name: String, +} + +fn main() { + let steve = Person { name: "Steve".to_string() }; + let bill = Person { name: "Bill".to_string() }; + + // Debug: 💡 allow debug print with {:?} + println!("{steve:?}"); // Person { name: "Steve" } + + // Clone: 💡 allow duplicate data via .clone() + let gates = bill.clone(); + + // PartialEq: 💡 support equate the two instances via == and != + println!("{}", steve == bill); // false + println!("{}", bill == gates); // true +} +``` + +Check bellow Rust STD documentation pages, of these normal derive-traits. + +| Trait | The functionality (implement to the type at compile-time) | +|-------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------| +| [`Debug`](https://doc.rust-lang.org/std/fmt/trait.Debug.html) | Enables debug-printing of the internal state with `{:?}` | +| [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) | Allows creating a default initial value with `::default()` | +| [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html) | Allows creating a deep copy explicitly with `.clone()` | +| [`PartialEq`](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) / [`Eq`](https://doc.rust-lang.org/std/cmp/trait.Eq.html) | Allows comparing instances with `==` and `!=` operators | +| [`PartialOrd`](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) / [`Ord`](https://doc.rust-lang.org/std/cmp/trait.Ord.html) | Allows comparing instances with `<`, `<=`, `>`, and `>=` operators | +| [`Hash`](https://doc.rust-lang.org/std/hash/trait.Hash.html) | Allows the type to be used as a key in a `HashMap` or `HashSet` | + +Marker traits are the traits that have no associated constants, types, methods, etc. So, the compiler-generated implementation has only an empty `impl Trait for Type { }` block. + +```rust +#![allow(unused)] + +#[derive(Debug, Clone, Copy)] // 💡 Copy trait needs the Clone trait as a supertrait +struct Point { + x: i32, + y: i32, +} + +fn main() { + let a = Point { x: 0, y: 1 }; + let b = a; // Copy: 💡 duplicate data. a and b are separate instances + + println!("{a:?}"); + println!("{b:?}"); +} +// 👨‍🏫 Try remove Copy derive on Point and explisitly copy via .clone() +``` + +> [!search] +> [`Copy`](https://doc.rust-lang.org/std/marker/trait.Copy.html) set the ability to duplicate via simply copying bits (pass by duplicating) + +### Auto Traits + +Implicitly bound/ automatically implemented by the compiler without any keywords. + +```rust +pub trait Sized { } +pub unsafe auto trait Sync { } +pub auto trait Unpin { } +``` + +```rust +#![allow(unused)] + +#[derive(Debug)] +struct Point { // 💡 Sized, Send, Sync: (Automatic) Implicitly bound for most simple types in Rust + x: i32, + y: i32, +} + +fn main() { + let a = Point { x: 0, y: 1 }; + + std::thread::scope(|s| { + s.spawn(|| println!("{a:?}")); + s.spawn(|| println!("{a:?}")); + }); + + std::thread::spawn(move || println!("{a:?}")).join().unwrap(); +} +``` + +| Trait | The properties (set to the type automatically) | +|---------------------------------------------------------------------------------|--------------------------------------------------| +| [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html) | Able to determine the size at compile time | +| [`Unpin`](https://doc.rust-lang.org/std/marker/trait.Unpin.html) | Safe to move in memory | +| [`Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) | Safe to move ownership across threads | +| [`Sync`](https://doc.rust-lang.org/std/marker/trait.Sync.html) | Safe to shared references between threads | +| [`UnwindSafe`](https://doc.rust-lang.org/std/panic/trait.UnwindSafe.html) | Safe to use in panic unwindings | +| [`RefUnwindSafe`](https://doc.rust-lang.org/std/panic/trait.RefUnwindSafe.html) | Safe to use shared references in panic unwinding | + +> [!recap] +> **Sized/ Unsized** +> - Most simple types in Rust have a fixed size known at compile time and automatically implement `Sized` marker trait. +> - Unsized types (or dynamically sized types, DSTs) cannot be used directly as local variables, function parameters, or return values. They must always be placed behind an indirection, such as a reference (`&` or `&mut`), a `Box`, `Rc`, or `Arc`. Common examples include slices (`[T]`, `&str`) and trait objects (`dyn Trait`). +> - Pointers to these types are known as "wide pointers" or "fat pointers" because they contain both a pointer to the data and additional metadata. Metadata: for slices - length (number of elements) and for trait objects - a pointer to the vtable/ virtual function table. Ex: `&[T]`, `*mut str`, `Box` +> - To allow a type to be unsized (relaxes the default Sized requirement), we can use the `?Sized` bound. + +## Static Dispatch + +> [!note] +> **Monomorphization** is a compiler optimization and implementation strategy that transforms polymorphic (generic) code into specialized, monomorphic (single-type) versions for each unique set of concrete types used in a program. This increases the binary size. +> +> **Dispatch** is the process of deciding which implementation of a polymorphic function to execute, either statically at compile time or dynamically at runtime (via a lookup in a vtable). + +Rust compiler generates highly optimized code blocks for each type used for a generic function. So, function calls are statically resolved at compile time and no runtime overhead. + +### Trait Bounds + +Specify the type constraints by traits in generics (``) rather than using exact types. Allowed in a field types & function return and argument. + +```rust +struct Point { x: i32, y: i32 } +impl Addr for Point { + fn addr(&self) -> String { + format!("{}, {}", self.x, self.y) + } +} + +struct MapLocation(String, String); // latitude, longitude +impl Addr for MapLocation { + fn addr(&self) -> String { + format!("{}, {}", self.0, self.1) + } +} + +// --- ⭐️ main Trait & Type with Trait Bounds ⭐️ +trait Addr { + fn addr(&self) -> String; +} + +struct Delivery { + location: T, +} + +impl Delivery { + fn new(location: T) -> Self { + Self { location } + } +} + +// --- ⭐️ fn with Trait Bounds ⭐️ +fn location_info(location: T) { + println!("Latitude/ Longitude: {}", location.addr()) +} + +// --- +fn main() { + let ocean = Point { x: 35, y: 20 }; + let tokyo = MapLocation("35.68951".to_string(), "139.69170".to_string()); + + let pkg1 = Delivery::new(ocean); + let pkg2 = Delivery::new(tokyo); + + location_info(pkg1.location); // Latitude/ Longitude: 35, 20 + location_info(pkg2.location); // Latitude/ Longitude: 35.68951, 139.69170 +} +``` + +### Opaque Types + +Specify the type constraints via traits (`:impl Trait`) without specifying full generic syntax. But only allowed as a function return or an argument. + +```rust +struct Point { x: i32, y: i32 } +impl Addr for Point { + fn addr(&self) -> String { + format!("{}, {}", self.x, self.y) + } +} + +struct MapLocation(String, String); // latitude, longitude +impl Addr for MapLocation { + fn addr(&self) -> String { + format!("{}, {}", self.0, self.1) + } +} + +trait Addr { + fn addr(&self) -> String; +} + +// --- ⭐️ Opaque Types are only allowed in function parameters +fn location_info(location: impl Addr) { // 💡 Argument-Position-Impl-Trait/ APIT + println!("Latitude/ Longitude: {}", location.addr()) +} + +fn tokyo_location() -> impl Addr { // 💡 Return-Position-Impl-Trait/ RPIT + MapLocation("35.68951".to_string(), "139.69170".to_string()) +} + +// --- +fn main() { + let ocean = Point { x: 35, y: 20 }; + let tokyo = tokyo_location(); + + location_info(ocean); // Latitude/ Longitude: 35, 20 + location_info(tokyo); // Latitude/ Longitude: 35.68951, 139.69170 +} +``` + +## Dynamic Dispatch + +Dynamic dispatch is used when we need to handle multiple different concrete types (mix types) and resolve the trait implementation dynamically at runtime (because this is not possible with static dispatch). + +> [!search] +> The compiler uses **vtable** (virtual method table) pointers. Instead of knowing the type at compile time, the program follows a pointer to a table that contains the addresses of the methods for that specific instance. + +### Trait Objects + +```rust +struct Point { x: i32, y: i32 } +impl Addr for Point { + fn addr(&self) -> String { + format!("{}, {}", self.x, self.y) + } +} + +struct MapLocation(String, String); // latitude, longitude +impl Addr for MapLocation { + fn addr(&self) -> String { + format!("{}, {}", self.0, self.1) + } +} + +trait Addr { + fn addr(&self) -> String; +} + +// --- ⭐️ Trait Object, allow mix types and check dynamically at runtime +fn locations_info(locations: &[Box]) { // 💡 `dyn Addr` is unsized (DST) and needs a pointer + for location in locations { + println!("Latitude/ Longitude: {}", location.addr()) + } +} + +// --- +fn main() { + let ocean = Point { x: 35, y: 20 }; + let tokyo = MapLocation("35.68951".to_string(), "139.69170".to_string()); + + let locations: Vec> = vec![Box::new(ocean), Box::new(tokyo)]; + + locations_info(&locations); + // Latitude/ Longitude: 35, 20 + // Latitude/ Longitude: 35.68951, 139.69170 +} +``` + +## Blanket Impls (One-to-Many Manual Implementation) + +Blanket impls are a special kind of trait implementation that applies to all types that have a certain type constraint/bound. + +### A Simple Blanket Impl + +```rust +#![allow(unused)] + +// --- 💡 Two types with Greet implentation +struct Person { name: String } +impl Greet for Person {} + +struct Stranger {} +impl Greet for Stranger {} + +trait Greet { + const PREFIX: &'static str = "Hello"; + + fn greet(&self) -> String { + format!("{}!", String::from(Self::PREFIX)) + } +} + +// --- New Farewell trait +trait Farewell { + const PREFIX: &'static str = "Goodbye"; + + fn farewell(&self) -> String { + format!("{}!", String::from(Self::PREFIX)) + } +} + +// ⭐️ Any type that implements Greet gets this Farewell implementation +impl Farewell for T {} // 💡or impl Farewell for T where T: Greet {} + +// --- +fn main() { + let steve = Person { name: "Steve".to_string() }; + let stranger = Stranger {}; + + println!("{}", steve.greet()); // Hello! + println!("{}", stranger.greet()); // Hello! + + println!("{}", steve.farewell()); // Goodbye! + println!("{}", stranger.farewell()); // Goodbye! +} +``` + +### Useful Blanket Impls in STD + +1. When implement [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) Rust STD automatically implements [`ToString`](https://doc.rust-lang.org/std/string/trait.ToString.html) to the type. + + ```rust + use std::fmt; + + struct Person { + name: String, + } + + impl fmt::Display for Person { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Person {{ name: {}}}", self.name) // 💡 {{ }} escaping curly braces + } + } + + fn main() { + let steve = Person { + name: "Steve".to_string(), + }; + + println!("{steve}"); // Person { name: Steve} + println!("{}", steve.to_string()); // Person { name: Steve} + } + ``` + +2. When implement [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) Rust STD automatically implements [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html) to the type. + + ```rust + #![allow(unused)] + + use std::convert::From; + + #[derive(Debug)] + struct Person { + name: String, + } + + impl From<&str> for Person { + fn from(name: &str) -> Self { + Person { + name: name.to_string(), + } + } + } + + fn main() { + let steve = Person::from("Steve"); + let bill: Person = "Bill".into(); + + println!("{:?}", steve); // Person { name: "Steve" } + println!("{:?}", bill); // Person { name: "Bill" } + } + ``` + +> [!assignment] +> Try to implement [TryFrom](https://doc.rust-lang.org/std/convert/trait.TryFrom.html) and check the blanket implementation it provides. + +## Trait Coherence & Orphan Rule + +Rust ensures there is only one implementation of a specific trait for a specific type in the global scope/ in the entire program (Only One Implementation Per Type). This is primarily done to prevent multiple conflicting implementations across different layers of an application. This is called Coherence (or Trait Coherence). If multiple implementations exist, the code will not compile. + +To prevent overwriting an existing implementation in a different layer of the application, Rust prohibits implementing a foreign trait for a foreign type. In other words, either the trait or the type must be defined in the current crate. This is called the Orphan Rule. + +Suppose you want to implement a `std` trait for a `std`/ foreign type but Rust not allow it due to the orphan rule. In that case, we can create a brand-new type that wraps the foreign type and implements the trait for the wrapper type. This is called Newtype Pattern. + +```rust +use std::fmt; +use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH}; + +struct Timestamp(SystemTime); // 💡 The newtype to wrap the std type + +impl Timestamp { + fn get_seconds(&self) -> Result { + self.0 + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_secs()) + } +} + +// 💡 Implement the std trait Display for the newtype +impl fmt::Display for Timestamp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let secs = self.get_seconds().map_err(|_| fmt::Error)?; + write!(f, "{secs}") + } +} + +fn main() -> Result<(), SystemTimeError> { + let now = Timestamp(SystemTime::now()); + println!("Current time: {}", now); + Ok(()) +} +``` diff --git a/content/en/docs/c1.ownership.md b/content/en/docs/c1.ownership.md index bb40933f..49b94d7c 100755 --- a/content/en/docs/c1.ownership.md +++ b/content/en/docs/c1.ownership.md @@ -3,44 +3,313 @@ title: Ownership slug: ownership --- +We discussed in the [Derive Traits](/docs/traits/#derive-traits), the usage of [`Copy` marker trait](https://doc.rust-lang.org/std/marker/trait.Copy.html) and [`.clone()`](https://doc.rust-lang.org/std/clone/index.html) with the below code. + +```rust +#[derive(Debug, Clone, Copy)] +struct Point { + x: i32, + y: i32, +} + +fn main() { + let a = Point { x: 0, y: 1 }; + let b = a; + + println!("{a:?}, {b:?}"); +} +``` + +If we try to remove `Copy` derive on `Point` and run, we will get the following error while compiling. + +```rust +error[E0382]: borrow of moved value: `a` + --> src/main.rs:11:16 + | + 8 | let a = Point { x: 0, y: 1 }; + | - move occurs because `a` has type `Point`, which does not implement the `Copy` trait + 9 | let b = a; + | - value moved here +``` + +This is because of Ownership, which is used to achieve Rust's memory safety. + +## Ownership + +In Rust, + +- Each piece of data has a single owner, and data is only scoped to its owner (💯 if it is not borrowed). + - This means that when the owner of the data goes out of scope, the bound resource will be dropped/ released from memory. +- There can only be one owner at a time. Assigning a variable to another variable or passing it to a function (if not passed by referencing) triggers one of the following behaviors: + - Copy (for the types that implement the `Copy` trait): + - The value is duplicated. + - Both variables become independent owners of their own data and remain accessible. + - Move (default behavior/ if not implemented `Copy` trait): + - Ownership transfers to the new variable. + - The original variable is invalidated and can no longer be used. + +> [!important] +> In Rust, every assignment is technically a bitwise move (a memcpy/ byte-for-byte copy). The difference is that if a type implements the `Copy` trait, the compiler simply doesn't invalidate the original variable afterward. + +## Copy Types + +Rust Standard Library has implemented the `Copy` trait inside `std` for primitive types and most simpler types mainly, +- Due to the orphan rule, we are not allowed to implement `std` traits for `std` types. +- To avoid needing to call the `.clone()` method each time when assigning data to a new variable or passing it to a function. + +Also as we discussed, the `Copy` marker trait can be added to custom types via a `derive` macro. However, the Rust compiler will fail to compile if the type contains any non-`Copy` members. + +```rust +#[derive(Debug, Clone, Copy)] +struct Person { + name: String, // 💡 String is not a Copy type. It's a Move type. +} + +fn main() { + let a = Person { name: "Steve".to_string() }; + dbg!(a); +} +``` + +```rust +error[E0204]: the trait `Copy` cannot be implemented for this type + --> src/main.rs:1:24 + | +1 | #[derive(Debug, Clone, Copy)] + | ^^^^ +2 | struct Person { +3 | name: String, + | ------------ this field does not implement `Copy` +``` + +### Implementations in `std` + +- Primitive types, arrays of `Copy` types, tuples of `Copy` types, raw pointers `*const T`/ `*mut T`, shared references/ `&T` + ```rust + marker_impls! { + #[stable(feature = "rust1", since = "1.0.0")] + Copy for + usize, u8, u16, u32, u64, u128, + isize, i8, i16, i32, i64, i128, + f16, f32, f64, f128, + bool, char, + {T: PointeeSized} *const T, + {T: PointeeSized} *mut T, + } + + #[stable(feature = "copy_clone_array_lib", since = "1.58.0")] + impl Copy for [T; N] {} + + /// Shared references can be copied, but mutable references *cannot*! + #[stable(feature = "rust1", since = "1.0.0")] + impl Copy for &T {} + ``` + +- When `T`, `E`, `P` are `Copy` types, then `Option`, `Result`, `Pin

`, `Poll` +- `IpAddr`, `Ipv4Addr`, `Ipv6Addr`, `Ipv6MulticastScope`, `SocketAddr`, `SocketAddrV4`, `SocketAddrV6`, `SystemTime`, `Duration`, `Instant`, `PhantomData`, and [more](https://doc.rust-lang.org/std/marker/trait.Copy.html#implementors) + +### More Examples + ```rust +#![allow(unused)] + fn main() { - let a = [1, 2, 3]; + let a = [1, 2, 3]; // 💡 Array of Copy types let b = a; + + let c = (true, 'C', 32, 1.2); // 💡 Tuple of Copy types + let d = c; + + let e = Color { red: 255, green: 0, blue: 0 }; // 💡 Type with Copy typed members + Copy marker + let f = e; + println!("{:?} {:?}", a, b); // [1, 2, 3] [1, 2, 3] + println!("{:?} {:?}", c, d); // (true, 'C', 32, 1.2) (true, 'C', 32, 1.2) + println!("{:?} {:?}", e, f); // Color { red: 255, green: 0, blue: 0 } Color { red: 255, green: 0, blue: 0 } +} + +#[derive(Debug, Clone, Copy)] +struct Color { + red: u8, + green: u8, + blue: u8, +} +``` + +```rust +fn main() { + let x: [i32; 3]; + + { + let a = [1, 2, 3]; + x = a; + println!("{:?}", a); // [1, 2, 3] + + // `a` is dropped here, but `x` holds its own independent copy + } + + println!("{:?}", x); // [1, 2, 3] +} +``` + +## Move Types + +By default, data in Rust follows move semantics unless the type implements the `Copy` marker trait. + +Since Rust prohibits implementing `Copy` for types containing non-`Copy` members, we must manually call the `.clone()` method to duplicate the data. + +Types like `String`, `Vec`, and `Box` in Rust store the unsized actual data on the heap and small, fixed-size metadata on the stack. When we assign these types to a new variable, only the stack metadata is copied, the original variable is invalidated, ownership of the heap data is transferred to the new variable, and only the new owner is responsible for freeing the heap memory. + +However, when we call `.clone()` on these types, the heap data is duplicated (deep copy), unless types like `Rc` and `Arc`, which are specifically designed to be cloned efficiently by incrementing a reference count on the heap rather than duplicating the actual data in heap. + +### Move Types in `std` + +- Heap allocated collections like `String`, `Vec`, `VecDeque`, `LinkedList`, `HashMap`, `HashSet`, `BTreeMap`, `BTreeSet`, `BinaryHeap` +- `&mut T` and most iterators +- When `T`, `E`, `P` are non-`Copy` types, then `Option`, `Result`, `Pin

`, `Poll` +- `PathBuf`, `File`, `TcpStream`, `TcpListener`, `JoinHandle`, `Child` processes, channel `Sender`/`Receiver`, `Mutex`, `RwLock` +- Smart Pointers like `Box`, `Rc`, `Arc`, `Pin

` +- `Cell`, `RefCell` + +### More Examples + +```rust +fn main() { + let a = String::from("Hello"); + let b = a.clone(); + + let c = vec![1, 2, 3]; + let d = c.clone(); + + let e: [String; 2] = ["Steve".to_owned(), "Jony".to_owned()]; + let f = e.clone(); + + let g = (128, "Steve".to_string()); + let h = g.clone(); + + println!("{:?} {:?}", a, b); // "Hello" "Hello" + println!("{:?} {:?}", c, d); // [1, 2, 3] [1, 2, 3] + println!("{:?} {:?}", e, f); // ["Steve", "Jony"] ["Steve", "Jony"] + println!("{:?} {:?}", g, h); // (128, "Steve") (128, "Steve") } +``` + +Iterators created with `into_iter()` consume the collection and move ownership of its data into the iterator. In contrast, `iter()` and `iter_mut()` are used to iterate over shared and mutable references of the collection, without moving it. +```rust fn main() { let a = vec![1, 2, 3]; - let b = a; - println!("{:?} {:?}", a, b); // Error; use of moved value: `a` + + // ⭐️ By the way, we can use references without cloning which we'll discuss under borrowing + for x in a.clone() { // 👨‍🏫 Try remove `.clone()` + println!("{x}"); + } + + println!("{}", a.len()); +} + +// 💡`for x in a` uses `into_iter()` +// 💯`for x in &a` uses `iter()` and `for x in &mut a` uses `iter_mut()` +``` + +Structs with non-`Copy` members are always moved. We need to explicitly call `.clone()` when we want to duplicate the data. + +```rust +#![allow(unused)] + +#[derive(Debug, Clone)] +struct Person { + name: String, + company_name: String, + age: f32, +} + +fn main() { + let steve = Person{ name: "Steve Jobs".to_owned(), company_name: "Apple".to_owned(), age: 56.0 }; + let steve_copy = steve.clone(); + + // ⭐️ copy fields values from another instance, steve_copy. steve_copy.company_name moves to wozniak + let wozniak = Person { name: "Steve Wozniak".to_owned(), .. steve_copy }; + + println!("{} {}" , steve_copy.name, steve_copy.age); // We can't access steve_copy.company_name here + + dbg!(steve, wozniak); +} +``` + +```rust +fn main() { + let x: String; + + { + let a = String::from("Hello!"); + x = a; // Ownership is transferred to `x` and `a` is invalidated + + // At the end of this block, `a` goes out of scope. + // However, no heap data is freed because ownership was moved to `x` + } + + println!("{x}"); // "Hello!" +} +``` + +## `Box` + +[`Box`](https://doc.rust-lang.org/std/boxed/struct.Box.html) stores its value on the heap and provides ownership for the allocation. + +```rust +fn main() { + let a = Box::new(5); // Data on the heap + Smart pointer Box on the stack + + let b = a.clone(); // Deep copy: data on heap + smart pointer on the stack & new owner + + let c = a; // Move ownership of original Box to c + + println!("{b} {c}"); } ``` -In the above examples, we are just trying to **assign the value of `a` to `b`** . Almost the same code in both code blocks, but having **two different data types**. And the second one gives an error. This is because of the **Ownership**. +- Conceptually, `Box::new(T)` creates `T` on the stack before moving it to the heap, the compiler (via LLVM) often performs Stack-to-Heap "Move" Optimization in `--release` mode. This avoids a **double move** (Stack to Heap) by constructing the value directly in its final heap location rather than copying it from the stack. The concept is called _"placement new"_ in some languages. + +- Conceptually, `Box::new(String::from("Hello!"))` involves two separate heap allocations/ **double allocation**. Original `"Hello!"` on one heap allocation and `String` structure on second heap allocation. If we use a `Box`, it requires only one heap allocation. + +```rust +use std::mem::size_of_val; +fn main() { + let a = Box::new(String::from("Hello!")); -## What is ownership? + let b: Box = "Hello!".into(); // 💡let b = Box::from("abc"); also works -⭐️ Variable bindings have **ownership** of what they’re bound to. A piece of data can only have **one owner at a time**. When a binding goes out of scope, Rust will free the bound resources. This is how Rust achieves **memory safety**. + println!("Sizes of Box"); + println!("1. Stack size (thin ptr): {} bytes", size_of_val(&a)); // 8 bytes + println!("2. Heap size (String struct): {} bytes", size_of_val(&*a)); // 24 bytes + println!("3. Heap size (actual data): {} bytes", size_of_val(&**a)); // 6 bytes -> [Ownership \(noun\)](https://github.com/nikomatsakis/rust-tutorials-keynote/blob/master/Ownership%20and%20Borrowing.pdf) -> The act, state, or right of possessing something. + println!("\nSizes of Box"); + println!("1. Stack size (fat ptr): {} bytes", size_of_val(&b)); // 16 bytes + println!("2. Heap size (actual data): {} bytes", size_of_val(&*b)); // 6 bytes +} +``` -## Copy types & move types -⭐️ **When assigning** a variable binding to another variable binding **or when passing it to a function**\(Without referencing\), if its data type is a +```rust + STACK HEAP (Alloc 1) HEAP (Alloc 2) +[ Box ptr ] -> [ String Struct ] -> [ "Hello!" ] + (Thin Ptr) (ptr, len, cap) + + STACK HEAP (Alloc 1) +[ Box ptr, len ] -> [ "Hello!" ] + (Fat Ptr) -1. **Copy Type** - * Bound resources are **made a copy and assign** or pass it to the function. - * The ownership state of the original bindings is set to **“copied” state**. - * **Mostly Primitive types** - -2. **Move type** - * Bound resources are **moved** to the new variable binding and we **can not access the original variable binding** anymore. - * The ownership state of the original bindings is set to **“moved” state**. - * **Non-primitive types** +💡 A thin pointer is just a memory address: 8 bytes on 64-bit systems (1 machine word) +💡 String: 24 bytes (3 words: ptr, len, cap) +💡 Actualy data: N bytes on heap +``` -> 🔎 The functionality of a type is handled by the traits which have been implemented to it. By default, variable bindings have ‘move semantics.’ However, if a type implements [**`core::marker::Copy trait`**](https://doc.rust-lang.org/core/marker/trait.Copy.html) , it has a 'copy semantics'. +## 👨‍🏫 Before going to the next... +- A pointer is a memory address that refers to data stored elsewhere in memory (Heap, stack, or in the binary's static memory). + - If the pointer points to a Sized type, only the memory address is stored (`&i32`, `Box`). We call this a thin pointer. + - If the pointer points to an Unsized type (DST/ Dynamically Sized Type), it contains the memory address and some metadata such as a length (`&str`, `&[T]`, `Box`) or a vtable pointer (`Box`). We call this a fat pointer. -💡 **So in the above second example, ownership of the Vec object moves to `b` and `a` doesn’t have any ownership to access the resource.** +- A smart pointer is a type that wraps a pointer and implements the `Deref` and `Drop` traits. This provides additional functionality such as heap management (`Box`), shared ownership (`Rc`, `Arc`), etc. diff --git a/content/en/docs/c2.borrowing.md b/content/en/docs/c2.borrowing.md index b30d1848..d7579ccd 100755 --- a/content/en/docs/c2.borrowing.md +++ b/content/en/docs/c2.borrowing.md @@ -3,120 +3,605 @@ title: Borrowing slug: borrowing --- -In real life applications, most of the times we have to pass variable bindings to other functions or assign them to other variable bindings. In this case, we are **referencing** the original binding; **borrow** the data of it. +## Borrowing -## What is Borrowing? +- In a general sense, borrowing is taking something with the promise of returning it. -> [Borrow \(verb\)](https://github.com/nikomatsakis/rust-tutorials-keynote/blob/master/Ownership%20and%20Borrowing.pdf) -> To receive something with the promise of returning it. +- In Rust, borrowing is the act of creating a reference to a value without copying the data and without taking ownership. + - This is similar to the concept of "passing by reference" in other languages. + - However in Rust, + - References are immutable by default: shared, read-only/ immutable references: `&T` + - To mutate the data, you must explicitly create a mutable reference: `&mut T` -## Shared & Mutable borrowings +- Liveness + - Owned values (`T`) are dropped at the end of their scope (unless moved early). + - References (`&T`, `&mut T`) effectively end/ expire at their last use. This is known as Non-Lexical Lifetimes (NLL). -⭐️ There are two types of Borrowing, + > [!recap] + > `'static` references are valid for the entire duration of the program. -1. **Shared Borrowing** `(&T)` +- While a reference exists, the original owner retains ownership, but its access to the data is restricted (or completely locked) until that reference's last use. + - Shared References: The owner keeps read-access but cannot mutate or move (destroy) the data. + - Mutable References: The owner is completely locked out; they cannot read, write, move, or create new references to the data. - * A piece of data can be **borrowed by a single or multiple users**, but **data should not be altered**. +### A Simple Example -2. **Mutable Borrowing** `(&mut T)` +```rust +fn main() { + let a = 3; + let mut b = 4; + + let c = &a; // 💡 A new shared reference + { + let d = &mut b; // 💡 A new mutable reference + *d += 1; // 💡 Dereference to modify the value at this memory address + } + + println!("{c} {b}"); // 3 5 +} +``` + +### Liveness + +Owned values (`T`) are dropped at the end of their scope, while references (`&T`, `&mut T`) are borrowed only until their last use. + +```rust +fn main() { + let mut a = String::from("Hello"); // 💡 `a` owns the String + let b = &a; // 💡 `b` borrows `a` - * A piece of data can be **borrowed and altered by a single user**, but the data should not be accessible for any other users at that time. + println!("{b}"); // 💡 The borrow ends here (at its last use) -## Rules for borrowings + a.push_str(", world!"); + println!("{a}"); +} // 💡 `a` is dropped here (at the end of its scope) +``` -There are very important rules regarding borrowing, +In Rust, a reference cannot outlive the value it borrows. This kind of reference is called a "dangling reference" which occurs when a program tries to access memory that has already been deallocated. -1. One piece of data can be borrowed **either** as a shared borrow **or** as a mutable borrow **at a given time. But not both at the same time**. +```rust +fn main() { + let a: &f64; + { + let b = 3.14159; + a = &b; + } // 💡 `b` is dropped here -2. Borrowing **applies for both copy types and move types**. + println!("{a}"); // 💡 `a` refers to `b` that no longer exists +} + +// The borrow checker will not let this compile. +// 💥 `b` does not live long enough +``` -3. The concept of **Liveness** ↴ +### Memory Layout + +References are pointers that are either a thin pointer that points to a Sized type or a fat pointer which points to an Unsized type. ```rust +use std::mem::size_of_val; + fn main() { - let mut a = vec![1, 2, 3]; - let b = &mut a; // &mut borrow of `a` starts here - // ⁝ - // some code // ⁝ - // some code // ⁝ -} // &mut borrow of `a` ends here + let a = 3; // 3 stores on the stack + let b = [1, 2, 3, 4, 5]; // [1, 2, 3, 4, 5] store inline on the stack + + let c = &a; // 💡 c is a thin pointer - [ptr] - points to `a` + let d = &b; // 💡 d is a thin pointer - [ptr] - points to `b` + let e = &b[1..3]; // 💡 e is a fat pointer - [ptr, len] - points to 2nd element of `b` and length is 2 + + println!("Size of actual data: a: {} bytes, b: {} bytes", + size_of_val(&a), size_of_val(&b) // 4, 20 + ); + + println!("Size of the references: c: {} bytes, d: {} bytes, e: {} bytes", + size_of_val(&c), size_of_val(&d), size_of_val(&e) // 8 , 8, 16 on 64-bit machines + ); +} +// `d`: A reference to a fixed-size array, &[i32;5]. The compiler knows the length from the type. +// `e`: A reference to a slice, &[i32]. Since the length is not part of the type, +// it stores [address, length] at runtime, making it a fat pointer. +``` + +| Variable Name | Type | Location | Size | +|-----------------------------------------------------------------------|------------|----------|----------| +| `a` | `i32` | Stack | 4 bytes | +| `b` | `[i32;5]` | Stack | 20 bytes | +| `c` (Stores the full memory address to `a`) | `&i32` | Stack | 8 bytes | +| `d` (Stores the full memory address to `b`) | `&[i32;5]` | Stack | 8 bytes | +| `e` (Stores the full memory address to 2nd element of `b` and length) | `&[i32]` | Stack | 16 bytes | + +```rust +use std::mem::size_of_val; fn main() { - let mut a = vec![1, 2, 3]; - let b = &mut a; // &mut borrow of `a` starts here - // some code + // 💡 String stores [ptr, cap, len] on the stack, which points to "Hello" on the heap + let a = String::from("Hello"); + + // 💡 Vec stores [ptr, cap, len] on the stack, which points to the array of &str on the heap + let b = vec!["A", "B", "C", "D", "E"]; + + let c = &a; // 💡 c is a thin pointer - [ptr] - points to the String struct `a` on the stack + let d = &b; // 💡 d is a thin pointer - [ptr] - points to the Vec struct `b` on the stack + let e = &b[1..3]; // 💡 e is a fat pointer - [ptr, len] - points to the 2nd element in the heap buffer + + println!("\nSize of stack metadata: a: {} bytes, b: {} bytes", + size_of_val(&a), size_of_val(&b) // 24, 24 + ); + + println!("Size of the references: c: {} bytes, d: {} bytes, e: {} bytes", + size_of_val(&c), size_of_val(&d), size_of_val(&e) // 8, 8, 16 on 64-bit machines + ); +} +``` + +## Shared Borrowing - println!("{:?}", a); // trying to access `a` as a shared borrow, so giving an error -} // &mut borrow of `a` ends here +Shared borrowing lets us read the same data from multiple places at once without allowing it to be modified. This is done by the compiler implicitly in some occasions to make the syntax more concise and ergonomic, or by creating a shared reference explicitly with the `&` operator. +### Implicit Shared Borrowing + +Formatting macros like `print!()`, `println!()`, and `format!()` automatically take a shared reference to the data. + +```rust +#[derive(Debug)] +struct Person { name: String } fn main() { - let mut a = vec![1, 2, 3]; - { - let b = &mut a; // &mut borrow of `a` starts here - // any other code - } // &mut borrow of `a` ends here + let a = Person { name: "Steve".to_string() }; - println!("{:?}", a); // allow borrowing `a` as a shared borrow + println!("{:?}", a); // 💡 An implicit borrowing + println!("{}", a.name); // 💡 An implicit borrowing + let greet = format!("Hello, {}!", a.name); // 💡 An implicit borrowing + println!("{greet}"); } ``` -💡 Let’s see how to use shared and mutable borrowings in examples. +> [!warning] +> But there is no implicit borrowing in the `dbg!` macro. Use a reference with it: `dbg!(&a);` +> ```rust +> #![allow(unused)] +> +> #[derive(Debug)] +> struct Person { name: String } +> +> #[derive(Debug, Clone, Copy)] +> struct Point { x: i32, y: i32 } +> +> fn main() { +> let a = Person { name: "Steve".to_string() }; +> dbg!(a); // 💡 Type without Copy marker. `a` moves into `dbg!` +> +> let b = Point { x: 0, y: 1 }; +> dbg!(b); // 💡 Type with Copy typed members + Copy marker. A bitwise copy of `b` moves into dbg! +> } +> ``` + +Method calls that require a shared reference (`&self`) on an owned value, the compiler automatically immutably borrows the value. + +```rust +struct Person { name: String } + +impl Person { + fn greet(&self) { + println!("Hello, {}!", self.name) + } +} -### Examples for Shared Borrowing +fn main() { + let a = Person { name: "Steve".to_string() }; + a.greet(); // 💡 Compiler effectively converts to `Person::greet(&a)` +} +``` + +If a closure only reads a variable from the surrounding scope, it borrows it implicitly. ```rust fn main() { - let a = [1, 2, 3]; - let b = &a; - println!("{:?} {}", a, b[0]); // [1, 2, 3] 1 + let name = "Steve".to_string(); + let greet = || println!("Hello, {name}!"); // 💡 An implicit borrowing + + greet(); + println!("{name}"); +} + +// 💯 Using the `move` keyword forces the closure to take ownership: `let greet = move || println!("Hello, {name}!");` +``` + +Accessing an element from a slice or collection like `Vec` or `HashMap` for read-only access involves the [`Index` trait](https://doc.rust-lang.org/std/ops/trait.Index.html). The compiler implicitly converts `a[0]` into `*a.index(0)`, which involves an implicite shared borrow of the collection. + +```rust +// Two concepts in the example: 1. Implicite shared reference; 2. Assigning Copy types/ non-Copy types to a new variable. + +fn main() { + let a = vec![1, 2, 3]; // 💡 contains Copy types + let b = vec!["Apple".to_string(), "Pear".to_string()]; // 💡 contains non-Copy/ move types + + let c = a[0]; // a[0] access the element via a shared reference + Copy the element into `c: i32` + // b[0] access the element via a shared reference + manually call `.clone()` as non-Copy types move/ destroy the original data + let d = b[0].clone(); + + println!("{c} {d}"); +} + +// 👨‍🏫 Try remove `.clone()`. The borrow checker will not let it compile. +// 💥 cannot move out of index of `Vec` +``` + +### Shared References with `&` + +The `&` operator creates a new shared reference to the data. + +```rust +fn main() { + let a = String::from("Hello"); + + let b = &a; // 💡 A new shared reference + borrow_string(b); + + borrow_string(&a); // 💡 A new shared reference +} + +fn borrow_string(s: &String) { + println!("{s}"); +} + +// 💯 It's idiomatic to use &str instead of &String on the function signature. We will discuss this under Deref Coercion. +``` + +A field behind a shared reference cannot move into a new variable, as the original data must not be mutated or destroyed while borrowed. + +```rust +struct Person { name: String, age: f32 } + +fn main() { + let steve = Person { name: "Steve".to_owned(), age: 56.0 }; + + borrow_string(&steve.name); // 💡 A new shared reference + borrow_struct(&steve); // 💡 A new shared reference + borrow_struct_to_assign(&steve); // 💡 A new shared reference } +fn borrow_string(s: &String) { + println!("{s}"); +} + +fn borrow_struct(p: &Person) { + println!("{} {}", p.name, p.age); +} + +fn borrow_struct_to_assign(p: &Person) { + // 💡 Because `p.name` is a non-Copy type behind a shared reference, Rust disallows moving/ detroying the original data + let (a, b) = (p.name.clone(), p.age); + println!("{a} {b}"); +} + +// ⭐️ Each reference is scoped to the function call. +// 👨‍🏫 Try remove `.clone()`. The borrow checker will not let it compile. +// 💥 cannot move out of `p.name` which is behind a shared reference +``` +Iterators created with `iter()` are used to iterate over a shared reference of the collection, without moving it into the loop. + +```rust fn main() { let a = vec![1, 2, 3]; - let b = get_first_element(&a); - println!("{:?} {}", a, b); // [1, 2, 3] 1 + for x in &a { // 💡 A new shared reference + println!("{x}"); + } + + println!("{}", a.len()); // 💡 We can still access `a` after the loop as we use a reference in the loop +} + +// 💯 `for x in a` uses `into_iter()` and `for x in &mut a` uses `iter_mut()` +``` + +## Mutable Borrowing + +Mutable borrowing lets us modify the same data from one place at a time without allowing other references to access it. This is done by the compiler implicitly in some cases the syntax more concise and ergonomic, or by creating a mutable reference explicitly with the `&mut` operator. + +### Implicit Mutable Borrowing + +Method calls that require a mutable reference (`&mut self`) on an owned value, the compiler automatically mutably borrows the value. + +```rust +struct Person { name: String } + +impl Person { + fn update_name(&mut self, new_name: String) { + self.name = new_name; + } +} + +fn main() { + let mut a = Person { name: "Steve".to_string() }; + a.update_name("Jobs".to_string()); // 💡 Compiler effectively converts to `Person::update_name(&mut a, "Jobs".to_string());` + + println!("{}", a.name); } +``` + +If a closure modifies a variable from the surrounding scope, it implicitly borrows it mutably. + +```rust +fn main() { + let mut count = 0; + let mut increment = || { count += 1 }; // 💡 An implicit mutable borrowing + + increment(); + increment(); + + println!("{count}"); // 2 +} +``` + +Accessing an element from a slice or collection like `Vec` for mutable access involves the [`IndexMut` trait](https://doc.rust-lang.org/std/ops/trait.IndexMut.html). The compiler implicitly converts `a[0]` into `*a.index_mut(0)`, which involves an implicit mutable borrow of the collection. + +```rust +fn main() { + let mut a = vec![1, 2, 3]; + a[0] = 10; // 💡 Compiler effectively converts to `*a.index_mut(0) = 10;` -fn get_first_element(a: &Vec) -> i32 { - a[0] + println!("{:?}", a); } ``` -### Examples for Mutable Borrowing +### Mutable References with `&mut` + +The `&mut` operator creates a new mutable reference to the data. When passing a `&mut` to a function, the reference is scoped to that function call. ```rust fn main() { - let mut a = [1, 2, 3]; - let b = &mut a; - b[0] = 4; - println!("{:?}", b); // [4, 2, 3] + let mut a = String::from("AAA"); + + println!("{a}"); + borrow_mut_string(&mut a); // 💡 A mutable reference; AAA-BBB + borrow_mut_string(&mut a); // 💡 A mutable reference; AAA-BBB-BBB + + let b = &mut a; // 💡 A mutable reference; AAA-BBB-BBB-CCC while printing + b.push_str("-CCC"); + println!("{a}"); + + borrow_mut_string(&mut a); // 💡 A mutable reference; AAA-BBB-BBB-CCC-BBB } +fn borrow_mut_string(a: &mut String) { + a.push_str("-BBB"); + println!("{a}"); +} +// Eventhough `let b = &mut a;` creates a new mutable reference, its last use only till the next line. +// So, the sahred reference creation via `println!()` is still possible. +``` + +Shared references are Copy types while mutable references are non-Copy/ move types. However, when a mutable reference is passed to a function or assigns it to a new variable with an explicit type, Rust doesn't move the original reference. Instead, Rust creates a new temporary reference to the same data with a shorter lifetime. This is called "reborrowing" as it's a "borrow from a borrow". While the reborrow is active, the original mutable reference cannot be used. + +```rust fn main() { - let mut a = [1, 2, 3]; + let mut a = 128; + let b = &mut a; // 💡 First mutable reference + { - let b = &mut a; - b[0] = 4; + let c: &mut i32 = b; // 💡 Reborrowing; A temporary mutable reference + *c += 1; + + // println!("{b}"); // ⭐️ cannot borrow `b` as immutable because it is also borrowed as mutable for `c` + println!("{c}"); + } + + *b += 1; + println!("{a}"); +} +``` + +## Reference Conversion with `AsRef` and `AsMut` + +`AsRef` is used to get a shared reference from a value explicitly. It makes functions flexible by letting them accept different types without manual conversion. + +```rust +struct Person { name: String } + +impl From<&str> for Person { + fn from(name: &str) -> Self { + Person { name: name.to_string() } } +} - println!("{:?}", a); // [4, 2, 3] +impl AsRef for Person { + fn as_ref(&self) -> &str { + &self.name + } } +fn greet(name: impl AsRef) { + println!("Hello, {}!", name.as_ref()); +} fn main() { - let mut a = vec![1, 2, 3]; - let b = change_and_get_first_element(&mut a); + let a = Person::from("Steve"); + let b = String::from("Jobs"); + let c = "Bill"; + + greet(a); // Hello, Steve! + greet(b); // Hello, Jobs! + greet(c); // Hello, Bill! +} +``` + +`AsMut` is used to get a mutable reference from a value explicitly. It makes functions flexible by letting them accept different types that can be modified without manual conversion. + +```rust +struct Person { name: String } + +impl AsMut for Person { + fn as_mut(&mut self) -> &mut str { + &mut self.name // 💡 `&mut String` is coerced to `&mut str` + } +} + +fn make_uppercase(mut text: impl AsMut) { + text.as_mut().make_ascii_uppercase(); +} + +fn main() { + let mut a = Person { name: String::from("Steve") }; + let mut b = String::from("Bill"); + + make_uppercase(&mut a); + make_uppercase(&mut b); - println!("{:?} {}", a, b); // [4, 2, 3] 4 + println!("{}", a.name); // STEVE + println!("{b}"); // BILL } +``` + +## Dereferencing + +Dereferencing lets us access the value behind a reference or smart pointer. The `*` operator does this explicitly, while `Deref`/ `DerefMut` traits let Rust do it automatically in some cases. + +### Explicit Dereferencing With `*` Operator + +The unary `*` operator is used to get the value from a reference or smart pointer. + +```rust +fn main() { + let a = 3; + let b = &a; + let c = *b; // 💡 Dereferencing + + println!("{a} {b} {c}"); // 3 3 3 +} + +// `a` has the ownership of 3. `b` is a reference to the data of `a`. +// `c` is a separate variable/ separate ownership, created by copying the data of `a`. +// ⭐️ `let c = *b;` is similar to `let c = a;`, we just use dereferenced reference to get the data. +// 💯 `let c = *b;` is only possible when `a` is a Copy type. If `a` is not Copy type, we will get a compiler error. +``` + +### `Deref` & `DerefMut` + +`Deref` lets a type behave like a reference. This allows Rust to automatically convert it to another reference type in some situations. `DerefMut` does the same for mutable references, allowing Rust to automatically dereference a type in contexts when needed. + +```rust +use std::convert::From; +use std::ops::Deref; + +struct Person { name: String } + +impl From<&str> for Person { + fn from(name: &str) -> Self { + Person { name: name.to_string() } + } +} + +// 💡 `Person` implements `Deref` +impl Deref for Person { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.name + } +} + +fn greet(name: &str) { + println!("Hello, {name}!"); +} + +fn main() { + let a = Person::from("Steve"); -fn change_and_get_first_element(a: &mut Vec) -> i32 { - a[0] = 4; - a[0] + // 💡 Person dereferences to String + println!("{}", a.to_uppercase()); // STEVE + + // `Deref` Coercion + greet(&a); // Hello, Steve! } ``` + +### `Deref` Coercion + +`Deref` coercion is Rust’s automatic conversion from one reference type to another when a type implements `Deref` or `DerefMut` traits. + +1. From `&T` to `&U` when `T: Deref` + + When a type implements the `Deref` trait, Rust can implicitly call `deref()` to convert a shared reference of that type into a reference of another type to match a function's signature. This makes it flexible to accept multiple reference types without manual conversion. + + ```rust + fn main() { + // Deref Coercion: &String -> &str + let a = String::from("Hello"); + borrow_str(&a); + + // Collection Coercion: &Vec -> &[i32] + let b = vec![1, 2, 3]; + borrow_slice(&b); + + // Chained Deref Coercion: &Box -> &String -> &str + let c = Box::new(String::from("Hello")); + borrow_str(&c); + } + + // 💡 This accepts &str, &String and &Box + fn borrow_str(s: &str) { + println!("{s}"); + } + + // 💡 This accepts &[i32], &Vec + fn borrow_slice(slice: &[i32]) { + println!("{:?}", slice); + } + ``` + +2. From `&mut T` to `&U` when `T: Deref` + + When passing a mutable reference (`&mut`) to a function that only expects a shared (immutable) reference (`&`), Rust automatically downgrades the mutable reference to a shared reference. + + ```rust + fn main() { + let mut a = String::from("Hello"); + borrow_str(&mut a); // 💡 Implicitly coerces the mutable reference into a shared one + } + + fn borrow_str(s: &str) { + println!("{s}"); + } + ``` + +3. From `&mut T` to `&mut U` when `T: DerefMut` + + When a type implements the `DerefMut` trait, Rust can implicitly call `deref_mut()` to convert a mutable reference of that type into a reference of another type to match a function's signature. + + ```rust + fn main() { + // Mutable Deref Coercion: &mut String -> &mut str + let mut a = String::from("Hello"); + borrow_mut_str(&mut a); + + // Collection Coercion: &mut Vec -> &mut [T] + let mut b = vec![1, 2, 3]; + borrow_mut_slice(&mut b); + + // Chained Mutable Deref Coercion: &mut Box -> &mut String -> &mut str + let mut c = Box::new(String::from("Hello")); + borrow_mut_str(&mut c); + + println!("{a}, {b:?}, {c}"); + } + + // 💡 This accepts &mut str, &mut String, &mut Box + fn borrow_mut_str(s: &mut str) { + s.make_ascii_uppercase(); + } + + // 💡 This accepts &mut [i32], &mut Vec + fn borrow_mut_slice(slice: &mut [i32]) { + if !slice.is_empty() { + slice[0] += 1; + } + } + ``` + +## 👨‍🏫 Before going to the next... + +- 💯 Check shared ownership, `Rc`, `Arc`, `Cell`, `RefCell` types and interior mutability. diff --git a/content/en/docs/c3.lifetimes.md b/content/en/docs/c3.lifetimes.md index dfdacd51..6adbd94d 100755 --- a/content/en/docs/c3.lifetimes.md +++ b/content/en/docs/c3.lifetimes.md @@ -3,250 +3,354 @@ title: Lifetimes slug: lifetimes --- -When we are dealing with references, we have to make sure that the referencing data stay alive until we stop using the references. +> [!recap] +> - Ownership: Each piece of data has a single owner, and data is only scoped to its owner (unless it is borrowed). +> - In Rust, borrowing is the act of creating a reference to a value without copying the data and without taking ownership (move). While a reference exists, the original owner retains ownership, but its access to the data is restricted (or completely locked if `&mut`) until that reference’s last use. +> - Liveness: +> - Owned values (`T`) are dropped at the end of their scope (unless moved early). +> - References (`&T`, `&mut T`) effectively end/ expire at their last use. This is known as Non-Lexical Lifetimes (NLL). +> - Lifetimes are Rust's way of guaranteeing that a reference is valid within a discrete region of code at compile time. -Think, +## Lifetimes -* We have a **variable binding**, `a`. -* We are **referencing** the value of `a`, **from another variable binding** `x`. - We have to make sure that **`a` lives until we stop using `x`**. +- A lifetime is a construct the Rust compiler's borrow checker uses to track how long references remain valid and every reference in Rust has a lifetime. +- However, most local lifetimes are implicitly figured out by the compiler through a mechanism called lifetime elision. +- The primary purpose of lifetimes is to prevent dangling references, ensuring data is never dropped while a pointer still looks at it. -> 🔎 **Memory management** is a form of resource management applied to computer memory. Up until the mid-1990s, the majority of programming languages used **Manual Memory Management** which **requires the programmer to give manual instructions** to identify and deallocate unused objects/ garbage. Around 1959 John McCarthy invented **Garbage collection**\(GC\), a form of **Automatic Memory Management**\(AMM\). It determines what memory is no longer used and frees it automatically instead of relying on the programmer. However **Objective-C and Swift** provide similar functionality through **Automatic Reference Counting**\(ARC\). +Annotating a lifetime does not extend the lifespan of an actual value. It is simply a contract that tells the compiler, "The lifespan of this reference is guaranteed to be tied to the lifespan of this data." +Lifetimes are declared with a single leading apostrophe `'`. By convention, lowercase letters are used starting from `'a`, moving alphabetically if multiple lifetimes are required. `'static` is a special lifetime that indicating the reference can live for the entire duration of the program execution. -## What is Lifetime? - -In Rust, - -* A resource can only have **one owner** at a time. When it goes **out of the scope**, Rust removes it from the Memory. -* When we want to reuse the same resource, we are **referencing** it/ **borrowing** its content. -* When dealing with **references**, we have to specify **lifetime annotations** to provide instructions for the **compiler** to set **how long** those referenced resources **should be alive**. -* ⭐ But because of lifetime annotations make the **code more verbose**, in order to make **common patterns** more ergonomic, Rust allows lifetimes to be **elided/omitted** in `fn` definitions. In this case, the compiler assigns lifetime annotations **implicitly**. +```rust +fn main() { + let a = "A"; -Lifetime annotations are **checked at compile-time**. Compiler checks when a data is used for the first and the last times. According to that, Rust manages memory in **run time**. This is the major reason for **slower compilation times** in Rust. + take_str(a); + take_str_a(a); + take_str_static(a); +} -> * Unlike C and C++, **usually**, Rust doesn’t explicitly drop values at all. -> * Unlike GC, Rust doesn’t place deallocation calls where the data is no longer referenced. -> * Rust places deallocation calls where the data is about to go out of the scope and then enforces that no references to that resource exist after that point. +fn take_str(s: &str) { + println!("{s}"); +} +fn take_str_a<'a>(s: &'a str) { + println!("{s}"); +} -## Usage +fn take_str_static(s: &'static str) { + println!("{s}"); +} +``` -Lifetimes are denoted with an apostrophe. By convention, a lowercase letter is used for naming. Usually **starts with** `'a` and **follows alphabetic order** when we need to add **multiple lifetime** annotations. +## Elisions -When using references, +Prior to Rust 1.0, around 2014, lifetime elision did not exist. We had to explicitly type out `'a` and `'b` annotations for every single reference, even in the simplest functions. The Rust community introduced lifetime elisions to automatically infer common patterns, mainly in function signatures from Rust 1.0. However, at that time, lifetimes remained rigidly tied to the lexical scope (reference lasted until the closing curly brace) of the code block. Non-Lexical Lifetimes(NLL) were introduced with Rust 2018, allowing references to expire as soon as they are last used instead of end of lexical scope. In Rust 2024, the language expanded support for Return Position Impl Trait (RPIT) functions (such as `fn foo() -> impl Trait`) which we had to explicitly mentioned in their bounds (`+ 'a` or `+ '_`). To prevent over-capturing Rust also introduced precise capturing syntax (`+ use<>`). -### 01. On Function Declaration +> [!tip] [Elision Rules](https://doc.rust-lang.org/nomicon/lifetime-elision.html) +> +> - Each elided lifetime in input position becomes a distinct lifetime parameter. +> - If there is exactly one input lifetime position (elided or not), that lifetime is assigned to all elided output lifetimes. +> - If there are multiple input lifetime positions, but one of them is `&self` or `&mut self`, the lifetime of `self` is assigned to all elided output lifetimes. +> - Otherwise, it is an error to elide an output lifetime. -* Input and output parameters with references should attach lifetimes after the `&` sign. - ex. `..(x: &'a str)` , `..(x: &'a mut str)` +These rules are still valid. But now, lifetime elision has expanded beyond function signatures. So, let's go through examples to identify where we can elide lifetimes and where we must explicitly add them. -* After the function name, we should mention that the given lifetimes are generic types. - ex. `fn foo<'a>(..)` , `fn foo<'a, 'b>(..)` -```rust -// No inputs, return a reference -fn function<'a>() -> &'a str {} +## On Function Declarations -// Single input -fn function<'a>(x: &'a str) {} +> [!tip] +> 1. Add lifetimes after the `&` sign to input and/or output references. +> - `(x: &str)` → `(x: &'a str)`, `(x: &mut str)` → `(x: &'a mut str)` +> 2. After the function name, mention the lifetimes like generic types, unless `'static`. +> - `fn foo()` → `fn foo<'a>()` or `fn foo<'a, 'b>()` -// Single input and output, both have the same lifetime -// The output should live at least as long as input exists -fn function<'a>(x: &'a str) -> &'a str {} +### Single Input -// Multiple inputs, only one input and the output share same lifetime -// The output should live at least as long as y exists -fn function<'a>(x: i32, y: &'a str) -> &'a str {} +```rust +fn main() { + let a = "A"; + take_str(a); +} -// Multiple inputs, both inputs and the output share same lifetime -// The output should live at least as long as x and y exist -fn function<'a>(x: &'a str, y: &'a str) -> &'a str {} +fn take_str(s: &str) { + println!("{s}"); +} -// Multiple inputs, inputs can have different lifetimes 🔎 -// The output should live at least as long as x exists -fn function<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {} +// take_str<'a>(s: &'a str){} +// take_str(s: &'static str){} ``` -### 02. On Struct or Enum Declaration +### Single Output -* Elements with references should attach lifetimes after the `&` sign. -* After the name of the struct or enum, we should mention that the given lifetimes are generic types. +⭐️ MUST ANNOTATE ```rust -// Single element -// Data of x should live at least as long as Struct exists -struct Struct<'a> { - x: &'a str -} +fn main() { + let a = return_str(); + let b = return_str_static(); -// Multiple elements -// Data of x and y should live at least as long as Struct exists -struct Struct<'a> { - x: &'a str, - y: &'a str + println!("{a} {b}"); } +fn return_str<'a>() -> &'a str { + "A" +} -// Variant with a single element -// Data of the variant should live at least as long as Enum exists -enum Enum<'a> { - Variant(&'a Type) +fn return_str_static() -> &'static str { + "B" } ``` -### 03. With Impls and Traits +### Single Input-Output ```rust -struct Struct<'a> { - x: &'a str -} - impl<'a> Struct<'a> { - fn function<'a>(&self) -> &'a str { - self.x - } - } - +fn main() { + let a = "A"; + let b = take_and_return_str(a); -struct Struct<'a> { - x: &'a str, - y: &'a str + println!("{a} {b}"); } - impl<'a> Struct<'a> { - fn new(x: &'a str, y: &'a str) -> Struct<'a> { // No need to specify <'a> after new; impl already has it - Struct { - x : x, - y : y - } - } - } +fn take_and_return_str(s: &str) -> &str { + println!("{s}"); + "B" +} -// 🔎 -impl<'a> Trait<'a> for Type -impl<'a> Trait for Type<'a> +// take_and_return_str<'a>(s: &'a str) -> &'a str{} +// take_and_return_str(s: &'static str) -> &'static str{} ``` -### 04. With Generic Types +### Multiple Input ```rust -// 🔎 -fn function(f: F) where for<'a> F: FnOnce(&'a Type) -struct Struct where for<'a> F: FnOnce(&'a Type) { x: F } -enum Enum where for<'a> F: FnOnce(&'a Type) { Variant(F) } -impl Struct where for<'a> F: FnOnce(&'a Type) { fn x(&self) -> &F { &self.x } } +fn main() { + let (a, b) = ("A", "B"); + take_strs(a, b); +} + +fn take_strs(a: &str, b: &str) { + println!("{a} {b}"); +} + +// take_strs<'a>(a: &'a str, b: &'a str) {} 💡both input share same lifetime +// take_strs<'a, 'b>(a: &'a str, b: &'b str){} 💡each input having different lifetimes ``` +### Multiple Input - Single Output -## Lifetime Elision +⭐️ MUST ANNOTATE -As I mentioned earlier, in order to make **common patterns** more ergonomic, Rust allows lifetimes to be **elided/omitted**. This process is called **Lifetime Elision**. +```rust +fn main() { + let (a, b) = ("A", "B"); + let c = take_strs_return_str(a, b); + println!("{c}") +} -💡 For the moment Rust supports Lifetime Elisions only on `fn` definitions. But in the future, it will support for `impl` headers as well. +fn take_strs_return_str<'a>(a: &'a str, b: &'a str) -> &'a str { // 💡both input, output share same lifetime + println!("{a} {b}"); + b +} +// OR take_strs_return_str<'a, 'b>(a: &'a str, b: &'b str) -> &'b str{} // 💡each input having different lifetimes +``` -Lifetime annotations of `fn` definitions can be elided -if its **parameter list** has either, +### Multiple Input-Output -* **only one input parameter passes by reference**. -* a parameter with **either** `&self` **or** **&mut self** reference. +⭐️ MUST ANNOTATE ```rust -fn triple(x: &u64) -> u64 { // Only one input parameter passes by reference - x * 3 +fn main() { + let (a, b) = ("A", "B"); + let (c, d) = take_strs_return_strs(a, b); + println!("{c} {d}") } +fn take_strs_return_strs<'a>(a: &'a str, b: &'a str) -> (&'a str, &'a str) { + println!("{a} {b}"); + (b, a) +} +// OR take_strs_return_strs<'a, 'b>(a: &'a str, b: &'b str) -> (&'b str, &'a str){} +``` + +## On Struct, Enum Definitions + +⭐️ MUST ANNOTATE -fn filter(x: u8, y: &str) -> &str { // Only one input parameter passes by reference - if x > 5 { y } else { "invalid inputs" } +> [!tip] +> 1. Add lifetimes after the `&` sign to element references. +> - `x: &str` → `x: &'a str`, `x: &mut str` → `x: &'a mut str` +> 2. After the name of the struct or enum, mention the lifetimes like generic types, unless `'static`. +> - `struct Person` → `struct Person<'a>` or `struct Person<'a, 'b>` +> - `enum Team` → `enum Team<'a>` or `enum Team<'a, 'b>` +> 3. In the `impl` block, +> - Either anonymous lifetimes: `impl Person` → `impl Person<'_>` or `impl Person<'_, '_>` , when methods inside the impl block only take `&self` or `&mut self` and return types that don't need to match the struct's inner lifetime. +> - Or named lifetimes: `impl Person` → `impl<'a> Person<'a>` or `impl<'a, 'b> Person<'a, 'b>` + +### Single Lifetime + +```rust +fn main() { + let steve = Person::new("Steve", "Jobs"); + steve.intro(); } +struct Person<'a> { + fname: &'a str, + lname: &'a str, +} -struct Player<'a> { - id: u8, - name: &'a str +impl<'a> Person<'a> { + fn new(fname: &'a str, lname: &'a str) -> Self { + Self { fname, lname } + } } - impl<'a> Player<'a> { // So far Lifetime Elisions are allowed only on fn definitions. But in the future, they might support on impl headers as well. - fn new(id: u8, name: &str) -> Player { // Only one input parameter passes by reference - Player { - id : id, - name : name - } - } - fn heading_text(&self) -> String { // An fn definition with &self (or &mut self) reference - format!("{}: {}", self.id, self.name) - } +impl Person<'_> { + fn intro(&self) { + println!("Hello! I am {} {}.", self.fname, self.lname) } +} +// 💡 fn intro(&self) {} can be moved to first impl block too +``` + +### Static Lifetime +```rust fn main() { - let player1 = Player::new(1, "Serena Williams"); - let player1_heading_text = player1.heading_text() - println!("{}", player1_heading_text); + let steve = Person::new("Steve", "Jobs"); + steve.intro(); } -``` -> 💡 In the Lifetime Elision process of fn definitions, -> -> * Each parameter passed by reference has got a distinct lifetime annotation. -> ex. `..(x: &str, y: &str)` → `..<'a, 'b>(x: &'a str, y: &'b str)` -> * If the parameter list only has one parameter passed by reference, that lifetime is assigned to all elided lifetimes in the return values of that function. -> ex. `..(x: i32, y: &str) -> &str` → `..<'a>(x: i32, y: &'a str) -> &'a str` -> * Even if it has multiple parameters passed by reference, if one of them has &self or &mut self, the lifetime of self is assigned to all elided output lifetimes. -> ex. `impl Impl{ fn function(&self, x: &str) -> &str {} }` → -> `impl<'a> Impl<'a>{ fn function(&'a self, x: &'b str) -> &'a str {} }` -> * For all other cases, we have to write lifetime annotations manually. +struct Person { + fname: &'static str, + lname: &'static str, +} +impl Person { + fn new(fname: &'static str, lname: &'static str) -> Self { + Self { fname, lname } + } -## `'static` Annotations + fn intro(&self) { + println!("Hello! I am {} {}.", self.fname, self.lname) + } +} +``` -`'static` lifetime annotation is a **reserved** lifetime annotation. These **references are valid for the entire program**. They are saved in the data segment of the binary and the data referred to will never go out of scope. +### Multiple Lifetimes ```rust -static N: i32 = 5; // A constant with 'static lifetime +fn main() { + let steve = Person::new("Steve", "Jobs"); + steve.intro(); +} -let a = "Hello, world."; // a: &'static str +struct Person<'a, 'b> { + fname: &'a str, + lname: &'b str, +} +impl<'a, 'b> Person<'a, 'b> { + fn new(fname: &'a str, lname: &'b str) -> Self { + Self { fname, lname } + } +} -fn index() -> &'static str { // No need to mention <'static> ; fn index ̶<̶'̶s̶t̶a̶t̶i̶c̶>̶ - "Hello, world!" +impl Person<'_, '_> { + fn intro(&self) { + println!("Hello! I am {} {}.", self.fname, self.lname) + } } +// 💡 fn intro(&self) {} can be moved to first impl block too ``` +## On Trait Implementations -## Few more examples about the usage of Rust lifetimes. +### With Anonymous Lifetime ```rust -fn greeting<'a>() -> &'a str { - "Hi!" +fn main() { + let steve = Person { name: "Steve" }; + + let has_steve = steve.has_name("Steve"); + println!("{has_steve}"); } +struct Person<'a> { + name: &'a str, +} -fn fullname<'a>(fname: &'a str, lname: &'a str) -> String { - format!("{} {}", fname, lname) +trait HasName { + fn has_name(&self, name: &str) -> bool; } +impl HasName for Person<'_> { + fn has_name(&self, name: &str) -> bool { + self.name == name + } +} +``` + +### With Named Lifetimes + +```rust +fn main() { + let steve = Person { name: "Steve" }; + + let longer_name = steve.longer_name("Jobs"); + println!("{longer_name}"); +} struct Person<'a> { - fname: &'a str, - lname: &'a str + name: &'a str, +} + +trait LongerName<'a> { + fn longer_name(&self, name: &'a str) -> &'a str; +} + +impl<'a> LongerName<'a> for Person<'a> { + fn longer_name(&self, name: &'a str) -> &'a str { + if self.name.len() >= name.len() { + self.name + } else { + name + } + } } - impl<'a> Person<'a> { - fn new(fname: &'a str, lname: &'a str) -> Person<'a> { // No need to specify <'a> after new; impl already has it - Person { - fname : fname, - lname : lname - } - } +// Person.name and the longer_name method input/return values use the same lifetime identifier +``` - fn fullname(&self) -> String { - format!("{} {}", self.fname , self.lname) - } - } +### With Separate Named Lifetimes +```rust fn main() { - let player = Person::new("Serena", "Williams"); - let player_fullname = player.fullname(); + let steve = Person { name: "Steve" }; + + let longer_name = steve.longer_name("Jobs"); + println!("{longer_name}"); +} - println!("Player: {}", player_fullname); +struct Person<'p> { + name: &'p str, +} + +trait LongerName<'n> { + fn longer_name(&self, name: &'n str) -> &'n str; +} + +impl<'p, 'n> LongerName<'n> for Person<'p> +where + 'p: 'n, +{ + fn longer_name(&self, name: &'n str) -> &'n str { + if self.name.len() >= name.len() { + self.name + } else { + name + } + } } +// impl<'p: 'n, 'n> LongerName<'n> for Person<'p> {} ``` diff --git a/content/en/docs/d1.code-organization.md b/content/en/docs/d1.code-organization.md deleted file mode 100644 index cd946418..00000000 --- a/content/en/docs/d1.code-organization.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Code Organization -slug: code-organization ---- - -When a single code block is getting larger, it should be decomposed into smaller pieces and should be organized in a proper manner. Rust supports different levels of code organization. - -## 1. Functions - -## 2. Modules - -Can be mapped to a, - - **Inline module** - - **File** - - **Directory hierarchy** - -## 3. Crates - -Can be mapped to a, - - **lib.rs file on the same executable crate** - - **Dependency crate specified on Cargo.toml** - - Can be specified from, - - **Path** - - **Git repository** - - **crates.io** - -## 4. Workspaces - -Helps to manage multiple crates as a single project. - - -Let’s discuss one by one. - -> 💡 To make examples more simpler, we use a simple function which prints `“Hello, world!”`. But regarding writing testable codes, always try to return the `String` from the function and print it when calling it, instead of printing the `String` inside the function. \ No newline at end of file diff --git a/content/en/docs/d1.modules.md b/content/en/docs/d1.modules.md new file mode 100644 index 00000000..787c30ff --- /dev/null +++ b/content/en/docs/d1.modules.md @@ -0,0 +1,494 @@ +--- +title: Modules +slug: modules +--- + +## Rust Module Tree + +- The Rust module tree is the hierarchical representation of all modules in a crate. +- It defines the namespace structure and governs how items are referenced via paths at compile time. +- Unlike some languages like Go and JavaScript, Rust's module tree is not purely based on the file system. + - The root of the module tree begins with either `main.rs` (in a binary crate) or `lib.rs` (in a library crate). + + > [!tip] + > When a Rust package contains both `main.rs` and `lib.rs`, Cargo builds two separate crates with isolated module trees: a library crate (`lib.rs`) and a binary crate (`main.rs`). + > - In an optimized incremental build, `lib.rs` is compiled first. + > - Then, `main.rs` links to it as an external dependency. + + - A file or folder becomes part of the module tree only if it is explicitly declared with the `mod` keyword in its parent module. + - A module can be defined in one of two ways: + 1. File module → `mod foo;` (resolved to foo.rs or foo/mod.rs) + 2. Inline module → `mod foo { ... }` (defined directly inside a file) + +```shell {title="Folder Structure"} +my_app +├── Cargo.toml (package name my_app) +└── src + ├── main.rs (binary crate) + ├── lib.rs (library crate) + ├── foo.rs (module foo, declared in lib.rs with `pub mod foo;`) + │ (may contain inline modules via `mod x { ... }`) + │ + ├── bar + │ ├── mod.rs (module bar, declared in lib.rs with `pub mod bar;`) + │ └── file.rs (submodule of bar, declared in bar/mod.rs with `mod file;`) + │ + ├── buzz.rs (module buzz, declared in lib.rs with `pub mod buzz;`) + └── buzz + └── file.rs (submodule of buzz, declared in buzz.rs with `mod file;`) +``` + +Assume, +- `main.rs` depends on `std`, `rand`(3rd party crate) and library crate. +- `lib.rs` contains `pub mod foo;`, `pub mod bar;` and `pub mod buzz;`. +- `foo.rs`, `bar/file.rs`, `buzz/file.rs` contains `pub fn hello_world()` +- `bar/mod.rs` and `buzz.rs` contains `mod file;` and `pub use file::hello_world;` + +The module trees are, + +```rust +lib.rs (library crate) (compiled as crate `my_app`) + crate:: + ├── foo + ├── bar + └── buzz + +main.rs (binary crate) (depends on a compiled crate `my_app`) + crate:: + (binary crate root) + + extern prelude: + ├── std + └── rand + + available crates: + ├── my_app ← library crate from lib.rs +``` + +Cargo automatically passes each dependency listed in `Cargo.toml` to the Rust compiler (`rustc`) using the `--extern` flag. The compiler then makes these crates available through the `extern prelude`. This was introduced in Rust 2018 to make external crates automatically available in every module without requiring `extern crate` declarations or explicit imports at the crate root. + +## Visibility + +### Default Visibility + +> [!tip] +> - Private by default to outer levels. +> - Accessible to same/child levels. +> - Use `pub` keyword to exposes to outer levels. + +- Everything inside a module is private by default to outer modules. +- However, all items are accessible from within the same module or from the nested modules. +- The `pub` keyword is used to make a module or its items public to outer modules. + +To access elements at different levels, use the `::` path operator with these patterns: + +- Current module: direct item name or `self::` +- Parent module: `super::` +- Crate root: `crate::` +- Cross-Crate: `my_app::` (the package name), Ex. accessing the library crate from the binary crate. +- External crates in `Cargo.toml`: The full path with crate name (`uuid::Uuid::new_v4()`) or a `use` declaration (`use uuid::Uuid`) to import the elements to the current level first. + +### Visibility Modifiers + +> [!tip] +> - The `pub` keyword makes an item fully public (exposing to all outer levels). +> - Visibility modifiers restrict access to specific scopes. + +| Modifier | Visible To | +|----------------|--------------------------| +| `pub(self)` | Current module | +| `pub(super)` | Parent module | +| `pub(crate)` | Entire crate | +| `pub(in path)` | Specific ancestor module | + +## Inline Modules + +- Defines with the `mod` keyword. +- Rust supports nested modules. + +### Simple + +```rust +fn main() { + inline::greet(); +} + +mod inline { // 💡 No pub keyword but can be accessible from the same level/ main.rs + pub fn greet() { // 💡 function has to be public to access from outside + println!("Hello, world!"); + } +} +``` + +### Nested + +```rust +fn main() { + inline::nested::greet(); +} + +mod inline { + pub mod nested { // 💡 Use `pub` keyword to access from outside + pub fn greet() { + println!("Hello, world!"); + } + } +} +``` + +### Visibility + +#### Default Visibility + +```rust +fn main() { + inline::greet(); + inline::nested::greet(); + + inline::nested::self_greet(); + inline::nested::super_greet(); + inline::nested::super_greet_private(); + + inline::nested_private_greet(); + + inline2::inline_greet(); +} + +mod inline { + pub fn greet() { + println!("Inline"); + } + + fn greet_private() { // ⭐️ public mod: private fn; can call from same level or child mod level + println!("Inline 🔒"); + } + + // ⭐️ pub mod: pub fn; can call directly from outside + pub mod nested { + pub fn greet() { + println!("Nested"); + } + + pub fn self_greet() { + self::greet(); // 💡 or greet() ; call inline::nested::greet + } + + pub fn super_greet() { + super::greet(); // 💡 call inline::greet + } + + pub fn super_greet_private() { + super::greet_private(); + } + } + + // ⭐️ private mod: pub fn ; can call from parent level + mod nested_private { + pub fn greet() { + println!("Nested 🔑mod"); + } + } + + pub fn nested_private_greet() { + nested_private::greet(); + } +} + +mod inline2 { + use crate::inline; // 💡 or `use super::inline;` reexport inline into the module. + + pub fn inline_greet() { + inline::greet(); // 💡 if not reexport, crate::inline::greet(); or super::inline::greet(); + } +} +``` + +#### Visibility Modifiers + +```rust +fn main() { + inline::nested_pub_super(); + inline::nested_pub_crate(); + inline::nested_pub_in_path(); + + inline::nested::exposed_pub_self(); + inline::nested::exposed_parent_pub_self(); + inline::nested::pub_crate(); +} + +mod inline { + pub mod nested { + // Accessible only within current module or sub modules + pub(self) fn pub_self() { + println!("Nested self"); + } + + // Accessible only from inside or super/ 1st parent + pub(super) fn pub_super() { + println!("Nested super"); + } + + // Accessible anywhere within the current crate + pub(crate) fn pub_crate() { + println!("Nested crate"); + } + + // Accessible only within `crate::inline` and its submodules + pub(in crate::inline) fn pub_in_path() { + println!("Nested in-path"); + } + + // Exposed pub(self), but Clippy triggers a warning "unnecessary `pub(self)`" + pub fn exposed_pub_self() { + pub_self() + } + + // Exposed parent's pub(self), but Clippy triggers a warning "unnecessary `pub(self)`" + pub fn exposed_parent_pub_self() { + super::pub_self() + } + } + + pub(self) fn pub_self() { + println!("Inline Self"); + } + + pub fn nested_pub_super() { + nested::pub_super() + } + + pub fn nested_pub_crate() { + nested::pub_crate() + } + + pub fn nested_pub_in_path() { + nested::pub_in_path() + } +} +``` + +### Test + +When writing tests it’s a good practice to write tests inside a test module because they compile only when running tests. + +```rust +#![allow(unused)] + +fn greet() -> String { + "Hello, world!".to_string() +} + +#[cfg(test)] // Only compiles when running tests +mod tests { + use super::greet; // Import root greet function + + #[test] + fn test_greet() { + assert_eq!("Hello, world!", greet()); + } +} +``` + +## Files In The Same Directory + +```shell {title="Folder Structure"} +├── Cargo.toml (package name my_app) +└── src + ├── main.rs + ├── lib.rs + └── foo.rs +``` + +{{< tabs count=3 group="mod-files-in-same-dir" label="Select File" >}} +{{< tab label="main.rs" >}} +```rust {title="src/main.rs"} +mod foo; + +fn main() { + my_app::greet(); + my_app::inline::greet(); + + foo::greet(); + foo::inline::greet(); +} +``` +{{< /tab >}} +{{< tab label="lib.rs" >}} +```rust {title="src/lib.rs"} +pub fn greet() { + println!("Lib"); +} + +pub mod inline { + pub fn greet() { + println!("Lib:Inline"); + } +} +``` +{{< /tab >}} +{{< tab label="foo.rs" >}} +```rust {title="src/foo.rs"} +pub fn greet() { + println!("Foo"); +} + +pub mod inline { + pub fn greet() { + println!("Foo:Inline"); + } +} +``` +{{< /tab >}} +{{< /tabs >}} + +> [!tip] +> - Accessing the library crate from the binary crate (Cross-Crate), +> - Use the crate name `my_app::` path prefix to access public items defined in `lib.rs`. +> - Local files like `foo.rs`, +> - First, must be added to the module tree using `mod foo;` in `main.rs` (or `lib.rs`). +> - Then access its public items via `foo::` path prefix. + +If we load `foo.rs` via `lib.rs`, we must add `pub mod foo;` to `lib.rs`. + +{{< tabs count=3 group="mod-files-in-same-dir" label="Select File" >}} +{{< tab label="main.rs" >}} +```rust {title="src/main.rs"} +fn main() { + my_app::greet(); + my_app::inline::greet(); + + my_app::foo::greet(); + my_app::foo::inline::greet(); +} +``` +{{< /tab >}} +{{< tab label="lib.rs" >}} +```rust {title="src/lib.rs"} +pub mod foo; + +pub fn greet() { + println!("Lib"); +} + +pub mod inline { + pub fn greet() { + println!("Lib:Inline"); + } +} +``` +{{< /tab >}} +{{< tab label="foo.rs" >}} +```rust {title="src/foo.rs"} +pub fn greet() { + println!("Foo"); +} + +pub mod inline { + pub fn greet() { + println!("Foo:Inline"); + } +} +``` +{{< /tab >}} +{{< /tabs >}} + +## Files In Different Directory + +### With `mod.rs` + +Before Rust 2018: + +- Directory-based modules were conventionally defined using a `mod.rs` file. +- Submodules of the directory were declared within `mod.rs` using `mod` (or `pub mod`) statements. +- Ex. `bar/mod.rs` acts as the entry point to the `bar` directory module. + +```shell {title="Folder Structure"} +my_app +├── Cargo.toml +└── src + ├── main.rs + └── bar + ├── mod.rs + └── qux.rs +``` + +{{< tabs count=3 label="Select File" >}} +{{< tab label="main.rs" >}} +```rust {title="src/main.rs"} +mod bar; + +fn main() { + bar::qux::greet(); + bar::qux::inline::greet(); +} +``` +{{< /tab >}} +{{< tab label="bar/mod.rs" >}} +```rust {title="src/bar/mod.rs"} +pub mod qux; +``` +{{< /tab >}} +{{< tab label="bar/qux.rs" >}} +```rust {title="src/bar/qux.rs"} +pub fn greet() { + println!("Bar:Qux"); +} + +pub mod inline { + pub fn greet() { + println!("Bar:Qux:Inline"); + } +} +``` +{{< /tab >}} +{{< /tabs >}} + +### Without `mod.rs` + +Since Rust 2018: + +- Rust added an alternative approach. +- Instead of a `mod.rs` file, we can use a `.rs` file with the same name as the directory, placed outside that directory. +- Submodules of the directory can be declared within that `.rs` file using `mod` (or `pub mod`) statements. +- Ex. `buzz.rs` acts as the entry point to the `buzz` directory module. + +```shell {title="Folder Structure"} +my_app +├── Cargo.toml +└── src + ├── main.rs + ├── buzz.rs + └── buzz + └── qux.rs +``` + +{{< tabs count=3 label="Select File" >}} +{{< tab label="main.rs" >}} +```rust {title="src/main.rs"} +mod buzz; + +fn main() { + buzz::qux::greet(); + buzz::qux::inline::greet(); +} +``` +{{< /tab >}} +{{< tab label="buzz.rs" >}} +```rust {title="src/buzz.rs"} +pub mod qux; +``` +{{< /tab >}} +{{< tab label="buzz/qux.rs" >}} +```rust {title="src/buzz/qux.rs"} +pub fn greet() { + println!("Buzz:Qux"); +} + +pub mod inline { + pub fn greet() { + println!("Buzz:Qux:Inline"); + } +} +``` +{{< /tab >}} +{{< /tabs >}} diff --git a/content/en/docs/d2.functions.md b/content/en/docs/d2.functions.md deleted file mode 100644 index fc25b496..00000000 --- a/content/en/docs/d2.functions.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Functions (02) -slug: functions-02 ---- - -Functions are the first line of organization in any program. - -```rust -fn main() { - greet(); // Do one thing - ask_location(); // Do another thing -} - -fn greet() { - println!("Hello!"); -} - -fn ask_location() { - println!("Where are you from?"); -} -``` - -We can add unit tests in the same file. - -```rust -fn main() { - greet(); -} - -fn greet() -> String { - "Hello, world!".to_string() -} - -#[test] // Test attribute indicates this is a test function -fn test_greet() { - assert_eq!("Hello, world!", greet()) -} - -// 💡 Always put test functions inside a tests module with #[cfg(test)] attribute. -// cfg(test) module compiles only when running tests. We discuss more about this in the next section. -``` - -> 💭 An [attribute](https://doc.rust-lang.org/reference/attributes.html) is a general, free-form **metadatum** that is interpreted according to name, convention, and language and compiler version. \ No newline at end of file diff --git a/content/en/docs/d2.use.md b/content/en/docs/d2.use.md new file mode 100644 index 00000000..3b198e6b --- /dev/null +++ b/content/en/docs/d2.use.md @@ -0,0 +1,223 @@ +--- +title: Use +slug: use +--- + +- A `use` statement is used to bring items (types, functions, traits, modules, etc.) into the current scope. + - It doesn't import anything; instead, it creates a local name (an alias) in the current scope. +- This allows us: + - to refer to items by shorter names instead of their full paths. + - to re-export items (by publicly exposing items from another module or crate using `pub use`). + +## Path Prefixes + +A `use` statement can start from: + +- The current crate: + - `use crate::` : Absolute path from the current crate. + - `use super::` : Relative path from the parent module. + - `use self::` : Relative path from the current module. Since Rust 2018, this is often optional. +- External crates: + - `use my_app::` : The package name when having both `main.rs`/`lib.rs` (Cross-Crate). (`my_app` is the package name in the root `Cargo.toml`) + - `use uuid::` : Third-party crates. (`uuid` is a crate for UUID generation) +- Rust library crates: + - `use std::` : Rust [standard](https://doc.rust-lang.org/std/index.html) library. + - `use core::` : Rust [core](https://doc.rust-lang.org/core/index.html) library. (contains functionality that does not require an operating system or a heap allocator) + - `use alloc::` : Rust [core allocation and collections](https://doc.rust-lang.org/alloc/index.html) library. (Usually used in `#![no_std]` projects) + + +## Item Selection + +A `use` statement can select items from a path in different ways: + +- `use path::item;` : Bring a single item (type, function, trait, module, etc.) into scope. +- `use path::{item_a, item_b};` : Bring multiple specific items into scope. +- `use path::*;` : Bring all public items from a module into scope. +- `use path::{self, item_a};` : Bring both the module itself and specific items into scope. +- `use path::nested::item;` : Bring an item from a nested path into scope. +- `use path::{nested::item, nested2::item};` : Bring items from different nested modules under a shared base path in a single statement. +- `use path::item as custom_name;` or `use path::{self, item_a, item_b as custom_name};` : Rename items when bringing them into scope. + +## Re-exporting + +- Re-exporting publicly exposes items from another module or crate using `pub use`, allowing us to decouple the public API from internal module structure. +- Re-exporting can be combined with visibility modifiers to control how far items are exposed within a crate. + - `pub use path::item;` : Visible to anyone who can access the crate (fully public). + - `pub(crate) use path::item;` : Visible anywhere inside the current crate. + - `pub(super) use path::item;` : Visible only in the parent module. + - `pub(in crate::path) use path::item;` : Visible only within the specified module path inside the crate. + +## Start From The Current Crate + +```rust +use inline::nested::greet; +use inline2::greet as greet2; +use inline2::nested::greet as greet3; + +fn main() { + greet(); + greet2(); + greet3(); +} + +mod inline { + pub fn greet() { + println!("Inline"); + } + + pub mod nested { + pub fn greet() { + println!("Nested"); + } + } +} + +mod inline2 { + use crate::inline; // Bring `inline` into this module's scope. + + pub fn greet() { + inline::greet(); + } + + pub mod nested { + pub use super::greet; // Re-export `inline2::greet` as `inline2::nested::greet` + } +} +// ⭐️ `use crate::inline;` can move directly inside `inline2::greet()` as well. +``` + +## Start From External crates + +> [!lab] +> ```shell +> cargo new my_app && cd my_app +> touch src/lib.rs src/foo.rs +> cargo add uuid -F v7 +> ``` + +```shell {title="Folder Structure"} +my_app +├── Cargo.toml (package name my_app) +└── src + ├── main.rs + ├── lib.rs + └── foo.rs +``` + +{{< tabs count=3 label="Select File" >}} +{{< tab label="foo.rs" >}} +```rust {title="src/foo.rs"} +use uuid::Uuid; + +pub fn new_id() -> Uuid { + Uuid::now_v7() +} + +#[cfg(test)] +mod tests { + use super::*; + use uuid::Version; + + #[test] + fn test_new_id_is_version_7() { + let id = new_id(); + assert_eq!(id.get_version(), Some(Version::SortRand)); + } +} +``` +{{< /tab >}} +{{< tab label="lib.rs" >}} +```rust {title="src/lib.rs"} +mod foo; +pub use foo::new_id; + +pub struct Person { + pub name: String, +} + +pub trait Greet { + const PREFIX: &'static str; // 💡 Required constant + fn greet(&self) -> String; // 💡 Required method +} + +impl Greet for Person { + const PREFIX: &'static str = "Hello"; + + fn greet(&self) -> String { + format!("{} {}!", Self::PREFIX.to_owned(), self.name) + } +} +``` +{{< /tab >}} +{{< tab label="main.rs" >}} +```rust {title="src/main.rs"} +use my_app::{Greet, Person as Employer, new_id}; + +fn main() { + let steve = Employer { name: "Steve".to_string() }; + let apple = Company { name: "Apple".to_string() }; + + println!("{}", steve.greet()); + println!("{}", apple.greet()); + + print!("{}", new_id()) +} + +struct Company { + name: String, +} + +impl Greet for Company { + const PREFIX: &'static str = "Welcome to"; + + fn greet(&self) -> String { + format!("{} {}!", Self::PREFIX.to_owned(), self.name) + } +} +``` +{{< /tab >}} +{{< /tabs >}} + +## Start From Rust Library Crates + +```rust +use std::fs::{self, File}; // Bringing the `fs` module and `fs::File` struct into scope + +fn main() { + fs::create_dir("some_dir").expect("Failed to create the directory!"); + File::create("some_dir/empty.txt").expect("Failed to create the file!"); +} +``` + +```rust +use core::cmp::Ordering; // Bring Ordering into scope + +// Bring collections (HashMap, VecDeque), sleep, and Duration into scope +use std::{collections::{HashMap, VecDeque}, thread::sleep, time::Duration }; + +extern crate alloc; // Enable the alloc crate +use alloc::vec::Vec; // Bring Vec into scope + +fn main() { + let two_sec = Duration::from_secs(2); + sleep(two_sec); + + let mut map = HashMap::new(); + map.insert("Key", 10); + println!("map: {:?}", map); + + let mut queue = VecDeque::new(); + queue.push_back(20); + println!("queue: {:?}", queue); + + let vec: Vec = alloc::vec![1, 2, 3]; + println!("vec: {:?}", vec); + + let (x, y) = (5, 10); + match x.cmp(&y) { + Ordering::Less => println!("x is smaller"), + Ordering::Greater => println!("x is bigger"), + Ordering::Equal => println!("x and y are equal"), + } +} +``` diff --git a/content/en/docs/d3.modules.md b/content/en/docs/d3.modules.md deleted file mode 100644 index fb246452..00000000 --- a/content/en/docs/d3.modules.md +++ /dev/null @@ -1,245 +0,0 @@ ---- -title: Modules -slug: modules ---- - -## 01. In the same file - -Related code and data are grouped into a module and stored in the same file. - -```rust -fn main() { - greetings::hello(); -} - -mod greetings { - // ⭐️ By default, everything inside a module is private - pub fn hello() { // ⭐️ So function has to be public to access from outside - println!("Hello, world!"); - } -} -``` - -Modules can also be nested. - -```rust -fn main() { - phrases::greetings::hello(); -} - -mod phrases { - pub mod greetings { - pub fn hello() { - println!("Hello, world!"); - } - } -} -``` - -Private functions can be called from the same module or from a child module. - -```rust -// 01. Calling private functions of the same module -fn main() { - phrases::greet(); -} - -mod phrases { - pub fn greet() { - hello(); // Or `self::hello();` - } - - fn hello() { - println!("Hello, world!"); - } -} - -// 02. Calling private functions of the parent module -fn main() { - phrases::greetings::hello(); -} - -mod phrases { - fn private_fn() { - println!("Hello, world!"); - } - - pub mod greetings { - pub fn hello() { - super::private_fn(); - } - } -} -``` - -> 💡 The `self` keyword is used to refer the same module, while the `super` keyword is used to refer parent module. Also, the `super` keyword can be used to access root functions from inside a module. - -```rust -fn main() { - greetings::hello(); -} - -fn hello() { - println!("Hello, world!"); -} - -mod greetings { - pub fn hello() { - super::hello(); - } -} -``` - -> 🔎 When writing tests it’s a good practice to write tests inside a test module because they compile only when running tests. - -```rust -fn greet() -> String { - "Hello, world!".to_string() -} - -#[cfg(test)] // Only compiles when running tests -mod tests { - use super::greet; // Import root greet function - - #[test] - fn test_greet() { - assert_eq!("Hello, world!", greet()); - } -} -``` - -## 02. In a different file, same directory - -```rust -// ↳ main.rs -mod greetings; // Import greetings module - -fn main() { - greetings::hello(); -} - -// ↳ greetings.rs -// ⭐️ No need to wrap the code with a mod declaration. The file itself acts as a module. -pub fn hello() { // The function has to be public to access from outside - println!("Hello, world!"); -} -``` - -If we wrap file content with a `mod` declaration, it will act as a nested module. - -```rust -// ↳ main.rs -mod phrases; - -fn main() { - phrases::greetings::hello(); -} - -// ↳ phrases.rs -pub mod greetings { // ⭐️ The module has to be public to access from outside - pub fn hello() { - println!("Hello, world!"); - } -} -``` - -## 03. In a different file, different directory - -`mod.rs` in the directory module root is the entry point to the directory module. All other files in that directory root, act as sub-modules of the directory module. - -```rust -// ↳ main.rs -mod greetings; - -fn main() { - greetings::hello(); -} - -// ↳ greetings/mod.rs -pub fn hello() { // ⭐️ The function has to be public to access from outside - println!("Hello, world!"); -} -``` - -Again, If we wrap file content with a `mod` declaration, it will act as a nested module. - -```rust -// ↳ main.rs -mod phrases; - -fn main() { - phrases::greetings::hello(); -} - -// ↳ phrases/mod.rs -pub mod greetings { // ⭐️ The module has to be public to access from outside - pub fn hello() { - println!("Hello, world!"); - } -} -``` - -Other files in the directory module act as sub-modules for `mod.rs`. - -```rust -// ↳ main.rs -mod phrases; - -fn main() { - phrases::hello() -} - -// ↳ phrases/mod.rs -mod greetings; - -pub fn hello() { - greetings::hello() -} - -// ↳ phrases/greetings.rs -pub fn hello() { - println!("Hello, world!"); -} -``` - -⭐️ If you need to access an element of `phrases/greetings.rs` from outside the module, you have to import the `greetings` module as a public module. - -```rust -// ↳ main.rs -mod phrases; - -fn main() { - phrases::greetings::hello(); -} - -// ↳ phrases/mod.rs -pub mod greetings; // ⭐️ `pub mod` instead `mod` - -// ↳ phrases/greetings.rs -pub fn hello() { - println!("Hello, world!"); -} -``` - -> 🔎 It’s unable to import child file modules of directory modules to `main.rs`, so you can’t use `mod phrases::greetings;` from `main.rs`. But there is a way to import `phrases::greetings::hello()` to `phrases` module by re-exporting `hello` to `phrases` module. So you can call it directly as `phrases::hello()`. - -```rust -// ↳ phrases/greetings.rs -pub fn hello() { - println!("Hello, world!"); -} - -// ↳ phrases/mod.rs -pub mod greetings; - -pub use self::greetings::hello; // Re-export `greetings::hello` to phrases - -// ↳ main.rs -mod phrases; - -fn main() { - phrases::hello(); // You can call `hello()` directly from phrases -} -``` - -This allows you to present an external interface that **may not directly map** to your internal code organization. If still it is not clear, don’t worry; We discuss the usages of `use` on an upcoming section in this post. \ No newline at end of file diff --git a/content/en/docs/d4.crates.md b/content/en/docs/d4.crates.md index 7955fe68..942ddbae 100644 --- a/content/en/docs/d4.crates.md +++ b/content/en/docs/d4.crates.md @@ -3,6 +3,9 @@ title: Crates slug: crates --- +> [!caution] +> This needs a refresh! + 💭 Crates are a bit similar to the packages in some other languages. Crates compile individually. If the crate has child file modules, those files will get merged with the crate file and compile as a single unit. 💭 A crate can produce an executable/ a binary or a library. `src/main.rs` is the crate root/ entry point for a binary crate and `src/lib.rs` is the entry point for a library crate. @@ -31,14 +34,12 @@ pub fn hello() { } // 02. greetings/src/main.rs -extern crate greetings; - fn main() { greetings::hello(); } ``` -> 💯 As I mentioned earlier, in here we use simplest examples to reduce the complexity of learning materials. But this is how we need to write `greetings/src/lib.rs` to make the code more testable. +As I mentioned earlier, in here we use simplest examples to reduce the complexity of learning materials. But this is how we need to write `greetings/src/lib.rs` to make the code more testable. ```rust // greetings/src/lib.rs @@ -91,8 +92,6 @@ pub fn hello() { } // 02. phrases/src/main.rs -extern crate phrases; - fn main() { phrases::greetings::hello(); } @@ -223,7 +222,8 @@ mod tests { } ``` -> 💭 **//! doc comments** are used to write crate and module-level documentation. On other places, we have to use /// outside of the block. And when uploading a crate to [crates.io](http://crates.io/), cargo generates the documentation from these doc comments and host it on [docs.rs](https://docs.rs/). +> [!recap] +> **//! doc comments** are used to write crate and module-level documentation. On other places, we have to use /// outside of the block. And when uploading a crate to [crates.io](http://crates.io/), cargo generates the documentation from these doc comments and host it on [docs.rs](https://docs.rs/). 💡 We have to add the **description** and **license** fields to `Cargo.toml`. Otherwise, we will get `error: api errors: missing or empty metadata fields: description, license. Please see http://doc.crates.io/manifest.html` @@ -243,7 +243,8 @@ The name of our crate is `test_crate_hello_world`. So it can be found on, 📦 [https://**crates.io/crates**/test_crate_hello_world](https://crates.io/crates/test_crate_hello_world) 📑 [https://**docs.rs**/test_crate_hello_world](https://docs.rs/test_crate_hello_world) -> 💯 crates.io supports readme files as well. To enable it, we have to add the readme field to Cargo.toml. Ex: `readme="README.md"` +> [!tip] +> crates.io supports readme files as well. To enable it, we have to add the readme field to Cargo.toml. Ex: `readme="README.md"` 🏗️ Okay then, Let’s see how we can **use this from another crate**. diff --git a/content/en/docs/d5.workspaces.md b/content/en/docs/d5.workspaces.md index 90e07a6c..0fb4f9a4 100644 --- a/content/en/docs/d5.workspaces.md +++ b/content/en/docs/d5.workspaces.md @@ -3,74 +3,154 @@ title: Workspaces slug: workspaces --- -When the code base is getting larger, you might need to work with **multiple crates on the same project**. Rust supports this via Workspaces. You can **analyze (`cargo check`), build, run tests or generate docs for all crates** at once by running `cargo` commands from the project root. +## Rust Workspaces -⭐️ When working on multiple crates same time, there is a higher possibility of having shared dependencies on crates. To prevent downloading and compiling the same dependency multiple times, Rust uses a **shared build directory** under the project root, while running `cargo build` from the project root. +- Rust workspaces provide a convenient way to manage multiple related crates together as a single project or monorepo. +- A Rust workspace uses a shared `Cargo.lock` file and a common `target` directory to efficiently build and manage shared dependencies, reducing build times. +- Cargo commands such as `cargo test`, `cargo build`, `cargo check`, `cargo fmt`, `cargo clippy`, and `cargo clean` can be run once from the workspace root to apply to all workspace members. -Let's create a library crate with a simple hello world function and a binary crate which uses the library crate. +## Simple -Assume we run, -```bash -mkdir greetings -touch greetings/Cargo.toml -cargo new greetings/lib --lib -cargo new greetings/examples/hello -``` +Let's build a sample Rust workspace with two simple crates. + +> [!lab] +> ```shell +> mkdir my_project && cd my_project +> echo -e "[workspace]\nresolver = \"3\"" > ./Cargo.toml +> cargo new app_bar && cargo new app_buzz +> cargo add uuid -F v7 -p app_bar && cargo add uuid -F v7 -p app_buzz +> +> echo 'fn main() { +> \tprintln!("Bar ID: {}", uuid::Uuid::now_v7()); +> }' > app_bar/src/main.rs +> +> echo 'fn main() { +> \tprintln!("Buzz ID: {}", uuid::Uuid::now_v7()); +> }' > app_buzz/src/main.rs +> ``` -That generates, -```bash -greetings - ├── Cargo.toml - ├── examples - │ └── hello - │ ├── Cargo.toml - │ └── src - │ └── main.rs - └── lib - ├── Cargo.toml - └── src - └── lib.rs +```shell {title="Folder Structure"} +my_project +├── app_bar +│ ├── Cargo.toml +│ └── src +│ └── main.rs +├── app_buzz +│ ├── Cargo.toml +│ └── src +│ └── main.rs +├── Cargo.lock +└── Cargo.toml ``` -We have to modify the following files, -```rust -// 01. greetings/Cargo.toml to mark as a workspace and to add members +{{< tabs count=3 label="Select File" >}} +{{< tab label="app_bar → main.rs" >}} +```rust {title="my_project/app_bar/src/main.rs"} +fn main() { + println!("Bar ID: {}", uuid::Uuid::now_v7()); +} +``` +{{< /tab >}} +{{< tab label="app_buzz → main.rs" >}} +```rust {title="my_project/app_buzz/src/main.rs"} +fn main() { + println!("Buzz ID: {}", uuid::Uuid::now_v7()); +} +``` +{{< /tab >}} +{{< tab label="Cargo.toml" >}} +```toml {title="my_project/Cargo.toml"} [workspace] -members = [ - "lib", - "examples/hello" -] +resolver = "3" +members = ["app_bar", "app_buzz"] +``` +{{< /tab >}} +{{< /tabs >}} -// 02.1 greetings/lib/Cargo.toml to change the package name to greetings -[package] -name = "greetings" -version = "0.1.0" -authors = ["Dumindu Madunuwan"] +As you can see, +- Cargo commands can automatically add workspace members to the `Cargo.toml` file in the workspace root(`my_project`). +- Try running `cargo build` and observe how each dependency is compiled only once through the shared `target` directory in the workspace root. -[dependencies] +> [!assignment] +> - Examine the `Cargo.toml` files in the workspace root and in each crate, and identify the main tables(sections) they contain. +> - Examine the `Cargo.lock` file in the workspace root and identify how the `app_buzz` and `app_bar` crates record their dependencies. +> - Run `cargo run -p app_bar` and `cargo run -p app_buzz` commands from the workspace root. -// 02.2 greetings/lib/src/lib.rs to add a simple hello world function -pub fn hello() { - println!("Hello, world!"); -} +## Advanced -// 03.1 greetings/examples/hello/Cargo.toml to add the `greetings` lib as a dependency +> [!lab] +> ``` +> mkdir my_project2 && cd my_project2 +> echo -e "[workspace]\nresolver = \"3\"\nmembers = [\"crates/*\"]" > ./Cargo.toml +> cargo new crates/bar && cargo new crates/buzz +> cargo new crates/common --lib +> ``` + +```shell {title="Folder Structure"} +my_project +├── crates +│ ├── bar +│ │ ├── Cargo.toml +│ │ └── src +│ │ └── main.rs +│ ├── buzz +│ │ ├── Cargo.toml +│ │ └── src +│ │ └── main.rs +│ └── common +│ ├── Cargo.toml +│ └── src +│ └── lib.rs +├── Cargo.toml +└── Cargo.lock +``` + +- To use the `common` crate from `crates/bar` and `crates/buzz`, + - We have to add `common` crate as a dependency in both `crates/bar/Cargo.toml` and `crates/buzz/Cargo.toml`. + ```toml + [dependencies] + common = { path = "../common" } + ``` + - Then, we can use the `common` crate like any other dependency in `crates/bar` and `crates/buzz`. + ```rust + fn main() { + let x = common::add(1, 1); + println!("{x}"); + } + ``` + +{{< tabs count=3 label="Select File" >}} +{{< tab label="bar → Cargo.toml" >}} +```toml {title="my_project2/crates/bar/Cargo.toml"} [package] -name = "hello" +name = "bar" version = "0.1.0" -authors = ["Dumindu Madunuwan"] +edition = "2024" [dependencies] -greetings = { path = "../../lib" } - -// 03.2 greetings/examples/hello/src/main.rs to import the `greetings` lib and call its hello world function -extern crate greetings; +common = { path = "../common" } +``` +{{< /tab >}} +{{< tab label="buzz → Cargo.toml" >}} +```toml {title="my_project2/crates/buzz/Cargo.toml"} +[package] +name = "buzz" +version = "0.1.0" +edition = "2024" -fn main() { - greetings::hello(); -} +[dependencies] +common = { path = "../common" } +``` +{{< /tab >}} +{{< tab label="my_project2 → Cargo.toml" >}} +```toml {title="my_project/Cargo.toml"} +[workspace] +resolver = "3" +members = ["crates/*"] ``` +{{< /tab >}} +{{< /tabs >}} -💡 On Linux and Mac, you can run `cargo` commands on each crate without changing the working directory all the times via Subshells (A command list embedded between parentheses). For example, if you are in the `greetings` directory, even you run `(cd examples/hello && cargo run)` your working directory will be kept as same in `greetings` folder. +## 👨‍🏫 Before going to the next... -> 🔎 [rust-lang/rust source folder](https://github.com/rust-lang/rust/tree/master/src) is a good example for a workspace. +- [rust-lang/rust library folder](https://github.com/rust-lang/rust/tree/main/library) is a good example for a workspace. Explore and identify Rust library workspace structure. diff --git a/content/en/docs/d6.use.md b/content/en/docs/d6.use.md deleted file mode 100644 index 1e39aacd..00000000 --- a/content/en/docs/d6.use.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: Use -slug: use ---- - -Let's see the main usages of the `use` keyword. - - -## 01. Bind a full path to a new name - -Mainly `use` keyword is used to bind a full path of an element to a new name. So the user doesn’t want to repeat the full path each time. - -```rust -// -- Initial code without the `use` keyword -- -mod phrases { - pub mod greetings { - pub fn hello() { - println!("Hello, world!"); - } - } -} - -fn main() { - phrases::greetings::hello(); // Using full path -} - - -// -- Usage of the `use` keyword -- -// 01. Create an alias for module -use phrases::greetings; -fn main() { - greetings::hello(); -} - -// 02. Create an alias for module elements -use phrases::greetings::hello; -fn main() { - hello(); -} - -// 03. Customize names with the `as` keyword -use phrases::greetings::hello as greet; -fn main() { - greet(); -} -``` - - -## 02. Import elements to scope - -Another common usage of `use` is importing elements to scope. Remember that, this is also a bit similar to creating an alias and using it instead of using the full path. - -```rust -fn hello() -> String { - "Hello, world!".to_string() -} - -#[cfg(test)] -mod tests { - use super::hello; // Import the `hello()` function into the scope - - #[test] - fn test_hello() { - assert_eq!("Hello, world!", hello()); // If not using the above `use` statement, we can run same via `super::hello()` - } -} -``` - -> 💡 By default, `use` declarations use absolute paths, starting from the crate root. But `self` and `super` declarations make that path relative to the current module. - -Same way the `use` keyword is used to import the elements of other crates including the `std`, Rust’s [Standard Library](https://github.com/rust-lang/rust/tree/master/src/libstd). - -```rust -// -- 01. Importing elements -- -use std::fs::File; - -fn main() { - File::create("empty.txt").expect("Can not create the file!"); -} - - -// -- 02. Importing module and elements -- -use std::fs::{self, File} // `use std::fs; use std::fs::File;` - -fn main() { - fs::create_dir("some_dir").expect("Can not create the directry!"); - File::create("some_dir/empty.txt").expect("Can not create the file!"); -} - - -// -- 03. Importing multiple elements -- -use std::fs::File; -use std::io::{BufReader, BufRead}; // `use std::io::BufReader; use std::io::BufRead;` - -fn main() { - let file = File::open("src/hello.txt").expect("file not found"); - let buf_reader = BufReader::new(file); - - for line in buf_reader.lines() { - println!("{}", line.unwrap()); - } -} -``` - -> We **don’t need** to use `extern crate std;` when using the `std` library. We will discuss more about this under the Standard Library section. - -💡 `use` statements import only what we’ve specified into the scope, instead of importing all elements of a module or crate. So it improves the efficiency of the program. - - -## 03. Re-exporting - -Another special case is `pub use`. When creating a module, you can export things from another module into your module. So after that, they can be accessed directly from your module. This is called **re-exporting**. - -```rust -// ↳ main.rs -mod phrases; - -fn main() { - phrases::hello(); // Not directly map -} - -// ↳ phrases/mod.rs -pub mod greetings; - -pub use self::greetings::hello; // Re-export `greetings::hello` to phrases - -// ↳ phrases/greetings.rs -pub fn hello() { - println!("Hello, world!"); -} -``` - -This pattern is quite common in large libraries. It helps to hide the complexity of the internal module structure of the library from users. Because users don’t need to know/follow the whole directory map of the elements of the library while working with them. diff --git a/content/en/docs/d7.std-primitives-and-preludes.md b/content/en/docs/d7.std-primitives-and-preludes.md index 2f8659fe..6f435d1d 100644 --- a/content/en/docs/d7.std-primitives-and-preludes.md +++ b/content/en/docs/d7.std-primitives-and-preludes.md @@ -3,6 +3,9 @@ title: STD, Primitives and Preludes slug: std-primitives-and-preludes --- +> [!caution] +> This needs a refresh! + ⭐️ In Rust, language elements are implemented by not only **`std` library** crate but also **compiler** as well. Examples, - **[Primitives](https://doc.rust-lang.org/std/#primitives)**: Defined by the compiler and methods are implemented by `std` library directly on primitives. - **[Standard Macros](https://doc.rust-lang.org/std/#macros)**: Defined by both compiler and `std` @@ -93,7 +96,8 @@ intrinsics // intrinsics: nightly-only experimental API raw // raw: nightly-only experimental API ``` -> 🔎 When examining [Rust’s source code](https://github.com/rust-lang/rust), you can see that the [`src` directory](https://github.com/rust-lang/rust/tree/master/src) is a **workspace**. Even though it is having many library crates, by examining [root `Cargo.toml`](https://github.com/rust-lang/rust/blob/master/src/Cargo.toml) file you can easily identify that main crates are **[rustc](https://github.com/rust-lang/rust/tree/master/src/rustc)**(compiler) and **[libstd](https://github.com/rust-lang/rust/tree/master/src/libstd)** (std). In libstd/lib.rs std modules have been **re-exported** via `pub use` and the original location of most of the `std` modules is [`src/libcore`](https://github.com/rust-lang/rust/tree/master/src/libcore). +> [!search] +> When examining [Rust’s source code](https://github.com/rust-lang/rust), you can see that the [`src` directory](https://github.com/rust-lang/rust/tree/master/src) is a **workspace**. Even though it is having many library crates, by examining [root `Cargo.toml`](https://github.com/rust-lang/rust/blob/master/src/Cargo.toml) file you can easily identify that main crates are **[rustc](https://github.com/rust-lang/rust/tree/master/src/rustc)**(compiler) and **[libstd](https://github.com/rust-lang/rust/tree/master/src/libstd)** (std). In libstd/lib.rs std modules have been **re-exported** via `pub use` and the original location of most of the `std` modules is [`src/libcore`](https://github.com/rust-lang/rust/tree/master/src/libcore). **Few important `std` modules are,** - `std::io` - Core **I/O** functionality @@ -106,7 +110,8 @@ raw // raw: nightly-only experimental API - `std::thread` - Native **threads** specific functionality - `std::collections` - Core **Collection types** -> 💯 Refer [Rust Standard Library Documentation](https://doc.rust-lang.org/std/) for more details. +> [!tip] +> Refer [Rust Standard Library Documentation](https://doc.rust-lang.org/std/) for more details. ## Preludes diff --git a/content/en/docs/e1.smart-compiler.md b/content/en/docs/e1.smart-compiler.md index 1c1a75ad..9a6e0e89 100644 --- a/content/en/docs/e1.smart-compiler.md +++ b/content/en/docs/e1.smart-compiler.md @@ -3,6 +3,9 @@ title: Smart Compiler slug: smart-compiler --- +> [!caution] +> This needs a refresh! + ## Why Compiler? The Rust compiler does the most significant job to prevent errors in Rust programs. It **analyzes the code at compile-time** and issues warnings, if the code does not follow memory management rules or lifetime annotations correctly. @@ -37,7 +40,8 @@ For more information about this error, try `rustc --explain E0382`. // Also you can use "let _ =" to completely ignore return values ``` -> 💭 In the previous sections, we have discussed memory management concepts like [ownership](c1.ownership.html), [borrowing](c2.borrowing.html), [lifetimes](c3.lifetimes.md) and etc. +> [!recap] +> In the previous sections, we have discussed memory management concepts like [ownership](/docs/ownership), [borrowing](/docs/borrowing), [lifetimes](/docs/lifetimes) and etc. Rust compiler checks not only issues related with lifetimes or memory management and also common coding mistakes, like the following code. @@ -104,4 +108,4 @@ let result = loop { // ok! ``` ``` -💡 Also you can read the same explanations via [Rust Compiler Error Index](https://medium.com/r/?url=https%3A%2F%2Fdoc.rust-lang.org%2Ferror-index.html). For example to check the explanation of `E0571` error, you can use https://doc.rust-lang.org/error-index.html#E0571. +💡 Also you can read the same explanations via [Rust Compiler Error Index](https://doc.rust-lang.org/error_codes/error-index.html). For example to check the explanation of `E0571` error, you can use https://doc.rust-lang.org/error-index.html#E0571. diff --git a/content/en/docs/e2.panicking.md b/content/en/docs/e2.panicking.md index 1c89d197..e8b7c6ce 100644 --- a/content/en/docs/e2.panicking.md +++ b/content/en/docs/e2.panicking.md @@ -72,7 +72,7 @@ fn main() { thread 'main' panicked at 'Color { r: 255, g: 255, b: 0 }', src/main.rs:16:5 ``` -As you can see in the above examples `panic!()` supports [`println!()` type style arguments](a3.hello_world.html#Usages-of-println). By default, it prints the **error message, file path and line & column numbers** where the error happens. +As you can see in the above examples `panic!()` supports [`println!()` type style arguments](/docs/hello-world/#-before-going-to-the-next). By default, it prints the **error message, file path and line & column numbers** where the error happens. ## unimplemented!() diff --git a/content/en/docs/e3.option-and-result.md b/content/en/docs/e3.option-and-result.md index b909851f..c11caadd 100644 --- a/content/en/docs/e3.option-and-result.md +++ b/content/en/docs/e3.option-and-result.md @@ -5,9 +5,10 @@ slug: option-and-result ## Why Option and Result? -Many languages use **`null`\ `nil`\ `undefined` types** to represent empty outputs, and **`Exceptions`** to handle errors. Rust skips using both, especially to prevent issues like **null pointer exceptions, sensitive data leakages through exceptions** and etc. Instead, Rust provides two special **generic enums**;`Option` and `Result` to deal with above cases. +Many languages use **`null`\ `nil`\ `undefined` types** to represent empty outputs, and **`Exceptions`** to handle errors. Rust skips using both, especially to prevent issues like **null pointer exceptions, sensitive data leakages through exceptions**, etc. Instead, Rust provides two special **generic enums**;`Option` and `Result` to deal with above cases. -> 💭 In the previous sections, we have discussed about the basics of [enums](b3.enums.html), [generics](b4.generics.html) and [`Result` & `Option` types](b4.generics.html#Generalizing-enums). +> [!recap] +> In the previous sections, we have discussed about the basics of [enums](/docs/enums), [generics](/docs/generics) and [`Result` & `Option` types](/docs/generics/#generalizing-enums). As you know, - An **optional value** can have either **Some** value or no value/ **None**. @@ -27,17 +28,19 @@ enum Result { // T and E are generics. T can contain any type of value, E } ``` -💭 Also as we discussed in [preludes](d7.std_primitives_and_preludes.html#Preludes), not only `Option` and `Result`, and also their variants are in preludes. So, we can use them directly without using namespaces in the code. +💭 Also as we discussed in [preludes](/docs/std-primitives-and-preludes/#preludes), not only `Option` and `Result`, and also their variants are in preludes. So, we can use them directly without using namespaces in the code. ## Basic usages of Option When writing a function or data type, + - if an **argument** of the function is optional, -- If the function is non-void and if the output it **returns** can be empty, -- If the value, of a **property of the data type** can be empty, -We have to use their data type as an `Option` type +- if the function is non-void and if the output it **returns** can be empty, +- if the value of a **property of the data type** can be empty, + +we have to use their data type as an `Option` type. -For example, if the function outputs a `&str` value and the output can be empty, the return type of the function should set as `Option<&str>`. +For example, if the function outputs a `&str` value and the output can be empty, the return type of the function should be set as `Option<&str>`. ```rust fn get_an_optional_value() -> Option<&str> { @@ -50,7 +53,7 @@ fn get_an_optional_value() -> Option<&str> { } ``` -Same way, if the value of a property of a data type can be empty or optional like the `middle_name` of `Name` data type in the following example, we should set its data type as an `Option` type. +In the same way, if the value of a property of a data type can be empty or optional like the `middle_name` of the `Name` data type in the following example, we should set its data type as an `Option` type. ```rust struct Name { @@ -60,7 +63,7 @@ struct Name { } ``` -💭 As you know, we can use pattern matching to catch the relevant return type (`Some`/ `None`) via `match`. There is a function to get the current user’s home directory in **`std::env`** as **[`home_dir()`](https://doc.rust-lang.org/std/env/fn.home_dir.html)**. Because of all users doesn’t have a home directory in the systems like Linux, home directory of the user can be optional. So it returns an `Option` type; [`Option`](https://doc.rust-lang.org/std/path/struct.PathBuf.html). +💭 As you know, we can use pattern matching to catch the relevant return type (`Some`/ `None`) via `match`. There is a function in **`std::env`** called **[`home_dir()`](https://doc.rust-lang.org/std/env/fn.home_dir.html)** to get the current user's home directory. However, not all users have a home directory in systems like Linux, so the home directory of a user can be optional. So it returns an `Option` type; [`Option`](https://doc.rust-lang.org/std/path/struct.PathBuf.html). ```rust use std::env; @@ -96,7 +99,7 @@ fn main() { ## Basic usages of Result -If a function can produce an error, we have to use a `Result` type by **combining the data type of the valid output and the data type of the error**. For example, if the data type of the valid output is `u64` and error type is `String`, return type should be `Result`. +If a function can produce an error, we have to use a `Result` type by **combining the data type of the valid output and the data type of the error**. For example, if the data type of the valid output is `u64` and error type is `String`, the return type should be `Result`. ```rust fn function_with_error() -> Result { @@ -109,7 +112,7 @@ fn function_with_error() -> Result { } ``` -💭 As you know, we can use the pattern matching to catch the relevant return types (`Ok`/`Err`) via `match`. There is a function to fetch the value of any environment variable in **`std::env`** as **[`var()`](https://doc.rust-lang.org/std/env/fn.var.html)** . Its input is the environment variable name. This can produce an error, if we passes a wrong environment variable or the program can not extract the value of the environment variable while running. So, its return type is a `Result` type; [`Result`](https://doc.rust-lang.org/std/env/enum.VarError.html). +💭 As you know, we can use the pattern matching to catch the relevant return types (`Ok`/`Err`) via `match`. There is a function to fetch the value of any environment variable in **`std::env`** called **[`var()`](https://doc.rust-lang.org/std/env/fn.var.html)**. Its input is the environment variable name. This can produce an error if we pass a wrong environment variable or the program cannot extract the value of the environment variable while running. So, its return type is a `Result` type; [`Result`](https://doc.rust-lang.org/std/env/enum.VarError.html). ```rust use std::env; @@ -141,7 +144,7 @@ fn main() { ## ok(), err() for Result types -In addition to that Rust provides `ok()` and `err()` for `Result` types. They convert the `Ok` and `Err` values of a **`Result` type to `Option` types**. +In addition to that, Rust provides `ok()` and `err()` for `Result` types. They convert the `Ok` and `Err` values of a **`Result` type to `Option` types**. ```rust fn main() { diff --git a/content/en/docs/e4.unwrap-and-expect.md b/content/en/docs/e4.unwrap-and-expect.md index 812e8c1d..5b500311 100644 --- a/content/en/docs/e4.unwrap-and-expect.md +++ b/content/en/docs/e4.unwrap-and-expect.md @@ -153,7 +153,8 @@ thread 'main' panicked at 'Should not get Ok value: 8', libcore/result.rs:945:5 ## unwrap_or(), unwrap_or_default() and unwrap_or_else() ->💡 These are bit similar to `unwrap()`, If an `Option` type has `Some` value or a `Result` type has a `Ok` value, the value inside them passes to the next step. But when having `None` or `Err`, the functionalities are bit different. +> [!tip] +> These are bit similar to `unwrap()`, If an `Option` type has `Some` value or a `Result` type has a `Ok` value, the value inside them passes to the next step. But when having `None` or `Err`, the functionalities are bit different. - `unwrap_or()` : With `None` or `Err`, **the value you passes to `unwrap_or()`** is passing to the next step. But the data type of the value you passes should match with the data type of the relevant `Some` or `Ok`. @@ -197,7 +198,7 @@ fn main() { } ``` -- `unwrap_or_else()` : Similar to `unwrap_or()`. The only difference is, instead of passing a value, you have to pass a **[closure](a7.functions.html#Closures)** which returns a value with the same data type of the relevant `Some` or `Ok`. +- `unwrap_or_else()` : Similar to `unwrap_or()`. The only difference is, instead of passing a value, you have to pass a **[closure](/docs/functions/#closures)** which returns a value with the same data type of the relevant `Some` or `Ok`. ```rust fn main() { diff --git a/content/en/docs/e5.error-and-none-propagation.md b/content/en/docs/e5.error-and-none-propagation.md index 349eba48..781c94d6 100644 --- a/content/en/docs/e5.error-and-none-propagation.md +++ b/content/en/docs/e5.error-and-none-propagation.md @@ -93,7 +93,7 @@ let x = try!(function_with_error()); Before Rust version 1.26, we couldn't propagate `Result` and `Option` types from the `main()` function. But now, we **can propagate `Result` types** from the `main()` function and it prints the **`Debug` representation of the `Err`**. -💡 We are going to discuss about `Debug` representations under [Error trait section](e7.custom_error_types.html#Error-trait). +💡 We are going to discuss about `Debug` representations under [Error trait section](/docs/custom-error-types/#error-trait). ```rust use std::fs::File; @@ -110,4 +110,5 @@ fn main() -> std::io::Result<()> { // Error: Os { code: 2, kind: NotFound, message: "No such file or directory" } ``` -> 💯 If you want to know about the all kind of errors `std::fs::File::open()` can produce, check the [error list on `std::fs::OpenOptions`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#errors). +> [!assignment] +> If you want to know about the all kind of errors `std::fs::File::open()` can produce, check the [error list on `std::fs::OpenOptions`](https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#errors). diff --git a/content/en/docs/e6.combinators.md b/content/en/docs/e6.combinators.md index f77d18a5..32cc7f2b 100644 --- a/content/en/docs/e6.combinators.md +++ b/content/en/docs/e6.combinators.md @@ -13,17 +13,20 @@ __ John Hughes—[Generalizing Monads to Arrows](http://www.cse.chalmers.se/~rjm The exact definition of "combinators" in Rust ecosystem is bit unclear.  -- `or()`, `and()`, `or_else()`, `and_then()` +- `or()`, `and()`, `or_else()`, `and_then()` - **Combine two values of type T** and **return same type T**. +- `xor()` for `Option` types + - **Combine two values of type T** and **return same type T**, **only if exactly one value is T** + - `filter()` for `Option` types - **Filter type T** by using a closure as a conditional function - **Return same type T** - -- `map()`, `map_err()` - - **Convert type T by applying a closure**. - - The **data type of the value inside T can be changed**. - ex. `Some<&str>` can be converted to `Some` or `Err<&str>` to `Err` and etc. + +- `map()`, `map_err()` + - **Convert type T by applying a closure**. + - The **data type of the value inside T can be changed**. + ex. `Some<&str>` can be converted to `Some` or `Err<&str>` to `Err` and etc. - `map_or()`, `map_or_else()` - **Transform type T by applying a closure** & **return the value inside type T**. @@ -32,170 +35,200 @@ The exact definition of "combinators" in Rust ecosystem is bit unclear.  - `ok_or()`, `ok_or_else()` for `Option` types - **Transform `Option` type into a `Result` type**. -- `as_ref()`, `as_mut()` +- `as_ref()`, `as_mut()` - **Transform type T into a reference or a mutable reference**. ## or() and and() While combining two expressions, which return either `Option`/ `Result` + - `or()`: If either one got `Some` or `Ok`, that value returns immediately. - `and()`: If both got `Some` or `Ok`, the value in the second expression returns. If either one got `None` or `Err` that value returns immediately. +### With Option + ```rust -fn main() { - let s1 = Some("some1"); - let s2 = Some("some2"); - let n: Option<&str> = None; - - let o1: Result<&str, &str> = Ok("ok1"); - let o2: Result<&str, &str> = Ok("ok2"); - let e1: Result<&str, &str> = Err("error1"); - let e2: Result<&str, &str> = Err("error2"); - - assert_eq!(s1.or(s2), s1); // Some1 or Some2 = Some1 - assert_eq!(s1.or(n), s1); // Some or None = Some - assert_eq!(n.or(s1), s1); // None or Some = Some - assert_eq!(n.or(n), n); // None1 or None2 = None2 - - assert_eq!(o1.or(o2), o1); // Ok1 or Ok2 = Ok1 - assert_eq!(o1.or(e1), o1); // Ok or Err = Ok - assert_eq!(e1.or(o1), o1); // Err or Ok = Ok - assert_eq!(e1.or(e2), e2); // Err1 or Err2 = Err2 - - assert_eq!(s1.and(s2), s2); // Some1 and Some2 = Some2 - assert_eq!(s1.and(n), n); // Some and None = None - assert_eq!(n.and(s1), n); // None and Some = None - assert_eq!(n.and(n), n); // None1 and None2 = None1 - - assert_eq!(o1.and(o2), o2); // Ok1 and Ok2 = Ok2 - assert_eq!(o1.and(e1), e1); // Ok and Err = Err - assert_eq!(e1.and(o1), e1); // Err and Ok = Err - assert_eq!(e1.and(e2), e1); // Err1 and Err2 = Err1 -} +let s1 = Some("some1"); +let s2 = Some("some2"); +let n: Option<&str> = None; + +assert_eq!(s1.or(s2), s1); // Some1 or Some2 = Some1 +assert_eq!(s1.or(n), s1); // Some or None = Some +assert_eq!(n.or(s1), s1); // None or Some = Some +assert_eq!(n.or(n), n); // None1 or None2 = None2 + +assert_eq!(s1.and(s2), s2); // Some1 and Some2 = Some2 +assert_eq!(s1.and(n), n); // Some and None = None +assert_eq!(n.and(s1), n); // None and Some = None +assert_eq!(n.and(n), n); // None1 and None2 = None1 ``` -> 🔎 Rust nightly support [`xor()`](https://doc.rust-lang.org/std/option/enum.Option.html#method.xor) for `Option` types, which returns `Some` only if one expression got `Some`, but not both. +### With Result + +```rust +let o1: Result<&str, &str> = Ok("ok1"); +let o2: Result<&str, &str> = Ok("ok2"); +let e1: Result<&str, &str> = Err("error1"); +let e2: Result<&str, &str> = Err("error2"); + +assert_eq!(o1.or(o2), o1); // Ok1 or Ok2 = Ok1 +assert_eq!(o1.or(e1), o1); // Ok or Err = Ok +assert_eq!(e1.or(o1), o1); // Err or Ok = Ok +assert_eq!(e1.or(e2), e2); // Err1 or Err2 = Err2 + +assert_eq!(o1.and(o2), o2); // Ok1 and Ok2 = Ok2 +assert_eq!(o1.and(e1), e1); // Ok and Err = Err +assert_eq!(e1.and(o1), e1); // Err and Ok = Err +assert_eq!(e1.and(e2), e1); // Err1 and Err2 = Err1 +``` + +## xor() + +While combining two Options, which return either Option, only if exactly one option is T. + +The same `Some` type is returned, only if we pass only one `Some` value. `None` is returned, if both `Some` or `None` type. Rust support `xor()` only for `Option` types. + +```rust +let s1 = Some("some1"); +let s2 = Some("some2"); +let n: Option<&str> = None; + +assert_eq!(s1.xor(s2), n); // Some1 xor Some2 = None +assert_eq!(s1.xor(n), s1); // Some xor None = Some +assert_eq!(n.xor(s1), s1); // None xor Some = Some +assert_eq!(n.xor(n), n); // None1 xor None2 = None2 +``` ## or_else() -Similar to `or()`. The only difference is, the second expression should be a **[closure](a7.functions.html#Closures)** which returns same type T. +Similar to `or()`. The only difference is, the second expression should be a **[closure](/docs/functions/#closures)** which returns same type T. + +### With Option ```rust -fn main() { - // or_else with Option - let s1 = Some("some1"); - let s2 = Some("some2"); - let fn_some = || Some("some2"); // similar to: let fn_some = || -> Option<&str> { Some("some2") }; - - let n: Option<&str> = None; - let fn_none = || None; - - assert_eq!(s1.or_else(fn_some), s1); // Some1 or_else Some2 = Some1 - assert_eq!(s1.or_else(fn_none), s1); // Some or_else None = Some - assert_eq!(n.or_else(fn_some), s2); // None or_else Some = Some - assert_eq!(n.or_else(fn_none), None); // None1 or_else None2 = None2 - - // or_else with Result - let o1: Result<&str, &str> = Ok("ok1"); - let o2: Result<&str, &str> = Ok("ok2"); - let fn_ok = |_| Ok("ok2"); // similar to: let fn_ok = |_| -> Result<&str, &str> { Ok("ok2") }; - - let e1: Result<&str, &str> = Err("error1"); - let e2: Result<&str, &str> = Err("error2"); - let fn_err = |_| Err("error2"); - - assert_eq!(o1.or_else(fn_ok), o1); // Ok1 or_else Ok2 = Ok1 - assert_eq!(o1.or_else(fn_err), o1); // Ok or_else Err = Ok - assert_eq!(e1.or_else(fn_ok), o2); // Err or_else Ok = Ok - assert_eq!(e1.or_else(fn_err), e2); // Err1 or_else Err2 = Err2 -} +let s1 = Some("some1"); +let s2 = Some("some2"); +let fn_some = || Some("some2"); // similar to: let fn_some = || -> Option<&str> { Some("some2") }; + +let n: Option<&str> = None; +let fn_none = || None; + +assert_eq!(s1.or_else(fn_some), s1); // Some1 or_else Some2 = Some1 +assert_eq!(s1.or_else(fn_none), s1); // Some or_else None = Some +assert_eq!(n.or_else(fn_some), s2); // None or_else Some = Some +assert_eq!(n.or_else(fn_none), None); // None1 or_else None2 = None2 +``` + +### With Result + +```rust +let o1: Result<&str, &str> = Ok("ok1"); +let o2: Result<&str, &str> = Ok("ok2"); +let fn_ok = |_| Ok("ok2"); // similar to: let fn_ok = |_| -> Result<&str, &str> { Ok("ok2") }; + +let e1: Result<&str, &str> = Err("error1"); +let e2: Result<&str, &str> = Err("error2"); +let fn_err = |_| Err("error2"); + +assert_eq!(o1.or_else(fn_ok), o1); // Ok1 or_else Ok2 = Ok1 +assert_eq!(o1.or_else(fn_err), o1); // Ok or_else Err = Ok +assert_eq!(e1.or_else(fn_ok), o2); // Err or_else Ok = Ok +assert_eq!(e1.or_else(fn_err), e2); // Err1 or_else Err2 = Err2 ``` ## and_then() -Similar to `and()`. The only difference is, the second expression should be a **[closure](a7.functions.html#Closures)** which returns same type T. +Similar to `and()`. The only difference is, the second expression should be a **[closure](/docs/functions/#closures)** which returns same type T. + +### With Option ```rust -fn main() { - // and_then with Option - let s1 = Some("some1"); - let s2 = Some("some2"); - let fn_some = |_| Some("some2"); // similar to: let fn_some = |_| -> Option<&str> { Some("some2") }; - - let n: Option<&str> = None; - let fn_none = |_| None; - - assert_eq!(s1.and_then(fn_some), s2); // Some1 and_then Some2 = Some2 - assert_eq!(s1.and_then(fn_none), n); // Some and_then None = None - assert_eq!(n.and_then(fn_some), n); // None and_then Some = None - assert_eq!(n.and_then(fn_none), n); // None1 and_then None2 = None1 - - // and_then with Result - let o1: Result<&str, &str> = Ok("ok1"); - let o2: Result<&str, &str> = Ok("ok2"); - let fn_ok = |_| Ok("ok2"); // similar to: let fn_ok = |_| -> Result<&str, &str> { Ok("ok2") }; - - let e1: Result<&str, &str> = Err("error1"); - let e2: Result<&str, &str> = Err("error2"); - let fn_err = |_| Err("error2"); - - assert_eq!(o1.and_then(fn_ok), o2); // Ok1 and_then Ok2 = Ok2 - assert_eq!(o1.and_then(fn_err), e2); // Ok and_then Err = Err - assert_eq!(e1.and_then(fn_ok), e1); // Err and_then Ok = Err - assert_eq!(e1.and_then(fn_err), e1); // Err1 and_then Err2 = Err1 -} +let s1 = Some("some1"); +let s2 = Some("some2"); +let fn_some = |_| Some("some2"); // similar to: let fn_some = |_| -> Option<&str> { Some("some2") }; + +let n: Option<&str> = None; +let fn_none = |_| None; + +assert_eq!(s1.and_then(fn_some), s2); // Some1 and_then Some2 = Some2 +assert_eq!(s1.and_then(fn_none), n); // Some and_then None = None +assert_eq!(n.and_then(fn_some), n); // None and_then Some = None +assert_eq!(n.and_then(fn_none), n); // None1 and_then None2 = None1 +``` + +### With Result + +```rust +let o1: Result<&str, &str> = Ok("ok1"); +let o2: Result<&str, &str> = Ok("ok2"); +let fn_ok = |_| Ok("ok2"); // similar to: let fn_ok = |_| -> Result<&str, &str> { Ok("ok2") }; + +let e1: Result<&str, &str> = Err("error1"); +let e2: Result<&str, &str> = Err("error2"); +let fn_err = |_| Err("error2"); + +assert_eq!(o1.and_then(fn_ok), o2); // Ok1 and_then Ok2 = Ok2 +assert_eq!(o1.and_then(fn_err), e2); // Ok and_then Err = Err +assert_eq!(e1.and_then(fn_ok), e1); // Err and_then Ok = Err +assert_eq!(e1.and_then(fn_err), e1); // Err1 and_then Err2 = Err1 ``` ## filter() -> 💡 Usually in programming languages `filter` functions are used with arrays or iterators to create a new array/ iterator by filtering own elements via a function/ closure. Rust also provides [`filter()` **as an iterator adaptor**](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter) to apply a closure on each element of an iterator to transform it into another iterator. However in here we are talking about the functionality of `filter()` with `Option` types. +> [!tip] +> Usually in programming languages `filter` functions are used with arrays or iterators to create a new array/ iterator by filtering own elements via a function/ closure. Rust also provides [`filter()` **as an iterator adaptor**](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter) to apply a closure on each element of an iterator to transform it into another iterator. However in here we are talking about the functionality of `filter()` with `Option` types. The same `Some` type is returned, only if we pass a `Some` value and the given closure returned true for it. `None` is returned, if `None` type passed or the closure returned false. The closure uses the value inside `Some` as an argument. Still Rust support `filter()` only for `Option` types. ```rust -fn main() { - let s1 = Some(3); - let s2 = Some(6); - let n = None; +let s1 = Some(3); +let s2 = Some(6); +let n = None; - let fn_is_even = |x: &i8| x % 2 == 0; +let fn_is_even = |x: &i8| x % 2 == 0; - assert_eq!(s1.filter(fn_is_even), n); // Some(3) -> 3 is not even -> None - assert_eq!(s2.filter(fn_is_even), s2); // Some(6) -> 6 is even -> Some(6) - assert_eq!(n.filter(fn_is_even), n); // None -> no value -> None -} +assert_eq!(s1.filter(fn_is_even), n); // Some(3) -> 3 is not even -> None +assert_eq!(s2.filter(fn_is_even), s2); // Some(6) -> 6 is even -> Some(6) +assert_eq!(n.filter(fn_is_even), n); // None -> no value -> None ``` ## map() and map_err() ->💡 Usually in programming languages `map()` functions are used with arrays or iterators, **to apply a closure on each element** of the array or iterator. Rust also provides [`map()` **as an iterator adaptor**](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) to apply a closure on each element of an iterator to transform it into another iterator. However in here we are talking about the functionality of `map()` with `Option` and `Result` types. +> [!tip] +> Usually in programming languages `map()` functions are used with arrays or iterators, **to apply a closure on each element** of the array or iterator. Rust also provides [`map()` **as an iterator adaptor**](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map) to apply a closure on each element of an iterator to transform it into another iterator. However in here we are talking about the functionality of `map()` with `Option` and `Result` types. - `map()` : Convert type T by applying a closure. **The data type of `Some` or `Ok` blocks can be changed** according to the return type of the closure. Convert `Option` to `Option`, `Result` to `Result` ⭐ Via `map()`, only `Some` and `Ok` values are getting changed. No affect to the values inside `Err` (`None` doesn’t contain any value at all). +### With Option + ```rust -fn main() { - let s1 = Some("abcde"); - let s2 = Some(5); - - let n1: Option<&str> = None; - let n2: Option = None; - - let o1: Result<&str, &str> = Ok("abcde"); - let o2: Result = Ok(5); - - let e1: Result<&str, &str> = Err("abcde"); - let e2: Result = Err("abcde"); - - let fn_character_count = |s: &str| s.chars().count(); - - assert_eq!(s1.map(fn_character_count), s2); // Some1 map = Some2 - assert_eq!(n1.map(fn_character_count), n2); // None1 map = None2 - - assert_eq!(o1.map(fn_character_count), o2); // Ok1 map = Ok2 - assert_eq!(e1.map(fn_character_count), e2); // Err1 map = Err2 -} +let s1 = Some("abcde"); +let s2 = Some(5); + +let n1: Option<&str> = None; +let n2: Option = None; + +let fn_character_count = |s: &str| s.chars().count(); + +assert_eq!(s1.map(fn_character_count), s2); // Some1 map = Some2 +assert_eq!(n1.map(fn_character_count), n2); // None1 map = None2 +``` + +### With Result + +```rust +let o1: Result<&str, &str> = Ok("abcde"); +let o2: Result = Ok(5); + +let e1: Result<&str, &str> = Err("abcde"); +let e2: Result = Err("abcde"); + +let fn_character_count = |s: &str| s.chars().count(); + +assert_eq!(o1.map(fn_character_count), o2); // Ok1 map = Ok2 +assert_eq!(e1.map(fn_character_count), e2); // Err1 map = Err2 ``` - `map_err()` for `Result` types : **The data type of `Err` blocks can be changed** according to the return type of the closure. Convert `Result` to `Result`. @@ -203,62 +236,59 @@ fn main() { ⭐ Via `map_err()`, only `Err` values are getting changed. No affect to the values inside `Ok`. ```rust -fn main() { - let o1: Result<&str, &str> = Ok("abcde"); - let o2: Result<&str, isize> = Ok("abcde"); +let o1: Result<&str, &str> = Ok("abcde"); +let o2: Result<&str, isize> = Ok("abcde"); - let e1: Result<&str, &str> = Err("404"); - let e2: Result<&str, isize> = Err(404); +let e1: Result<&str, &str> = Err("404"); +let e2: Result<&str, isize> = Err(404); - let fn_character_count = |s: &str| -> isize { s.parse().unwrap() }; // convert str to isize +let fn_character_count = |s: &str| -> isize { s.parse().unwrap() }; // convert str to isize - assert_eq!(o1.map_err(fn_character_count), o2); // Ok1 map = Ok2 - assert_eq!(e1.map_err(fn_character_count), e2); // Err1 map = Err2 -} +assert_eq!(o1.map_err(fn_character_count), o2); // Ok1 map = Ok2 +assert_eq!(e1.map_err(fn_character_count), e2); // Err1 map = Err2 ``` ## map_or() and map_or_else() -Hope you remember the functionality of [`unwrap_or()` and `unwrap_or_else()`](e4.unwrap_and_expect.html#unwrap-or-unwrap-or-default-and-unwrap-or-else) functions. These functions also bit similar to them. But `map_or()` and `map_or_else()` apply a closure on `Some` and `Ok` values and **return the value inside type T**. +Hope you remember the functionality of [`unwrap_or()` and `unwrap_or_else()`](/docs/unwrap-and-expect/#unwrap_or-unwrap_or_default-and-unwrap_or_else) functions. These functions also bit similar to them. But `map_or()` and `map_or_else()` apply a closure on `Some` and `Ok` values and **return the value inside type T**. - `map_or()` : Support only for `Option` types (not supporting `Result`). Apply the closure to the value inside `Some` and return the output according to the closure. The given default value is returned for `None` types. ```rust -fn main() { - const V_DEFAULT: i8 = 1; - - let s = Some(10); - let n: Option = None; - let fn_closure = |v: i8| v + 2; - - assert_eq!(s.map_or(V_DEFAULT, fn_closure), 12); - assert_eq!(n.map_or(V_DEFAULT, fn_closure), V_DEFAULT); -} +const V_DEFAULT: i8 = 1; + +let s = Some(10); +let n: Option = None; +let fn_closure = |v: i8| v + 2; + +assert_eq!(s.map_or(V_DEFAULT, fn_closure), 12); +assert_eq!(n.map_or(V_DEFAULT, fn_closure), V_DEFAULT); ``` -- `map_or_else()` : Support for both `Option` and `Result` types (`Result` still nightly only). Similar to `map_or()` but should provide another closure instead a default value for the first parameter. +- `map_or_else()` : Support for both `Option` and `Result` types. Similar to `map_or()` but should provide another closure instead a default value for the first parameter. ⭐ `None` types doesn’t contain any value. So no need to pass anything to the closure as input with `Option` types. But `Err` types contain some value inside it. So default closure should able to read it as an input, while using this with `Result` types. ```rust -#![feature(result_map_or_else)] // enable unstable library feature 'result_map_or_else' on nightly -fn main() { - let s = Some(10); - let n: Option = None; +let s = Some(10); +let n: Option = None; - let fn_closure = |v: i8| v + 2; - let fn_default = || 1; // None doesn't contain any value. So no need to pass anything to closure as input. +let fn_closure = |v: i8| v + 2; +let fn_default = || 1; // None doesn't contain any value. So no need to pass anything to closure as input. - assert_eq!(s.map_or_else(fn_default, fn_closure), 12); - assert_eq!(n.map_or_else(fn_default, fn_closure), 1); +assert_eq!(s.map_or_else(fn_default, fn_closure), 12); +assert_eq!(n.map_or_else(fn_default, fn_closure), 1); +``` + +```rust +let o = Ok(10); +let e = Err(5); - let o = Ok(10); - let e = Err(5); - let fn_default_for_result = |v: i8| v + 1; // Err contain some value inside it. So default closure should able to read it as input +let fn_closure = |v: i8| v + 2; +let fn_default_for_result = |v: i8| v + 1; // Err contain some value inside it. So default closure should able to read it as input - assert_eq!(o.map_or_else(fn_default_for_result, fn_closure), 12); - assert_eq!(e.map_or_else(fn_default_for_result, fn_closure), 6); -} +assert_eq!(o.map_or_else(fn_default_for_result, fn_closure), 12); +assert_eq!(e.map_or_else(fn_default_for_result, fn_closure), 6); ``` ## ok_or() and ok_or_else() @@ -268,39 +298,35 @@ As mentioned earlier, `ok_or()`, `ok_or_else()` transform `Option` type into `Re - `ok_or()` : A default `Err` message should pass as argument. ```rust -fn main() { - const ERR_DEFAULT: &str = "error message"; +const ERR_DEFAULT: &str = "error message"; - let s = Some("abcde"); - let n: Option<&str> = None; +let s = Some("abcde"); +let n: Option<&str> = None; - let o: Result<&str, &str> = Ok("abcde"); - let e: Result<&str, &str> = Err(ERR_DEFAULT); +let o: Result<&str, &str> = Ok("abcde"); +let e: Result<&str, &str> = Err(ERR_DEFAULT); - assert_eq!(s.ok_or(ERR_DEFAULT), o); // Some(T) -> Ok(T) - assert_eq!(n.ok_or(ERR_DEFAULT), e); // None -> Err(default) -} +assert_eq!(s.ok_or(ERR_DEFAULT), o); // Some(T) -> Ok(T) +assert_eq!(n.ok_or(ERR_DEFAULT), e); // None -> Err(default) ``` - `ok_or_else()` : Similar to `ok_or()`. A closure should be passed as the argument. ```rust -fn main() { - let s = Some("abcde"); - let n: Option<&str> = None; - let fn_err_message = || "error message"; +let s = Some("abcde"); +let n: Option<&str> = None; +let fn_err_message = || "error message"; - let o: Result<&str, &str> = Ok("abcde"); - let e: Result<&str, &str> = Err("error message"); +let o: Result<&str, &str> = Ok("abcde"); +let e: Result<&str, &str> = Err("error message"); - assert_eq!(s.ok_or_else(fn_err_message), o); // Some(T) -> Ok(T) - assert_eq!(n.ok_or_else(fn_err_message), e); // None -> Err(default) -} +assert_eq!(s.ok_or_else(fn_err_message), o); // Some(T) -> Ok(T) +assert_eq!(n.ok_or_else(fn_err_message), e); // None -> Err(default) ``` ## as_ref() and as_mut() 🔎 As mentioned earlier, these functions are used to **borrow type T as a reference or as a mutable reference**. -- `as_ref()` : Convert `Option` to `Option<&T>` and `Result` to `Result<&T, &E>` +- `as_ref()` : Convert `Option` to `Option<&T>` and `Result` to `Result<&T, &E>` - `as_mut()` : Converts `Option` to `Option<&mut T>` and `Result` to `Result<&mut T, &mut E>` diff --git a/content/en/docs/e7.custom-error-types.md b/content/en/docs/e7.custom-error-types.md index d1508337..5ba6a654 100644 --- a/content/en/docs/e7.custom-error-types.md +++ b/content/en/docs/e7.custom-error-types.md @@ -17,7 +17,7 @@ pub trait Error: Debug + Display { } ``` -> As we discussed under [traits inheritance](b5.impls_and_traits.html#Traits-inheritance), a trait can be inherited from another traits. `trait Error: Debug + Display` means `Error` trait inherits from `fmt::Debug` and `fmt::Display` traits. +> As we discussed under [traits inheritance](/docs/impls-and-traits/#traits-inheritance), a trait can be inherited from another traits. `trait Error: Debug + Display` means `Error` trait inherits from `fmt::Debug` and `fmt::Display` traits. ```rust // traits inside Rust standard library core fmt module/ std::fmt @@ -197,7 +197,8 @@ pub trait From: Sized { } ``` -> 💡 As you know, `String::from()` function is used to create a `String` from `&str` data type. Actually this also an implementation of `std::convert::From` trait. +> [!tip] +> As you know, `String::from()` function is used to create a `String` from `&str` data type. Actually this also an implementation of `std::convert::From` trait. Let’s see how to implement `std::convert::From` trait on a custom error type. @@ -292,4 +293,5 @@ Error: AppError { kind: "io", message: "Permission denied (os error 13)" } Error: AppError { kind: "parse", message: "invalid digit found in string" } ``` -> 🔎 Search about the implementation of [`std::io::ErrorKind`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html), to see how to organize error types further. +> [!assignment] +> Search about the implementation of [`std::io::ErrorKind`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html), to see how to organize error types further. diff --git a/content/en/docs/learning_rust_medium.png b/content/en/docs/learning_rust_medium.png index cf7a6592..f2e7a465 100644 Binary files a/content/en/docs/learning_rust_medium.png and b/content/en/docs/learning_rust_medium.png differ diff --git a/content/en/docs/rust_playground.png b/content/en/docs/rust_playground.png index 5833453e..580f0049 100644 Binary files a/content/en/docs/rust_playground.png and b/content/en/docs/rust_playground.png differ diff --git a/content/en/home/showcase/_index.md b/content/en/home/showcase/_index.md new file mode 100644 index 00000000..c6aabedd --- /dev/null +++ b/content/en/home/showcase/_index.md @@ -0,0 +1,115 @@ +--- +headless: true + +title: See the syntax in action +description: Let's explore a few syntaxes of the language. +--- + +{{< accordion-vertical-tabs count="3" tabHeight="3.5rem" label="Select feature" >}} +{{< tab label="Data and behavior" >}} + +```rust {title="Structs, Impls and Traits"} +struct Person { + name: String, + company_name: String, +} + +impl Person { + fn new(name: String, company_name: String) -> Self { + Self { name, company_name } + } + + fn intro(&self) -> String { + format!("I'm {} from {}", self.name, self.company_name) + } +} + +trait Greet { + const PREFIX: &'static str = "Hello"; + + fn greet(&self) -> String { + format!("{}!", String::from(Self::PREFIX)) + } +} +impl Greet for Person {} + +fn main() { + let steve = Person::new("Steve".to_string(), "Apple".to_string()); + + println!("{}", steve.greet()); // Hello! + println!("{}", steve.intro()); // I'm Steve from Apple +} +``` + +{{< /tab >}} +{{< tab label="Functional" >}} + +```rust {title="Iterators, Filters, and Maps"} +fn main() { + let marks = ["10", "20", "30", "0.4", "0.5", "invalid"]; + + let sum: i32 = marks + // Iterate through marks + .iter() + // Parse string to float, ignore if invalid + .filter_map(|&s| s.parse::().ok()) + // Multiply decimals under 1.0 by 100 + .map(|num| if num < 1.0 { num * 100.0 } else { num }) + // Trace the values + .inspect(|&val| println!("Parsed: {val}")) + // Cast float to integer + .map(|num| num as i32) + // Aggregate + .sum(); + + println!("Final: {sum}"); // 150 +} +``` + +{{< /tab >}} +{{< tab label="Advanced blueprint" >}} + +```rust {title="Enums, Generics and HashMap"} +use std::collections::HashMap; + +enum Data { + Value(V), + KeyValue(K, V), +} + +fn main() { + let data: Vec> = vec![ + Data::Value(10), + Data::KeyValue("Steve", 20), + Data::KeyValue("Tom", 30), + Data::Value(40), + Data::KeyValue("Mike", 50), + ]; + + let map: HashMap = data + .into_iter() + // Pattern matching and destructuring + .map(|item| match item { + Data::KeyValue(k, v) => { + println!("{}: {}", k, v); + (k.to_string(), v) + } + Data::Value(v) => { + println!("unknown: {}", v); + ("unknown".to_string(), v) + } + }) + // Accumulate items into a map + .fold(HashMap::new(), |mut map, (key, value)| { + map.entry(key) + .and_modify(|existing| *existing += value) + .or_insert(value); + map + }); + + println!("Map: {:?}", map); // Map: {"unknown": 50, "Steve": 20, "Tom": 30, "Mike": 50} +} +``` + +{{< /tab >}} +{{< /accordion-vertical-tabs >}} \ No newline at end of file diff --git a/data/en/docs/sidebar.yml b/data/en/docs/sidebar.yml index 69b966e8..d45e91ae 100644 --- a/data/en/docs/sidebar.yml +++ b/data/en/docs/sidebar.yml @@ -21,7 +21,8 @@ - title: Structs - title: Enums - title: Generics - - title: Impls and Traits + - title: Impls + - title: Traits - title: The Tough Part pages: @@ -31,12 +32,10 @@ - title: Let's Get It Started pages: - - title: Code Organization - - title: Functions (02) - title: Modules + - title: Use - title: Crates - title: Workspaces - - title: Use - title: STD, Primitives and Preludes - title: Error Handling diff --git a/data/en/home/card-grid.yaml b/data/en/home/card-grid.yaml new file mode 100644 index 00000000..102f93f9 --- /dev/null +++ b/data/en/home/card-grid.yaml @@ -0,0 +1,32 @@ +header: + title: Contribution + description: Without them, we couldn't build this website + +items: + - icon: build/gohugo + title: GoHugo + description: The world’s fastest framework for building websites. + link: + label: Contribute + url: https://gohugo.io/ + + - icon: build/pagefind + title: Pagefind + description: Fully static search library that aims to perform well on large sites. + link: + label: Contribute + url: https://pagefind.app/ + + - icon: build/terminal + title: e25DX + description: Modern & modular technical documentation and blog setup. + link: + label: Contribute + url: https://github.com/dumindu/e25dx + + - icon: build/web + title: Documentation + description: Help us to improve the documentation. + link: + label: Contribute + url: https://github.com/learning-rust/learning-rust.github.io \ No newline at end of file diff --git a/data/en/home/hero.yaml b/data/en/home/hero.yaml new file mode 100644 index 00000000..6d58b922 --- /dev/null +++ b/data/en/home/hero.yaml @@ -0,0 +1,15 @@ +badge: Made by a human · Made for a human + +svg: hero.svg + +title: Rust Illuminated.
Grasp Quickly. + +description: Rust Programming Language Tutorials for Everyone! + +btn_primary: + label: Get Started + url: /docs + +btn_secondary: + label: Contribute + url: https://github.com/learning-rust/learning-rust.github.io \ No newline at end of file diff --git a/docs/assets/css/docs.min.42ab30b1b82d7f004c644925b0ab731f513d3095c91a3fc339909d899d3a9cb9.css b/docs/assets/css/docs.min.42ab30b1b82d7f004c644925b0ab731f513d3095c91a3fc339909d899d3a9cb9.css new file mode 100644 index 00000000..97055f34 --- /dev/null +++ b/docs/assets/css/docs.min.42ab30b1b82d7f004c644925b0ab731f513d3095c91a3fc339909d899d3a9cb9.css @@ -0,0 +1,2 @@ +*:where(:not(html,iframe,canvas,img,svg,video,audio,pre,code):not(svg *,symbol *)){all:unset;display:revert}*,*::before,*::after{box-sizing:border-box}html{-moz-text-size-adjust:none;-webkit-text-size-adjust:none;text-size-adjust:none}a,button{cursor:revert}ol,ul,menu,summary{list-style:none}ol{counter-reset:revert}img{max-inline-size:100%;max-block-size:100%}table{border-collapse:collapse}input,textarea{-webkit-user-select:auto}textarea{white-space:revert}meter{-webkit-appearance:revert;appearance:revert}:where(pre){all:revert;box-sizing:border-box}::placeholder{color:unset}::marker{content:initial}:where([hidden]){display:none}:where([contenteditable]:not([contenteditable=false])){-moz-user-modify:read-write;-webkit-user-modify:read-write;overflow-wrap:break-word;-webkit-line-break:after-white-space;-webkit-user-select:auto}:where([draggable=true]){-webkit-user-drag:element}:where(dialog:modal){all:revert;box-sizing:border-box}pre,code{margin:0}::-webkit-details-marker{display:none}:root{--font-sans:ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-serif:ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--primary:rgb(245 212 31);--background:rgb(255 255 255);--background-subtle:rgb(200 200 200 / .1);--background-hover:rgb(220, 220, 220, .35);--background-dim:rgb(247 247 247 / .25);--color:rgb(34 40 40);--color-fade:rgb(153 153 153);--color-nav:rgb(0 0 0/ .7);--box-shadow:0 25px 50px -12px rgb(0 0 0/ .2);--model-background:#ffffff;--model-foreground:rgb(247 247 247/ .25);--model-box-shadow:0 8px 32px 0 rgb(31 38 135/ .37);--padding:12px;--padding_d2:6px;--padding_x2:24px;--blur:4px;--model-blur:10px;--border:rgb(0 0 0/ .063);--border-nav:rgb(0 0 0/ .133);--border-subtle:rgb(200 200 200 / .2);--radius:6px;--anchor:#235ce8;--hover:#547ce3;--chroma-base00:var(--background-subtle);--chroma-base01:#e0e0e0;--chroma-base02:rgb(159 218 159/ .2);--chroma-base03:#8e908c;--chroma-base04:#969896;--chroma-base05:#4d4d4c;--chroma-base06:#282a2e;--chroma-base07:#1d1f21;--chroma-base08:#c82829;--chroma-base09:#f5871f;--chroma-base0A:#eab700;--chroma-base0B:#718c00;--chroma-base0C:#3e999f;--chroma-base0D:#4271ae;--chroma-base0E:#8959a8;--chroma-base0F:#a3685a;--alert-note:rgb(from var(--chroma-base0D) r g b / .06);--alert-tip:rgb(from var(--chroma-base0B) r g b / .06);--alert-important:rgb(from var(--chroma-base0E) r g b / .06);--alert-warning:rgb(from var(--chroma-base09) r g b / .06);--alert-caution:rgb(from var(--chroma-base08) r g b / .06);--alert-search:rgb(from var(--chroma-base0E) r g b / .06);--alert-assignment:rgb(from var(--chroma-base0A) r g b / .06);--alert-lab:rgb(from var(--chroma-base0B) r g b / .06);--alert-recap:rgb(from var(--chroma-base0D) r g b / .06);--cover-img-height:32svh;--section-item-cover-img-height:24svh;--site-header-height:60px}:root[data-color=dark]{--primary:rgb(245 212 31/ .8);--background:rgb(16 16 16);--background-subtle:rgb(255 255 255/ .05);--background-hover:rgb(255 255 255/ .075);--background-dim:rgb(255 255 255/ .0126);--color:rgb(255 255 255);--color-fade:rgb(255 255 255/ .5);--color-nav:rgb(255 255 255/ .8);--box-shadow:0 25px 50px -12px rgb(255 255 255/ .2);--model-background:rgb(16 16 16);--model-foreground:rgb(255 255 255/ .05);--model-box-shadow:0 8px 32px 0 rgb(241 235 235/ .1);--border:rgb(225 225 225/ .1);--border-nav:rgb(255 255 255/ .2);--border-subtle:rgb(255 255 255/ .05);--anchor:#2e87f1;--hover:#044b9d;--chroma-base00:var(--background-subtle);--chroma-base01:#393939;--chroma-base02:rgb(159 218 159/ .1);--chroma-base03:#999999;--chroma-base04:#b4b7b4;--chroma-base05:#cccccc;--chroma-base06:#e0e0e0;--chroma-base07:#ffffff;--chroma-base08:#f2777a;--chroma-base09:#f99157;--chroma-base0A:#ffcc66;--chroma-base0B:#99cc99;--chroma-base0C:#66cccc;--chroma-base0D:#6699cc;--chroma-base0E:#cc99cc;--chroma-base0F:#a3685a;--alert-note:rgb(from var(--chroma-base0D) r g b / .12);--alert-tip:rgb(from var(--chroma-base0B) r g b / .12);--alert-important:rgb(from var(--chroma-base0E) r g b / .12);--alert-warning:rgb(from var(--chroma-base09) r g b / .12);--alert-caution:rgb(from var(--chroma-base08) r g b / .12);--alert-search:rgb(from var(--chroma-base0E) r g b / .12);--alert-assignment:rgb(from var(--chroma-base0A) r g b / .12);--alert-lab:rgb(from var(--chroma-base0B) r g b / .12);--alert-recap:rgb(from var(--chroma-base0D) r g b / .12);--pf-background:var(--background);--pf-text:var(--color-nav);--pf-text-muted:var(--color-fade);--pf-hover:var(--background-hover);--pf-text-secondary:var(--color-nav);--pf-modal-backdrop:var(--background-dim);--pf-border:var(--border);--pf-mark:var(--primary);--pf-skeleton:var(--background-hover)}@view-transition{navigation: auto; +}@media(min-width:744px) and (orientation:portrait){:root{--cover-img-height:30svh;--section-item-cover-img-height:20svh}}@media(min-width:1024px){:root{--padding:16px;--padding_d2:8px;--padding_x2:32px;--cover-img-height:43svh;--section-item-cover-img-height:28svh}}@media(min-width:1280px) and (orientation:landscape){:root{--cover-img-height:50svh;--section-item-cover-img-height:22svh}}.alert{overflow:hidden;backdrop-filter:blur(var(--blur));border-radius:var(--radius);border:1px solid var(--border-subtle)}.alert-header{display:flex;align-items:center;padding:var(--padding_d2);gap:calc(var(--padding_d2)/2);border-bottom:1px solid var(--border-subtle);fill:currentColor}.alert-body{overflow-x:auto;padding:var(--padding_d2)}@media(min-width:744px) and (max-width:1023px){.alert-body{padding:var(--padding)}}@media(min-width:1280px){.alert-body{padding:var(--padding)}}.alert-note{background:var(--alert-note)}.alert-tip{background:var(--alert-tip)}.alert-important{background:var(--alert-important)}.alert-warning{background:var(--alert-warning)}.alert-caution{background:var(--alert-caution)}.alert-search{background:var(--alert-search)}.alert-assignment{background:var(--alert-assignment)}.alert-lab{background:var(--alert-lab)}.alert-recap{background:var(--alert-recap)}.alert-note .alert-header{fill:rgb(from var(--alert-note) r g b/1)}.alert-tip .alert-header{fill:rgb(from var(--alert-tip) r g b/1)}.alert-important .alert-header{fill:rgb(from var(--alert-important) r g b/1)}.alert-warning .alert-header{fill:rgb(from var(--alert-warning) r g b/1)}.alert-caution .alert-header{fill:rgb(from var(--alert-caution) r g b/1)}.alert-search .alert-header{fill:rgb(from var(--alert-search) r g b/1)}.alert-assignment .alert-header{fill:rgb(from var(--alert-assignment) r g b/1)}.alert-lab .alert-header{fill:rgb(from var(--alert-lab) r g b/1)}.alert-recap .alert-header{fill:rgb(from var(--alert-recap) r g b/1)}.btn{display:flex;padding:var(--padding_d2);gap:calc(var(--padding_d2)/2);justify-items:center;align-items:center;background:var(--background-dim);backdrop-filter:blur(--blur);border:1px solid var(--border);border-radius:var(--radius);color:var(--color-nav);fill:currentColor;cursor:pointer}.btn:hover,.btn:focus{background:var(--background-hover)}.btn-primary{background:rgb(from var(--primary) r g b/.5);border:1px solid rgb(from var(--primary) r g b/.6);color:rgb(from var(--color-nav) r g b/.8)}.btn-primary:hover,.btn-primary:focus{background:var(--primary);color:var(--color-nav)}.dropdown{position:relative}.dropdown-btn{display:flex;flex-direction:row;gap:calc(var(--padding_d2)/3);padding:var(--padding_d2);background:var(--background-dim);backdrop-filter:blur(--blur);border:1px solid var(--border);border-radius:var(--radius);color:var(--color-nav);fill:currentColor;cursor:pointer}.dropdown-btn:hover,.dropdown-btn:focus{background:var(--background-hover)}.dropdown-btn>span:last-child{opacity:.8}.dropdown-menu{display:none;position:absolute;z-index:15;right:0;top:2.4rem;min-width:3.6rem;max-height:15rem;overflow-x:auto;background:var(--background-hover);color:var(--color-nav);border-radius:var(--radius);padding:var(--padding_d2)}.dropdown-menu.show{display:block}.dropdown-menu button,.dropdown-menu a{width:100%;display:flex;gap:var(--padding_d2);padding:var(--padding_d2);align-items:center;justify-content:center;cursor:pointer}.dropdown-menu button:hover,.dropdown-menu a:hover{background:var(--background-hover);color:var(--color-nav)}@media(min-width:1024px){.dropdown-menu{top:2.7rem}}input[type=checkbox]{pointer-events:none;margin-right:.3rem;width:1.15em;height:1.15em;border:.15em solid var(--color-nav);border-radius:.15em;display:inline-flex;align-items:center;justify-content:center;vertical-align:middle}input[type=checkbox]::before{content:"";width:.75em;height:.75em;background-color:currentColor;transform:scale(0);transition:120ms transform ease-in-out;mask-image:url('data:image/svg+xml;utf8,');mask-size:contain;mask-repeat:no-repeat}input[type=checkbox]:checked::before{transform:scale(1)}input[type=checkbox]:disabled{color:var(--color-fade)}.code-window{background:var(--background-subtle);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}.code-header{display:flex;justify-content:space-between;align-items:center;padding:var(--padding_d2)var(--padding);border-bottom:1px solid var(--border-subtle)}.traffic-lights{display:flex;gap:.375rem}.traffic-lights .light{width:.625rem;height:.625rem;border-radius:50%;opacity:.8}.traffic-lights .light-red{background:#ff5f56}.traffic-lights .light-yellow{background:#ffbd2e}.traffic-lights .light-green{background:#27c93f}.code-title{font-size:.875rem;font-family:var(--font-mono);color:var(--color-nav)}.code-label{font-size:.625rem;line-height:2;background:var(--background-hover);padding:calc(var(--padding_d2) * .32)calc(var(--padding_d2) * .7);text-transform:uppercase}.code-body{box-shadow:var(--box-shadow)}.code-body>.chroma{background:0 0;border:none;width:100%;overflow-x:auto}.chroma{width:100%;overflow-x:auto;font-size:.9rem;color:var(--chroma-base05);background-color:var(--chroma-base00);border-radius:var(--radius);padding:var(--padding)calc(var(--padding_d2)*3)}.chroma .x{color:var(--chroma-base05)}.chroma .err{color:var(--chroma-base08)}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:var(--chroma-base02)}.chroma .lnt{margin-right:.4em;padding:0 .4em}.chroma .ln{margin-right:.4em;padding:0 .4em;border-right:1px solid var(--chroma-base0A)}.chroma .line{display:flex}.chroma .k{color:var(--chroma-base0E)}.chroma .kc{color:var(--chroma-base0E)}.chroma .kd{color:var(--chroma-base0E)}.chroma .kn{color:var(--chroma-base0E)}.chroma .kp{color:var(--chroma-base0D)}.chroma .kr{color:var(--chroma-base0E)}.chroma .kt{color:var(--chroma-base0E)}.chroma .n{color:var(--chroma-base05)}.chroma .na{color:var(--chroma-base05)}.chroma .nb{color:var(--chroma-base0D)}.chroma .bp{color:var(--chroma-base0D)}.chroma .nc{color:var(--chroma-base0A)}.chroma .no{color:var(--chroma-base09)}.chroma .nd{color:var(--chroma-base09)}.chroma .ni{color:var(--chroma-base0A)}.chroma .ne{color:var(--chroma-base0A)}.chroma .nf{color:var(--chroma-base05)}.chroma .fm{color:var(--chroma-base05)}.chroma .nl{color:var(--chroma-base08)}.chroma .nn{color:var(--chroma-base0A)}.chroma .nx{color:var(--chroma-base0D)}.chroma .py{color:var(--chroma-base08)}.chroma .nt{color:var(--chroma-base0D)}.chroma .nv{color:var(--chroma-base0D)}.chroma .vc{color:var(--chroma-base0D)}.chroma .vg{color:var(--chroma-base0D)}.chroma .vi{color:var(--chroma-base08)}.chroma .vm{color:var(--chroma-base0D)}.chroma .l{color:var(--chroma-base0B)}.chroma .ld{color:var(--chroma-base0B)}.chroma .s{color:var(--chroma-base0B)}.chroma .sa{color:var(--chroma-base0B)}.chroma .sb{color:var(--chroma-base0B)}.chroma .sc{color:var(--chroma-base0B)}.chroma .dl{color:var(--chroma-base0F)}.chroma .sd{color:var(--chroma-base03)}.chroma .s2{color:var(--chroma-base0B)}.chroma .se{color:var(--chroma-base0C)}.chroma .sh{color:var(--chroma-base0B)}.chroma .si{color:var(--chroma-base0F)}.chroma .sx{color:var(--chroma-base0B)}.chroma .sr{color:var(--chroma-base0C)}.chroma .s1{color:var(--chroma-base0B)}.chroma .ss{color:var(--chroma-base0B)}.chroma .m{color:var(--chroma-base09)}.chroma .mb{color:var(--chroma-base09)}.chroma .mf{color:var(--chroma-base09)}.chroma .mh{color:var(--chroma-base09)}.chroma .mi{color:var(--chroma-base09)}.chroma .il{color:var(--chroma-base09)}.chroma .mo{color:var(--chroma-base09)}.chroma .o{color:var(--chroma-base05)}.chroma .ow{color:var(--chroma-base05)}.chroma .p{color:var(--chroma-base05)}.chroma .c{color:var(--chroma-base03)}.chroma .ch{color:var(--chroma-base03)}.chroma .cm{color:var(--chroma-base03)}.chroma .c1{color:var(--chroma-base03)}.chroma .cs{color:var(--chroma-base03)}.chroma .cp{color:var(--chroma-base0F)}.chroma .cpf{color:var(--chroma-base0B)}.chroma .g{color:var(--chroma-base05)}.chroma .gd{color:var(--chroma-base08)}.chroma .ge{color:var(--chroma-base05);font-style:italic}.chroma .gr{color:var(--chroma-base05)}.chroma .gh{color:var(--chroma-base0D)}.chroma .gi{color:var(--chroma-base0B)}.chroma .go{color:var(--chroma-base05)}.chroma .gp{color:var(--chroma-base05)}.chroma .gs{color:var(--chroma-base05);font-weight:700}.chroma .gu{color:var(--chroma-base0D)}.chroma .gt{color:var(--chroma-base05)}.chroma .gl{color:var(--chroma-base05);text-decoration:underline}.chroma .w{color:var(--chroma-base00)}.tabs{display:grid;grid-template-columns:repeat(var(--tabs-count,3),minmax(80px,1fr));grid-template-rows:auto 1fr}.tabs>details{display:grid;grid-column:1/-1;grid-row:1/span 2;grid-template-columns:subgrid;grid-template-rows:subgrid}.tabs>details>summary{grid-column:var(--n)/span 1;grid-row:1;display:grid;z-index:1;cursor:pointer;font-size:.875rem;font-family:var(--font-mono);color:var(--color-nav);border:1px solid var(--border-subtle);border-top-left-radius:calc(var(--radius)*2);border-top-right-radius:calc(var(--radius)*2);background:var(--background-subtle);padding:var(--padding_d2)}.tabs>details>summary:hover,.tabs>details>summary:focus{background:var(--background-hover)}.tabs>details[open] :is(summary,.summary){background:var(--primary);color:var(--color-nav);pointer-events:none}.tabs>details::details-content{grid-row:2;grid-column:1/-1;background:var(--background-subtle);padding:0 var(--padding)var(--padding);border-bottom-left-radius:calc(var(--radius));border-bottom-right-radius:calc(var(--radius))}.tabs>details[open]::details-content{z-index:1}.tabs>details:not([open])::details-content{display:none}.accordion-vertical-tabs{display:flex;flex-direction:column;width:100%;gap:var(--padding_d2);overflow:hidden}.accordion-vertical-tabs>details{background:var(--background-subtle);border-radius:var(--radius);border:1px solid var(--border);overflow:hidden}.accordion-vertical-tabs>details>summary{font-weight:500;cursor:pointer;color:var(--color-nav);padding:var(--padding)}.accordion-vertical-tabs>details>summary:hover,.accordion-vertical-tabs>details>summary:focus{background:var(--background-hover)}.accordion-vertical-tabs>details[open] :is(summary,.summary){background:var(--primary);color:var(--color-nav);pointer-events:none}.accordion-vertical-tabs>details[open]::details-content{background:var(--background-subtle);padding:0 var(--padding)var(--padding)}@media(min-width:1024px){.accordion-vertical-tabs{display:grid;grid-template-columns:var(--tab-width,20svw)1fr;grid-template-rows:repeat(calc(var(--tabs-count,5) + 1),auto);background:var(--background-subtle);border:1px solid var(--border);border-radius:var(--radius)}.accordion-vertical-tabs>details{display:grid;grid-column:1/-1;grid-row:1/span calc(var(--tabs-count,5) + 1);grid-template-columns:subgrid;grid-template-rows:subgrid;gap:0;background:0 0;border:none}.accordion-vertical-tabs>details>summary{grid-column:1;grid-row:var(--n);height:var(--tab-height,3.75rem);display:flex;align-items:center;z-index:1;background:var(--background-subtle);border-bottom:1px solid var(--border)}.accordion-vertical-tabs>details[open]::details-content{grid-column:2;grid-row:1/span calc(var(--tabs-count,5) + 1);border-left:1px solid var(--border);overflow:auto;z-index:1}.accordion-vertical-tabs>details:not([open])::details-content{display:none}}#site-header{display:grid;grid-template-columns:1fr auto;grid-template-areas:"logo actions" "menu menu";border-bottom:1px solid var(--border);align-items:center}#site-header-logo{grid-area:logo;padding:0}#site-header-actions{grid-area:actions;display:flex;align-items:center;gap:var(--padding_d2);padding-right:var(--padding)}#site-header-menu{grid-area:menu;display:flex;flex-wrap:wrap;background:var(--background-subtle);backdrop-filter:blur(var(--blur));padding:var(--padding_d2);gap:var(--padding_d2);border-top:1px solid var(--border)}#site-header-menu a{display:flex;gap:calc(var(--padding)/3);padding:var(--padding_d2);align-items:center;text-transform:uppercase;font-size:.86em;color:var(--color-fade);fill:currentColor;border-radius:var(--radius);border:1px solid transparent}#site-header-menu a:not(.active):hover{background:var(--background-hover);color:var(--color-nav)}#site-header-menu a.active{background:var(--background-subtle);backdrop-filter:blur(var(--blur));border:1px solid var(--border-subtle);pointer-events:none}#search-box{display:flex;align-items:center;justify-content:center}#repo-main{display:flex;align-items:center;justify-content:center;fill:var(--color-nav)}#repo-main:hover{fill:var(--color)}@media(max-width:743px){.pf-trigger-shortcut{display:none!important}}@media(min-width:1024px){#site-header{grid-template-columns:minmax(0,1fr)auto minmax(0,1fr);grid-template-areas:"logo menu actions";backdrop-filter:blur(var(--blur));position:sticky;top:0;z-index:10}#site-header-logo{justify-self:start}#site-header-menu{background:0 0;border:none;justify-content:center;justify-self:center}#site-header-actions{justify-self:end}}#site-footer{display:flex;align-items:center;justify-content:center;gap:var(--padding_d2);text-align:center;flex-wrap:wrap;white-space:initial;padding:var(--padding_x2);color:var(--color-fade);fill:currentColor}#site-footer>a.btn-primary{padding:0 var(--padding_d2);font-weight:400}@media(min-width:1024px){#site-footer{width:75vw}}#right-sidebar{width:85%;right:-85%;display:none;overflow-x:auto}#right-sidebar.open{display:flex;flex-direction:column;position:fixed;top:0;height:100%;z-index:20;transition:.3s;animation:slide-in-right .3s forwards;background:var(--model-background);box-shadow:var(--model-box-shadow)}@keyframes slide-in-right{from{transform:translateX(0)}to{transform:translateX(-100%)}}#right-sidebar>div{display:flex;padding:var(--padding)}#right-sidebar>div>.btn{font-size:.75em;font-weight:800}#right-sidebar header h3{display:flex;padding:var(--padding);color:var(--color-fade);font-weight:700}#right-sidebar>nav{padding:0 var(--padding)}#TableOfContents ul{border-left:1px solid var(--border)}#TableOfContents a{color:var(--color-nav);display:inline-block;cursor:pointer;padding:var(--padding_d2)}#TableOfContents a code{padding:calc(var(--padding)/3.7);border-radius:var(--radius);font-size:.875em;font-family:var(--font-mono);background:var(--background-subtle);border:1px solid var(--border-subtle)}#TableOfContents ul a::before{content:'';display:inline-block;width:calc(var(--padding)/1.5);height:calc(var(--padding)/1.5);background:var(--background);border:1px solid var(--border-nav);position:relative;left:calc((var(--padding) * -.89));top:-3px;border-radius:30%;transform:rotate(45deg)}#TableOfContents a.active{border-top-right-radius:var(--radius);border-bottom-right-radius:var(--radius);background:var(--primary);color:var(--color-primary)}#TableOfContents ul a.active::before{background:var(--background)}#TableOfContents a:hover:not(.active){background:var(--background-hover);border-top-right-radius:var(--radius);border-bottom-right-radius:var(--radius)}#TableOfContents ul ul{margin-left:calc(var(--padding) * 1.75)}@media(min-width:744px) and (max-width:1023px){#right-sidebar{width:50%;right:-50%}}@media(min-width:1024px){#right-sidebar{width:25%;display:flex;flex-direction:column;position:sticky;top:var(--site-header-height);height:auto;max-height:calc(100svh - var(--site-header-height));overflow-x:auto;flex-shrink:0;align-self:start}#right-sidebar>div{display:none}}#back-to-top{display:inline-block;padding:var(--padding);color:var(--color-nav);border-radius:var(--radius)}#back-to-top:hover{background:var(--background-hover);color:var(--color)}#article-header{padding:var(--padding)var(--padding_x2)}#article-header>h1{font-size:3em}#article-header>p{font-size:1.25em;color:var(--color-fade);padding-top:var(--padding)}#article-nav{display:flex;padding:var(--padding_d2)var(--padding);justify-content:space-between;position:sticky;top:0;z-index:10;background:var(--background-subtle);backdrop-filter:blur(var(--blur))}#article-nav .btn{border-color:var(--border-subtle)}#article-cover{height:var(--cover-img-height);object-fit:cover;margin:var(--padding)var(--padding_x2);border-radius:calc(var(--radius)*3)}#article-footer{display:grid;grid-template-columns:repeat(2,1fr);padding:0 var(--padding_x2);row-gap:var(--padding)}#article-footer>time{grid-column:1/3;display:flex;color:var(--color-fade);fill:currentColor;justify-content:center;padding:var(--padding)0;gap:var(--padding_d2)}#article-footer>a{display:flex;padding:var(--padding);gap:calc(var(--padding)/4);background:var(--background-dim);backdrop-filter:blur(var(--blur));border:1px solid var(--border);border-radius:var(--radius);color:var(--color-nav);fill:currentColor;cursor:pointer;width:75%}#article-footer>a:hover{background:var(--background-hover)}#article-footer>a.hidden{visibility:hidden}#article-footer>a:last-child{display:flex;justify-content:end;margin-left:25%}#article-body{display:flex;flex-direction:column;flex:1;padding:var(--padding)var(--padding_x2);overflow-x:auto}#article-body p,#article-body dl,#article-body ol,#article-body ul,#article-body blockquote,#article-body table,#article-body pre:not(.chroma),#article-body .highlight,#article-body iframe{line-height:1.8em;letter-spacing:-.1px}#article-body strong,#article-body b,#article-body table th{font-weight:600}#article-body em{font-style:italic}#article-body del{text-decoration:line-through;text-decoration-thickness:.1em}#article-body h1,#article-body h2,#article-body h3,#article-body h4,#article-body h5,#article-body h6{line-height:1.25em;margin:2.5rem 0 .75rem;color:var(--color)}#article-body h1{font-size:2.369em}#article-body h2{font-size:1.777em}#article-body h3{font-size:1.4em}#article-body h4{font-size:1.2em}#article-body h2:first-child{margin-top:.2em}#article-body ul{list-style:disc}#article-body ol{list-style:decimal}#article-body dl{list-style:square}#article-body li>ul{list-style:circle}#article-body li>ol{list-style:lower-alpha}#article-body li>ul>li>ul{list-style:square}#article-body li>ol>li>ol{list-style:lower-roman}#article-body ul:has(li input[type=checkbox]){list-style-type:none;margin-left:.8rem}#article-body blockquote{padding:var(--padding);background:var(--background-subtle);border:1px solid var(--border-subtle);border-radius:var(--radius)}#article-body blockquote footer{margin:1em 0;font-style:italic}#article-body blockquote footer cite:before{content:"—";padding:0 .3em}#article-body blockquote footer cite a{color:var(--border)}#article-body hr:before{display:block;text-align:center;content:"∙ ∙ ∙";color:var(--color-fade);letter-spacing:.6em;top:calc(var(--padding)/3.7);margin:2.6em 0 1em}#article-body table{max-width:100%;border-radius:var(--radius);box-shadow:0 0 0 1px var(--border)}#article-body table thead th:first-child{border-top-left-radius:var(--radius)}#article-body table thead th:last-child{border-top-right-radius:var(--radius)}#article-body table tbody tr:last-child{border-bottom-left-radius:var(--radius);border-bottom-right-radius:var(--radius)}#article-body table tbody tr:last-child td:first-child{border-bottom-left-radius:var(--radius)}#article-body table tbody tr:last-child td:last-child{border-bottom-right-radius:var(--radius)}#article-body table td,#article-body table th{padding:calc(var(--padding)/3)var(--padding)}#article-body table thead tr,#article-body table tr:nth-child(2n){background:var(--background-subtle)}#article-body a{color:var(--anchor);text-decoration:none}#article-body a:hover{color:var(--hover);text-decoration:underline}#article-body img,#article-body video{max-width:100%;border-radius:var(--radius)}#article-body .highlight,#article-body blockquote,#article-body .alert,#article-body .tabs,#article-body .code-window,#article-body iframe,#article-body p,#article-body table{margin-top:1em}#article-body li p{margin-top:0}#article-body blockquote+blockquote{margin-top:0}#article-body blockquote,#article-body li .highlight,#article-body li .alert,#article-body li .tabs,#article-body li .code-window,#article-body li iframe,#article-body li table{margin-bottom:1em}#article-body dl,#article-body ol,#article-body ul{margin:1.8rem 0 0 1.8rem}#article-body dl dl,#article-body dl ol,#article-body dl ul,#article-body ol dl,#article-body ol ol,#article-body ol ul,#article-body ul dl,#article-body ul ol,#article-body ul ul{margin-top:0}#article-body li table{width:100%}#article-body li .highlight,#article-body li blockquote{margin:.6rem 1.2rem 1.2rem 0}#article-body li blockquote{padding:var(--padding_d2)}#article-body blockquote *:first-child,#article-body .alert *:first-child{margin-top:0}#article-body .chroma code,#article-body pre{font-family:var(--font-mono);border:1px solid var(--border-subtle);backdrop-filter:blur(var(--blur))}#article-body code{font-family:var(--font-mono)}#article-body h1 code,#article-body h2 code,#article-body h3 code,#article-body h4 code,#article-body h5 code,#article-body h6 code,#article-body p code{font-size:.9em}#article-body p code,#article-body blockquote code,#article-body ul code,#article-body ol code,#article-body dl code,#article-body table code{font-size:.9rem}#article-body h1 code,#article-body h2 code,#article-body h3 code,#article-body h4 code,#article-body h5 code,#article-body h6 code,#article-body p code,#article-body blockquote code,#article-body ul code,#article-body ol code,#article-body dl code,#article-body table code{padding:calc(var(--padding)/3.7);background:var(--background-subtle);border-radius:var(--radius);border:1px solid var(--border-subtle)}#article-body .chroma code{padding:unset;background:unset;border-radius:unset;border:unset;backdrop-filter:unset}#article-body .code-body>.chroma{background:0 0;border:none}#article-body blockquote code{background:var(--background-subtle)}#article-body pre:not(.chroma){font-size:.9em;color:var(--chroma-base05);background-color:var(--chroma-base00);border-radius:var(--radius);padding:var(--padding)calc(var(--padding_d2)*3);overflow-x:auto;margin-top:1em}#article-body blockquote .chroma,#article-body blockquote pre:not(.chroma){margin-bottom:1em}#article-body blockquote .chroma code,#article-body blockquote pre:not(.chroma) code{padding:0}#article-body li>.highlight>.chroma>code{display:block;background:0 0}@media(min-width:744px){#article-footer>a{width:50%}#article-footer>a:last-child{margin-left:50%}}@media(min-width:1024px){#article-nav{background:0 0;backdrop-filter:none;width:fit-content;z-index:10;position:fixed;top:var(--site-header-height);padding:0 0 0 var(--padding)}#article-nav>button:first-child{backdrop-filter:blur(var(--blur));color:var(--color-fade)}#article-nav>button:last-child{display:none}}#left-sidebar{width:85%;left:-85%;display:none;overflow-x:auto}#left-sidebar.open{display:flex;flex-direction:column;position:fixed;top:0;height:100%;z-index:20;transition:.3s;animation:slide-in-left .3s forwards;background:var(--model-background);box-shadow:var(--model-box-shadow)}@keyframes slide-in-left{from{transform:translateX(0)}to{transform:translateX(100%)}}#left-sidebar>div{display:flex;padding:var(--padding);justify-content:flex-end}#left-sidebar>a{display:none}#left-sidebar>div .btn{font-size:.75em;font-weight:800}#left-sidebar{position:sticky;top:0}#left-sidebar>nav{padding:0 var(--padding);view-transition-name:section-nav}#left-sidebar>nav>details{padding-bottom:0}#left-sidebar>nav>details[open]{padding-bottom:calc(var(--padding) * 1.5)}#left-sidebar>nav>details>summary{display:flex;padding:var(--padding)0;color:var(--color);font-weight:700}#left-sidebar>nav>details[open]>summary{color:var(--color-fade)}#left-sidebar>nav>details>ul>li{margin:0 var(--padding_d2);border-left:1px solid var(--border-nav)}#left-sidebar>nav>details>ul>li>a{color:var(--color-nav);display:flex;cursor:pointer;padding:var(--padding_d2)}#left-sidebar>nav>details>ul>li>a::before{content:'';display:inline-block;background:var(--background);width:var(--padding_d2);height:var(--padding_d2);transform:rotate(45deg);border:1px solid var(--border-nav);position:relative;left:calc((var(--padding) * -.78));top:3px}#left-sidebar>nav>details>ul>li>a.active{border-top-right-radius:var(--radius);border-bottom-right-radius:var(--radius);background:var(--primary);color:var(--color-primary)}#left-sidebar>nav>details>ul>li>a.active::before{left:calc((var(--padding) * -.76));background:var(--primary);border:var(--primary)}#left-sidebar>nav>details>ul>li>a:hover:not(.active){background:var(--background-hover);border-top-right-radius:var(--radius);border-bottom-right-radius:var(--radius)}@media(min-width:744px) and (max-width:1023px){#left-sidebar{width:50%;left:-50%}}@media(min-width:1024px) and (max-width:1279px){#left-sidebar{width:33%;left:-33%}}@media(min-width:1280px){#left-sidebar{width:20%;display:flex;flex-direction:column;height:100svh;background:var(--background-dim);border-right:1px solid var(--border);overflow-x:auto}#left-sidebar>div{display:none}#left-sidebar>nav{overflow-x:auto}#left-sidebar>a{display:flex;padding:2px 0}}html{scroll-behavior:smooth;scroll-padding:6em}@media(1024px <= width <= 1279px){html{scroll-padding:8em}}body{display:flex;flex-direction:column;height:100%;color:var(--color);background:var(--background);font-family:var(--font-sans)}#content-wrapper{display:flex;flex:1;flex-direction:column;min-height:100svh}main{display:flex;flex-direction:column;flex:1;width:100svw}main>article{display:flex;flex:1;flex-direction:column}body.model-open{overflow:hidden}#off-canvas-model{display:none;content:"";position:fixed;top:0;left:0;width:100%;height:100%;z-index:15;background:var(--model-foreground);backdrop-filter:blur(var(--blur))}@media(min-width:1024px){main{margin-top:0;flex-direction:row}main>article{width:75vw;overflow-x:auto;margin-top:calc(var(--padding_x2) *1.2)}}@media(min-width:1280px){body{flex-direction:row}main{width:80svw;padding:0 var(--padding_x2)}main>article{width:60vw;margin-top:0}#site-header{grid-template-columns:auto auto;grid-template-areas:"menu actions"}#site-header-logo{display:none}#site-header-menu{justify-self:start;width:60vw}#article-nav{display:none}#site-footer{width:60vw}#right-sidebar{width:25%}#article-header{padding:var(--padding_x2)var(--padding_x2)var(--padding)}} \ No newline at end of file diff --git a/docs/assets/css/home.min.2f64b534694f7783ee00c07c45f130ef260d59af33aefe9e41a6606a53c5de38.css b/docs/assets/css/home.min.2f64b534694f7783ee00c07c45f130ef260d59af33aefe9e41a6606a53c5de38.css new file mode 100644 index 00000000..279342f1 --- /dev/null +++ b/docs/assets/css/home.min.2f64b534694f7783ee00c07c45f130ef260d59af33aefe9e41a6606a53c5de38.css @@ -0,0 +1,2 @@ +*:where(:not(html,iframe,canvas,img,svg,video,audio,pre,code):not(svg *,symbol *)){all:unset;display:revert}*,*::before,*::after{box-sizing:border-box}html{-moz-text-size-adjust:none;-webkit-text-size-adjust:none;text-size-adjust:none}a,button{cursor:revert}ol,ul,menu,summary{list-style:none}ol{counter-reset:revert}img{max-inline-size:100%;max-block-size:100%}table{border-collapse:collapse}input,textarea{-webkit-user-select:auto}textarea{white-space:revert}meter{-webkit-appearance:revert;appearance:revert}:where(pre){all:revert;box-sizing:border-box}::placeholder{color:unset}::marker{content:initial}:where([hidden]){display:none}:where([contenteditable]:not([contenteditable=false])){-moz-user-modify:read-write;-webkit-user-modify:read-write;overflow-wrap:break-word;-webkit-line-break:after-white-space;-webkit-user-select:auto}:where([draggable=true]){-webkit-user-drag:element}:where(dialog:modal){all:revert;box-sizing:border-box}pre,code{margin:0}::-webkit-details-marker{display:none}:root{--font-sans:ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-serif:ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--primary:rgb(245 212 31);--background:rgb(255 255 255);--background-subtle:rgb(200 200 200 / .1);--background-hover:rgb(220, 220, 220, .35);--background-dim:rgb(247 247 247 / .25);--color:rgb(34 40 40);--color-fade:rgb(153 153 153);--color-nav:rgb(0 0 0/ .7);--box-shadow:0 25px 50px -12px rgb(0 0 0/ .2);--model-background:#ffffff;--model-foreground:rgb(247 247 247/ .25);--model-box-shadow:0 8px 32px 0 rgb(31 38 135/ .37);--padding:12px;--padding_d2:6px;--padding_x2:24px;--blur:4px;--model-blur:10px;--border:rgb(0 0 0/ .063);--border-nav:rgb(0 0 0/ .133);--border-subtle:rgb(200 200 200 / .2);--radius:6px;--anchor:#235ce8;--hover:#547ce3;--chroma-base00:var(--background-subtle);--chroma-base01:#e0e0e0;--chroma-base02:rgb(159 218 159/ .2);--chroma-base03:#8e908c;--chroma-base04:#969896;--chroma-base05:#4d4d4c;--chroma-base06:#282a2e;--chroma-base07:#1d1f21;--chroma-base08:#c82829;--chroma-base09:#f5871f;--chroma-base0A:#eab700;--chroma-base0B:#718c00;--chroma-base0C:#3e999f;--chroma-base0D:#4271ae;--chroma-base0E:#8959a8;--chroma-base0F:#a3685a;--alert-note:rgb(from var(--chroma-base0D) r g b / .06);--alert-tip:rgb(from var(--chroma-base0B) r g b / .06);--alert-important:rgb(from var(--chroma-base0E) r g b / .06);--alert-warning:rgb(from var(--chroma-base09) r g b / .06);--alert-caution:rgb(from var(--chroma-base08) r g b / .06);--alert-search:rgb(from var(--chroma-base0E) r g b / .06);--alert-assignment:rgb(from var(--chroma-base0A) r g b / .06);--alert-lab:rgb(from var(--chroma-base0B) r g b / .06);--alert-recap:rgb(from var(--chroma-base0D) r g b / .06);--cover-img-height:32svh;--section-item-cover-img-height:24svh;--site-header-height:60px}:root[data-color=dark]{--primary:rgb(245 212 31/ .8);--background:rgb(16 16 16);--background-subtle:rgb(255 255 255/ .05);--background-hover:rgb(255 255 255/ .075);--background-dim:rgb(255 255 255/ .0126);--color:rgb(255 255 255);--color-fade:rgb(255 255 255/ .5);--color-nav:rgb(255 255 255/ .8);--box-shadow:0 25px 50px -12px rgb(255 255 255/ .2);--model-background:rgb(16 16 16);--model-foreground:rgb(255 255 255/ .05);--model-box-shadow:0 8px 32px 0 rgb(241 235 235/ .1);--border:rgb(225 225 225/ .1);--border-nav:rgb(255 255 255/ .2);--border-subtle:rgb(255 255 255/ .05);--anchor:#2e87f1;--hover:#044b9d;--chroma-base00:var(--background-subtle);--chroma-base01:#393939;--chroma-base02:rgb(159 218 159/ .1);--chroma-base03:#999999;--chroma-base04:#b4b7b4;--chroma-base05:#cccccc;--chroma-base06:#e0e0e0;--chroma-base07:#ffffff;--chroma-base08:#f2777a;--chroma-base09:#f99157;--chroma-base0A:#ffcc66;--chroma-base0B:#99cc99;--chroma-base0C:#66cccc;--chroma-base0D:#6699cc;--chroma-base0E:#cc99cc;--chroma-base0F:#a3685a;--alert-note:rgb(from var(--chroma-base0D) r g b / .12);--alert-tip:rgb(from var(--chroma-base0B) r g b / .12);--alert-important:rgb(from var(--chroma-base0E) r g b / .12);--alert-warning:rgb(from var(--chroma-base09) r g b / .12);--alert-caution:rgb(from var(--chroma-base08) r g b / .12);--alert-search:rgb(from var(--chroma-base0E) r g b / .12);--alert-assignment:rgb(from var(--chroma-base0A) r g b / .12);--alert-lab:rgb(from var(--chroma-base0B) r g b / .12);--alert-recap:rgb(from var(--chroma-base0D) r g b / .12);--pf-background:var(--background);--pf-text:var(--color-nav);--pf-text-muted:var(--color-fade);--pf-hover:var(--background-hover);--pf-text-secondary:var(--color-nav);--pf-modal-backdrop:var(--background-dim);--pf-border:var(--border);--pf-mark:var(--primary);--pf-skeleton:var(--background-hover)}@view-transition{navigation: auto; +}@media(min-width:744px) and (orientation:portrait){:root{--cover-img-height:30svh;--section-item-cover-img-height:20svh}}@media(min-width:1024px){:root{--padding:16px;--padding_d2:8px;--padding_x2:32px;--cover-img-height:43svh;--section-item-cover-img-height:28svh}}@media(min-width:1280px) and (orientation:landscape){:root{--cover-img-height:50svh;--section-item-cover-img-height:22svh}}.btn{display:flex;padding:var(--padding_d2);gap:calc(var(--padding_d2)/2);justify-items:center;align-items:center;background:var(--background-dim);backdrop-filter:blur(--blur);border:1px solid var(--border);border-radius:var(--radius);color:var(--color-nav);fill:currentColor;cursor:pointer}.btn:hover,.btn:focus{background:var(--background-hover)}.btn-primary{background:rgb(from var(--primary) r g b/.5);border:1px solid rgb(from var(--primary) r g b/.6);color:rgb(from var(--color-nav) r g b/.8)}.btn-primary:hover,.btn-primary:focus{background:var(--primary);color:var(--color-nav)}.dropdown{position:relative}.dropdown-btn{display:flex;flex-direction:row;gap:calc(var(--padding_d2)/3);padding:var(--padding_d2);background:var(--background-dim);backdrop-filter:blur(--blur);border:1px solid var(--border);border-radius:var(--radius);color:var(--color-nav);fill:currentColor;cursor:pointer}.dropdown-btn:hover,.dropdown-btn:focus{background:var(--background-hover)}.dropdown-btn>span:last-child{opacity:.8}.dropdown-menu{display:none;position:absolute;z-index:15;right:0;top:2.4rem;min-width:3.6rem;max-height:15rem;overflow-x:auto;background:var(--background-hover);color:var(--color-nav);border-radius:var(--radius);padding:var(--padding_d2)}.dropdown-menu.show{display:block}.dropdown-menu button,.dropdown-menu a{width:100%;display:flex;gap:var(--padding_d2);padding:var(--padding_d2);align-items:center;justify-content:center;cursor:pointer}.dropdown-menu button:hover,.dropdown-menu a:hover{background:var(--background-hover);color:var(--color-nav)}@media(min-width:1024px){.dropdown-menu{top:2.7rem}}.badge{display:inline-block;align-items:center;padding:var(--padding_d2)var(--padding);gap:var(--padding_d2);font-size:.7rem;font-weight:600;color:var(--color);background:var(--background-subtle);border-radius:var(--radius)}#site-header{display:grid;grid-template-columns:1fr auto;grid-template-areas:"logo actions" "menu menu";border-bottom:1px solid var(--border);align-items:center}#site-header-logo{grid-area:logo;padding:0}#site-header-actions{grid-area:actions;display:flex;align-items:center;gap:var(--padding_d2);padding-right:var(--padding)}#site-header-menu{grid-area:menu;display:flex;flex-wrap:wrap;background:var(--background-subtle);backdrop-filter:blur(var(--blur));padding:var(--padding_d2);gap:var(--padding_d2);border-top:1px solid var(--border)}#site-header-menu a{display:flex;gap:calc(var(--padding)/3);padding:var(--padding_d2);align-items:center;text-transform:uppercase;font-size:.86em;color:var(--color-fade);fill:currentColor;border-radius:var(--radius);border:1px solid transparent}#site-header-menu a:not(.active):hover{background:var(--background-hover);color:var(--color-nav)}#site-header-menu a.active{background:var(--background-subtle);backdrop-filter:blur(var(--blur));border:1px solid var(--border-subtle);pointer-events:none}#search-box{display:flex;align-items:center;justify-content:center}#repo-main{display:flex;align-items:center;justify-content:center;fill:var(--color-nav)}#repo-main:hover{fill:var(--color)}@media(max-width:743px){.pf-trigger-shortcut{display:none!important}}@media(min-width:1024px){#site-header{grid-template-columns:minmax(0,1fr)auto minmax(0,1fr);grid-template-areas:"logo menu actions";backdrop-filter:blur(var(--blur));position:sticky;top:0;z-index:10}#site-header-logo{justify-self:start}#site-header-menu{background:0 0;border:none;justify-content:center;justify-self:center}#site-header-actions{justify-self:end}}#site-footer{display:flex;align-items:center;justify-content:center;gap:var(--padding_d2);text-align:center;flex-wrap:wrap;white-space:initial;padding:var(--padding_x2);color:var(--color-fade);fill:currentColor}#site-footer>a.btn-primary{padding:0 var(--padding_d2);font-weight:400}@media(min-width:1024px){#site-footer{width:75vw}}html{scroll-behavior:smooth;scroll-padding:2em}body{display:flex;flex-direction:column;height:100%;color:var(--color);background:var(--background);font-family:var(--font-sans)}#content-wrapper{display:flex;flex:1;flex-direction:column;min-height:100svh}main{display:flex;flex:1;flex-direction:column;align-items:center;justify-content:center;gap:calc(var(--padding_x2) * 2)}#site-footer{width:100%}main>section{scroll-snap-align:start}main h2{font-size:2.5rem}main h3{font-size:1.4rem}main h4{font-size:1.1rem}.hero-section{display:flex;flex-direction:column;align-items:center;text-align:center;justify-content:center;gap:var(--padding_x2);min-height:64svh}.hero-header .hero-svg svg{padding-top:var(--padding_x2);max-width:80%;height:auto}.hero-header h2{font-size:clamp(2.5rem,11vw,4rem);font-weight:700;line-height:1.1;letter-spacing:-.05em}.hero-header h2 span{color:var(--primary);text-shadow:0 0 3px var(--border-nav)}.hero-content{max-width:48rem;font-size:1.12rem;line-height:1.3}.hero-controls{display:flex;flex-direction:row;gap:var(--padding);padding-top:var(--padding)}.hero-section .btn{font-size:1.125rem;font-weight:500;letter-spacing:.05em;text-transform:uppercase;border-radius:calc(var(--radius) * 4);padding:var(--padding_d2)var(--padding)}.hero-section .badge{text-transform:uppercase;letter-spacing:.16em;margin-bottom:var(--padding_x2)}@media(min-width:744px){.hero-section{min-height:40svh}}@media(min-width:1024px) and (orientation:landscape){.hero-section{min-height:60svh}}.card-grid-section{display:flex;flex-direction:column;gap:var(--padding_x2);padding:0 var(--padding)}.card-grid-header{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--padding)}.card-grid-body{display:grid;grid-template-columns:repeat(2,1fr);margin:0 auto;gap:var(--padding)}.card-grid-card{display:flex;flex-direction:column;justify-content:space-between;gap:var(--padding);padding:var(--padding);background:var(--background-subtle);border-radius:var(--radius);border:1px solid var(--border);transition:all .3s}.card-grid-card:hover{transform:translateY(-.5rem);background:var(--background-hover);box-shadow:var(--box-shadow)}.card-grid-card span{font-size:1.875rem;padding:var(--padding)var(--padding)var(--padding)0;fill:var(--color-nav)}.card-grid-card p{font-size:.875rem;color:var(--color-nav)}.card-grid-card footer .btn{justify-content:center}@media(min-width:744px){.card-grid-card header{display:flex;flex-direction:row-reverse;justify-content:space-between}}@media(min-width:1024px){.card-grid-body{grid-template-columns:repeat(4,1fr)}.card-grid-section{min-height:50svh;justify-content:center}}@media(min-width:1024px) and (orientation:portrait){.card-grid-section{min-height:30svh;justify-content:center}}@media(min-width:1280px){.card-grid-body{gap:var(--padding_x2)}.card-grid-card{padding:var(--padding_x2)}.card-grid-card span{padding:0}}@media(min-width:1440px){.card-grid-section{max-width:80svw;margin:0 auto}}.chroma{width:100%;overflow-x:auto;font-size:.9rem;color:var(--chroma-base05);background-color:var(--chroma-base00);border-radius:var(--radius);padding:var(--padding)calc(var(--padding_d2)*3)}.chroma .x{color:var(--chroma-base05)}.chroma .err{color:var(--chroma-base08)}.chroma .lntd{vertical-align:top;padding:0;margin:0;border:0}.chroma .lntable{border-spacing:0;padding:0;margin:0;border:0;width:auto;overflow:auto;display:block}.chroma .hl{display:block;width:100%;background-color:var(--chroma-base02)}.chroma .lnt{margin-right:.4em;padding:0 .4em}.chroma .ln{margin-right:.4em;padding:0 .4em;border-right:1px solid var(--chroma-base0A)}.chroma .line{display:flex}.chroma .k{color:var(--chroma-base0E)}.chroma .kc{color:var(--chroma-base0E)}.chroma .kd{color:var(--chroma-base0E)}.chroma .kn{color:var(--chroma-base0E)}.chroma .kp{color:var(--chroma-base0D)}.chroma .kr{color:var(--chroma-base0E)}.chroma .kt{color:var(--chroma-base0E)}.chroma .n{color:var(--chroma-base05)}.chroma .na{color:var(--chroma-base05)}.chroma .nb{color:var(--chroma-base0D)}.chroma .bp{color:var(--chroma-base0D)}.chroma .nc{color:var(--chroma-base0A)}.chroma .no{color:var(--chroma-base09)}.chroma .nd{color:var(--chroma-base09)}.chroma .ni{color:var(--chroma-base0A)}.chroma .ne{color:var(--chroma-base0A)}.chroma .nf{color:var(--chroma-base05)}.chroma .fm{color:var(--chroma-base05)}.chroma .nl{color:var(--chroma-base08)}.chroma .nn{color:var(--chroma-base0A)}.chroma .nx{color:var(--chroma-base0D)}.chroma .py{color:var(--chroma-base08)}.chroma .nt{color:var(--chroma-base0D)}.chroma .nv{color:var(--chroma-base0D)}.chroma .vc{color:var(--chroma-base0D)}.chroma .vg{color:var(--chroma-base0D)}.chroma .vi{color:var(--chroma-base08)}.chroma .vm{color:var(--chroma-base0D)}.chroma .l{color:var(--chroma-base0B)}.chroma .ld{color:var(--chroma-base0B)}.chroma .s{color:var(--chroma-base0B)}.chroma .sa{color:var(--chroma-base0B)}.chroma .sb{color:var(--chroma-base0B)}.chroma .sc{color:var(--chroma-base0B)}.chroma .dl{color:var(--chroma-base0F)}.chroma .sd{color:var(--chroma-base03)}.chroma .s2{color:var(--chroma-base0B)}.chroma .se{color:var(--chroma-base0C)}.chroma .sh{color:var(--chroma-base0B)}.chroma .si{color:var(--chroma-base0F)}.chroma .sx{color:var(--chroma-base0B)}.chroma .sr{color:var(--chroma-base0C)}.chroma .s1{color:var(--chroma-base0B)}.chroma .ss{color:var(--chroma-base0B)}.chroma .m{color:var(--chroma-base09)}.chroma .mb{color:var(--chroma-base09)}.chroma .mf{color:var(--chroma-base09)}.chroma .mh{color:var(--chroma-base09)}.chroma .mi{color:var(--chroma-base09)}.chroma .il{color:var(--chroma-base09)}.chroma .mo{color:var(--chroma-base09)}.chroma .o{color:var(--chroma-base05)}.chroma .ow{color:var(--chroma-base05)}.chroma .p{color:var(--chroma-base05)}.chroma .c{color:var(--chroma-base03)}.chroma .ch{color:var(--chroma-base03)}.chroma .cm{color:var(--chroma-base03)}.chroma .c1{color:var(--chroma-base03)}.chroma .cs{color:var(--chroma-base03)}.chroma .cp{color:var(--chroma-base0F)}.chroma .cpf{color:var(--chroma-base0B)}.chroma .g{color:var(--chroma-base05)}.chroma .gd{color:var(--chroma-base08)}.chroma .ge{color:var(--chroma-base05);font-style:italic}.chroma .gr{color:var(--chroma-base05)}.chroma .gh{color:var(--chroma-base0D)}.chroma .gi{color:var(--chroma-base0B)}.chroma .go{color:var(--chroma-base05)}.chroma .gp{color:var(--chroma-base05)}.chroma .gs{color:var(--chroma-base05);font-weight:700}.chroma .gu{color:var(--chroma-base0D)}.chroma .gt{color:var(--chroma-base05)}.chroma .gl{color:var(--chroma-base05);text-decoration:underline}.chroma .w{color:var(--chroma-base00)}.code-window{background:var(--background-subtle);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}.code-header{display:flex;justify-content:space-between;align-items:center;padding:var(--padding_d2)var(--padding);border-bottom:1px solid var(--border-subtle)}.traffic-lights{display:flex;gap:.375rem}.traffic-lights .light{width:.625rem;height:.625rem;border-radius:50%;opacity:.8}.traffic-lights .light-red{background:#ff5f56}.traffic-lights .light-yellow{background:#ffbd2e}.traffic-lights .light-green{background:#27c93f}.code-title{font-size:.875rem;font-family:var(--font-mono);color:var(--color-nav)}.code-label{font-size:.625rem;line-height:2;background:var(--background-hover);padding:calc(var(--padding_d2) * .32)calc(var(--padding_d2) * .7);text-transform:uppercase}.code-body{box-shadow:var(--box-shadow)}.code-body>.chroma{background:0 0;border:none;width:100%;overflow-x:auto}.accordion-vertical-tabs{display:flex;flex-direction:column;width:100%;gap:var(--padding_d2);overflow:hidden}.accordion-vertical-tabs>details{background:var(--background-subtle);border-radius:var(--radius);border:1px solid var(--border);overflow:hidden}.accordion-vertical-tabs>details>summary{font-weight:500;cursor:pointer;color:var(--color-nav);padding:var(--padding)}.accordion-vertical-tabs>details>summary:hover,.accordion-vertical-tabs>details>summary:focus{background:var(--background-hover)}.accordion-vertical-tabs>details[open] :is(summary,.summary){background:var(--primary);color:var(--color-nav);pointer-events:none}.accordion-vertical-tabs>details[open]::details-content{background:var(--background-subtle);padding:0 var(--padding)var(--padding)}@media(min-width:1024px){.accordion-vertical-tabs{display:grid;grid-template-columns:var(--tab-width,20svw)1fr;grid-template-rows:repeat(calc(var(--tabs-count,5) + 1),auto);background:var(--background-subtle);border:1px solid var(--border);border-radius:var(--radius)}.accordion-vertical-tabs>details{display:grid;grid-column:1/-1;grid-row:1/span calc(var(--tabs-count,5) + 1);grid-template-columns:subgrid;grid-template-rows:subgrid;gap:0;background:0 0;border:none}.accordion-vertical-tabs>details>summary{grid-column:1;grid-row:var(--n);height:var(--tab-height,3.75rem);display:flex;align-items:center;z-index:1;background:var(--background-subtle);border-bottom:1px solid var(--border)}.accordion-vertical-tabs>details[open]::details-content{grid-column:2;grid-row:1/span calc(var(--tabs-count,5) + 1);border-left:1px solid var(--border);overflow:auto;z-index:1}.accordion-vertical-tabs>details:not([open])::details-content{display:none}}.showcase-section{background:var(--background-subtle);padding-top:var(--padding_x2);padding-bottom:var(--padding_x2);width:100%}.showcase-header{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--padding)}.showcase-body{margin:var(--padding);gap:var(--padding);overflow-x:auto}@media(min-width:1024px){.showcase-body{max-width:80svw;margin:var(--padding)auto}}@media(min-width:1280px){.showcase-body{max-width:60svw}}.showcase-body{font-size:.95rem}.showcase-body p,.showcase-body pre:not(.chroma),.showcase-body .highlight{line-height:1.8em;letter-spacing:-.1px}.showcase-body strong,.showcase-body b,.showcase-body table th{font-weight:600}.showcase-body em{font-style:italic}.showcase-body a{color:var(--primary);text-decoration:none}.showcase-body a:hover{text-decoration:underline;background:var(--background-hover)}.showcase-body p,.showcase-body .highlight{margin-top:1rem}.showcase-body code,.showcase-body pre{font-family:var(--font-mono);font-size:.875rem}.showcase-body pre,.showcase-body pre code{display:inline-grid}.showcase-body .chroma code{padding:unset;background:unset;border-radius:unset;border:unset}.showcase-body :not(.code-window).highlight .chroma{border:1px solid var(--border)} \ No newline at end of file diff --git a/docs/assets/js/docs.min.e183be143fc7b87545ffa964089b780f9e8418abeaf311a1092975a438a6b1bb.js b/docs/assets/js/docs.min.e183be143fc7b87545ffa964089b780f9e8418abeaf311a1092975a438a6b1bb.js new file mode 100644 index 00000000..eda9e569 --- /dev/null +++ b/docs/assets/js/docs.min.e183be143fc7b87545ffa964089b780f9e8418abeaf311a1092975a438a6b1bb.js @@ -0,0 +1 @@ +const dropdowns=document.querySelectorAll(".dropdown"),dropdownOpenSelector=".dropdown-menu.show";dropdowns.forEach(e=>{e.addEventListener("click",function(){const n=e.querySelector(".dropdown-menu.show");document.querySelectorAll(dropdownOpenSelector).forEach(e=>e.classList.remove("show")),n||e.querySelector(".dropdown-menu").classList.toggle("show")})}),document.body.addEventListener("click",function(e){const t=e.target.closest(".dropdown");t||document.querySelectorAll(dropdownOpenSelector).forEach(e=>e.classList.remove("show"))}),document.addEventListener("DOMContentLoaded",()=>{const e=e=>`tabs:${e}`,t=(t,n)=>{if(!t||!n)return;document.querySelectorAll(`.tabs[data-group="${CSS.escape(t)}"] details[name]`).forEach(e=>{const t=e.querySelector("summary")?.dataset.index===n;e.open!==t&&(e.open=t)}),sessionStorage.setItem(e(t),n)};new Set([...document.querySelectorAll(".tabs[data-group]")].map(e=>e.dataset.group)).forEach(n=>t(n,sessionStorage.getItem(e(n)))),document.addEventListener("click",e=>{const n=e.target.closest(".tabs[data-group] summary");if(!n)return;e.preventDefault(),t(n.closest(".tabs").dataset.group,n.dataset.index)})});const lsKeyColorPreference="color-preference",getColorPreference=()=>{let e=localStorage.getItem(lsKeyColorPreference);return e!==null?e:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"};let colorPreference=getColorPreference();localStorage.setItem(lsKeyColorPreference,colorPreference),document.firstElementChild.setAttribute("data-color",colorPreference);let colorSchemes=document.querySelectorAll(".color-scheme");colorSchemes.forEach(e=>{e.addEventListener("click",function(){let t=e.dataset.value;t!==colorPreference&&(colorPreference=t,setColorPreference())})});const setColorPreference=()=>{localStorage.setItem(lsKeyColorPreference,colorPreference),document.firstElementChild.setAttribute("data-color",colorPreference)};window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",({matches:e})=>{colorPreference=e?"dark":"light",setColorPreference()});const body=document.body,bodyModelOuter=document.querySelector("#off-canvas-model"),leftSidebar=document.querySelector("#left-sidebar"),leftSidebarOpenButton=document.querySelector("#article-nav > button:first-child"),leftSidebarCloseButton=document.querySelector("#left-sidebar > div .btn");leftSidebar&&leftSidebarOpenButton&&leftSidebarOpenButton.addEventListener("click",function(){body.classList.add("model-open"),bodyModelOuter.style.display="block",leftSidebar.classList.add("open"),leftSidebarCloseButton.addEventListener("click",function(){body.classList.remove("model-open"),bodyModelOuter.style.display="none",leftSidebar.classList.remove("open")}),bodyModelOuter.addEventListener("click",function(){body.classList.remove("model-open"),bodyModelOuter.style.display="none",leftSidebar.classList.remove("open")})});const rightSidebar=document.querySelector("#right-sidebar"),rightSidebarOpenButton=document.querySelector("#article-nav > button:last-child"),rightSidebarCloseButton=document.querySelector("#right-sidebar > div > .btn");rightSidebar&&rightSidebarOpenButton&&rightSidebarOpenButton.addEventListener("click",function(){body.classList.add("model-open"),bodyModelOuter.style.display="block",rightSidebar.classList.add("open"),rightSidebarCloseButton.addEventListener("click",function(){body.classList.remove("model-open"),bodyModelOuter.style.display="none",rightSidebar.classList.remove("open")}),bodyModelOuter.addEventListener("click",function(){body.classList.remove("model-open"),bodyModelOuter.style.display="none",rightSidebar.classList.remove("open")})}),window.addEventListener("resize",function(){body.classList.contains("model-open")&&(body.classList.remove("model-open"),bodyModelOuter.style.display="none",rightSidebar.classList.contains("open")&&rightSidebar.classList.remove("open"),leftSidebar.classList.contains("open")&&leftSidebar.classList.remove("open"))});const leftSidebarSiteLogo=document.querySelector("#left-sidebar .site-logo"),leftSidebarNav=document.querySelector("#left-sidebar > nav"),leftSidebarNavActiveItem=document.querySelector("#left-sidebar > nav > details > ul > li > a.active"),adjustLeftSidebarNavHeight=function(){window.innerWidth>1280?leftSidebarNav.style.height=`${window.innerHeight-1-leftSidebarSiteLogo.getBoundingClientRect().height}px`:leftSidebarNav.style.height=""};if(leftSidebarNav){adjustLeftSidebarNavHeight(),window.addEventListener("resize",function(){adjustLeftSidebarNavHeight()});const t=window.location.pathname.split("/").filter(Boolean)[0]||"default",e=`${t}-sidebar-position`;window.addEventListener("DOMContentLoaded",()=>{const t=sessionStorage.getItem(e);if(t===null){const e=leftSidebarNav.getBoundingClientRect(),t=leftSidebarNavActiveItem.getBoundingClientRect(),n=t.top>=e.top&&t.bottom<=e.bottom;n||leftSidebarNav.scrollTo({top:leftSidebarNavActiveItem.offsetTop-leftSidebarNav.offsetTop-leftSidebarSiteLogo.getBoundingClientRect().height,behavior:"auto"})}else leftSidebarNav.scrollTop=parseInt(t,10)}),window.addEventListener("beforeunload",()=>{sessionStorage.setItem(e,leftSidebarNav.scrollTop)})}"IntersectionObserver"in window&&document.addEventListener("DOMContentLoaded",function(){const n=document.querySelectorAll("#TableOfContents a");let e=null;const t={},s=new IntersectionObserver(n=>{n.forEach(n=>{n.isIntersecting&&(e&&e.classList.remove("active"),e=t[n.target.id],e&&e.classList.add("active"))})},{rootMargin:`0% 0% -80% 0%`});n.forEach(e=>{const n=e.getAttribute("href")?e.getAttribute("href").slice(1):null;if(n){const o=document.getElementById(n);o&&(t[n]=e,s.observe(o))}e.addEventListener("click",function(){body.classList.contains("model-open")&&rightSidebarCloseButton.click()})});const o=document.getElementById("back-to-top");o.addEventListener("click",function(){rightSidebarCloseButton.click()})}) \ No newline at end of file diff --git a/docs/assets/js/home.min.51a47df7121b26e6f7df240d19aec33a62ef684229e20e9238740b6dd8e53328.js b/docs/assets/js/home.min.51a47df7121b26e6f7df240d19aec33a62ef684229e20e9238740b6dd8e53328.js new file mode 100644 index 00000000..93176b33 --- /dev/null +++ b/docs/assets/js/home.min.51a47df7121b26e6f7df240d19aec33a62ef684229e20e9238740b6dd8e53328.js @@ -0,0 +1 @@ +const dropdowns=document.querySelectorAll(".dropdown"),dropdownOpenSelector=".dropdown-menu.show";dropdowns.forEach(e=>{e.addEventListener("click",function(){const n=e.querySelector(".dropdown-menu.show");document.querySelectorAll(dropdownOpenSelector).forEach(e=>e.classList.remove("show")),n||e.querySelector(".dropdown-menu").classList.toggle("show")})}),document.body.addEventListener("click",function(e){const t=e.target.closest(".dropdown");t||document.querySelectorAll(dropdownOpenSelector).forEach(e=>e.classList.remove("show"))});const lsKeyColorPreference="color-preference",getColorPreference=()=>{let e=localStorage.getItem(lsKeyColorPreference);return e!==null?e:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"};let colorPreference=getColorPreference();localStorage.setItem(lsKeyColorPreference,colorPreference),document.firstElementChild.setAttribute("data-color",colorPreference);let colorSchemes=document.querySelectorAll(".color-scheme");colorSchemes.forEach(e=>{e.addEventListener("click",function(){let t=e.dataset.value;t!==colorPreference&&(colorPreference=t,setColorPreference())})});const setColorPreference=()=>{localStorage.setItem(lsKeyColorPreference,colorPreference),document.firstElementChild.setAttribute("data-color",colorPreference)};window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",({matches:e})=>{colorPreference=e?"dark":"light",setColorPreference()}) \ No newline at end of file diff --git a/docs/docs/borrowing/index.html b/docs/docs/borrowing/index.html new file mode 100644 index 00000000..0170a610 --- /dev/null +++ b/docs/docs/borrowing/index.html @@ -0,0 +1,420 @@ +Borrowing · Learning Rust

Borrowing

Borrowing

  • In a general sense, borrowing is taking something with the promise of returning it.

  • In Rust, borrowing is the act of creating a reference to a value without copying the data and without taking ownership.

    • This is similar to the concept of “passing by reference” in other languages.
    • However in Rust,
      • References are immutable by default: shared, read-only/ immutable references: &T
      • To mutate the data, you must explicitly create a mutable reference: &mut T
  • Liveness

    • Owned values (T) are dropped at the end of their scope (unless moved early).
    • References (&T, &mut T) effectively end/ expire at their last use. This is known as Non-Lexical Lifetimes (NLL).
    +Recap

    'static references are valid for the entire duration of the program.

  • While a reference exists, the original owner retains ownership, but its access to the data is restricted (or completely locked) until that reference’s last use.

    • Shared References: The owner keeps read-access but cannot mutate or move (destroy) the data.
    • Mutable References: The owner is completely locked out; they cannot read, write, move, or create new references to the data.

A Simple Example

fn main() {
+    let a = 3;
+    let mut b = 4;
+
+    let c = &a; // 💡 A new shared reference
+    {
+        let d = &mut b; // 💡 A new mutable reference
+        *d += 1; // 💡 Dereference to modify the value at this memory address
+    }
+
+    println!("{c} {b}"); // 3 5
+}

Liveness

Owned values (T) are dropped at the end of their scope, while references (&T, &mut T) are borrowed only until their last use.

fn main() {
+    let mut a = String::from("Hello"); // 💡 `a` owns the String
+    let b = &a; // 💡 `b` borrows `a`
+
+    println!("{b}"); // 💡 The borrow ends here (at its last use)
+
+    a.push_str(", world!");
+    println!("{a}");
+} // 💡 `a` is dropped here (at the end of its scope)
+

In Rust, a reference cannot outlive the value it borrows. This kind of reference is called a “dangling reference” which occurs when a program tries to access memory that has already been deallocated.

fn main() {
+    let a: &f64;
+    {
+        let b = 3.14159;
+        a = &b;
+    } // 💡 `b` is dropped here
+
+    println!("{a}"); // 💡 `a` refers to `b` that no longer exists
+}
+
+// The borrow checker will not let this compile.
+// 💥 `b` does not live long enough
+

Memory Layout

References are pointers that are either a thin pointer that points to a Sized type or a fat pointer which points to an Unsized type.

use std::mem::size_of_val;
+
+fn main() {
+    let a = 3; // 3 stores on the stack
+    let b = [1, 2, 3, 4, 5]; // [1, 2, 3, 4, 5] store inline on the stack
+
+    let c = &a;        // 💡 c is a thin pointer - [ptr] - points to `a`
+    let d = &b;        // 💡 d is a thin pointer - [ptr] - points to `b`
+    let e = &b[1..3];  // 💡 e is a fat pointer  - [ptr, len] - points to 2nd element of `b` and length is 2
+
+    println!("Size of actual data: a: {} bytes, b: {} bytes",
+        size_of_val(&a), size_of_val(&b) // 4, 20
+    );
+
+    println!("Size of the references: c: {} bytes, d: {} bytes, e: {} bytes",
+        size_of_val(&c), size_of_val(&d), size_of_val(&e) // 8 , 8, 16 on 64-bit machines
+    );
+}
+
+// `d`: A reference to a fixed-size array, &[i32;5]. The compiler knows the length from the type.
+// `e`: A reference to a slice, &[i32]. Since the length is not part of the type, 
+//      it stores [address, length] at runtime, making it a fat pointer.
+
Variable NameTypeLocationSize
ai32Stack4 bytes
b[i32;5]Stack20 bytes
c (Stores the full memory address to a)&i32Stack8 bytes
d (Stores the full memory address to b)&[i32;5]Stack8 bytes
e (Stores the full memory address to 2nd element of b and length)&[i32]Stack16 bytes
use std::mem::size_of_val;
+
+fn main() {
+    // 💡 String stores [ptr, cap, len] on the stack, which points to "Hello" on the heap
+    let a = String::from("Hello");
+    
+    // 💡 Vec stores [ptr, cap, len] on the stack, which points to the array of &str on the heap
+    let b = vec!["A", "B", "C", "D", "E"]; 
+
+    let c = &a;       // 💡 c is a thin pointer - [ptr] - points to the String struct `a` on the stack
+    let d = &b;       // 💡 d is a thin pointer - [ptr] - points to the Vec struct `b` on the stack
+    let e = &b[1..3]; // 💡 e is a fat pointer  - [ptr, len] - points to the 2nd element in the heap buffer
+
+    println!("\nSize of stack metadata: a: {} bytes, b: {} bytes",
+        size_of_val(&a), size_of_val(&b) // 24, 24
+    );
+
+    println!("Size of the references: c: {} bytes, d: {} bytes, e: {} bytes",
+        size_of_val(&c), size_of_val(&d), size_of_val(&e) // 8, 8, 16 on 64-bit machines
+    );
+}

Shared Borrowing

Shared borrowing lets us read the same data from multiple places at once without allowing it to be modified. This is done by the compiler implicitly in some occasions to make the syntax more concise and ergonomic, or by creating a shared reference explicitly with the & operator.

Implicit Shared Borrowing

Formatting macros like print!(), println!(), and format!() automatically take a shared reference to the data.

#[derive(Debug)]
+struct Person { name: String }
+
+fn main() {
+    let a = Person { name: "Steve".to_string() };
+
+    println!("{:?}", a); // 💡 An implicit borrowing
+    println!("{}", a.name); // 💡 An implicit borrowing
+    let greet = format!("Hello, {}!", a.name); // 💡 An implicit borrowing
+    println!("{greet}");
+}
+Warning

But there is no implicit borrowing in the dbg! macro. Use a reference with it: dbg!(&a);

#![allow(unused)]
+
+#[derive(Debug)]
+struct Person { name: String }
+
+#[derive(Debug, Clone, Copy)]
+struct Point { x: i32, y: i32 }
+
+fn main() {
+    let a = Person { name: "Steve".to_string() };
+    dbg!(a); // 💡 Type without Copy marker. `a` moves into `dbg!`
+
+    let b = Point { x: 0, y: 1 };
+    dbg!(b); // 💡 Type with Copy typed members + Copy marker. A bitwise copy of `b` moves into dbg!
+}

Method calls that require a shared reference (&self) on an owned value, the compiler automatically immutably borrows the value.

struct Person { name: String }
+
+impl Person {
+    fn greet(&self) {
+        println!("Hello, {}!", self.name)
+    }
+}
+
+fn main() {
+    let a = Person { name: "Steve".to_string() };
+    a.greet(); // 💡 Compiler effectively converts to `Person::greet(&a)`
+}

If a closure only reads a variable from the surrounding scope, it borrows it implicitly.

fn main() {
+    let name = "Steve".to_string();
+    let greet = || println!("Hello, {name}!"); // 💡 An implicit borrowing
+
+    greet();
+    println!("{name}"); 
+}
+
+// 💯 Using the `move` keyword forces the closure to take ownership: `let greet = move || println!("Hello, {name}!");`
+

Accessing an element from a slice or collection like Vec or HashMap for read-only access involves the Index trait. The compiler implicitly converts a[0] into *a.index(0), which involves an implicite shared borrow of the collection.

// Two concepts in the example: 1. Implicite shared reference; 2. Assigning Copy types/ non-Copy types to a new variable.
+
+fn main() {
+    let a = vec![1, 2, 3]; // 💡 contains Copy types
+    let b = vec!["Apple".to_string(), "Pear".to_string()]; // 💡 contains non-Copy/ move types
+
+    let c = a[0]; // a[0] access the element via a shared reference + Copy the element into `c: i32`
+    // b[0] access the element via a shared reference + manually call `.clone()` as non-Copy types move/ destroy the original data
+    let d = b[0].clone();
+
+    println!("{c} {d}");
+}
+
+// 👨‍🏫 Try remove `.clone()`. The borrow checker will not let it compile.
+// 💥 cannot move out of index of `Vec<String>`
+

Shared References with &

The & operator creates a new shared reference to the data.

fn main() {
+    let a = String::from("Hello");
+
+    let b = &a; // 💡 A new shared reference
+    borrow_string(b);
+
+    borrow_string(&a); // 💡 A new shared reference
+}
+
+fn borrow_string(s: &String) {
+    println!("{s}");
+}
+
+// 💯 It's idiomatic to use &str instead of &String on the function signature. We will discuss this under Deref Coercion.
+

A field behind a shared reference cannot move into a new variable, as the original data must not be mutated or destroyed while borrowed.

struct Person { name: String, age: f32 }
+
+fn main() {
+    let steve = Person { name: "Steve".to_owned(), age: 56.0 };
+
+    borrow_string(&steve.name);      // 💡 A new shared reference
+    borrow_struct(&steve);           // 💡 A new shared reference
+    borrow_struct_to_assign(&steve); // 💡 A new shared reference
+}
+
+fn borrow_string(s: &String) {
+    println!("{s}");
+}
+
+fn borrow_struct(p: &Person) {
+    println!("{} {}", p.name, p.age);
+}
+
+fn borrow_struct_to_assign(p: &Person) {
+    // 💡 Because `p.name` is a non-Copy type behind a shared reference, Rust disallows moving/ detroying the original data
+    let (a, b) = (p.name.clone(), p.age);
+    println!("{a} {b}");
+}
+
+// ⭐️ Each reference is scoped to the function call.
+// 👨‍🏫 Try remove `.clone()`. The borrow checker will not let it compile.
+// 💥 cannot move out of `p.name` which is behind a shared reference
+

Iterators created with iter() are used to iterate over a shared reference of the collection, without moving it into the loop.

fn main() {
+    let a = vec![1, 2, 3];
+
+    for x in &a { // 💡 A new shared reference
+        println!("{x}");
+    }
+
+    println!("{}", a.len()); // 💡 We can still access `a` after the loop as we use a reference in the loop
+}
+
+// 💯 `for x in a` uses `into_iter()` and `for x in &mut a` uses `iter_mut()`
+

Mutable Borrowing

Mutable borrowing lets us modify the same data from one place at a time without allowing other references to access it. This is done by the compiler implicitly in some cases the syntax more concise and ergonomic, or by creating a mutable reference explicitly with the &mut operator.

Implicit Mutable Borrowing

Method calls that require a mutable reference (&mut self) on an owned value, the compiler automatically mutably borrows the value.

struct Person { name: String }
+
+impl Person {
+    fn update_name(&mut self, new_name: String) {
+        self.name = new_name;
+    }
+}
+
+fn main() {
+    let mut a = Person { name: "Steve".to_string() };
+    a.update_name("Jobs".to_string()); // 💡 Compiler effectively converts to `Person::update_name(&mut a, "Jobs".to_string());`
+
+    println!("{}", a.name);
+}

If a closure modifies a variable from the surrounding scope, it implicitly borrows it mutably.

fn main() {
+    let mut count = 0;
+    let mut increment = || { count += 1 }; // 💡 An implicit mutable borrowing
+
+    increment();
+    increment();
+
+    println!("{count}"); // 2
+}

Accessing an element from a slice or collection like Vec for mutable access involves the IndexMut trait. The compiler implicitly converts a[0] into *a.index_mut(0), which involves an implicit mutable borrow of the collection.

fn main() {
+    let mut a = vec![1, 2, 3];
+    a[0] = 10; // 💡 Compiler effectively converts to `*a.index_mut(0) = 10;`
+
+    println!("{:?}", a);
+}

Mutable References with &mut

The &mut operator creates a new mutable reference to the data. When passing a &mut to a function, the reference is scoped to that function call.

fn main() {
+    let mut a = String::from("AAA");
+
+    println!("{a}");
+    borrow_mut_string(&mut a); // 💡 A mutable reference; AAA-BBB
+    borrow_mut_string(&mut a); // 💡 A mutable reference; AAA-BBB-BBB
+
+    let b = &mut a; // 💡 A mutable reference; AAA-BBB-BBB-CCC while printing
+    b.push_str("-CCC");
+    println!("{a}");
+
+    borrow_mut_string(&mut a); // 💡 A mutable reference; AAA-BBB-BBB-CCC-BBB
+}
+
+fn borrow_mut_string(a: &mut String) {
+    a.push_str("-BBB");
+    println!("{a}");
+}
+
+// Eventhough `let b = &mut a;` creates a new mutable reference, its last use only till the next line. 
+// So, the sahred reference creation via `println!()` is still possible.
+

Shared references are Copy types while mutable references are non-Copy/ move types. However, when a mutable reference is passed to a function or assigns it to a new variable with an explicit type, Rust doesn’t move the original reference. Instead, Rust creates a new temporary reference to the same data with a shorter lifetime. This is called “reborrowing” as it’s a “borrow from a borrow”. While the reborrow is active, the original mutable reference cannot be used.

fn main() {
+    let mut a = 128;
+    let b = &mut a; // 💡 First mutable reference
+
+    {
+        let c: &mut i32 = b; // 💡 Reborrowing; A temporary mutable reference
+        *c += 1;
+
+        // println!("{b}"); // ⭐️ cannot borrow `b` as immutable because it is also borrowed as mutable for `c`
+        println!("{c}");
+    }
+
+    *b += 1;
+    println!("{a}");
+}

Reference Conversion with AsRef<T> and AsMut<T>

AsRef is used to get a shared reference from a value explicitly. It makes functions flexible by letting them accept different types without manual conversion.

struct Person { name: String }
+
+impl From<&str> for Person {
+    fn from(name: &str) -> Self {
+        Person { name: name.to_string() }
+    }
+}
+
+impl AsRef<str> for Person {
+    fn as_ref(&self) -> &str {
+        &self.name
+    }
+}
+
+fn greet(name: impl AsRef<str>) {
+    println!("Hello, {}!", name.as_ref());
+}
+
+fn main() {
+    let a = Person::from("Steve");
+    let b = String::from("Jobs");
+    let c = "Bill";
+
+    greet(a); // Hello, Steve!
+    greet(b); // Hello, Jobs!
+    greet(c); // Hello, Bill!
+}

AsMut is used to get a mutable reference from a value explicitly. It makes functions flexible by letting them accept different types that can be modified without manual conversion.

struct Person { name: String }
+
+impl AsMut<str> for Person {
+    fn as_mut(&mut self) -> &mut str {
+        &mut self.name // 💡 `&mut String` is coerced to `&mut str`
+    }
+}
+
+fn make_uppercase(mut text: impl AsMut<str>) {
+    text.as_mut().make_ascii_uppercase();
+}
+
+fn main() {
+    let mut a = Person { name: String::from("Steve") };
+    let mut b = String::from("Bill");
+
+    make_uppercase(&mut a);
+    make_uppercase(&mut b);
+
+    println!("{}", a.name); // STEVE
+    println!("{b}"); // BILL
+}

Dereferencing

Dereferencing lets us access the value behind a reference or smart pointer. The * operator does this explicitly, while Deref/ DerefMut traits let Rust do it automatically in some cases.

Explicit Dereferencing With * Operator

The unary * operator is used to get the value from a reference or smart pointer.

fn main() {
+    let a = 3;
+    let b = &a;
+    let c = *b; // 💡 Dereferencing
+
+    println!("{a} {b} {c}"); // 3 3 3
+}
+
+// `a` has the ownership of 3. `b` is a reference to the data of `a`.
+// `c` is a separate variable/ separate ownership, created by copying the data of `a`.
+// ⭐️ `let c = *b;` is similar to `let c = a;`, we just use dereferenced reference to get the data.
+// 💯 `let c = *b;` is only possible when `a` is a Copy type. If `a` is not Copy type, we will get a compiler error.
+

Deref & DerefMut

Deref lets a type behave like a reference. This allows Rust to automatically convert it to another reference type in some situations. DerefMut does the same for mutable references, allowing Rust to automatically dereference a type in contexts when needed.

use std::convert::From;
+use std::ops::Deref;
+
+struct Person { name: String }
+
+impl From<&str> for Person {
+    fn from(name: &str) -> Self {
+        Person { name: name.to_string() }
+    }
+}
+
+// 💡 `Person` implements `Deref<Target = String>`
+impl Deref for Person {
+    type Target = String;
+
+    fn deref(&self) -> &Self::Target {
+        &self.name
+    }
+}
+
+fn greet(name: &str) {
+    println!("Hello, {name}!");
+}
+
+fn main() {
+    let a = Person::from("Steve");
+
+    // 💡 Person dereferences to String
+    println!("{}", a.to_uppercase()); // STEVE
+    
+    // `Deref` Coercion
+    greet(&a); // Hello, Steve!
+}

Deref Coercion

Deref coercion is Rust’s automatic conversion from one reference type to another when a type implements Deref or DerefMut traits.

  1. From &T to &U when T: Deref<Target=U>

    When a type implements the Deref trait, Rust can implicitly call deref() to convert a shared reference of that type into a reference of another type to match a function’s signature. This makes it flexible to accept multiple reference types without manual conversion.

    fn main() {
    +    // Deref Coercion: &String -> &str
    +    let a = String::from("Hello");
    +    borrow_str(&a);
    +
    +    // Collection Coercion: &Vec<i32> -> &[i32]
    +    let b = vec![1, 2, 3];
    +    borrow_slice(&b);
    +
    +    // Chained Deref Coercion: &Box<String> -> &String -> &str
    +    let c = Box::new(String::from("Hello"));
    +    borrow_str(&c);
    +}
    +
    +// 💡 This accepts &str, &String and &Box<String>
    +fn borrow_str(s: &str) {
    +    println!("{s}");
    +}
    +
    +// 💡 This accepts &[i32], &Vec<i32>
    +fn borrow_slice(slice: &[i32]) {
    +    println!("{:?}", slice);
    +}
  2. From &mut T to &U when T: Deref<Target=U>

    When passing a mutable reference (&mut) to a function that only expects a shared (immutable) reference (&), Rust automatically downgrades the mutable reference to a shared reference.

    fn main() {
    +    let mut a = String::from("Hello");
    +    borrow_str(&mut a); // 💡 Implicitly coerces the mutable reference into a shared one
    +}
    +
    +fn borrow_str(s: &str) {
    +    println!("{s}");
    +}
  3. From &mut T to &mut U when T: DerefMut<Target=U>

    When a type implements the DerefMut trait, Rust can implicitly call deref_mut() to convert a mutable reference of that type into a reference of another type to match a function’s signature.

    fn main() {
    +    // Mutable Deref Coercion: &mut String -> &mut str
    +    let mut a = String::from("Hello");
    +    borrow_mut_str(&mut a);
    +
    +    // Collection Coercion: &mut Vec<T> -> &mut [T]
    +    let mut b = vec![1, 2, 3];
    +    borrow_mut_slice(&mut b);
    +
    +    // Chained Mutable Deref Coercion: &mut Box<String> -> &mut String -> &mut str
    +    let mut c = Box::new(String::from("Hello"));
    +    borrow_mut_str(&mut c);
    +
    +    println!("{a}, {b:?}, {c}");
    +}
    +
    +// 💡 This accepts &mut str, &mut String, &mut Box<String>
    +fn borrow_mut_str(s: &mut str) {
    +    s.make_ascii_uppercase();
    +}
    +
    +// 💡 This accepts &mut [i32], &mut Vec<i32>
    +fn borrow_mut_slice(slice: &mut [i32]) {
    +    if !slice.is_empty() {
    +        slice[0] += 1;
    +    }
    +}

👨‍🏫 Before going to the next…

  • 💯 Check shared ownership, Rc<T>, Arc<T>, Cell<T>, RefCell<T> types and interior mutability.
\ No newline at end of file diff --git a/docs/docs/borrowing/og.jpg b/docs/docs/borrowing/og.jpg new file mode 100644 index 00000000..7616cba3 Binary files /dev/null and b/docs/docs/borrowing/og.jpg differ diff --git a/docs/docs/cargo-crates-and-basic-project-structure/index.html b/docs/docs/cargo-crates-and-basic-project-structure/index.html new file mode 100644 index 00000000..ce56143b --- /dev/null +++ b/docs/docs/cargo-crates-and-basic-project-structure/index.html @@ -0,0 +1,62 @@ +Cargo, Crates and Basic Project Structure · Learning Rust

Cargo, Crates and Basic Project Structure

Cargo

Cargo is Rust’s built-in package manager and build system. It also supports the following actions,

CommandAction
cargo newCreate a new project
cargo initCreate a new project in an existing directory
cargo checkVerify the project compiles without errors
cargo buildBuild the executable
cargo runBuild the executable and run
cargo cleanRemove the build system directories/ target directory
+Tip
  • The cargo check command verifies that the project compiles without errors, without producing an executable. +Thus, it is often faster than cargo build.
  • Cargo places executables compiled with cargo build or cargo run in the target/debug/ directory. +But, while those built with cargo build --release for release purposes are stored in target/release/ directory. +Release builds use more optimizations and remove some runtime safety checks to increase performance, although this comes at the cost of longer compile time.
CommandAction
cargo addAdd a dependency crate to the project
cargo removeRemove a dependency crate from the project
cargo fetchDownload the dependencies specified in Cargo.lock
cargo updateUpdate project dependencies
+Tip
  • A crate is a package that can be shared via crates.io, Rust community’s crate registry. +cargo add, cargo remove, cargo fetch, and cargo update commands manage project dependencies through the crate hosted on crates.io.
  • The cargo add command includes a specified crate in the [dependencies] section of Cargo.toml, while cargo add --dev adds a crate to the [dev-dependencies] section. This indicates that the crate is only used for development purposes like testing and will not be included in the final compiled code.
CommandAction
cargo testRun tests
cargo benchRun benchmarks
cargo docGenerate the project documentation via rustdoc

In addition, there are cargo commands to publish the project as a crate to crates.io.

CommandAction
cargo loginLogin to crates.io with the API token
cargo packageMake the local crate uploadable to crates.io
cargo publishUpload the crate to crates.io
cargo installInstall a Rust binary
cargo uninstallUninstall a Rust binary
+Tip

You need to get an API token from crates.io to publish a crate to it. The API token can be found in the Account Settings page, after login to that site. We will discuss more about this under code organization with crates.

Crate

  • A crate is a package, which can be shared via Rust community’s crate registry, crates.io.

  • A crate can produce an executable or a library. In other words, it can be a binary crate or a library crate.

    1. cargo new crate_name --bin or cargo new crate_name: Produces an executable
    2. cargo new crate_name --lib: Produces a library

The first one generates,

├── Cargo.toml
+└── src
+    └── main.rs

and the second one generates,

├── Cargo.toml
+└── src
+    └── lib.rs
  • Cargo.toml(capital c) is the configuration file which contains all of the metadata that Cargo needs to compile your project.
  • src folder is the place to store the source code.
  • Each crate has an implicit crate root/ entry point. main.rs is the crate root for a binary crate and lib.rs is the crate root for a library crate.

Project Structure

This is how Cargo documentation describes about the recommended project layout,

.
+├── Cargo.toml
+├── Cargo.lock
+├── src
+│   ├── main.rs
+│   ├── lib.rs
+│   └── bin
+│       ├── another_executable.rs
+│       └── multi_file_executable
+│           ├── main.rs
+│           └── some_module.rs
+├── tests
+│   └── some_integration_tests.rs
+├── benches
+│   └── simple_bench.rs
+└── examples
+    └── simple_example.rs
  • The source code goes in the src directory.
    • The default executable file is src/main.rs.
    • The default library file is src/lib.rs.
    • Other executables can be placed in,
      • src/bin/*.rs
      • src/bin/*/main.rs
  • Integration tests go in the tests directory (unit tests go in each file they’re testing).
  • Benchmarks go in the benches directory.
  • Examples go in the examples directory.

Rust Editions

Rust guarantees backward compatibility while introducing major updates to the language. To support this, the edition field was added to the Cargo.toml file in Rust 2018, marking the first major update to the language ecosystem three years after its initial release. Editions are opt-in, meaning existing crates will not experience these changes until they explicitly migrate to the new edition.

The major editions of Rust are:

  • Rust 2015: The initial edition, introduced with Rust 1.0. It established the core language features like ownership, borrowing, and lifetimes, laying the foundation for Rust’s safety and concurrency guarantees.

  • Rust 2018: The first major update, introduced the edition field in Cargo.toml, simplified the module system, stabilized async/await, improved error handling with the ? operator, and made several syntactic changes.

  • Rust 2021: Focused on improving ergonomics and removing inconsistencies, such as disjoint closure capture, IntoIterator for arrays, and the introduction of or-patterns in macros.

  • Rust 2024: The latest edition, includes enhancements like refined async features, more const generics, better diagnostics, and improved Cargo features.

For new projects created by cargo new, it will set edition = "2024" by default in the Cargo.toml file. For example,

[package]
+name = "hello_world"
+version = "0.1.0"
+edition = "2024"

👨‍🏫 Before going to the next…

  • The .cargo/bin directory of your home directory is the default location of Rust binaries. Not only the official binaries like rustc, cargo, rustup, rustfmt, rustdoc, rust-analyzer and also the binaries you can install via cargo install command, will be stored in this directory.

  • Even though the initial convention for naming crates and file names is using the snake_case, some crate developers are using kebab-case on both crates and file names. To make your code more consistent, use the initial convention snake_case; especially on file names.

  • Create,

    1. an executable crate via cargo new command, run it via cargo run and examine the files and project structure.

    2. a library crate via cargo new command, run cargo test and examine the files and project structure.

    3. a multiple executables project and try to run each executable.

      • You can name executables in the Cargo.toml file.
        [[bin]]
        +name = "app"
        +path = "src/bin/app/main.rs"
      • You can use the --bin flag to specify the executable, while running cargo commands.
        Ex. cargo build --bin app, cargo run --bin app
      • You can set default executable in the Cargo.toml file.
        [package]
        +name = "hello_world"
        +version = "0.1.0"
        +edition = "2024"
        +default-run = "app"
    4. Run cargo build --release and check the files in the target folder.

\ No newline at end of file diff --git a/docs/docs/cargo-crates-and-basic-project-structure/og.jpg b/docs/docs/cargo-crates-and-basic-project-structure/og.jpg new file mode 100644 index 00000000..9d05f064 Binary files /dev/null and b/docs/docs/cargo-crates-and-basic-project-structure/og.jpg differ diff --git a/docs/docs/combinators/index.html b/docs/docs/combinators/index.html new file mode 100644 index 00000000..72994671 --- /dev/null +++ b/docs/docs/combinators/index.html @@ -0,0 +1,191 @@ +Combinators · Learning Rust

Combinators

What is a combinator?

  • One meaning of “combinator” is a more informal sense referring to the combinator pattern, a style of organizing libraries centered around the idea of combining things. Usually there is some type T, some functions for constructing “primitive” values of type T, and some “combinators” which can combine values of type T in various ways to build up more complex values of type T. The other definition is “function with no free variables”. +__ wiki.haskell.org

  • A combinator is a function which builds program fragments from program fragments; in a sense the programmer using combinators constructs much of the desired program automatically, rather that writing every detail by hand. +__ John Hughes—Generalizing Monads to Arrows via Functional Programming Concepts

The exact definition of “combinators” in Rust ecosystem is bit unclear. 

  • or(), and(), or_else(), and_then()

    • Combine two values of type T and return same type T.
  • xor() for Option types

    • Combine two values of type T and return same type T, only if exactly one value is T
  • filter() for Option types

    • Filter type T by using a closure as a conditional function
    • Return same type T
  • map(), map_err()

    • Convert type T by applying a closure.
    • The data type of the value inside T can be changed. +ex. Some<&str> can be converted to Some<usize> or Err<&str> to Err<isize> and etc.
  • map_or(), map_or_else()

    • Transform type T by applying a closure & return the value inside type T.
    • For None and Err, a default value or another closure is applied.
  • ok_or(), ok_or_else() for Option types

    • Transform Option type into a Result type.
  • as_ref(), as_mut()

    • Transform type T into a reference or a mutable reference.

or() and and()

While combining two expressions, which return either Option/ Result

  • or(): If either one got Some or Ok, that value returns immediately.
  • and(): If both got Some or Ok, the value in the second expression returns. If either one got None or Err that value returns immediately.

With Option

let s1 = Some("some1");
+let s2 = Some("some2");
+let n: Option<&str> = None;
+
+assert_eq!(s1.or(s2), s1); // Some1 or Some2 = Some1
+assert_eq!(s1.or(n), s1);  // Some or None = Some
+assert_eq!(n.or(s1), s1);  // None or Some = Some
+assert_eq!(n.or(n), n);    // None1 or None2 = None2
+
+assert_eq!(s1.and(s2), s2); // Some1 and Some2 = Some2
+assert_eq!(s1.and(n), n);   // Some and None = None
+assert_eq!(n.and(s1), n);   // None and Some = None
+assert_eq!(n.and(n), n);    // None1 and None2 = None1
+

With Result

let o1: Result<&str, &str> = Ok("ok1");
+let o2: Result<&str, &str> = Ok("ok2");
+let e1: Result<&str, &str> = Err("error1");
+let e2: Result<&str, &str> = Err("error2");
+
+assert_eq!(o1.or(o2), o1); // Ok1 or Ok2 = Ok1
+assert_eq!(o1.or(e1), o1); // Ok or Err = Ok
+assert_eq!(e1.or(o1), o1); // Err or Ok = Ok
+assert_eq!(e1.or(e2), e2); // Err1 or Err2 = Err2
+
+assert_eq!(o1.and(o2), o2); // Ok1 and Ok2 = Ok2
+assert_eq!(o1.and(e1), e1); // Ok and Err = Err
+assert_eq!(e1.and(o1), e1); // Err and Ok = Err
+assert_eq!(e1.and(e2), e1); // Err1 and Err2 = Err1
+

xor()

While combining two Options, which return either Option, only if exactly one option is T.

The same Some type is returned, only if we pass only one Some value. None is returned, if both Some or None type. Rust support xor() only for Option types.

let s1 = Some("some1");
+let s2 = Some("some2");
+let n: Option<&str> = None;
+
+assert_eq!(s1.xor(s2), n); // Some1 xor Some2 = None
+assert_eq!(s1.xor(n), s1); // Some xor None = Some
+assert_eq!(n.xor(s1), s1); // None xor Some = Some
+assert_eq!(n.xor(n), n); // None1 xor None2 = None2
+

or_else()

Similar to or(). The only difference is, the second expression should be a closure which returns same type T.

With Option

let s1 = Some("some1");
+let s2 = Some("some2");
+let fn_some = || Some("some2"); // similar to: let fn_some = || -> Option<&str> { Some("some2") };
+
+let n: Option<&str> = None;
+let fn_none = || None;
+
+assert_eq!(s1.or_else(fn_some), s1);  // Some1 or_else Some2 = Some1
+assert_eq!(s1.or_else(fn_none), s1);  // Some or_else None = Some
+assert_eq!(n.or_else(fn_some), s2);   // None or_else Some = Some
+assert_eq!(n.or_else(fn_none), None); // None1 or_else None2 = None2
+

With Result

let o1: Result<&str, &str> = Ok("ok1");
+let o2: Result<&str, &str> = Ok("ok2");
+let fn_ok = |_| Ok("ok2"); // similar to: let fn_ok = |_| -> Result<&str, &str> { Ok("ok2") };
+
+let e1: Result<&str, &str> = Err("error1");
+let e2: Result<&str, &str> = Err("error2");
+let fn_err = |_| Err("error2");
+
+assert_eq!(o1.or_else(fn_ok), o1);  // Ok1 or_else Ok2 = Ok1
+assert_eq!(o1.or_else(fn_err), o1); // Ok or_else Err = Ok
+assert_eq!(e1.or_else(fn_ok), o2);  // Err or_else Ok = Ok
+assert_eq!(e1.or_else(fn_err), e2); // Err1 or_else Err2 = Err2
+

and_then()

Similar to and(). The only difference is, the second expression should be a closure which returns same type T.

With Option

let s1 = Some("some1");
+let s2 = Some("some2");
+let fn_some = |_| Some("some2"); // similar to: let fn_some = |_| -> Option<&str> { Some("some2") };
+
+let n: Option<&str> = None;
+let fn_none = |_| None;
+
+assert_eq!(s1.and_then(fn_some), s2); // Some1 and_then Some2 = Some2
+assert_eq!(s1.and_then(fn_none), n);  // Some and_then None = None
+assert_eq!(n.and_then(fn_some), n);   // None and_then Some = None
+assert_eq!(n.and_then(fn_none), n);   // None1 and_then None2 = None1
+

With Result

let o1: Result<&str, &str> = Ok("ok1");
+let o2: Result<&str, &str> = Ok("ok2");
+let fn_ok = |_| Ok("ok2"); // similar to: let fn_ok = |_| -> Result<&str, &str> { Ok("ok2") };
+
+let e1: Result<&str, &str> = Err("error1");
+let e2: Result<&str, &str> = Err("error2");
+let fn_err = |_| Err("error2");
+
+assert_eq!(o1.and_then(fn_ok), o2);  // Ok1 and_then Ok2 = Ok2
+assert_eq!(o1.and_then(fn_err), e2); // Ok and_then Err = Err
+assert_eq!(e1.and_then(fn_ok), e1);  // Err and_then Ok = Err
+assert_eq!(e1.and_then(fn_err), e1); // Err1 and_then Err2 = Err1
+

filter()

+Tip

Usually in programming languages filter functions are used with arrays or iterators to create a new array/ iterator by filtering own elements via a function/ closure. Rust also provides filter() as an iterator adaptor to apply a closure on each element of an iterator to transform it into another iterator. However in here we are talking about the functionality of filter() with Option types.

The same Some type is returned, only if we pass a Some value and the given closure returned true for it. None is returned, if None type passed or the closure returned false. The closure uses the value inside Some as an argument. Still Rust support filter() only for Option types.

let s1 = Some(3);
+let s2 = Some(6);
+let n = None;
+
+let fn_is_even = |x: &i8| x % 2 == 0;
+
+assert_eq!(s1.filter(fn_is_even), n);  // Some(3) -> 3 is not even -> None
+assert_eq!(s2.filter(fn_is_even), s2); // Some(6) -> 6 is even -> Some(6)
+assert_eq!(n.filter(fn_is_even), n);   // None -> no value -> None
+

map() and map_err()

+Tip

Usually in programming languages map() functions are used with arrays or iterators, to apply a closure on each element of the array or iterator. Rust also provides map() as an iterator adaptor to apply a closure on each element of an iterator to transform it into another iterator. However in here we are talking about the functionality of map() with Option and Result types.

  • map() : Convert type T by applying a closure. The data type of Some or Ok blocks can be changed according to the return type of the closure. Convert Option<T> to Option<U>, Result<T, E> to Result<U, E>

⭐ Via map(), only Some and Ok values are getting changed. No affect to the values inside Err (None doesn’t contain any value at all).

With Option

let s1 = Some("abcde");
+let s2 = Some(5);
+
+let n1: Option<&str> = None;
+let n2: Option<usize> = None;
+
+let fn_character_count = |s: &str| s.chars().count();
+
+assert_eq!(s1.map(fn_character_count), s2); // Some1 map = Some2
+assert_eq!(n1.map(fn_character_count), n2); // None1 map = None2
+

With Result

let o1: Result<&str, &str> = Ok("abcde");
+let o2: Result<usize, &str> = Ok(5);
+
+let e1: Result<&str, &str> = Err("abcde");
+let e2: Result<usize, &str> = Err("abcde");
+
+let fn_character_count = |s: &str| s.chars().count();
+
+assert_eq!(o1.map(fn_character_count), o2); // Ok1 map = Ok2
+assert_eq!(e1.map(fn_character_count), e2); // Err1 map = Err2
+
  • map_err() for Result types : The data type of Err blocks can be changed according to the return type of the closure. Convert Result<T, E> to Result<T, F>.

⭐ Via map_err(), only Err values are getting changed. No affect to the values inside Ok.

let o1: Result<&str, &str> = Ok("abcde");
+let o2: Result<&str, isize> = Ok("abcde");
+
+let e1: Result<&str, &str> = Err("404");
+let e2: Result<&str, isize> = Err(404);
+
+let fn_character_count = |s: &str| -> isize { s.parse().unwrap() }; // convert str to isize
+
+assert_eq!(o1.map_err(fn_character_count), o2); // Ok1 map = Ok2
+assert_eq!(e1.map_err(fn_character_count), e2); // Err1 map = Err2
+

map_or() and map_or_else()

Hope you remember the functionality of unwrap_or() and unwrap_or_else() functions. These functions also bit similar to them. But map_or() and map_or_else() apply a closure on Some and Ok values and return the value inside type T.

  • map_or() : Support only for Option types (not supporting Result). Apply the closure to the value inside Some and return the output according to the closure. The given default value is returned for None types.
const V_DEFAULT: i8 = 1;
+
+let s = Some(10);
+let n: Option<i8> = None;
+let fn_closure = |v: i8| v + 2;
+
+assert_eq!(s.map_or(V_DEFAULT, fn_closure), 12);
+assert_eq!(n.map_or(V_DEFAULT, fn_closure), V_DEFAULT);
  • map_or_else() : Support for both Option and Result types. Similar to map_or() but should provide another closure instead a default value for the first parameter.

None types doesn’t contain any value. So no need to pass anything to the closure as input with Option types. But Err types contain some value inside it. So default closure should able to read it as an input, while using this with Result types.

let s = Some(10);
+let n: Option<i8> = None;
+
+let fn_closure = |v: i8| v + 2;
+let fn_default = || 1; // None doesn't contain any value. So no need to pass anything to closure as input.
+
+assert_eq!(s.map_or_else(fn_default, fn_closure), 12);
+assert_eq!(n.map_or_else(fn_default, fn_closure), 1);
let o = Ok(10);
+let e = Err(5);
+
+let fn_closure = |v: i8| v + 2;
+let fn_default_for_result = |v: i8| v + 1; // Err contain some value inside it. So default closure should able to read it as input
+
+assert_eq!(o.map_or_else(fn_default_for_result, fn_closure), 12);
+assert_eq!(e.map_or_else(fn_default_for_result, fn_closure), 6);

ok_or() and ok_or_else()

As mentioned earlier, ok_or(), ok_or_else() transform Option type into Result type. Some to Ok and None to Err.

  • ok_or() : A default Err message should pass as argument.
const ERR_DEFAULT: &str = "error message";
+
+let s = Some("abcde");
+let n: Option<&str> = None;
+
+let o: Result<&str, &str> = Ok("abcde");
+let e: Result<&str, &str> = Err(ERR_DEFAULT);
+
+assert_eq!(s.ok_or(ERR_DEFAULT), o); // Some(T) -> Ok(T)
+assert_eq!(n.ok_or(ERR_DEFAULT), e); // None -> Err(default)
+
  • ok_or_else() : Similar to ok_or(). A closure should be passed as the argument.
let s = Some("abcde");
+let n: Option<&str> = None;
+let fn_err_message = || "error message";
+
+let o: Result<&str, &str> = Ok("abcde");
+let e: Result<&str, &str> = Err("error message");
+
+assert_eq!(s.ok_or_else(fn_err_message), o); // Some(T) -> Ok(T)
+assert_eq!(n.ok_or_else(fn_err_message), e); // None -> Err(default)
+

as_ref() and as_mut()

🔎 As mentioned earlier, these functions are used to borrow type T as a reference or as a mutable reference.

  • as_ref() : Convert Option<T> to Option<&T> and Result<T, E> to Result<&T, &E>
  • as_mut() : Converts Option<T> to Option<&mut T> and Result<T, E> to Result<&mut T, &mut E>
\ No newline at end of file diff --git a/docs/docs/combinators/og.jpg b/docs/docs/combinators/og.jpg new file mode 100644 index 00000000..b6be4af0 Binary files /dev/null and b/docs/docs/combinators/og.jpg differ diff --git a/docs/docs/comments-and-documenting-the-code/index.html b/docs/docs/comments-and-documenting-the-code/index.html new file mode 100644 index 00000000..e1f6cbc3 --- /dev/null +++ b/docs/docs/comments-and-documenting-the-code/index.html @@ -0,0 +1,61 @@ +Comments and Documenting the code · Learning Rust

Comments and Documenting the code

Comments

// Line comments
+/* Block comments */

Nested block comments are supported.

+Tip

By convention, try to avoid using block comments. Use line comments instead.

Doc Comments

As we discussed, we can generate the project documentation via rustdoc by running the cargo doc command. It uses the doc comments to generate the documentation.

💡 Usually we are adding doc comments on library crates. Also, we can use Markdown notations inside the doc comments.

/// Line comments; document the next item
+/** Block comments; document the next item */
+
+//! Line comments; document the enclosing item
+/*! Block comments; document the enclosing item !*/

For example,

/// This module contains tests; Outer comment
+mod tests {
+
+}
+
+mod tests {
+    //! This module contains tests; Inner comment
+
+}
+Recap

The mod keyword is used for modules. Don’t worry about this for now; it’ll be discussed later.

Doc Attributes

Doc attributes are alternatives for doc comments. Especially, we use these doc attributes while we need to set controls on rustdoc. Refer the doc attributes section of rustdoc documentation for more details.

In the following example, each comment is equivalent to relevant doc attribute.

/// Outer comment
+#[doc = "Outer comment"]
+
+//! Inner comment
+#![doc = "Inner comment"]
+Tip

An attribute is a general, free-form metadatum that is interpreted according to the name, convention, language and compiler version. Any item declaration may have an attribute applied to it. Syntax:

  • Outer attribute: #[attr]
  • Inner attribute: #![attr]

👨‍🏫 Before going to the next…

  • Use //! only to write crate-level documentation, nothing else. When using mod blocks, use /// outside of the block. Check the usage of //! and /// doc comments of few popular crates on crates.io. For example, check serde/src/lib.rs and rand/src/lib.rs.

  • Run cargo new hello_lib --lib command to create a sample crate and replace its src/lib.rs file with the following code. Then run cd hello_lib && cargo doc --open to generate the documentation and open it from your web browser.

    //! A Simple Hello World Crate
    +
    +/// This function returns the greeting; Hello, world!
    +pub fn hello() -> String {
    +    ("Hello, world!").to_string()
    +}
    +
    +#[cfg(test)]
    +mod tests {
    +    use super::hello;
    +
    +    #[test]
    +    fn test_hello() {
    +        assert_eq!(hello(), "Hello, world!");
    +    }
    +}
\ No newline at end of file diff --git a/docs/docs/comments-and-documenting-the-code/og.jpg b/docs/docs/comments-and-documenting-the-code/og.jpg new file mode 100644 index 00000000..20658698 Binary files /dev/null and b/docs/docs/comments-and-documenting-the-code/og.jpg differ diff --git a/docs/docs/control-flows/index.html b/docs/docs/control-flows/index.html new file mode 100644 index 00000000..e56ab505 --- /dev/null +++ b/docs/docs/control-flows/index.html @@ -0,0 +1,235 @@ +Control Flows · Learning Rust

Control Flows

if - else if - else

if

let age = 13;
+
+if age < 18 {
+    println!("Hello, child!"); // The code prints this
+}

if else

let i = 7;
+
+if i % 2 == 0 {
+    println!("Even");
+} else {
+    println!("Odd"); // The code prints this
+}

With let Statements

let age: u8 = 13;
+let is_below_eighteen = if age < 18 { true } else { false }; // true
+

if else if else

i. A simple example,

let team_size = 7;
+
+if team_size < 5 {
+    println!("Small");
+} else if team_size < 10 {
+    println!("Medium"); // The code prints this
+} else {
+    println!("Large");
+}

ii. Let’s refactor the above code,

let team_size = 7;
+let team_size_in_text;
+
+if team_size < 5 {
+    team_size_in_text = "Small";
+} else if team_size < 10 {
+    team_size_in_text = "Medium";
+} else {
+    team_size_in_text = "Large";
+}
+
+println!("Current team size : {}", team_size_in_text); // Current team size : Medium
+

iii. Let’s refactor further (variable shadowing),

let team_size = 7;
+let team_size = if team_size < 5 {
+    "Small" // ⭐️ no ;
+} else if team_size < 10 {
+    "Medium"
+} else {
+    "Large"
+};
+
+println!("Current team size : {}", team_size); // Current team size : Medium
+

⭐️ Return data type should be the same on each block when using this as an expression.

match

With Multiple Patterns

let tshirt_width = 20;
+let tshirt_size = match tshirt_width {
+    13 => "XS",     // check 13
+    14 | 15 => "S", // check 14 and 15
+    16..18 => "M",  // check from 16 to 17 / 18 exclusive (16,17)
+    18..=20 => "L", // check from 18 to 20 (18,19,20)
+
+    x if x > 20 && x < 26 => "XL", // check 21 to 25 (21,22,23,24,25)
+    // >,>=,<,<= via assigning the value to a variable (x) + if
+
+    _ => "Not Available", // default behavior, if none of conditions matches
+};
+
+println!("{}", tshirt_size); // L
+

Without Default Behavior

let is_allowed = false;
+let list_type = match is_allowed {
+    true => "Full",
+    false => "Restricted"
+    // no default/ _ condition can be skipped
+    // Because data type of is_allowed is boolean and all possibilities checked on conditions
+};
+
+println!("{}", list_type); // Restricted
+

With Multiple Variable Matchings

let marks_paper_a: u8 = 25;
+let marks_paper_b: u8 = 30;
+
+let output = match (marks_paper_a, marks_paper_b) {
+    (50, 50) => "Full marks for both papers",
+    (50, _) => "Full marks for paper A",
+    (_, 50) => "Full marks for paper B",
+    (x, y) if x > 25 && y > 25 => "Good",
+    (_, _) => "Work hard"
+};
+
+println!("{}", output); // Work hard
+

loop

Infinite loop

// ⚠️ This will run forever without terminating. Termininate manually (Ctrl+C)
+loop {
+    println!("Loop forever!");
+}

With break and continue

let mut a = 0;
+
+loop {
+    if a == 0 {
+        println!("Skip Value : {}", a);
+        a += 1;
+        continue;
+    } else if a == 2 {
+        println!("Break At : {}", a);
+        break;
+    }
+
+    println!("Current Value : {}", a);
+    a += 1;
+}

Returning a Value with break

let (mut x, y) = (1, 10);
+
+let z = loop {
+    x *= 2; // x = 2,4,8,16...
+
+    if x >= y { // 16 >= 10, so return 16
+        break x;
+    }
+};
+
+println!("{z}"); // 16
+

Labeling and break the Outer loop

let mut b1 = 1;
+
+'outer: loop { // set label outer (or any other snake_case name)
+    let mut b2 = 1;
+
+    'inner: loop { // set label inner
+        println!("Current Value : [{}][{}]", b1, b2);
+
+        if b1 == 2 && b2 == 2 {
+            break 'outer; // kill outer loop
+        } else if b2 == 5 {
+            break;
+        }
+
+        b2 += 1;
+    }
+
+    b1 += 1;
+}

while

Infinite while

// ⚠️ This will run forever without terminating. Termininate manually (Ctrl+C)
+while true {
+    println!("While forever!");
+}

A simple while

let mut a = 1;
+
+while a <= 10 {
+    println!("Current value : {}", a);
+    a += 1; //no ++ or -- on Rust
+}

With break and continue

let mut b = 0;
+
+while b < 5 {
+    if b == 0 {
+        println!("Skip value : {}", b);
+        b += 1;
+        continue;
+    } else if b == 2 {
+        println!("Break At : {}", b);
+        break;
+    }
+
+    println!("Current value : {}", b);
+    b += 1;
+}
+
+// 💡 You can't break with a value in a while
+

Labeling and break the Outer while

let mut c1 = 1;
+
+'outer: while c1 < 6 { // set label outer (or any other snake_case name)
+    let mut c2 = 1;
+
+    'inner: while c2 < 6 { // set label inner
+        println!("Current Value : [{}][{}]", c1, c2);
+
+        if c1 == 2 && c2 == 2 {
+            break 'outer; // kill outer while
+        }
+
+        c2 += 1;
+    }
+
+    c1 += 1;
+}

for

A simple for

// 0 to 10 (10 exclusive); In other languages, `for(i = 0; i < 10; i++)`
+for i in 0..10 {
+    println!("Current value : {}", i);
+}
// 1 to 10 (10 inclusive); In other languages, `for(i = 1; i <= 10; i++)`
+for i in 1..=10 {
+    println!("Current value : {}", i);
+}

With break and continue

// 💡 You can't break with a value in a for
+for b in 0..6 {
+    if b == 0 {
+        println!("Skip Value : {}", b);
+        continue;
+    } else if b == 2 {
+        println!("Break At : {}", b);
+        break;
+    }
+
+    println!("Current value : {}", b);
+}

Labeling and break the Outer for

'outer: for c1 in 1..6 { // set label outer (or any other snake_case name)
+
+    'inner: for c2 in 1..6 { // set label inner
+        println!("Current Value : [{}][{}]", c1, c2);
+
+        if c1 == 2 && c2 == 2 {
+            break 'outer; // kill outer for
+        }
+    }
+}

With Arrays and Vectors

let group : [&str; 4] = ["Mark", "Larry", "Bill", "Steve"];
+
+// 👎 group.len() = 4 check on each iteration of the for loop
+for n in 0..group.len() {
+    println!("Current Person : {}", group[n]);
+}
+
+// 👍 group.iter() turn the array into a simple iterator
+for person in group.iter() {
+    println!("Current Person : {person}");
+}
+
+// 👍 group.iter().enumerate() helps to read both the current index (starting from zero) and the value
+for (index, person) in group.iter().enumerate() {
+    println!("Person {index} : {person}");
+}

With a Vector of Tuples

let list = vec![(1, "Mark"), (2, "Larry"), (3, "Steve")];
+
+for (index, person) in list {
+    println!("Person {index} : {person}");
+}
\ No newline at end of file diff --git a/docs/docs/control-flows/og.jpg b/docs/docs/control-flows/og.jpg new file mode 100644 index 00000000..aa79424c Binary files /dev/null and b/docs/docs/control-flows/og.jpg differ diff --git a/docs/docs/crates/index.html b/docs/docs/crates/index.html new file mode 100644 index 00000000..75114f5b --- /dev/null +++ b/docs/docs/crates/index.html @@ -0,0 +1,215 @@ +Crates · Learning Rust

Crates

+Caution

This needs a refresh!

💭 Crates are a bit similar to the packages in some other languages. Crates compile individually. If the crate has child file modules, those files will get merged with the crate file and compile as a single unit.

💭 A crate can produce an executable/ a binary or a library. src/main.rs is the crate root/ entry point for a binary crate and src/lib.rs is the entry point for a library crate.

01. lib.rs on executable crate

💡 When writing binary crates, we can move the main functionalities to src/lib.rs and use it as a library from src/main.rs. This pattern is quite common on executable crates.

// # Think we run,
+cargo new greetings
+touch greetings/src/lib.rs
+
+// # It generates,
+greetings
+ ├── Cargo.toml
+ └── src
+    ├── lib.rs
+    └── main.rs
+
+// # Think we modify following files,
+
+// 01. greetings/src/lib.rs
+pub fn hello() {
+    println!("Hello, world!");
+}
+
+// 02. greetings/src/main.rs
+fn main() {
+    greetings::hello();
+}

As I mentioned earlier, in here we use simplest examples to reduce the complexity of learning materials. But this is how we need to write greetings/src/lib.rs to make the code more testable.

// greetings/src/lib.rs
+pub fn hello() -> String {
+  //! This returns `Hello, world!` String
+  ("Hello, world!").to_string()
+}
+
+// 01. Tests for `hello()`
+#[test] // Indicates that this is a test function
+fn test_hello() {
+  assert_eq!(hello(), "Hello, world!");
+}
+
+// 02. Tests for `hello()`, Idiomatic way
+#[cfg(test)] // Only compiles when running tests
+mod tests { // Separates tests from code
+  use super::hello; // Import root `hello()` function
+  
+    #[test]
+    fn test_hello() {
+        assert_eq!(hello(), "Hello, world!");
+    }
+}

📖 When importing a crate that has dashes in its name “like-this”, which is not a valid Rust identifier, it will be converted by changing the dashes to underscores, so you would write extern crate like_this;

lib.rs can link with multiple files.

// # Think we run,
+cargo new phrases
+touch phrases/src/lib.rs
+touch phrases/src/greetings.rs
+
+// # It generates,
+phrases
+ ├── Cargo.toml
+ └── src
+    ├── greetings.rs
+    ├── lib.rs
+    └── main.rs
+   
+// # Think we modify following files,
+
+// 01. phrases/src/greetings.rs
+pub fn hello() {
+    println!("Hello, world!");
+}
+
+// 02. phrases/src/main.rs
+fn main() {
+    phrases::greetings::hello();
+}
+
+// 03. phrases/src/lib.rs
+pub mod greetings; // ⭐️ Import `greetings` module as a public module
+

02. Dependency crate on Cargo.toml

When the code in the lib.rs file is getting larger, we can move those into a separate library crate and use it as a dependency of the main crate. As we mentioned earlier, a dependency can be specified from a folder path, git repository or by crates.io.

a. Using folder path

Let’s see how to create a nested crate and use it as a dependency using folder path,

// # Think we run,
+cargo new phrases
+cargo new phrases/greetings --lib
+
+// # It generates,
+phrases
+ ├── Cargo.toml
+ ├── greetings
+   ├── Cargo.toml
+   └── src
+      └── lib.rs
+ └── src
+    └── main.rs
+
+// # Think we modify following files,
+
+// 01. phrases/Cargo.toml
+[package]
+name = "phrases"
+version = "0.1.0"
+authors = ["Dumindu Madunuwan"]
+
+[dependencies]
+greetings = { path = "greetings" }
+
+// 02. phrases/greetings/src/lib.rs
+pub fn hello() {
+    println!("Hello, world!");
+}
+
+// 03. phrases/src/main.rs
+extern crate greetings;
+
+fn main() {
+    greetings::hello();
+}

b. Using git repository

If you want to use a library crate on multiple projects, one way is moving crate code to a git repository and use it as a dependency when needed.

// -- Cargo.toml --
+[dependencies]
+
+// 01. Get the latest commit on the master branch
+rocket = { git = "https://github.com/SergioBenitez/Rocket" }
+
+// 02. Get the latest commit of a specific branch
+rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "v0.3" }
+
+// 03. Get a specific tag
+rocket = { git = "https://github.com/SergioBenitez/Rocket", tag = "v0.3.2" }
+
+// 04. Get a specific revision (on master or any branch, according to rev)
+rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "8183f636305cef4adaa9525506c33cbea72d1745" }

c. Using crates.io

The other way is uploading it to crates.io and use it as a dependency when needed.

🚧 First, let’s create a simple “Hello world” crate and upload it to crates.io.

// # Think we run,
+cargo new test_crate_hello_world --lib
+
+// # It generates,
+test_crate_hello_world
+ ├── Cargo.toml
+ └── src
+    └── lib.rs
+   
+// # Think we modify following files,
+
+// 01. test_crate_hello_world/Cargo.toml
+[package]
+name = "test_crate_hello_world"
+version = "0.1.0"
+authors = ["Dumindu Madunuwan"]
+
+description = "A Simple Hello World Crate"
+repository = "https://github.com/dumindu/test_crate_hello_world"
+keywords = ["hello", "world"]
+license = "Apache-2.0"
+
+[dependencies]
+
+// 02. test_crate_hello_world/src/lib.rs
+//! A Simple Hello World Crate
+
+/// This function returns the greeting; `Hello, world!`
+pub fn hello() -> String {
+    ("Hello, world!").to_string()
+}
+
+#[cfg(test)]
+mod tests {
+
+    use super::hello;
+    
+    #[test]
+    fn test_hello() {
+        assert_eq!(hello(), "Hello, world!");
+    }
+}
+Recap

//! doc comments are used to write crate and module-level documentation. On other places, we have to use /// outside of the block. And when uploading a crate to crates.io, cargo generates the documentation from these doc comments and host it on docs.rs.

💡 We have to add the description and license fields to Cargo.toml. Otherwise, we will get error: api errors: missing or empty metadata fields: description, license. Please see http://doc.crates.io/manifest.html

To upload this to crates.io,

  1. We have to create an account on crates.io to acquire an API token
  2. Then run cargo login <token> with that API token and cargo publish

📖 This is how it describes on Cargo Docs with more details.

  • You’ll need an account on crates.io to acquire an API token. To do so, visit the home page and log in via a GitHub account (required for now). After this, visit your Account Settings page and run the cargo login command specified. +Ex. cargo login abcdefghijklmnopqrstuvwxyz012345
  • The next step is to package up your crate into a format that can be uploaded to crates.io. For this we’ll use the cargo package sub-command.
  • Now, it can be uploaded to crates.io with the cargo publish command.
  • If you’d like to skip the cargo package step, the cargo publish sub-command will automatically package up the local crate if a copy isn’t found already.

The name of our crate is test_crate_hello_world. So it can be found on, +📦 https://crates.io/crates/test_crate_hello_world +📑 https://docs.rs/test_crate_hello_world

+Tip

crates.io supports readme files as well. To enable it, we have to add the readme field to Cargo.toml. Ex: readme="README.md"

🏗️ Okay then, Let’s see how we can use this from another crate.

// # Think we run,
+cargo new greetings
+
+// # It generates,
+greetings
+ ├── Cargo.toml
+ └── src
+    └── main.rs
+
+// # Think we modify following files,
+
+// 01. greetings/Cargo.toml
+[package]
+name = "greetings"
+version = "0.1.0"
+authors = ["Dumindu Madunuwan"]
+
+[dependencies]
+test_crate_hello_world = "0.1.0"
+
+// 02. greetings/src/main.rs
+extern crate test_crate_hello_world;
+
+fn main() {
+    println!("{}", test_crate_hello_world::hello());
+}

By default, Cargo looks dependencies on crates.io. So we have to add only the crate name and a version string to Cargo.toml and then run cargo build to fetch the dependencies and compile them.

\ No newline at end of file diff --git a/docs/docs/crates/og.jpg b/docs/docs/crates/og.jpg new file mode 100644 index 00000000..7846e6ab Binary files /dev/null and b/docs/docs/crates/og.jpg differ diff --git a/docs/docs/custom-error-types/index.html b/docs/docs/custom-error-types/index.html new file mode 100644 index 00000000..be225a3e --- /dev/null +++ b/docs/docs/custom-error-types/index.html @@ -0,0 +1,243 @@ +Custom Error Types · Learning Rust

Custom Error Types

Rust allow us to create our own Err types. We call them “Custom Error Types”.

Error trait

As you know traits define the functionality a type must provide. But we don’t always need to define new traits for common functionalities, because Rust standard library provides reusable traits which can be implemented on our own types. While creating custom error types the std::error::Error trait helps us to convert any type to an Err type.

use std::fmt::{Debug, Display};
+
+pub trait Error: Debug + Display {
+    fn source(&self) -> Option<&(Error + 'static)> { ... }
+}

As we discussed under traits inheritance, a trait can be inherited from another traits. trait Error: Debug + Display means Error trait inherits from fmt::Debug and fmt::Display traits.

// traits inside Rust standard library core fmt module/ std::fmt
+pub trait Display {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), Error>;
+}
+
+pub trait Debug {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), Error>;
+}
  • Display

    • How should the end user see this error as a message/ user-facing output.
    • Usually print via println!("{}") or eprintln!("{}")
  • Debug

    • How should display the Err while debugging/ programmer-facing output.
    • Usually print via println!("{:?}") or eprintln!("{:?}")
    • To pretty-print, println!("{:#?}") or eprintln!("{:#?}") can be used.
  • source()

    • The lower-level source of this error, if any.
    • Optional.

First, let’s see how to implement std::error::Error trait on a simplest custom error type.

use std::fmt;
+
+// Custom error type; can be any type which defined in the current crate
+// 💡 In here, we use a simple "unit struct" to simplify the example
+struct AppError;
+
+// Implement std::fmt::Display for AppError
+impl fmt::Display for AppError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "An Error Occurred, Please Try Again!") // user-facing output
+    }
+}
+
+// Implement std::fmt::Debug for AppError
+impl fmt::Debug for AppError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{{ file: {}, line: {} }}", file!(), line!()) // programmer-facing output
+    }
+}
+
+// A sample function to produce an AppError Err
+fn produce_error() -> Result<(), AppError> {
+    Err(AppError)
+}
+
+fn main() {
+    match produce_error() {
+        Err(e) => eprintln!("{}", e), // An Error Occurred, Please Try Again!
+        _ => println!("No error"),
+    }
+
+    eprintln!("{:?}", produce_error()); // Err({ file: src/main.rs, line: 17 })
+}

Hope you understood the main points. Now, let’s see a custom error type with an error code and an error message.

use std::fmt;
+
+struct AppError {
+    code: usize,
+    message: String,
+}
+
+// Different error messages according to AppError.code
+impl fmt::Display for AppError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let err_msg = match self.code {
+            404 => "Sorry, Can not find the Page!",
+            _ => "Sorry, something is wrong! Please Try Again!",
+        };
+
+        write!(f, "{}", err_msg)
+    }
+}
+
+// A unique format for dubugging output
+impl fmt::Debug for AppError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "AppError {{ code: {}, message: {} }}",
+            self.code, self.message
+        )
+    }
+}
+
+fn produce_error() -> Result<(), AppError> {
+    Err(AppError {
+        code: 404,
+        message: String::from("Page not found"),
+    })
+}
+
+fn main() {
+    match produce_error() {
+        Err(e) => eprintln!("{}", e), // Sorry, Can not find the Page!
+        _ => println!("No error"),
+    }
+
+    eprintln!("{:?}", produce_error()); // Err(AppError { code: 404, message: Page not found })
+
+    eprintln!("{:#?}", produce_error());
+    // Err(
+    //     AppError { code: 404, message: Page not found }
+    // )
+}

⭐️ Rust standard library provides not only reusable traits and also it facilitates to magically generate implementations for few traits via #[derive] attribute. Rust support derive std::fmt::Debug, to provide a default format for debug messages. So we can skip std::fmt::Debug implementation for custom error types and use #[derive(Debug)] before struct declaration.

For a struct #[derive(Debug)] prints, the name of the struct , { , comma-separated list of each field’s name and debug value and }.

use std::fmt;
+
+#[derive(Debug)] // derive std::fmt::Debug on AppError
+struct AppError {
+    code: usize,
+    message: String,
+}
+
+impl fmt::Display for AppError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let err_msg = match self.code {
+            404 => "Sorry, Can not find the Page!",
+            _ => "Sorry, something is wrong! Please Try Again!",
+        };
+
+        write!(f, "{}", err_msg)
+    }
+}
+
+fn produce_error() -> Result<(), AppError> {
+    Err(AppError {
+        code: 404,
+        message: String::from("Page not found"),
+    })
+}
+
+fn main() {
+    match produce_error() {
+        Err(e) => eprintln!("{}", e), // Sorry, Can not find the Page!
+        _ => println!("No error"),
+    }
+
+    eprintln!("{:?}", produce_error()); // Err(AppError { code: 404, message: Page not found })
+
+    eprintln!("{:#?}", produce_error());
+    // Err(
+    //     AppError {
+    //         code: 404,
+    //         message: "Page not found"
+    //     }
+    // )
+}

From trait

When writing real programs, we mostly have to deal with different modules, different std and third party crates at the same time. Each crate uses their own error types. However, if we are using our own error type, we should convert those errors into our error type. For these conversions, we can use the standardized trait std::convert::From.

// traits inside Rust standard library core convert module/ std::convert
+pub trait From<T>: Sized {
+  fn from(_: T) -> Self;
+}
+Tip

As you know, String::from() function is used to create a String from &str data type. Actually this also an implementation of std::convert::From trait.

Let’s see how to implement std::convert::From trait on a custom error type.

use std::fs::File;
+use std::io;
+
+#[derive(Debug)]
+struct AppError {
+    kind: String,    // type of the error
+    message: String, // error message
+}
+
+// Implement std::convert::From for AppError; from io::Error
+impl From<io::Error> for AppError {
+    fn from(error: io::Error) -> Self {
+        AppError {
+            kind: String::from("io"),
+            message: error.to_string(),
+        }
+    }
+}
+
+fn main() -> Result<(), AppError> {
+    let _file = File::open("nonexistent_file.txt")?; // This generates an io::Error. But because of return type is Result<(), AppError>, it converts to AppError
+
+    Ok(())
+}
+
+
+// --------------- Run time error ---------------
+Error: AppError { kind: "io", message: "No such file or directory (os error 2)" }

In the above example, File::open(“nonexistent.txt”)? produces std::io::Error. But because of the return type is Result<(), AppError>, it converts to an AppError. Because of we are propagating the error from main() function, it prints the Debug representation of the Err.

In the above example we deal with only one std error type, std::io::Error. Let’s see some example which handles multiple std error types.

use std::fs::File;
+use std::io::{self, Read};
+use std::num;
+
+#[derive(Debug)]
+struct AppError {
+    kind: String,
+    message: String,
+}
+
+// Implement std::convert::From for AppError; from io::Error
+impl From<io::Error> for AppError {
+    fn from(error: io::Error) -> Self {
+        AppError {
+            kind: String::from("io"),
+            message: error.to_string(),
+        }
+    }
+}
+
+// Implement std::convert::From for AppError; from num::ParseIntError
+impl From<num::ParseIntError> for AppError {
+    fn from(error: num::ParseIntError) -> Self {
+        AppError {
+            kind: String::from("parse"),
+            message: error.to_string(),
+        }
+    }
+}
+
+fn main() -> Result<(), AppError> {
+    let mut file = File::open("hello_world.txt")?; // generates an io::Error, if can not open the file and converts to an AppError
+
+    let mut content = String::new();
+    file.read_to_string(&mut content)?; // generates an io::Error, if can not read file content and converts to an AppError
+
+    let _number: usize;
+    _number = content.parse()?; // generates num::ParseIntError, if can not convert file content to usize and converts to an AppError
+
+    Ok(())
+}
+
+
+// --------------- Few possible run time errors ---------------
+
+// 01. If hello_world.txt is a nonexistent file
+Error: AppError { kind: "io", message: "No such file or directory (os error 2)" }
+
+// 02. If user doesn't have relevant permission to access hello_world.txt
+Error: AppError { kind: "io", message: "Permission denied (os error 13)" }
+
+// 03. If hello_world.txt contains non-numeric content. ex Hello, world!
+Error: AppError { kind: "parse", message: "invalid digit found in string" }
+Assignment

Search about the implementation of std::io::ErrorKind, to see how to organize error types further.

\ No newline at end of file diff --git a/docs/docs/custom-error-types/og.jpg b/docs/docs/custom-error-types/og.jpg new file mode 100644 index 00000000..d13db674 Binary files /dev/null and b/docs/docs/custom-error-types/og.jpg differ diff --git a/docs/docs/enums/index.html b/docs/docs/enums/index.html new file mode 100644 index 00000000..cc158da4 --- /dev/null +++ b/docs/docs/enums/index.html @@ -0,0 +1,161 @@ +Enums · Learning Rust

Enums

  • An enum is a single type that contains variants, which represent the possible values of the enum at any given time.
  • By convention, the enum name and its variants’ names should follow PascalCase.
  • Can access the variants using the :: notation and the variant name. ex. Day::Sunday
enum Day {
+    Sunday,
+    Monday,
+    Tuesday,
+    Wednesday,
+    Thursday,
+    Friday,
+    Saturday,
+}
+
+// 💡 Day is the enum. Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday are its variants.
+
  • An enum variant can have either,
    • No data (a unit variant)
    • Unnamed ordered data (a tuple variant)
    • Named data/ fields (a struct variant)
    enum FlashMessage {
    +    Success, // 💡 A unit variant (no data)
    +    Error(u8, String), // 💡 A tuple variant (one or more , separated data)
    +    Warning { field: String, message: String }, // 💡 A struct variant (one or more , separated name: value data)
    +}
    +
    +// 💡 FlashMessage is the emnum, Success, Error, Warning are its variants.
    +
+Recap
  • In Rust, the term “instantiation” is used to describe the act of creating a concrete instance of a type (struct or enum).
  • In Rust, the term “field” is used to describe a named component in a C-like struct & struct-like enum variant, and the term “element” is used to describe an unnamed component in a tuple struct & tuple-like enum variant. The term “member” is used to describe both.
  • More complex examples can be found on Generics, Impls and Traits, Lifetimes and Modules sections.

Instantiation

#![allow(unused)] // 💡 skip unused warnings, as we don't read fields in the enums
+
+#[derive(Debug)]
+enum FlashMessage { // Definition
+    Success,
+    Error(u32, String),
+    Warning { field: String, message: String },
+}
+
+fn main() {
+    // 1. Instantiation with separate variable declaration and assignment
+    let x: FlashMessage; // Declaration with the data type
+    x = FlashMessage::Success;
+    println!("{x:?}"); // Success
+    
+    // 2. Instantiation with a direct variable initialization
+    let a = FlashMessage::Success;
+    let b = FlashMessage::Error(401, "Unauthorized".to_string());
+    let c = FlashMessage::Warning { field: "email".to_string(), message: "This is required".to_string() };
+
+    println!("{a:?}"); // Success
+    println!("{b:?}"); // Error(401, "Unauthorized")
+    println!("{c:?}"); // Warning { field: "email", message: "This is required" }    
+}
// 3. Instantiation with a default variant
+#![allow(unused)] // 💡 skip unused warnings, as we don't use the all variants of the enum
+
+#[derive(Debug, Default)]
+enum Hand {
+    Left,
+    #[default] // 💡Set Right as the default variant
+    Right,
+}
+
+fn main() {
+    let a = Hand::default(); // Instantiation with the default variant
+    println!("{a:?}"); // Right
+}
+Tip

In Rust, the #[derive()] attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The std::fmt::Debug trait allows us to format a value with {:?} or {:#?} in println! and similar macros. The std::default::Default trait allows us to create a new instance of a type with the Type::default() method.

Pattern Matching

With match

#![allow(unused)] // 💡 skip unused warnings, as we don't use the all variants of the enum
+
+enum Season {
+    Spring,
+    Summer,
+    Autumn,
+    Winter,
+}
+
+fn main() {
+    let a = Season::Winter;
+    let result = match a {
+        Season::Spring => "☀️",
+        Season::Summer => "🍁",
+        Season::Autumn => "🍂",
+        Season::Winter => "❄️",
+    };
+
+    println!("{result}"); // ❄️
+}

With if let, else if let, else

if let is useful when we only care about handling one (or few) specific patterns and don’t need to explicitly match every possible case.

#![allow(unused)] // 💡 skip unused warnings, as we don't use the all variants of the enum
+
+enum Season {
+    Spring,
+    Summer,
+    Autumn,
+    Winter,
+}
+
+fn main() {
+    let a = Season::Winter;
+    let result = if let Season::Spring = a {
+        "☀️"
+    } else if let Season::Summer = a {
+        "🍁"
+    } else if let Season::Autumn = a {
+        "🍂"
+    } else if let Season::Winter = a {
+        "❄️"
+    } else {
+        unreachable!()
+    };
+
+    println!("{result}"); // ❄️
+}

Destructuring & Accessing Variants’ Members

In Rust, directly accessing an enum variant’s fields without any form of pattern matching is not possible. We need to use pattern matching to access the fields by using a match expression or if let expression.

With match

#![allow(unused)] // 💡 skip unused warnings, as we don't use the all variants of the enum
+
+enum FlashMessage {
+    Success,
+    Error(u32, String),
+    Warning { field: String, message: String },
+}
+
+fn main() {
+    let a = FlashMessage::Error(401, "Unauthorized".to_string());
+
+    let result = match a {
+        FlashMessage::Success => "We'll get back to you.".to_string(),
+        FlashMessage::Error(_, msg) => msg, // 💡 Destructuring only the second element of the tuple variant.
+        FlashMessage::Warning { message, .. } => message, // 💡 Destructuring only the second field of the struct variant.
+    };
+
+    println!("{result}"); // Unauthorized
+}

With if let, else if let, else

if let is useful when we only care about handling one (or few) specific patterns and don’t need to explicitly match every possible case.

#![allow(dead_code)] // 💡 Remove dead_code warnings, as we don't access the all elements of variants.
+
+enum FlashMessage {
+    Success,
+    Error(u32, String),
+    Warning { field: String, message: String },
+}
+
+fn main() {
+    let a = FlashMessage::Error(401, "Unauthorized".to_string());
+
+    if let FlashMessage::Error(_, msg) = a {
+        println!("{msg}"); // Unauthorized
+    } else if let FlashMessage::Warning { message, .. } = a {
+        println!("{message}");
+    } else {
+        println!("We'll get back to you.");
+    }
+}
\ No newline at end of file diff --git a/docs/docs/enums/og.jpg b/docs/docs/enums/og.jpg new file mode 100644 index 00000000..689e982e Binary files /dev/null and b/docs/docs/enums/og.jpg differ diff --git a/docs/docs/error-and-none-propagation/index.html b/docs/docs/error-and-none-propagation/index.html new file mode 100644 index 00000000..63b85531 --- /dev/null +++ b/docs/docs/error-and-none-propagation/index.html @@ -0,0 +1,92 @@ +Error and None Propagation · Learning Rust

Error and None Propagation

We should use panics like panic!(), unwrap(), expect() only if we can not handle the situation in a better way. Also if a function contains expressions which can produce either None or Err,

  • we can handle them inside the same function. Or,
  • we can return None and Err types immediately to the caller. So the caller can decide how to handle them.

💡 None types no need to handle by the caller of the function always. But Rusts’ convention to handle Err types is, return them immediately to the caller to give more control to the caller to decide how to handle them.

? Operator

  • If an Option type has Some value or a Result type has a Ok value, the value inside them passes to the next step.
  • If the Option type has None value or the Result type has Err value, return them immediately to the caller of the function.

Example with Option type,

fn main() {
+    if complex_function().is_none() {
+        println!("X not exists!");
+    }
+}
+
+fn complex_function() -> Option<&'static str> {
+    let x = get_an_optional_value()?; // if None, returns immediately; if Some("abc"), set x to "abc"
+
+    // some other code, ex
+    println!("{}", x); // "abc" ; if you change line 19 `false` to `true` 
+
+    Some("")
+}
+
+fn get_an_optional_value() -> Option<&'static str> {
+
+    //if the optional value is not empty
+    if false {
+        return Some("abc");
+    }
+    
+    //else
+    None
+}

Example with Result Type,

fn main() {
+    // `main` function is the caller of `complex_function` function
+    // So we handle errors of complex_function(), inside main()
+    if complex_function().is_err() {
+        println!("Can not calculate X!");
+    }
+}
+
+fn complex_function() -> Result<u64, String> {
+    let x = function_with_error()?; // if Err, returns immediately; if Ok(255), set x to 255
+
+    // some other code, ex
+    println!("{}", x); // 255 ; if you change line 20 `true` to `false`
+
+    Ok(0)
+}
+
+fn function_with_error() -> Result<u64, String> {
+    //if error happens
+    if true {
+        return Err("some message".to_string());
+    }
+
+    // else, return valid output
+    Ok(255)
+}

try!()

? operator was added in Rust version 1.13. try!() macro is the old way to propagate errors before that. So we should avoid using this now.

  • If a Result type has Ok value, the value inside it passes to the next step. If it has Err value, returns it immediately to the caller of the function.
// using `?`
+let x = function_with_error()?; // if Err, returns immediately; if Ok(255), set x to 255
+
+// using `try!()`
+let x = try!(function_with_error());

Error propagation from main()

Before Rust version 1.26, we couldn’t propagate Result and Option types from the main() function. But now, we can propagate Result types from the main() function and it prints the Debug representation of the Err.

💡 We are going to discuss about Debug representations under Error trait section.

use std::fs::File;
+
+fn main() -> std::io::Result<()> {
+    let _ = File::open("not-existing-file.txt")?;
+
+    Ok(()) // Because of the default return value of Rust functions is an empty tuple/ ()
+}
+
+// Because of the program can not find not-existing-file.txt , it produces,
+//    Err(Os { code: 2, kind: NotFound, message: "No such file or directory" })
+// While propagating error, the program prints,
+//    Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }
+
+Assignment

If you want to know about the all kind of errors std::fs::File::open() can produce, check the error list on std::fs::OpenOptions.

\ No newline at end of file diff --git a/docs/docs/error-and-none-propagation/og.jpg b/docs/docs/error-and-none-propagation/og.jpg new file mode 100644 index 00000000..9f70457d Binary files /dev/null and b/docs/docs/error-and-none-propagation/og.jpg differ diff --git a/docs/docs/functions/index.html b/docs/docs/functions/index.html new file mode 100644 index 00000000..6619b4d3 --- /dev/null +++ b/docs/docs/functions/index.html @@ -0,0 +1,125 @@ +Functions · Learning Rust

Functions

Named Functions

  • Named functions are declared with the keyword fn
  • When using arguments, we must declare the data types.
  • By default, functions return an empty tuple/ (). If you want to return a value, the return type must be specified after ->

Hello world

fn main() {
+    println!("Hello, world!");
+}

Passing Arguments

fn print_sum(a: i8, b: i8) {
+    println!("sum is: {}", a + b);
+}

Returning Values

  • Without the return keyword. Only the last expression returns.

    fn plus_one(a: i32) -> i32 {
    +    a + 1
    +    // There is no ending ; in the above line.
    +    // It means this is an expression which equals to `return a + 1;`.
    +}
  • With the return keyword.

    fn plus_two(a: i32) -> i32 {
    +    return a + 2;
    +    // Should use return keyword only on conditional/ early returns.
    +    // Using return keyword in the last expression is a bad practice.
    +}

Function Pointers as a Data Type

fn main() {
+    let p1 = plus_one; // Without type declarations
+    let a = p1(5); // 6
+
+    let p1: fn(i32) -> i32 = plus_one; // With the type declarations
+    let b = p1(5); // 6
+}
+
+fn plus_one(i: i32) -> i32 {
+    i + 1
+}

Closures

  • Also known as anonymous functions or lambda functions.
  • The data types of arguments and returns are optional ⃰ⁱᵛ.

Example with a named function, before using closures.

fn main() {
+    let x = 2;
+    println!("{}", get_square_value(x));
+}
+
+fn get_square_value(i: i32) -> i32 {
+    i * i
+}

With Optional Type Annotations

fn main() {
+    let x = 2;
+    let square = |i: i32| -> i32 { // Input parameters are passed inside | | and expression body is wrapped within { }
+        i * i
+    };
+    println!("{}", square(x));
+}

Without Type Annotations

fn main() {
+    let x = 2;
+    let square = |i| i * i; // { } are optional for single-lined closures
+    println!("{}", square(x));
+}

With Optional Type Annotations; Define and Call Together

fn main() {
+    let x = 2;
+    let x_square = |i: i32| -> i32 { i * i }(x); // { } are mandatory while creating and calling same time.
+    println!("{}", x_square);
+}

Without Type Annotations; Define and Call Together

fn main() {
+    let x = 2;
+    let x_square = |i| -> i32 { i * i }(x); // ⭐️ The return type is mandatory.
+    println!("{}", x_square);
+}

Test Functions

  • Start the function name with the test_ prefix.
  • Add with the #[test] attribute, inside a tests module with the #[cfg(test)] attribute.
fn greet() -> String {
+    "Hello, world!".to_string()
+}
+
+#[cfg(test)]
+mod tests {
+    use super::greet; // 💡 Reimport the greet() function from the parent module.
+
+    #[test]
+    fn test_greet() { // The test function of greet()
+        assert_eq!("Hello, world!", greet());
+    }
+}

👨‍🏫 Before going to the next…

  • 💯 Usage of :: and . to call functions in different modules,

    +Recap

    This is a quick reference about the usage of :: and . operators while calling functions. So, please don’t worry about structs, enums, traits, or impls for now. We will discuss them later.

    • Functions are standalone blocks of code, declare with the fn keyword.

    • Associated functions are functions that are associated with a particular data type such as structs, enums, or traits via an impl block.

    • Methods are associated functions with a receiver of self, &self, &mut self, self: Box<Self> etc.

    +Tip
    • To call methods: use the . operator from an instance. ex. steve.intro_name()
    • To call associated functions that are not methods: use the :: operator from the data type. ex. Person::new(), String::from()
    struct Person {
    +    name: String,
    +    company_name: String,
    +}
    +
    +impl Person { // 💡 impls are used to define functions in Rust structs, enums, etc.
    +    // 💡 The constructor (new` is a conventional name, not a keyword)
    +    fn new(name: String, company_name: String) -> Person { // an associated function and not a method
    +        Person { name, company_name }
    +    }
    +
    +    fn intro_name(&self) -> String { // 💡 a method
    +        format!("I'm {}", self.name) // 💡 access fields via `.` operator
    +    }
    +
    +    fn intro_company(&self) -> String { // 💡 a method
    +        format!("I'm from {}", self.company_name)
    +    }
    +}
    +
    +fn main() {
    +    // 💡 calling associated functions with `::` operator
    +    let steve = Person::new(String::from("Steve Jobs"), String::from("Apple"));
    +
    +    // 💡 calling methods with `.` operator
    +    println!("{}. {}.", steve.intro_name(), steve.intro_company()); // I'm Steve Jobs. I'm from Apple.
    +
    +    // ⭐️ methods are also associated functions. So, we can call them with `::` operator as well but need to pass the instance as a parameter.
    +    println!("{}. {}.", Person::intro_name(&steve), Person::intro_company(&steve)); // I'm Steve Jobs. I'm from Apple.
    +}
    • Other than that, :: operator is used to call functions in different modules.

      mod my_mod {
      +    pub fn greet(name: &str) {
      +        println!("Hello, {name}!")
      +    }
      +}
      +
      +fn main() {
      +    my_mod::greet("Steve Jobs"); // Hello, Steve Jobs!
      +}
    +Assignment

    Refer path separator and member access operators for more information about the usage of the :: and . operators.

\ No newline at end of file diff --git a/docs/docs/functions/og.jpg b/docs/docs/functions/og.jpg new file mode 100644 index 00000000..85b584ed Binary files /dev/null and b/docs/docs/functions/og.jpg differ diff --git a/docs/docs/generics/index.html b/docs/docs/generics/index.html new file mode 100644 index 00000000..14783ac6 --- /dev/null +++ b/docs/docs/generics/index.html @@ -0,0 +1,112 @@ +Generics · Learning Rust

Generics

  • The core concept of generics is abstraction over types. They let us write one piece of code to operate with any data type without repeating ourselves to write separate versions for each type. At the compile time, Rust ensures the type safety and generates an optimized code for each concrete type used in the program.
  • Use an uppercase letter (T, U, …) or a PascalCase identifier for the data type.
    • Instead of x: u8 we use x: T.
    • Inform the compiler that T is a generic type by adding <T> at first.

With One Generic Type

struct Point<T> {
+    x: T,
+    y: T,
+}
+
+fn to_tuple<T>(x: T, y: T) -> (T, T) {
+    (x, y)
+}
+
+fn main() {
+    let a = Point { x: 0, y: 1 }; // a: Point<i32>
+    let b = to_tuple(a.x, a.y); // (i32, i32)
+    println!("{b:?}"); // (0, 1)
+
+    let c = Point { x: false, y: true }; // a: Point<bool>
+    let d = to_tuple(c.x, c.y); // (bool, bool)
+    println!("{d:?}"); // (false, true)
+}

With Multiple Generic Types

struct Point<T, U> {
+    x: T,
+    y: U,
+}
+
+fn to_shuffled_tuple<T, U>(x: T, y: U) -> (U, T) {
+    (y, x)
+}
+
+fn main() {
+    let a = Point { x: 1u8, y: true }; // a: Point<u8, bool>
+    let b = to_shuffled_tuple(a.x, a.y); // (bool, u8)
+    println!("{b:?}"); // (true, 1)
+}

On some occasions, the compiler cannot inter the type, and we have to specify the type when using the generic type. By the way, it’s good practice to specify the type on variables when using a generic implementation.

#[derive(Debug)]
+enum Data<K, V> {
+    Value(V),
+    KeyValue(K, V),
+}
+
+fn main() {
+    let a: Data<(), bool> = Data::Value(true); // ⭐️ The compiler can not inter the type here. We have to specify the type.
+    let b = Data::KeyValue(1, true); // The compiler can infer the type; i32, bool
+
+    println!("{a:?}"); // Value(true)
+    println!("{b:?}"); // KeyValue(1, true)
+}

👨‍🏫 Before going to the next…

  • Option and Result

    +Recap

    This is a quick reference to Option and Result as enums. Please don’t worry too much about them for now, as we will discuss them in detail later in Error Handling—Option & Result.

    Many languages use null\ nil\ undefined types to represent empty outputs, and Exceptions to handle errors. Rust skips using both, especially to prevent issues like null pointer exceptions, sensitive data leakages through exceptions, etc. Option and Result types are two special generic enums defined in Rust’s standard library to deal with these cases.

    // An output can have either Some value or no value/ None.
    +enum Option<T> { // T is a generic and it can contain any type of value.
    +    Some(T),
    +    None,
    +}
    +
    +// A result can represent either success/ Ok or failure/ Err.
    +enum Result<T, E> { // T and E are generics. T can contain any type of value, E can be any error.
    +    Ok(T),
    +    Err(E),
    +}
    +Tip
    • An optional value can have either Some value or no value/ None ⇒ possibility of absence
    • A result can represent either success/ Ok or failure/ Err ⇒ possibility of failure
    • Option

      struct Task {
      +    title: String,
      +    assignee: Option<Person>, // 💡 Instead of `assignee: Person`, we use `assignee: Option<Person>` as the assignee can be `None`.
      +}
      fn get_id_by_username(username: &str) -> Option<usize> { // 💡 Instead of setting return type as `usize`, set it `Option<usize>`
      +    // if username can be found in the system, return userId
      +        return Some(userId); // 💡 Instead of return userId, return Some(userId)
      +
      +    // else
      +        None // 💡 The last return statement no need `return` keyword and ending `;`
      +}
      +
      +fn main() {
      +    let username = "anonymous";
      +    match get_id_by_username(username) { // 💡 We can use pattern matching to catch the relevant return type (Some/None)
      +        None => println!("User not found"),
      +        Some(i) => println!("User Id: {}", i),
      +    }
      +}
    • Result

      fn get_word_count_from_file(file_name: &str) -> Result<u32, &str> { // 💡 Instead of setting return type as `u32`, set it `Result<u32, &str>`
      +    // if the file is not found on the system, return error
      +        return Err("File can not be found!"); // 💡 Instead panic/ break when the file can not be found; return Err(something)
      +
      +    // else, count and return the word count
      +        Ok(word_count) // 💡 Instead of return `word_count`, return `Ok(word_count)`
      +        // 💡 The last return statement no need `return` keyword and ending `;`
      +}
      +
      +fn main() {
      +    let mut file_name = "file_a";
      +    match get_word_count_from_file(file_name) { // 💡 We can use pattern matching to catch the relevant return type (Ok/Err)
      +        Ok(i) => println!("Word Count: {}", i),
      +        Err(e) => println!("Error: {}", e)
      +    }
      +}
\ No newline at end of file diff --git a/docs/docs/generics/og.jpg b/docs/docs/generics/og.jpg new file mode 100644 index 00000000..a6bf91b1 Binary files /dev/null and b/docs/docs/generics/og.jpg differ diff --git a/docs/docs/hello-world/index.html b/docs/docs/hello-world/index.html new file mode 100644 index 00000000..183d9cb6 --- /dev/null +++ b/docs/docs/hello-world/index.html @@ -0,0 +1,78 @@ +Hello World · Learning Rust

Hello World

Hello, World!

fn main() {
+    println!("Hello, world!");
+}

fn means function. The main function is the beginning of every Rust program. +println!() prints text to the console and its ! indicates that it’s a macro rather than a function.

+Tip

Rust files should have .rs file extension and if you’re using more than one word for the file name, follow the snake_case convention.

  • Save the above code in file.rs , but it can be any name with .rs extension.
  • Compile it with rustc file.rs
  • Execute it with ./file on Linux and Mac or file.exe on Windows

Rust Playground

Rust Playground is a web interface for running Rust code.

Rust Playground

👨‍🏫 Before going to the next…

  • These are the other usages of the println!() macro,

    println!("{}, {}!", "Hello", "world"); // Hello, world!
    +
    +println!("{0}, {1}!", "Hello", "world"); // Hello, world!
    +
    +println!("{a}, {b}!", a = "Hello", b = "world"); // Hello, world!
    +
    +let (a, b) = ("Hello", "world"); // 💡 Two Variable bindings declare & initialize in one line.
    +println!("{a}, {b}!"); // Hello, world!
    +
    +println!(); // A new line
    +
    // Debug print and pretty-print debug print format specifiers
    +println!("{:?}", [1, 2, 3]); // [1, 2, 3]
    +
    +println!("{:#?}", [1, 2, 3]);
    +/*
    +    [
    +        1,
    +        2,
    +        3
    +    ]
    +*/
    +
    +// 💡 We can use the variable directly with the format specifier as well.
    +let a = [1, 2, 3];
    +
    +println!("{a:?}"); // similar to println!("{:?}", a);
    +println!("{a:#?}"); // similar to println!("{:#?}", a);
    +
  • Rust has a print!() macro as well.

    print!("Hello, world!\n"); // With new line
    +print!("Hello, world!"); // Without new line
    +
  • The format!() macro is used to store the formatted string.

    let a = format!("{}, {}!", "Hello", "world");
    +println!("{a}"); // Hello, world!
    +
  • Let’s play a bit more…

    println!("{}", "Hello, world!".to_uppercase()); // HELLO, WORLD!
    +println!("{}", "Hello, world!".to_lowercase()); // hello, world!
    +
    +println!("{}", "⭐️".repeat(3)); // ⭐️⭐️⭐️
    +
    +println!("{}", "Hello, world!".chars().count()); // 13
    +// 💡 For more accurate results, you should use a crate like unicode_segmentation that follows more accurate Unicode text segmentation standards.
    +
  • Different format specifiers.

    let a = 255;
    +println!("{a}"); // 255
    +
    +println!("{a:b}"); // Binary 💡 11111111
    +println!("{a:o}"); // Octal 💡 377
    +println!("{a:x}"); // LowerHex 💡 ff
    +println!("{a:X}"); // UpperHex 💡 FF
    +
    +println!("{a:0>5}"); // Add leading zeros till character lengh 5 💡 00255
    +println!("{a:0<5}"); // Add tailing zeros till character lengh 5 💡 25500
    +
  • Check the difference between macros and functions.

  • For more rustc commands, check the rustc --help command.

\ No newline at end of file diff --git a/docs/docs/hello-world/og.jpg b/docs/docs/hello-world/og.jpg new file mode 100644 index 00000000..8a117961 Binary files /dev/null and b/docs/docs/hello-world/og.jpg differ diff --git a/docs/docs/impls/index.html b/docs/docs/impls/index.html new file mode 100644 index 00000000..8de11b7a --- /dev/null +++ b/docs/docs/impls/index.html @@ -0,0 +1,94 @@ +Impls · Learning Rust

Impls

  • Earlier, we discussed that structs and enums group related data, while impl blocks and traits add associated and shared behavior to the data.
  • Usage of Self vs self keywords:
    • Self: Refers to the type itself (the blueprint).

    • self: Refers to the instance of the type (the actual data).

      +Tip

      This can be any form of self, &self, &mut self, self: Box<Self>, self: Pin<&mut Self>, etc.

  • There are multiple ways to implement a behavior for a type. We discuss only about the impl blocks with this article. The patterns involving traits are discussed under Traits.
+Important

The implementation must be in the same crate as the type.

Inherent impls

Implement associated functions, methods, and constants directly for a type.

impl Type

struct Person {
+    name: String,
+}
+
+impl Person {
+    const GREET: &str = "Hello!";
+
+    fn greet(&self) -> String { // &self` is shorthand for self: &Self
+        format!("{} I am {}.", Self::GREET, self.name) // 💡Self to access type; self to access instance
+    }
+}
+
+fn main() {
+    let steve = Person { name: "Steve".to_string() };
+    println!("{}", steve.greet()) // Hello! I am Steve.
+}
  • Inside the impl block,
    • Constants belong to the type itself.
      • To access them, use the Self keyword or the type name.
      • Example: Self::GREET (preferred in Rust) or Person::GREET.
    • Methods (functions that access instance fields) must send the instance/ self as the first parameter.
      • This can be self, &self, &mut self, self: Box<Self>, self: Pin<&mut Self>, etc.) as the first parameter.

impl<T> Type<T>

struct Point<T> {
+    x: T,
+    y: T,
+}
+
+impl<T> Point<T> {
+    fn into_tuple(self) -> (T, T) {
+        (self.x, self.y)
+    }
+}
+
+fn main() {
+    let a = Point { x: 0, y: 1 }; // a: Point<i32>
+    let b = a.into_tuple(); // (0, 1)
+    println!("{b:?}")
+}
+
+// 💡 into_tuple() take self (not &self) and consumes the self / instance.
+// 💡 we can pass &self (as a reference) and use as_ prefix to borrow the instance.
+// fn as_tuple(&self) -> (&T, &T) { (&self.x, &self.y) }
+

Associated Functions & Methods

  • Associated functions:
    • Functions that are associated with a particular data type via the impl block.
    • Can call from the type with :: operator.
      • Person::new(), Vec::new(), String::from()
  • Methods:
    • Associated functions with a receiver of self, &self, &mut self, self: Box<Self>, self: Pin<&mut Self>, etc.
    • Can call from the instance with . operator or from the type (as methods are also associated functions) with :: operator, but we need to pass the instance as the first parameter always.
      • steve.greet() or Person::greet(&steve)
      • "hello".to_string() or String::to_string("hello")
struct Person {
+    name: String,
+    company_name: String,
+}
+
+impl Person {
+    // 💡 The constructor (new` is a conventional name, not a keyword)
+    fn new(name: String, company_name: String) -> Self { // an associated function and not a method
+        Self { name, company_name }
+    }
+
+    fn intro_name(&self) -> String { // a method
+        format!("I'm {}", self.name)
+    }
+
+    fn intro_company(&self) -> String { // a method
+        format!("I'm from {}", self.company_name)
+    }
+}
+
+fn main() {
+    // Call from the type with `::` operator
+    let steve = Person::new(String::from("Steve Jobs"), String::from("Apple"));
+
+    // Call from the instance with `.` operator
+    println!("{}. {}.", steve.intro_name(), steve.intro_company()); // I'm Steve Jobs. I'm from Apple.
+
+    // As methods are also associated functions; Call from the type with `::` operator and pass the instance as the first parameter
+    println!("{}. {}.", Person::intro_name(&steve), Person::intro_company(&steve)); // I'm Steve Jobs. I'm from Apple.
+}

We can use the type/ Person instead Self keyword in the new function. By the way, using the Self keyword is considered idiomatic in Rust.

fn new(name: String, company_name: String) -> Person {
+    Person { name, company_name }
+}

👨‍🏫 Before going to the next…

  • Familiarize with the conventional prefixes and suffixes used in methods names.
    PrefixPostfixself takenself type
    as_none&self or &mut selfany
    from_nonenoneany
    into_noneselfany
    is_none&mut self or &self or noneany
    to__mut&mut selfany
    to_not _mutselfCopy
    to_not _mut&selfnot Copy
\ No newline at end of file diff --git a/docs/docs/impls/og.jpg b/docs/docs/impls/og.jpg new file mode 100644 index 00000000..dd52ce0d Binary files /dev/null and b/docs/docs/impls/og.jpg differ diff --git a/docs/docs/index.html b/docs/docs/index.html new file mode 100644 index 00000000..d5acc52f --- /dev/null +++ b/docs/docs/index.html @@ -0,0 +1 @@ +https://learning-rust.github.io/docs/overview/ \ No newline at end of file diff --git a/docs/docs/index.xml b/docs/docs/index.xml new file mode 100644 index 00000000..d97fcb3f --- /dev/null +++ b/docs/docs/index.xml @@ -0,0 +1,948 @@ +Docs on Learning Rusthttps://learning-rust.github.io/docs/Recent content in Docs on Learning RustHugoen-USBorrowinghttps://learning-rust.github.io/docs/borrowing/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/borrowing/<h2 id="borrowing">Borrowing</h2> +<ul> +<li> +<p>In a general sense, borrowing is taking something with the promise of returning it.</p> +</li> +<li> +<p>In Rust, borrowing is the act of creating a reference to a value without copying the data and without taking ownership.</p> +<ul> +<li>This is similar to the concept of &ldquo;passing by reference&rdquo; in other languages.</li> +<li>However in Rust, +<ul> +<li>References are immutable by default: shared, read-only/ immutable references: <code>&amp;T</code></li> +<li>To mutate the data, you must explicitly create a mutable reference: <code>&amp;mut T</code></li> +</ul> +</li> +</ul> +</li> +<li> +<p>Liveness</p>Cargo, Crates and Basic Project Structurehttps://learning-rust.github.io/docs/cargo-crates-and-basic-project-structure/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/cargo-crates-and-basic-project-structure/<h2 id="cargo">Cargo</h2> +<p>Cargo is Rust’s built-in package manager and build system. It also supports the following actions,</p> +<table> + <thead> + <tr> + <th>Command</th> + <th>Action</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>cargo new</code></td> + <td>Create a new project</td> + </tr> + <tr> + <td><code>cargo init</code></td> + <td>Create a new project in an existing directory</td> + </tr> + <tr> + <td><code>cargo check</code></td> + <td>Verify the project compiles without errors</td> + </tr> + <tr> + <td><code>cargo build</code></td> + <td>Build the executable</td> + </tr> + <tr> + <td><code>cargo run</code></td> + <td>Build the executable and run</td> + </tr> + <tr> + <td><code>cargo clean</code></td> + <td>Remove the build system directories/ <code>target</code> directory</td> + </tr> + </tbody> +</table> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <ul> +<li>The <code>cargo check</code> command verifies that the project compiles without errors, without producing an executable. +Thus, it is often faster than <code>cargo build</code>.</li> +<li>Cargo places executables compiled with <code>cargo build</code> or <code>cargo run</code> in the <code>target/debug/</code> directory. +But, while those built with <strong><code>cargo build --release</code></strong> for release purposes are stored in <code>target/release/</code> directory. +Release builds use more optimizations and remove some runtime safety checks to increase performance, although this comes at the cost of longer compile time.</li> +</ul> + </div> +</div><table> + <thead> + <tr> + <th>Command</th> + <th>Action</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>cargo add</code></td> + <td>Add a dependency crate to the project</td> + </tr> + <tr> + <td><code>cargo remove</code></td> + <td>Remove a dependency crate from the project</td> + </tr> + <tr> + <td><code>cargo fetch</code></td> + <td>Download the dependencies specified in Cargo.lock</td> + </tr> + <tr> + <td><code>cargo update</code></td> + <td>Update project dependencies</td> + </tr> + </tbody> +</table> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <ul> +<li>A crate is a package that can be shared via <a href="https://crates.io" target="_blank" >crates.io</a>, Rust community’s crate registry. +<code>cargo add</code>, <code>cargo remove</code>, <code>cargo fetch</code>, and <code>cargo update</code> commands manage project dependencies through the crate hosted on crates.io.</li> +<li>The <code>cargo add</code> command includes a specified crate in the <code>[dependencies]</code> section of <code>Cargo.toml</code>, while <code>cargo add --dev</code> adds a crate to the <code>[dev-dependencies]</code> section. This indicates that the crate is only used for development purposes like testing and will not be included in the final compiled code.</li> +</ul> + </div> +</div><table> + <thead> + <tr> + <th>Command</th> + <th>Action</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>cargo test</code></td> + <td>Run tests</td> + </tr> + <tr> + <td><code>cargo bench</code></td> + <td>Run benchmarks</td> + </tr> + <tr> + <td><code>cargo doc</code></td> + <td>Generate the project documentation via <a href="https://doc.rust-lang.org/stable/rustdoc/" target="_blank" >rustdoc</a></td> + </tr> + </tbody> +</table> +<p>In addition, there are <code>cargo</code> commands to publish the project as a crate to <a href="https://crates.io/" target="_blank" >crates.io</a>.</p>Combinatorshttps://learning-rust.github.io/docs/combinators/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/combinators/<h2 id="what-is-a-combinator">What is a combinator?</h2> +<ul> +<li> +<p>One meaning of “combinator” is a more informal sense referring to the <strong>combinator pattern</strong>, a style of organizing libraries centered around the idea of combining things. Usually there is <strong>some type T</strong>, some <strong>functions for constructing “primitive” values of type T</strong>, and some “<strong>combinators</strong>” which can <strong>combine values of type T</strong> in various ways to <strong>build up more complex values of type T</strong>. The other definition is <strong>&ldquo;function with no free variables&rdquo;</strong>. +__ <a href="https://wiki.haskell.org/Combinator" target="_blank" >wiki.haskell.org</a></p>Comments and Documenting the codehttps://learning-rust.github.io/docs/comments-and-documenting-the-code/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/comments-and-documenting-the-code/<h2 id="comments">Comments</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// Line comments +</span></span></span><span class="line"><span class="cl"><span class="cm">/* Block comments */</span></span></span></code></pre></div><p>Nested block comments are supported.</p> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>By convention, try to avoid using block comments. Use line comments instead.</p>Control Flowshttps://learning-rust.github.io/docs/control-flows/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/control-flows/<h2 id="if---else-if---else">if - else if - else</h2> +<h3 id="if"><code>if</code></h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">13</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">18</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Hello, child!&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// The code prints this +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="if-else"><code>if</code> <code>else</code></h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">7</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Even&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Odd&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// The code prints this +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="with-let-statements">With <code>let</code> Statements</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u8</span> <span class="o">=</span><span class="w"> </span><span class="mi">13</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">is_below_eighteen</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">18</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// true +</span></span></span></code></pre></div><h3 id="if-else-if-else"><code>if</code> <code>else if</code> <code>else</code></h3> +<p>i. A simple example,</p>Crateshttps://learning-rust.github.io/docs/crates/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/crates/<div class="alert alert-caution"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M480-300q15 0 25.5-10.5T516-336q0-15-10.5-25.5T480-372q-15 0-25.5 10.5T444-336q0 15 10.5 25.5T480-300Zm25.5-142.5Q516-453 516-468v-168q0-15-10.5-25.5T480-672q-15 0-25.5 10.5T444-636v168q0 15 10.5 25.5T480-432q15 0 25.5-10.5ZM371-144q-14 0-27-5t-24-16L165-321q-10-10-15.5-23.5T144-372v-217q0-14 5-27t16-24l155-155q11-11 24-16t27-5h218q14 0 27 5t24 16l155 155q11 11 16 24t5 27v218q0 14-5 27t-16 24L639-165q-10 10-23.5 15.5T588-144H371Zm0-72h218l155-155v-218L588-744H371L216-589v218l155 155Zm109-264Z"/></svg> + + + <span> + + Caution + + </span> + </div> + <div class="alert-body"> + <p>This needs a refresh!</p> + </div> +</div><p>💭 Crates are a bit similar to the packages in some other languages. Crates compile individually. If the crate has child file modules, those files will get merged with the crate file and compile as a single unit.</p>Custom Error Typeshttps://learning-rust.github.io/docs/custom-error-types/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/custom-error-types/<p>Rust allow us to create our own <code>Err</code> types. We call them “<em>Custom Error Types</em>”.</p> +<h2 id="error-trait">Error trait</h2> +<p>As you know <strong>traits define the functionality a type must provide</strong>. But we don’t always need to define new traits for common functionalities, because Rust <strong>standard library provides reusable traits</strong> which can be implemented on our own types. While creating custom error types the <a href="https://doc.rust-lang.org/std/error/trait.Error.html" target="_blank" ><code>std::error::Error</code> trait</a> helps us to convert any type to an <code>Err</code> type.</p>Enumshttps://learning-rust.github.io/docs/enums/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/enums/<ul> +<li>An enum is a single type that contains variants, which represent the possible values of the enum at any given time.</li> +<li>By convention, the enum name and its variants&rsquo; names should follow <a href="https://en.wikipedia.org/wiki/Camel_case" target="_blank" ><code>PascalCase</code></a>.</li> +<li>Can access the variants using the <code>::</code> notation and the variant name. ex. Day::Sunday</li> +</ul> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Day</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Sunday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Monday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Tuesday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Wednesday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Thursday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Friday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Saturday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// 💡 Day is the enum. Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday are its variants. +</span></span></span></code></pre></div><ul> +<li>An enum variant can have either, +<ul> +<li>No data (a unit variant)</li> +<li>Unnamed ordered data (a tuple variant)</li> +<li>Named data/ fields (a struct variant)</li> +</ul> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">FlashMessage</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Success</span><span class="p">,</span><span class="w"> </span><span class="c1">// 💡 A unit variant (no data) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Error</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="p">),</span><span class="w"> </span><span class="c1">// 💡 A tuple variant (one or more , separated data) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Warning</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">field</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">message</span>: <span class="nb">String</span> <span class="p">},</span><span class="w"> </span><span class="c1">// 💡 A struct variant (one or more , separated name: value data) +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// 💡 FlashMessage is the emnum, Success, Error, Warning are its variants. +</span></span></span></code></pre></div></li> +</ul> + + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <ul> +<li>In Rust, the term &ldquo;instantiation&rdquo; is used to describe the act of creating a concrete instance of a type (struct or enum).</li> +<li>In Rust, the term &ldquo;field&rdquo; is used to describe a named component in a C-like struct &amp; struct-like enum variant, and the term &ldquo;element&rdquo; is used to describe an unnamed component in a tuple struct &amp; tuple-like enum variant. The term &ldquo;member&rdquo; is used to describe both.</li> +<li>More complex examples can be found on <a href="https://learning-rust.github.io/docs/generics/" >Generics</a>, <a href="https://learning-rust.github.io/docs/impls-and-traits" >Impls and Traits</a>, <a href="https://learning-rust.github.io/docs/lifetimes" >Lifetimes</a> and <a href="https://learning-rust.github.io/docs/modules" >Modules</a> sections.</li> +</ul> + </div> +</div><h2 id="instantiation">Instantiation</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#![allow(unused)]</span><span class="w"> </span><span class="c1">// 💡 skip unused warnings, as we don&#39;t read fields in the enums +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#[derive(Debug)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">FlashMessage</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// Definition +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Success</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Error</span><span class="p">(</span><span class="kt">u32</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="p">),</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Warning</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">field</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">message</span>: <span class="nb">String</span> <span class="p">},</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 1. Instantiation with separate variable declaration and assignment +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">x</span>: <span class="nc">FlashMessage</span><span class="p">;</span><span class="w"> </span><span class="c1">// Declaration with the data type +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FlashMessage</span>::<span class="n">Success</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{x:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Success +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 2. Instantiation with a direct variable initialization +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FlashMessage</span>::<span class="n">Success</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FlashMessage</span>::<span class="n">Error</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Unauthorized&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FlashMessage</span>::<span class="n">Warning</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">field</span>: <span class="s">&#34;email&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">(),</span><span class="w"> </span><span class="n">message</span>: <span class="s">&#34;This is required&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">()</span><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{a:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Success +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{b:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Error(401, &#34;Unauthorized&#34;) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{c:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Warning { field: &#34;email&#34;, message: &#34;This is required&#34; } +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// 3. Instantiation with a default variant +</span></span></span><span class="line"><span class="cl"><span class="cp">#![allow(unused)]</span><span class="w"> </span><span class="c1">// 💡 skip unused warnings, as we don&#39;t use the all variants of the enum +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#[derive(Debug, Default)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Hand</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Left</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[default]</span><span class="w"> </span><span class="c1">// 💡Set Right as the default variant +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Right</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Hand</span>::<span class="n">default</span><span class="p">();</span><span class="w"> </span><span class="c1">// Instantiation with the default variant +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{a:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Right +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>In Rust, the <code>#[derive()]</code> attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The <a href="https://doc.rust-lang.org/std/fmt/trait.Debug.html" target="_blank" ><code>std::fmt::Debug</code></a> trait allows us to format a value with <code>{:?}</code> or <code>{:#?}</code> in <code>println!</code> and similar macros. The <a href="https://doc.rust-lang.org/std/default/trait.Default.html" target="_blank" ><code>std::default::Default</code></a> trait allows us to create a new instance of a type with the <code>Type::default()</code> method.</p>Error and None Propagationhttps://learning-rust.github.io/docs/error-and-none-propagation/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/error-and-none-propagation/<p>We should use panics like <code>panic!()</code>, <code>unwrap()</code>, <code>expect()</code> only if we can not handle the situation in a better way. Also if a function contains expressions which can produce either <code>None</code> or <code>Err</code>,</p> +<ul> +<li>we can handle them inside the same function. Or,</li> +<li>we can return <code>None</code> and <code>Err</code> types immediately to the caller. So the caller can decide how to handle them.</li> +</ul> +<p>💡 <code>None</code> types no need to handle by the caller of the function always. But Rusts’ convention to handle <strong><code>Err</code></strong> types is, <strong>return them immediately to the caller to give more control to the caller to decide how to handle them.</strong></p>Functionshttps://learning-rust.github.io/docs/functions/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/functions/<h2 id="named-functions">Named Functions</h2> +<ul> +<li>Named functions are declared with the keyword <strong><code>fn</code></strong></li> +<li>When using <strong>arguments</strong>, we <strong>must declare the data types</strong>.</li> +<li>By default, functions <strong>return an empty <a href="https://learning-rust.github.io/docs/primitive-data-types/#tuple" >tuple</a>/ <code>()</code></strong>. If you want to return a value, the <strong>return type must be specified</strong> after <strong><code>-&gt;</code></strong></li> +</ul> +<h3 id="hello-world">Hello world</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Hello, world!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="passing-arguments">Passing Arguments</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">print_sum</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i8</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i8</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;sum is: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="returning-values">Returning Values</h3> +<ul> +<li> +<p>Without the <code>return</code> keyword. Only the last expression returns.</p>Genericshttps://learning-rust.github.io/docs/generics/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/generics/<ul> +<li>The core concept of generics is abstraction over types. They let us write one piece of code to operate with any data type without repeating ourselves to write separate versions for each type. At the compile time, Rust ensures the type safety and generates an optimized code for each concrete type used in the program.</li> +<li>Use an uppercase letter (<code>T</code>, <code>U</code>, &hellip;) or a <a href="https://en.wikipedia.org/wiki/Camel_case" target="_blank" ><code>PascalCase</code></a> identifier for the data type. +<ul> +<li>Instead of <code>x: u8</code> we use <code>x: T</code>.</li> +<li>Inform the compiler that <code>T</code> is a generic type by adding <code>&lt;T&gt;</code> at first.</li> +</ul> +</li> +</ul> +<h2 id="with-one-generic-type">With One Generic Type</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Point</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">y</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">to_tuple</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">T</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="p">(</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="mi">1</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// a: Point&lt;i32&gt; +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">to_tuple</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">y</span><span class="p">);</span><span class="w"> </span><span class="c1">// (i32, i32) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{b:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// (0, 1) +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="nc">false</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">true</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// a: Point&lt;bool&gt; +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">to_tuple</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">.</span><span class="n">y</span><span class="p">);</span><span class="w"> </span><span class="c1">// (bool, bool) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{d:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// (false, true) +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="with-multiple-generic-types">With Multiple Generic Types</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Point</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">U</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">y</span>: <span class="nc">U</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">to_shuffled_tuple</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">U</span><span class="o">&gt;</span><span class="p">(</span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">U</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="p">(</span><span class="n">U</span><span class="p">,</span><span class="w"> </span><span class="n">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="mi">1</span><span class="k">u8</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">true</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// a: Point&lt;u8, bool&gt; +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">to_shuffled_tuple</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">y</span><span class="p">);</span><span class="w"> </span><span class="c1">// (bool, u8) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{b:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// (true, 1) +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>On some occasions, the compiler cannot inter the type, and we have to specify the type when using the generic type. By the way, it&rsquo;s good practice to specify the type on variables when using a generic implementation.</p>Hello Worldhttps://learning-rust.github.io/docs/hello-world/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/hello-world/<h2 id="hello-world">Hello, World!</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Hello, world!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><code>fn</code> means function. The <code>main</code> function is the beginning of every Rust program. +<code>println!()</code> prints text to the console and its <code>!</code> indicates that it’s a <a href="https://doc.rust-lang.org/book/ch19-06-macros.html" target="_blank" >macro</a> rather than a function.</p> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>Rust files should have <code>.rs</code> file extension and if you’re using more than one word for the file name, follow the <a href="https://en.wikipedia.org/wiki/Snake_case" target="_blank" >snake_case</a> convention.</p>Implshttps://learning-rust.github.io/docs/impls/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/impls/<ul> +<li>Earlier, we discussed that structs and enums group related data, while impl blocks and traits add associated and shared behavior to the data.</li> +<li>Usage of <code>Self</code> vs <code>self</code> keywords: +<ul> +<li> +<p><code>Self</code>: Refers to the type itself (the blueprint).</p> +</li> +<li> +<p><code>self</code>: Refers to the instance of the type (the actual data).</p> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>This can be any form of <code>self</code>, <code>&amp;self</code>, <code>&amp;mut self</code>, <code>self: Box&lt;Self&gt;</code>, <code>self: Pin&lt;&amp;mut Self&gt;</code>, etc.</p>Installationhttps://learning-rust.github.io/docs/installation/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/installation/<h2 id="rustup">Rustup</h2> +<p>There are many ways to install Rust on your system. For the moment the official way to install Rust is using <a href="https://rustup.rs/" target="_blank" >Rustup</a>.</p> +<p><a href="https://rust-lang.github.io/rustup/index.html" target="_blank" >📖</a> Rustup installs The Rust Programming Language from the official release channels, enabling you to easily switch between <strong>stable, beta, and nightly</strong> compilers and keep them updated. It also makes cross-compiling simpler with binary builds of the standard library for common platforms.</p> +<p><a href="https://rust-lang.github.io/rustup/installation/index.html" target="_blank" >📖</a> Rustup installs <code>rustc</code>, <code>cargo</code>, <code>rustup</code> and other standard tools to <strong>Cargo&rsquo;s <code>bin</code> directory</strong>. On Unix it is located at <code>$HOME/.cargo/bin</code> and on Windows at <code>%USERPROFILE%\.cargo\bin</code>. This is the same directory that <code>cargo install</code> will install Rust programs and Cargo plugins.</p>Lifetimeshttps://learning-rust.github.io/docs/lifetimes/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/lifetimes/<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <ul> +<li>Ownership: Each piece of data has a single owner, and data is only scoped to its owner (unless it is borrowed).</li> +<li>In Rust, borrowing is the act of creating a reference to a value without copying the data and without taking ownership (move). While a reference exists, the original owner retains ownership, but its access to the data is restricted (or completely locked if <code>&amp;mut</code>) until that reference’s last use.</li> +<li>Liveness: +<ul> +<li>Owned values (<code>T</code>) are dropped at the end of their scope (unless moved early).</li> +<li>References (<code>&amp;T</code>, <code>&amp;mut T</code>) effectively end/ expire at their last use. This is known as Non-Lexical Lifetimes (NLL).</li> +</ul> +</li> +<li>Lifetimes are Rust&rsquo;s way of guaranteeing that a reference is valid within a discrete region of code at compile time.</li> +</ul> + </div> +</div><h2 id="lifetimes">Lifetimes</h2> +<ul> +<li>A lifetime is a construct the Rust compiler&rsquo;s borrow checker uses to track how long references remain valid and every reference in Rust has a lifetime.</li> +<li>However, most local lifetimes are implicitly figured out by the compiler through a mechanism called lifetime elision.</li> +<li>The primary purpose of lifetimes is to prevent dangling references, ensuring data is never dropped while a pointer still looks at it.</li> +</ul> +<p>Annotating a lifetime does not extend the lifespan of an actual value. It is simply a contract that tells the compiler, &ldquo;The lifespan of this reference is guaranteed to be tied to the lifespan of this data.&rdquo;</p>Moduleshttps://learning-rust.github.io/docs/modules/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/modules/<h2 id="rust-module-tree">Rust Module Tree</h2> +<ul> +<li>The Rust module tree is the hierarchical representation of all modules in a crate.</li> +<li>It defines the namespace structure and governs how items are referenced via paths at compile time.</li> +<li>Unlike some languages like Go and JavaScript, Rust&rsquo;s module tree is not purely based on the file system. +<ul> +<li> +<p>The root of the module tree begins with either <code>main.rs</code> (in a binary crate) or <code>lib.rs</code> (in a library crate).</p>Operatorshttps://learning-rust.github.io/docs/operators/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/operators/<h2 id="arithmetic">Arithmetic</h2> +<p><code>+ - * / %</code></p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="c1">// 6 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="c1">// 4 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="c1">// 10 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="c1">// ⭐️ 2 not 2.5 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="c1">// 1 +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">5.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">2.0</span><span class="p">;</span><span class="w"> </span><span class="c1">// 2.5 +</span></span></span></code></pre></div><h2 id="comparison">Comparison</h2> +<p><code>== != &lt; &gt; &lt;= &gt;=</code></p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"> </span><span class="c1">// false +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"> </span><span class="c1">// false +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// 🔎 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="sc">&#39;A&#39;</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span></code></pre></div><h2 id="logical">Logical</h2> +<p><code>! &amp;&amp; ||</code></p>Option and Resulthttps://learning-rust.github.io/docs/option-and-result/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/option-and-result/<h2 id="why-option-and-result">Why Option and Result?</h2> +<p>Many languages use <strong><code>null</code>\ <code>nil</code>\ <code>undefined</code> types</strong> to represent empty outputs, and <strong><code>Exceptions</code></strong> to handle errors. Rust skips using both, especially to prevent issues like <strong>null pointer exceptions, sensitive data leakages through exceptions</strong>, etc. Instead, Rust provides two special <strong>generic enums</strong>;<code>Option</code> and <code>Result</code> to deal with above cases.</p> + + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <p>In the previous sections, we have discussed about the basics of <a href="https://learning-rust.github.io/docs/enums" >enums</a>, <a href="https://learning-rust.github.io/docs/generics" >generics</a> and <a href="https://learning-rust.github.io/docs/generics/#generalizing-enums" ><code>Result</code> &amp; <code>Option</code> types</a>.</p>Overviewhttps://learning-rust.github.io/docs/overview/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/overview/<h2 id="about-me">About me</h2> +<blockquote> +<p>🧑‍💻 I am an expat working in Singapore as a Go Backend and DevOps Engineer. Feel free to reach out if you find any mistakes or anything that needs to be changed, including spelling or grammar errors. Alternatively, you can create a pull request, open an issue, or <a href="https://gist.github.com/dumindu/00a0be2d175ed5ff3bc3c17bbf1ca5b6" target="_blank" >share your awesome ideas in this gist</a>. Good luck with learning Rust!</p> +</blockquote> +<p><a href="https://github.com/learning-rust/learning-rust.github.io" target="_blank" ><img src="https://img.shields.io/github/stars/learning-rust/learning-rust.github.io?style=for-the-badge&amp;logo=rust&amp;label=learning-rust.github.io&amp;logoColor=333333&amp;labelColor=f9f9f9&amp;color=F46623" alt="learning-rust.github.io"></a> +<a href="https://learning-cloud-native-go.github.io" target="_blank" ><img src="https://img.shields.io/github/stars/learning-cloud-native-go/learning-cloud-native-go.github.io?style=for-the-badge&amp;logo=go&amp;logoColor=333333&amp;label=learning-cloud-native-go.github.io&amp;labelColor=f9f9f9&amp;color=00ADD8" alt="learning-cloud-native-go.github.io"></a></p> +<p><a href="https://github.com/dumindu" target="_blank" ><img src="https://img.shields.io/badge/dumindu-866ee7?style=for-the-badge&amp;logo=GitHub&amp;logoColor=333333&amp;labelColor=f9f9f9" alt="github.com"></a> +<a href="https://www.buymeacoffee.com/dumindu" target="_blank" ><img src="https://img.shields.io/badge/Buy%20me%20a%20coffee-dumindu-FFDD00?style=for-the-badge&amp;logo=buymeacoffee&amp;logoColor=333333&amp;labelColor=f9f9f9" alt="buymeacoffee"></a></p> +<h2 id="overview">Overview</h2> +<p>This publication has its origins in the posts I authored on Medium at <a href="https://medium.com/learning-rust" target="_blank" >https://medium.com/learning-rust</a>. However, please note that I have ceased updating the Medium posts. All current and future updates, new content, code, and grammar fixes will be exclusively maintained and released here, <a href="https://learning-rust.github.io" target="_blank" >https://learning-rust.github.io</a>.</p>Ownershiphttps://learning-rust.github.io/docs/ownership/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/ownership/<p>We discussed in the <a href="https://learning-rust.github.io/docs/traits/#derive-traits" >Derive Traits</a>, the usage of <a href="https://doc.rust-lang.org/std/marker/trait.Copy.html" target="_blank" ><code>Copy</code> marker trait</a> and <a href="https://doc.rust-lang.org/std/clone/index.html" target="_blank" ><code>.clone()</code></a> with the below code.</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[derive(Debug, Clone, Copy)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">x</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">y</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="mi">1</span><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{a:?}</span><span class="s">, </span><span class="si">{b:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>If we try to remove <code>Copy</code> derive on <code>Point</code> and run, we will get the following error while compiling.</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">error</span><span class="p">[</span><span class="n">E0382</span><span class="p">]</span>: <span class="nc">borrow</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">moved</span><span class="w"> </span><span class="n">value</span>: <span class="err">`</span><span class="n">a</span><span class="err">`</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">-</span>-&gt; <span class="nc">src</span><span class="o">/</span><span class="n">main</span><span class="p">.</span><span class="n">rs</span>:<span class="mi">11</span>:<span class="mi">16</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">|</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="mi">8</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="mi">1</span><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="k">move</span><span class="w"> </span><span class="n">occurs</span><span class="w"> </span><span class="n">because</span><span class="w"> </span><span class="err">`</span><span class="n">a</span><span class="err">`</span><span class="w"> </span><span class="n">has</span><span class="w"> </span><span class="k">type</span> <span class="err">`</span><span class="n">Point</span><span class="err">`</span><span class="p">,</span><span class="w"> </span><span class="n">which</span><span class="w"> </span><span class="n">does</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">implement</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="err">`</span><span class="nb">Copy</span><span class="err">`</span><span class="w"> </span><span class="k">trait</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="mi">9</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="n">moved</span><span class="w"> </span><span class="n">here</span></span></span></code></pre></div><p>This is because of Ownership, which is used to achieve Rust&rsquo;s memory safety.</p>Panickinghttps://learning-rust.github.io/docs/panicking/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/panicking/<h2 id="panic">panic!()</h2> +<ul> +<li>In some cases, when an error occurs we can not do anything to handle it, <strong>if the error is something which should not have happened</strong>. In other words, if it’s an <strong>unrecoverable error</strong>.</li> +<li>Also <strong>when we are not using a feature-rich debugger or proper logs</strong>, sometimes we need to <strong>debug the code by quitting the program from a specific line of code</strong> by printing out a specific message or a value of a variable binding to understand the current flow of the program. +For above cases, we can use <code>panic!</code> macro.</li> +</ul> +<p>⭐ <code>panic!()</code> runs <strong>thread based</strong>. One thread can be panicked, while other threads are running.</p>Primitive Data Typeshttps://learning-rust.github.io/docs/primitive-data-types/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/primitive-data-types/<h2 id="bool">bool</h2> +<p>true or false</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="kt">bool</span> <span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// ⭐️ no TRUE, FALSE, 1, 0 +</span></span></span></code></pre></div><p>bool is a single byte(8 bits) in size.</p> +<h2 id="char">char</h2> +<p>A single Unicode scalar value</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">&#39;x&#39;</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="kt">char</span> <span class="o">=</span><span class="w"> </span><span class="sc">&#39;😎&#39;</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// ⭐️ no &#34;x&#34;, only single quotes +</span></span></span></code></pre></div><p>Because of Unicode support, char is not a single byte, but four(32 bits).</p> +<h2 id="i8-i16-i32-i64-i128">i8, i16, i32, i64, i128</h2> +<p>8, 16, 32, 64 and 128 bit fixed sized signed(+/-) integer types</p>Smart Compilerhttps://learning-rust.github.io/docs/smart-compiler/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/smart-compiler/<div class="alert alert-caution"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M480-300q15 0 25.5-10.5T516-336q0-15-10.5-25.5T480-372q-15 0-25.5 10.5T444-336q0 15 10.5 25.5T480-300Zm25.5-142.5Q516-453 516-468v-168q0-15-10.5-25.5T480-672q-15 0-25.5 10.5T444-636v168q0 15 10.5 25.5T480-432q15 0 25.5-10.5ZM371-144q-14 0-27-5t-24-16L165-321q-10-10-15.5-23.5T144-372v-217q0-14 5-27t16-24l155-155q11-11 24-16t27-5h218q14 0 27 5t24 16l155 155q11 11 16 24t5 27v218q0 14-5 27t-16 24L639-165q-10 10-23.5 15.5T588-144H371Zm0-72h218l155-155v-218L588-744H371L216-589v218l155 155Zm109-264Z"/></svg> + + + <span> + + Caution + + </span> + </div> + <div class="alert-body"> + <p>This needs a refresh!</p> + </div> +</div><h2 id="why-compiler">Why Compiler?</h2> +<p>The Rust compiler does the most significant job to prevent errors in Rust programs. It <strong>analyzes the code at compile-time</strong> and issues warnings, if the code does not follow memory management rules or lifetime annotations correctly.</p>STD, Primitives and Preludeshttps://learning-rust.github.io/docs/std-primitives-and-preludes/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/std-primitives-and-preludes/<div class="alert alert-caution"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M480-300q15 0 25.5-10.5T516-336q0-15-10.5-25.5T480-372q-15 0-25.5 10.5T444-336q0 15 10.5 25.5T480-300Zm25.5-142.5Q516-453 516-468v-168q0-15-10.5-25.5T480-672q-15 0-25.5 10.5T444-636v168q0 15 10.5 25.5T480-432q15 0 25.5-10.5ZM371-144q-14 0-27-5t-24-16L165-321q-10-10-15.5-23.5T144-372v-217q0-14 5-27t16-24l155-155q11-11 24-16t27-5h218q14 0 27 5t24 16l155 155q11 11 16 24t5 27v218q0 14-5 27t-16 24L639-165q-10 10-23.5 15.5T588-144H371Zm0-72h218l155-155v-218L588-744H371L216-589v218l155 155Zm109-264Z"/></svg> + + + <span> + + Caution + + </span> + </div> + <div class="alert-body"> + <p>This needs a refresh!</p> + </div> +</div><p>⭐️ In Rust, language elements are implemented by not only <strong><code>std</code> library</strong> crate but also <strong>compiler</strong> as well. Examples,</p> +<ul> +<li><strong><a href="https://doc.rust-lang.org/std/#primitives" target="_blank" >Primitives</a></strong>: Defined by the compiler and methods are implemented by <code>std</code> library directly on primitives.</li> +<li><strong><a href="https://doc.rust-lang.org/std/#macros" target="_blank" >Standard Macros</a></strong>: Defined by both compiler and <code>std</code></li> +</ul> +<p>The <strong><code>std</code></strong> library has been divided into <strong><a href="https://doc.rust-lang.org/std/#modules" target="_blank" >modules</a></strong>, according to the main areas each covered.</p>Structshttps://learning-rust.github.io/docs/structs/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/structs/<ul> +<li>Used to <strong>encapsulate related properties into one unified data type</strong>.</li> +<li>By convention, the name should follow <a href="https://en.wikipedia.org/wiki/Camel_case" target="_blank" ><code>PascalCase</code></a>.</li> +<li>3 variants, +<ul> +<li> +<p>C-like structs: One or more <code>,</code> separated <code>name: value pairs</code> enclosed in <code>{}</code></p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">red</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">green</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></li> +<li> +<p>Tuple structs: One or more <code>,</code> separated <code>values</code> enclosed in <code>()</code></p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Color</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="kt">u8</span><span class="p">);</span></span></span></code></pre></div></li> +<li> +<p>Unit structs: A struct with no fields/ members</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Black</span><span class="p">;</span></span></span></code></pre></div></li> +</ul> +</li> +</ul> + + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <ul> +<li>In Rust, data (attributes) and behavior (associated functions and methods) are placed separately. Structs and Enums are used to group related data, and impls and traits are used to add associated and shared behavior to that data.</li> +<li>In Rust, the term &ldquo;instantiation&rdquo; is used to describe the act of creating a concrete instance of a type (struct or enum).</li> +<li>In Rust, the term &ldquo;field&rdquo; is used to describe a named component in a C-like struct &amp; struct-like enum variant, and the term &ldquo;element&rdquo; is used to describe an unnamed component in a tuple struct &amp; tuple-like enum variant. The term &ldquo;member&rdquo; is used to describe both.</li> +<li>More complex examples can be found on <a href="https://learning-rust.github.io/docs/impls-and-traits" >Impls and Traits</a>, <a href="https://learning-rust.github.io/docs/lifetimes" >Lifetimes</a> and <a href="https://learning-rust.github.io/docs/modules" >Modules</a> sections.</li> +</ul> + </div> +</div><h2 id="c-like-structs">C-like Structs</h2> +<ul> +<li>Similar to classes (without its methods) in OOP languages.</li> +<li>Can access fields using the <code>.</code>/ dot notation and the field name.</li> +</ul> +<h3 id="definition">Definition</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">red</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">green</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="instantiation--accessing-fields">Instantiation &amp; Accessing Fields</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">red</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">green</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 1. Instantiation +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">white</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">red</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">green</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 2. Instantiation without redundant field names, when using the same variable names +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 3. Instantiation + copy fields&#39; values from another instance +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">red</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// 💡 Copy green and blue from black +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">green</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">green</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// 💡 Copy red and blue from black +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// 💡 Copy all fields&#39; values from black +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span><span class="p">.</span><span class="n">blue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">255</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">white</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">white</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">white</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(255, 255, 255) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">black</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">black</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">black</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(0, 0, 0) +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">red</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">red</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">red</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(255, 0, 0) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(0, 255, 0) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(0, 0, 255) +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// 4. Instantiation with default values +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#[derive(Default)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">age</span>: <span class="kt">f32</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Person</span>::<span class="n">default</span><span class="p">();</span><span class="w"> </span><span class="c1">// Instantiation with default values +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// String default value &#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">age</span><span class="p">,</span><span class="w"> </span><span class="mf">0.0</span><span class="p">);</span><span class="w"> </span><span class="c1">// f32 default value 0.0 +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>In Rust, the <code>#[derive()]</code> attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The <a href="https://doc.rust-lang.org/std/default/trait.Default.html" target="_blank" ><code>std::default::Default</code></a> trait allows us to create a new instance of a type with the <code>Type::default()</code> method.</p>Traitshttps://learning-rust.github.io/docs/traits/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/traits/<p>A trait is a contract that defines a set of behaviors or properties that a type must implement. It can contain associated types, constants, function or method signatures, and overridable default implementations.</p> +<h2 id="definition">Definition</h2> +<h3 id="with-no-associates">With No Associates</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="nb">Sized</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">}</span></span></span></code></pre></div> + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>Mostly used to mark a type as having certain properties to allow in certain operations. Known as Marker Traits.</p>Unwrap and Expecthttps://learning-rust.github.io/docs/unwrap-and-expect/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/unwrap-and-expect/<h2 id="unwrap">unwrap()</h2> +<ul> +<li>If an <code>Option</code> type has <strong><code>Some</code></strong> value or a <code>Result</code> type has a <strong><code>Ok</code></strong> value, <strong>the value inside them</strong> passes to the next step.</li> +<li>If the <code>Option</code> type has <strong><code>None</code></strong> value or the <code>Result</code> type has <strong><code>Err</code></strong> value, <strong>program panics</strong>; If <code>Err</code>, panics with the error message.</li> +</ul> +<p>The functionality is bit similar to the following codes, which are using <code>match</code> instead <code>unwrap()</code>.</p> +<p>Example with <code>Option</code> and <code>match</code>, before using <code>unwrap()</code></p>Usehttps://learning-rust.github.io/docs/use/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/use/<ul> +<li>A <code>use</code> statement is used to bring items (types, functions, traits, modules, etc.) into the current scope. +<ul> +<li>It doesn&rsquo;t import anything; instead, it creates a local name (an alias) in the current scope.</li> +</ul> +</li> +<li>This allows us: +<ul> +<li>to refer to items by shorter names instead of their full paths.</li> +<li>to re-export items (by publicly exposing items from another module or crate using <code>pub use</code>).</li> +</ul> +</li> +</ul> +<h2 id="path-prefixes">Path Prefixes</h2> +<p>A <code>use</code> statement can start from:</p>Variable bindings, Constants & Staticshttps://learning-rust.github.io/docs/variable-bindings-constants-and-statics/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/variable-bindings-constants-and-statics/<ul> +<li>Rust is a statically typed language; it checks data types at compile-time. But it doesn’t require you to actually type data types when declaring variable bindings. In that case, the compiler checks the usage and sets a better data type for it.</li> +<li>⭐️ For <strong>constants and statics, we must annotate the data type</strong>.</li> +<li>Types come after a <code>:</code> (colon) sign.</li> +<li>The naming convention for the variable bindings is using the <a href="https://en.wikipedia.org/wiki/Snake_case" target="_blank" ><code>snake_case</code></a>. But, for constants and statics, we should follow the <a href="https://en.wikipedia.org/wiki/Snake_case" target="_blank" ><code>SCREAMING_SNAKE_CASE</code></a>.</li> +</ul> + + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <p>In the following examples, we will use <a href="https://learning-rust.github.io/docs/primitive-data-types" >data types</a> like <code>bool</code>, <code>i32</code>, <code>i64</code> and <code>f64</code>. Don&rsquo;t worry about them for now; they&rsquo;ll be discussed later.</p>Vectorshttps://learning-rust.github.io/docs/vectors/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/vectors/<p>If you remember, the array is a fixed-size list of elements, of the same data type. Even with mut, its element count cannot be changed. A vector is <strong>kind of a re-sizable array</strong> but <strong>all elements must be in the same type</strong>.</p> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <ul> +<li><code>Vec&lt;T&gt;</code>: capital “V” as <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html" target="_blank" >it’s a struct</a>.</li> +<li>It’s a generic type, written as <strong><code>Vec&lt;T&gt;</code></strong>. T can have any type, ex. A vector of i32s is <code>Vec&lt;i32&gt;</code>. Also, Vectors always allocate their data in a dynamically allocated heap.</li> +</ul> + </div> +</div><h2 id="creation">Creation</h2> +<h3 id="empty-vector">Empty Vector</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"> </span><span class="c1">// 1. With new() keyword +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[];</span><span class="w"> </span><span class="c1">// 2. Using the vec! macro (💡 usually create with values same time) +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// ⭐️ If you need an immutable empty vector, you must have to specify the data type. +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[];</span></span></span></code></pre></div><h3 id="with-type-annotations">With Type Annotations</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="k">i32</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> </span><span class="c1">// Suffixing 1st value with data type +</span></span></span></code></pre></div><h3 id="with-values">With Values</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="k">i32</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span></code></pre></div> + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="mi">10</span><span class="p">];</span><span class="w"> </span><span class="c1">// Ten zeroes +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="s">&#34;&#34;</span><span class="p">;</span><span class="w"> </span><span class="mi">10</span><span class="p">];</span><span class="w"> </span><span class="c1">// Ten &#34;&#34; str +</span></span></span></code></pre></div><h3 id="with-a-capacity">With a Capacity</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">a</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Length: </span><span class="si">{}</span><span class="s">, Capacity : </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">len</span><span class="p">(),</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">capacity</span><span class="p">());</span><span class="w"> </span><span class="c1">// Length: 0, Capacity : 10 +</span></span></span></code></pre></div> + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <p>We&rsquo;ll discuss this in the <a href="https://learning-rust.github.io/docs/vectors/#length-and-capacity" >Length and Capacity</a>.</p>Why Rust?https://learning-rust.github.io/docs/why-rust/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/why-rust/<h2 id="history-of-rust">History of Rust</h2> +<p>Rust was initially designed and developed by former Mozilla employee <strong><a href="https://github.com/graydon" target="_blank" >Graydon Hoare</a></strong> as a personal project. Mozilla began sponsoring the project in 2009 and announced it in 2010. But the first stable release, Rust 1.0, was released on May 15, 2015.</p> +<p>Since Rust 1.0, major updates have been released as <a href="https://learning-rust.github.io/docs/cargo-crates-and-basic-project-structure/#rust-editions" ><code>Editions</code></a> approximately every three years: Rust 2015 (with the release of Rust 1.0) , Rust 2018, Rust 2021, and Rust 2024, all maintaining backward compatibility.</p>Workspaceshttps://learning-rust.github.io/docs/workspaces/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/workspaces/<h2 id="rust-workspaces">Rust Workspaces</h2> +<ul> +<li>Rust workspaces provide a convenient way to manage multiple related crates together as a single project or monorepo.</li> +<li>A Rust workspace uses a shared <code>Cargo.lock</code> file and a common <code>target</code> directory to efficiently build and manage shared dependencies, reducing build times.</li> +<li>Cargo commands such as <code>cargo test</code>, <code>cargo build</code>, <code>cargo check</code>, <code>cargo fmt</code>, <code>cargo clippy</code>, and <code>cargo clean</code> can be run once from the workspace root to apply to all workspace members.</li> +</ul> +<h2 id="simple">Simple</h2> +<p>Let&rsquo;s build a sample Rust workspace with two simple crates.</p> \ No newline at end of file diff --git a/docs/docs/installation/index.html b/docs/docs/installation/index.html new file mode 100644 index 00000000..8dc7875d --- /dev/null +++ b/docs/docs/installation/index.html @@ -0,0 +1,28 @@ +Installation · Learning Rust

Installation

Rustup

There are many ways to install Rust on your system. For the moment the official way to install Rust is using Rustup.

📖 Rustup installs The Rust Programming Language from the official release channels, enabling you to easily switch between stable, beta, and nightly compilers and keep them updated. It also makes cross-compiling simpler with binary builds of the standard library for common platforms.

📖 Rustup installs rustc, cargo, rustup and other standard tools to Cargo’s bin directory. On Unix it is located at $HOME/.cargo/bin and on Windows at %USERPROFILE%\.cargo\bin. This is the same directory that cargo install will install Rust programs and Cargo plugins.

+Tip

The main tools Rustup installs to the Cargo’s bin directory,

  • rustc: The Rust compiler.
  • cargo: The Rust’s built-in package manager and the build system.
  • rustup: The Rust toolchain installer.
  • rustfmt: The Rust’s official tool of formatting Rust code according to style guidelines.
  • cargo-fmt: Helps to run rustfmt on whole Rust projects, including multi-crate workspaces.
  • cargo-clippy: A lint tool that provides extra checks for common mistakes and stylistic choices.
  • cargo-miri:An experimental Rust interpreter, which can be used for checking for undefined-behavior.
  • rustdoc: A local copy of the Rust documentation.
  • rust-analyzer: A language server that provides support for editors and IDEs.
  • rust-gdb and rust-lldb: Rust debuggers that wrap the GNU Debugger(GDB) and Low-Level Debugger(LLDB).

Installation

For Mac and Linux Users

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

For Windows Users

Download rustup-init.exe from www.rustup.rs and run.

+Important

You may need to install Visual C++ Build Tools 2019 or higher, which requires an additional 3–4 GBs.

👨‍🏫 Before going to the next…

  • To verify the current Rust version, use the rustc --version or shorter form rustc -V command.
  • Rust follows six week release cycles. Use the rustup update command to update the Rust ecosystem.
  • You can access Rust’s offline documentation via the rustup doc command.
  • For a full list of rustup commands, refer to the rustup --help command.
\ No newline at end of file diff --git a/docs/docs/installation/og.jpg b/docs/docs/installation/og.jpg new file mode 100644 index 00000000..79d39cd2 Binary files /dev/null and b/docs/docs/installation/og.jpg differ diff --git a/docs/docs/learning_rust_medium.png b/docs/docs/learning_rust_medium.png new file mode 100644 index 00000000..f2e7a465 Binary files /dev/null and b/docs/docs/learning_rust_medium.png differ diff --git a/docs/docs/lifetimes/index.html b/docs/docs/lifetimes/index.html new file mode 100644 index 00000000..9520a38f --- /dev/null +++ b/docs/docs/lifetimes/index.html @@ -0,0 +1,253 @@ +Lifetimes · Learning Rust

Lifetimes

+Recap
  • Ownership: Each piece of data has a single owner, and data is only scoped to its owner (unless it is borrowed).
  • In Rust, borrowing is the act of creating a reference to a value without copying the data and without taking ownership (move). While a reference exists, the original owner retains ownership, but its access to the data is restricted (or completely locked if &mut) until that reference’s last use.
  • Liveness:
    • Owned values (T) are dropped at the end of their scope (unless moved early).
    • References (&T, &mut T) effectively end/ expire at their last use. This is known as Non-Lexical Lifetimes (NLL).
  • Lifetimes are Rust’s way of guaranteeing that a reference is valid within a discrete region of code at compile time.

Lifetimes

  • A lifetime is a construct the Rust compiler’s borrow checker uses to track how long references remain valid and every reference in Rust has a lifetime.
  • However, most local lifetimes are implicitly figured out by the compiler through a mechanism called lifetime elision.
  • The primary purpose of lifetimes is to prevent dangling references, ensuring data is never dropped while a pointer still looks at it.

Annotating a lifetime does not extend the lifespan of an actual value. It is simply a contract that tells the compiler, “The lifespan of this reference is guaranteed to be tied to the lifespan of this data.”

Lifetimes are declared with a single leading apostrophe '. By convention, lowercase letters are used starting from 'a, moving alphabetically if multiple lifetimes are required. 'static is a special lifetime that indicating the reference can live for the entire duration of the program execution.

fn main() {
+    let a = "A";
+
+    take_str(a);
+    take_str_a(a);
+    take_str_static(a);
+}
+
+fn take_str(s: &str) {
+    println!("{s}");
+}
+
+fn take_str_a<'a>(s: &'a str) {
+    println!("{s}");
+}
+
+fn take_str_static(s: &'static str) {
+    println!("{s}");
+}

Elisions

Prior to Rust 1.0, around 2014, lifetime elision did not exist. We had to explicitly type out 'a and 'b annotations for every single reference, even in the simplest functions. The Rust community introduced lifetime elisions to automatically infer common patterns, mainly in function signatures from Rust 1.0. However, at that time, lifetimes remained rigidly tied to the lexical scope (reference lasted until the closing curly brace) of the code block. Non-Lexical Lifetimes(NLL) were introduced with Rust 2018, allowing references to expire as soon as they are last used instead of end of lexical scope. In Rust 2024, the language expanded support for Return Position Impl Trait (RPIT) functions (such as fn foo() -> impl Trait) which we had to explicitly mentioned in their bounds (+ 'a or + '_). To prevent over-capturing Rust also introduced precise capturing syntax (+ use<>).

  • Each elided lifetime in input position becomes a distinct lifetime parameter.
  • If there is exactly one input lifetime position (elided or not), that lifetime is assigned to all elided output lifetimes.
  • If there are multiple input lifetime positions, but one of them is &self or &mut self, the lifetime of self is assigned to all elided output lifetimes.
  • Otherwise, it is an error to elide an output lifetime.

These rules are still valid. But now, lifetime elision has expanded beyond function signatures. So, let’s go through examples to identify where we can elide lifetimes and where we must explicitly add them.

On Function Declarations

+Tip
  1. Add lifetimes after the & sign to input and/or output references.
    • (x: &str)(x: &'a str), (x: &mut str)(x: &'a mut str)
  2. After the function name, mention the lifetimes like generic types, unless 'static.
    • fn foo()fn foo<'a>() or fn foo<'a, 'b>()

Single Input

fn main() {
+    let a = "A";
+    take_str(a);
+}
+
+fn take_str(s: &str) {
+    println!("{s}");
+}
+
+// take_str<'a>(s: &'a str){} 
+// take_str(s: &'static str){} 
+

Single Output

⭐️ MUST ANNOTATE

fn main() {
+    let a = return_str();
+    let b = return_str_static();
+
+    println!("{a} {b}");
+}
+
+fn return_str<'a>() -> &'a str {
+    "A"
+}
+
+fn return_str_static() -> &'static str {
+    "B"
+}

Single Input-Output

fn main() {
+    let a = "A";
+    let b = take_and_return_str(a);
+
+    println!("{a} {b}");
+}
+
+fn take_and_return_str(s: &str) -> &str {
+    println!("{s}");
+    "B"
+}
+
+// take_and_return_str<'a>(s: &'a str) -> &'a str{}
+// take_and_return_str(s: &'static str) -> &'static str{}
+

Multiple Input

fn main() {
+    let (a, b) = ("A", "B");
+    take_strs(a, b);
+}
+
+fn take_strs(a: &str, b: &str) {
+    println!("{a} {b}");
+}
+
+// take_strs<'a>(a: &'a str, b: &'a str) {} 💡both input share same lifetime 
+// take_strs<'a, 'b>(a: &'a str, b: &'b str){} 💡each input having different lifetimes
+

Multiple Input - Single Output

⭐️ MUST ANNOTATE

fn main() {
+    let (a, b) = ("A", "B");
+    let c = take_strs_return_str(a, b);
+    println!("{c}")
+}
+
+fn take_strs_return_str<'a>(a: &'a str, b: &'a str) -> &'a str { // 💡both input, output share same lifetime
+    println!("{a} {b}");
+    b
+}
+// OR take_strs_return_str<'a, 'b>(a: &'a str, b: &'b str) -> &'b str{} // 💡each input having different lifetimes
+

Multiple Input-Output

⭐️ MUST ANNOTATE

fn main() {
+    let (a, b) = ("A", "B");
+    let (c, d) = take_strs_return_strs(a, b);
+    println!("{c} {d}")
+}
+
+fn take_strs_return_strs<'a>(a: &'a str, b: &'a str) -> (&'a str, &'a str) {
+    println!("{a} {b}");
+    (b, a)
+}
+// OR take_strs_return_strs<'a, 'b>(a: &'a str, b: &'b str) -> (&'b str, &'a str){}
+

On Struct, Enum Definitions

⭐️ MUST ANNOTATE

+Tip
  1. Add lifetimes after the & sign to element references.
    • x: &strx: &'a str, x: &mut strx: &'a mut str
  2. After the name of the struct or enum, mention the lifetimes like generic types, unless 'static.
    • struct Personstruct Person<'a> or struct Person<'a, 'b>
    • enum Teamenum Team<'a> or enum Team<'a, 'b>
  3. In the impl block,
    • Either anonymous lifetimes: impl Personimpl Person<'_> or impl Person<'_, '_> , when methods inside the impl block only take &self or &mut self and return types that don’t need to match the struct’s inner lifetime.
    • Or named lifetimes: impl Personimpl<'a> Person<'a> or impl<'a, 'b> Person<'a, 'b>

Single Lifetime

fn main() {
+    let steve = Person::new("Steve", "Jobs");
+    steve.intro();
+}
+
+struct Person<'a> {
+    fname: &'a str,
+    lname: &'a str,
+}
+
+impl<'a> Person<'a> {
+    fn new(fname: &'a str, lname: &'a str) -> Self {
+        Self { fname, lname }
+    }
+}
+
+impl Person<'_> {
+    fn intro(&self) {
+        println!("Hello! I am {} {}.", self.fname, self.lname)
+    }
+}
+// 💡 fn intro(&self) {} can be moved to first impl block too
+

Static Lifetime

fn main() {
+    let steve = Person::new("Steve", "Jobs");
+    steve.intro();
+}
+
+struct Person {
+    fname: &'static str,
+    lname: &'static str,
+}
+
+impl Person {
+    fn new(fname: &'static str, lname: &'static str) -> Self {
+        Self { fname, lname }
+    }
+
+    fn intro(&self) {
+        println!("Hello! I am {} {}.", self.fname, self.lname)
+    }
+}

Multiple Lifetimes

fn main() {
+    let steve = Person::new("Steve", "Jobs");
+    steve.intro();
+}
+
+struct Person<'a, 'b> {
+    fname: &'a str,
+    lname: &'b str,
+}
+
+impl<'a, 'b> Person<'a, 'b> {
+    fn new(fname: &'a str, lname: &'b str) -> Self {
+        Self { fname, lname }
+    }
+}
+
+impl Person<'_, '_> {
+    fn intro(&self) {
+        println!("Hello! I am {} {}.", self.fname, self.lname)
+    }
+}
+// 💡 fn intro(&self) {} can be moved to first impl block too
+

On Trait Implementations

With Anonymous Lifetime

fn main() {
+    let steve = Person { name: "Steve" };
+
+    let has_steve = steve.has_name("Steve");
+    println!("{has_steve}");
+}
+
+struct Person<'a> {
+    name: &'a str,
+}
+
+trait HasName {
+    fn has_name(&self, name: &str) -> bool;
+}
+
+impl HasName for Person<'_> {
+    fn has_name(&self, name: &str) -> bool {
+        self.name == name
+    }
+}

With Named Lifetimes

fn main() {
+    let steve = Person { name: "Steve" };
+
+    let longer_name = steve.longer_name("Jobs");
+    println!("{longer_name}");
+}
+
+struct Person<'a> {
+    name: &'a str,
+}
+
+trait LongerName<'a> {
+    fn longer_name(&self, name: &'a str) -> &'a str;
+}
+
+impl<'a> LongerName<'a> for Person<'a> {
+    fn longer_name(&self, name: &'a str) -> &'a str {
+        if self.name.len() >= name.len() {
+            self.name
+        } else {
+            name
+        }
+    }
+}
+// Person.name and the longer_name method input/return values use the same lifetime identifier
+

With Separate Named Lifetimes

fn main() {
+    let steve = Person { name: "Steve" };
+
+    let longer_name = steve.longer_name("Jobs");
+    println!("{longer_name}");
+}
+
+struct Person<'p> {
+    name: &'p str,
+}
+
+trait LongerName<'n> {
+    fn longer_name(&self, name: &'n str) -> &'n str;
+}
+
+impl<'p, 'n> LongerName<'n> for Person<'p>
+where
+    'p: 'n,
+{
+    fn longer_name(&self, name: &'n str) -> &'n str {
+        if self.name.len() >= name.len() {
+            self.name
+        } else {
+            name
+        }
+    }
+}
+// impl<'p: 'n, 'n> LongerName<'n> for Person<'p> {}
+
\ No newline at end of file diff --git a/docs/docs/lifetimes/og.jpg b/docs/docs/lifetimes/og.jpg new file mode 100644 index 00000000..dc02025c Binary files /dev/null and b/docs/docs/lifetimes/og.jpg differ diff --git a/docs/docs/modules/index.html b/docs/docs/modules/index.html new file mode 100644 index 00000000..37995b94 --- /dev/null +++ b/docs/docs/modules/index.html @@ -0,0 +1,298 @@ +Modules · Learning Rust

Modules

Rust Module Tree

  • The Rust module tree is the hierarchical representation of all modules in a crate.
  • It defines the namespace structure and governs how items are referenced via paths at compile time.
  • Unlike some languages like Go and JavaScript, Rust’s module tree is not purely based on the file system.
    • The root of the module tree begins with either main.rs (in a binary crate) or lib.rs (in a library crate).

      +Tip

      When a Rust package contains both main.rs and lib.rs, Cargo builds two separate crates with isolated module trees: a library crate (lib.rs) and a binary crate (main.rs).

      • In an optimized incremental build, lib.rs is compiled first.
      • Then, main.rs links to it as an external dependency.
    • A file or folder becomes part of the module tree only if it is explicitly declared with the mod keyword in its parent module.

    • A module can be defined in one of two ways:

      1. File module → mod foo; (resolved to foo.rs or foo/mod.rs)
      2. Inline module → mod foo { ... } (defined directly inside a file)
Folder Structure
my_app
+├── Cargo.toml        (package name my_app)
+└── src
+    ├── main.rs       (binary crate)
+    ├── lib.rs        (library crate)
+    ├── foo.rs        (module foo, declared in lib.rs with `pub mod foo;`)
+(may contain inline modules via `mod x { ... }`)
+    │
+    ├── bar
+    │   ├── mod.rs    (module bar, declared in lib.rs with `pub mod bar;`)
+    │   └── file.rs   (submodule of bar, declared in bar/mod.rs with `mod file;`)
+    │
+    ├── buzz.rs       (module buzz, declared in lib.rs with `pub mod buzz;`)
+    └── buzz
+        └── file.rs   (submodule of buzz, declared in buzz.rs with `mod file;`)

Assume,

  • main.rs depends on std, rand(3rd party crate) and library crate.
  • lib.rs contains pub mod foo;, pub mod bar; and pub mod buzz;.
  • foo.rs, bar/file.rs, buzz/file.rs contains pub fn hello_world()
  • bar/mod.rs and buzz.rs contains mod file; and pub use file::hello_world;

The module trees are,

lib.rs (library crate)          (compiled as crate `my_app`)
+  crate::
+    ├── foo
+    ├── bar
+    └── buzz
+
+main.rs (binary crate)          (depends on a compiled crate `my_app`)
+  crate::
+    (binary crate root)
+
+  extern prelude:
+    ├── std
+    └── rand
+
+  available crates:
+    ├── my_app    library crate from lib.rs

Cargo automatically passes each dependency listed in Cargo.toml to the Rust compiler (rustc) using the --extern flag. The compiler then makes these crates available through the extern prelude. This was introduced in Rust 2018 to make external crates automatically available in every module without requiring extern crate declarations or explicit imports at the crate root.

Visibility

Default Visibility

+Tip
  • Private by default to outer levels.
  • Accessible to same/child levels.
  • Use pub keyword to exposes to outer levels.
  • Everything inside a module is private by default to outer modules.
  • However, all items are accessible from within the same module or from the nested modules.
  • The pub keyword is used to make a module or its items public to outer modules.

To access elements at different levels, use the :: path operator with these patterns:

  • Current module: direct item name or self::
  • Parent module: super::
  • Crate root: crate::
  • Cross-Crate: my_app:: (the package name), Ex. accessing the library crate from the binary crate.
  • External crates in Cargo.toml: The full path with crate name (uuid::Uuid::new_v4()) or a use declaration (use uuid::Uuid) to import the elements to the current level first.

Visibility Modifiers

+Tip
  • The pub keyword makes an item fully public (exposing to all outer levels).
  • Visibility modifiers restrict access to specific scopes.
ModifierVisible To
pub(self)Current module
pub(super)Parent module
pub(crate)Entire crate
pub(in path)Specific ancestor module

Inline Modules

  • Defines with the mod keyword.
  • Rust supports nested modules.

Simple

fn main() {
+    inline::greet();
+}
+
+mod inline { // 💡 No pub keyword but can be accessible from the same level/ main.rs
+    pub fn greet() { // 💡 function has to be public to access from outside
+        println!("Hello, world!");
+    }
+}

Nested

fn main() {
+    inline::nested::greet();
+}
+
+mod inline {
+    pub mod nested { // 💡 Use `pub` keyword to access from outside
+        pub fn greet() {
+            println!("Hello, world!");
+        }
+    }
+}

Visibility

Default Visibility

fn main() {
+    inline::greet();
+    inline::nested::greet();
+
+    inline::nested::self_greet();
+    inline::nested::super_greet();
+    inline::nested::super_greet_private();
+
+    inline::nested_private_greet();
+
+    inline2::inline_greet();
+}
+
+mod inline {
+    pub fn greet() {
+        println!("Inline");
+    }
+
+    fn greet_private() { // ⭐️ public mod: private fn; can call from same level or child mod level
+        println!("Inline 🔒");
+    }
+
+    // ⭐️ pub mod: pub fn; can call directly from outside
+    pub mod nested {
+        pub fn greet() {
+            println!("Nested");
+        }
+
+        pub fn self_greet() {
+            self::greet(); // 💡 or greet() ; call inline::nested::greet
+        }
+
+        pub fn super_greet() {
+            super::greet(); // 💡 call inline::greet
+        }
+
+        pub fn super_greet_private() {
+            super::greet_private();
+        }
+    }
+
+    // ⭐️ private mod: pub fn ; can call from parent level
+    mod nested_private {
+        pub fn greet() {
+            println!("Nested 🔑mod");
+        }
+    }
+
+    pub fn nested_private_greet() {
+        nested_private::greet();
+    }
+}
+
+mod inline2 {
+    use crate::inline; // 💡 or `use super::inline;`  reexport inline into the module.
+
+    pub fn inline_greet() {
+        inline::greet(); // 💡 if not reexport, crate::inline::greet(); or super::inline::greet();
+    }
+}

Visibility Modifiers

fn main() {
+    inline::nested_pub_super();
+    inline::nested_pub_crate();
+    inline::nested_pub_in_path();
+
+    inline::nested::exposed_pub_self();
+    inline::nested::exposed_parent_pub_self();
+    inline::nested::pub_crate();
+}
+
+mod inline {
+    pub mod nested {
+        // Accessible only within current module or sub modules
+        pub(self) fn pub_self() {
+            println!("Nested self");
+        }
+
+        // Accessible only from inside or super/ 1st parent
+        pub(super) fn pub_super() {
+            println!("Nested super");
+        }
+
+        // Accessible anywhere within the current crate
+        pub(crate) fn pub_crate() {
+            println!("Nested crate");
+        }
+
+        // Accessible only within `crate::inline` and its submodules
+        pub(in crate::inline) fn pub_in_path() {
+            println!("Nested in-path");
+        }
+
+        // Exposed pub(self), but Clippy triggers a warning "unnecessary `pub(self)`"
+        pub fn exposed_pub_self() {
+            pub_self()
+        }
+
+        // Exposed parent's pub(self), but Clippy triggers a warning "unnecessary `pub(self)`"
+        pub fn exposed_parent_pub_self() {
+            super::pub_self()
+        }
+    }
+
+    pub(self) fn pub_self() {
+        println!("Inline Self");
+    }
+
+    pub fn nested_pub_super() {
+        nested::pub_super()
+    }
+
+    pub fn nested_pub_crate() {
+        nested::pub_crate()
+    }
+
+    pub fn nested_pub_in_path() {
+        nested::pub_in_path()
+    }
+}

Test

When writing tests it’s a good practice to write tests inside a test module because they compile only when running tests.

#![allow(unused)]
+
+fn greet() -> String {
+    "Hello, world!".to_string()
+}
+
+#[cfg(test)] // Only compiles when running tests
+mod tests {
+    use super::greet; // Import root greet function
+
+    #[test]
+    fn test_greet() {
+        assert_eq!("Hello, world!", greet());
+    }
+}

Files In The Same Directory

Folder Structure
├── Cargo.toml    (package name my_app)
+└── src
+    ├── main.rs
+    ├── lib.rs
+    └── foo.rs
main.rs
src/main.rs
mod foo;
+
+fn main() {
+    my_app::greet();
+    my_app::inline::greet();
+
+    foo::greet();
+    foo::inline::greet();
+}
lib.rs
src/lib.rs
pub fn greet() {
+    println!("Lib");
+}
+
+pub mod inline {
+    pub fn greet() {
+        println!("Lib:Inline");
+    }
+}
foo.rs
src/foo.rs
pub fn greet() {
+    println!("Foo");
+}
+
+pub mod inline {
+    pub fn greet() {
+        println!("Foo:Inline");
+    }
+}
+Tip
  • Accessing the library crate from the binary crate (Cross-Crate),
    • Use the crate name my_app:: path prefix to access public items defined in lib.rs.
  • Local files like foo.rs,
    • First, must be added to the module tree using mod foo; in main.rs (or lib.rs).
    • Then access its public items via foo:: path prefix.

If we load foo.rs via lib.rs, we must add pub mod foo; to lib.rs.

main.rs
src/main.rs
fn main() {
+    my_app::greet();
+    my_app::inline::greet();
+
+    my_app::foo::greet();
+    my_app::foo::inline::greet();
+}
lib.rs
src/lib.rs
pub mod foo;
+
+pub fn greet() {
+    println!("Lib");
+}
+
+pub mod inline {
+    pub fn greet() {
+        println!("Lib:Inline");
+    }
+}
foo.rs
src/foo.rs
pub fn greet() {
+    println!("Foo");
+}
+
+pub mod inline {
+    pub fn greet() {
+        println!("Foo:Inline");
+    }
+}

Files In Different Directory

With mod.rs

Before Rust 2018:

  • Directory-based modules were conventionally defined using a mod.rs file.
  • Submodules of the directory were declared within mod.rs using mod (or pub mod) statements.
  • Ex. bar/mod.rs acts as the entry point to the bar directory module.
Folder Structure
my_app
+├── Cargo.toml
+└── src
+    ├── main.rs
+    └── bar
+        ├── mod.rs
+        └── qux.rs
main.rs
src/main.rs
mod bar;
+
+fn main() {
+    bar::qux::greet();
+    bar::qux::inline::greet();
+}
bar/mod.rs
src/bar/mod.rs
pub mod qux;
bar/qux.rs
src/bar/qux.rs
pub fn greet() {
+    println!("Bar:Qux");
+}
+
+pub mod inline {
+    pub fn greet() {
+        println!("Bar:Qux:Inline");
+    }
+}

Without mod.rs

Since Rust 2018:

  • Rust added an alternative approach.
  • Instead of a mod.rs file, we can use a .rs file with the same name as the directory, placed outside that directory.
  • Submodules of the directory can be declared within that .rs file using mod (or pub mod) statements.
  • Ex. buzz.rs acts as the entry point to the buzz directory module.
Folder Structure
my_app
+├── Cargo.toml
+└── src
+    ├── main.rs
+    ├── buzz.rs
+    └── buzz
+        └── qux.rs
main.rs
src/main.rs
mod buzz;
+
+fn main() {
+    buzz::qux::greet();
+    buzz::qux::inline::greet();
+}
buzz.rs
src/buzz.rs
pub mod qux;
buzz/qux.rs
src/buzz/qux.rs
pub fn greet() {
+    println!("Buzz:Qux");
+}
+
+pub mod inline {
+    pub fn greet() {
+        println!("Buzz:Qux:Inline");
+    }
+}
\ No newline at end of file diff --git a/docs/docs/modules/og.jpg b/docs/docs/modules/og.jpg new file mode 100644 index 00000000..d6639c26 Binary files /dev/null and b/docs/docs/modules/og.jpg differ diff --git a/docs/docs/operators/index.html b/docs/docs/operators/index.html new file mode 100644 index 00000000..7db41d53 --- /dev/null +++ b/docs/docs/operators/index.html @@ -0,0 +1,128 @@ +Operators · Learning Rust

Operators

Arithmetic

+ - * / %

let a = 5;
+let b = a + 1; // 6
+let c = a - 1; // 4
+let d = a * 2; // 10
+let e = a / 2; // ⭐️ 2 not 2.5
+let f = a % 2; // 1
+
+let g = 5.0 / 2.0; // 2.5
+

Comparison

== != < > <= >=

let a = 1;
+let b = 2;
+
+let c = a == b; // false
+let d = a != b; // true
+let e = a < b; // true
+let f = a > b; // false
+let g = a <= a; // true
+let h = a >= a; // true
+
+// 🔎
+let i = true > false; // true
+let j = 'a' > 'A'; // true
+

Logical

! && ||

let a = true;
+let b = false;
+
+let c = !a; // false
+let d = a && b; // false
+let e = a || b; // true
+
+Assignment

On integer types,! inverts the individual bits in the two’s complement representation of the value.

let a = !-2; // 1
+let b = !-1; // 0
+let c = !0; // -1
+let d = !1; // -2
+

Bitwise

& | ^ << >>

let a = 1;
+let b = 2;
+
+let c = a & b; // 0  (01 && 10 -> 00)
+let d = a | b; // 3  (01 || 10 -> 11)
+let e = a ^ b; // 3  (01 != 10 -> 11)
+let f = a << b; // 4  (Add b number of 0s to the end of a -> '01'+'00' -> 100)
+let g = a >> b; // 0  (Remove b number of bits from the end of a -> o̶1̶ -> 0)
+

Assignment & Compound Assignment

The = operator is used to assign a name to a value or a function. Compound Assignment Operators are created by composing one of + - * / % & | ^ << >> operators with = operator.

let mut a = 2;
+
+a += 5; // 2 + 5 = 7
+a -= 2; // 7 - 2 = 5
+a *= 5; // 5 * 5 = 25
+a /= 2; // 25 / 2 = 12 not 12.5
+a %= 5; // 12 % 5 = 2
+
+a &= 2; // 10 && 10 -> 10 -> 2
+a |= 5; // 010 || 101 -> 111 -> 7
+a ^= 2; // 111 != 010 -> 101 -> 5
+a <<= 1; // '101'+'0' -> 1010 -> 10
+a >>= 2; // 101̶0̶ -> 10 -> 2
+

Type Casting

as

let a = 15;
+let b = (a as f64) / 2.0; // 7.5
+

Borrowing & Dereference

& &mut *

fn main() {
+    let a = 3;
+    let mut b = 4;
+
+    let c = &a; // 💡 A new shared reference
+    {
+        let d = &mut b; // 💡 A new mutable reference
+        *d += 1; // 💡 Dereference to modify the value at this memory address
+    }
+
+    println!("{c} {b}"); // 3 5
+}
+Recap

The & or &mut operators are used for borrowing and * operator for dereferencing. We will discuss them with +Ownership, Borrowing & Lifetimes. Please focus on operators first.

Path Separator & Member Access

:: .

  • The :: operator is used to access modules, constants, structs, enums, functions (excluding methods) in std modules, crates or nested/ parent modules and to access variants of enums.
  • The . operator is used to access fields of tuples and structs and to call methods in datatypes.
+Recap
use std::collections::HashMap; // 💡load HashMap struct from the std::collections module
+
+fn main() {
+    // 💡 using `HashMap::from()` associated function to create a HashMap
+    let best_movies = HashMap::from([
+        ("01", "The Shawshank Redemption"),
+        ("02", "The Godfather"),
+        ("03", "The Dark Knight"),
+    ]);
+
+    println!("{:?}", best_movies);
+}
println!("{}", std::primitive::u8::MAX); // 💡 access MAX constant from the std::primitive::u8
+
let mut a = (1, "Steve"); // 💡 a is a tuple
+
+// 💡 access fields via dot operator
+a.0 = 2;
+a.1 = "Tim";
+println!("{} {}", a.0, a.1); // 2 Tim
+

👨‍🏫 Before going to the next…

  • About string concatenation,

    let (s1, s2) = ("abc", "123"); // both &str
    +// All bellow codes return abc123 (in String data type)
    +
    +// 1. via + operator
    +let s = String::from(s1) + s2; // String + &str
    +
    +// 2. via push_str method
    +let mut s = String::from(s1);
    +s.push_str(s2); // String + &str
    +
    +// 3. via format! macro
    +let s = format!("{s1}{s2}"); // &str/String + &str/String
    +
    +// 4. via concat method
    +let s = [s1, s2].concat(); // &str or String array
    +
\ No newline at end of file diff --git a/docs/docs/operators/og.jpg b/docs/docs/operators/og.jpg new file mode 100644 index 00000000..90c3b98a Binary files /dev/null and b/docs/docs/operators/og.jpg differ diff --git a/docs/docs/option-and-result/index.html b/docs/docs/option-and-result/index.html new file mode 100644 index 00000000..209bd76e --- /dev/null +++ b/docs/docs/option-and-result/index.html @@ -0,0 +1,101 @@ +Option and Result · Learning Rust

Option and Result

Why Option and Result?

Many languages use null\ nil\ undefined types to represent empty outputs, and Exceptions to handle errors. Rust skips using both, especially to prevent issues like null pointer exceptions, sensitive data leakages through exceptions, etc. Instead, Rust provides two special generic enums;Option and Result to deal with above cases.

+Recap

In the previous sections, we have discussed about the basics of enums, generics and Result & Option types.

As you know,

  • An optional value can have either Some value or no value/ None.
  • A result can represent either success/ Ok or failure/ Err
// An output can have either Some value or no value/ None.
+enum Option<T> { // T is a generic and it can contain any type of value.
+    Some(T),
+    None,
+}
+
+// A result can represent either success/ Ok or failure/ Err.
+enum Result<T, E> { // T and E are generics. T can contain any type of value, E can be any error.
+    Ok(T),
+    Err(E),
+}

💭 Also as we discussed in preludes, not only Option and Result, and also their variants are in preludes. So, we can use them directly without using namespaces in the code.

Basic usages of Option

When writing a function or data type,

  • if an argument of the function is optional,
  • if the function is non-void and if the output it returns can be empty,
  • if the value of a property of the data type can be empty,

we have to use their data type as an Option type.

For example, if the function outputs a &str value and the output can be empty, the return type of the function should be set as Option<&str>.

fn get_an_optional_value() -> Option<&str> {
+
+    //if the optional value is not empty
+    return Some("Some value");
+    
+    //else
+    None
+}

In the same way, if the value of a property of a data type can be empty or optional like the middle_name of the Name data type in the following example, we should set its data type as an Option type.

struct Name {
+  first_name: String,
+  middle_name: Option<String>, // middle_name can be empty
+  last_name: String,
+}

💭 As you know, we can use pattern matching to catch the relevant return type (Some/ None) via match. There is a function in std::env called home_dir() to get the current user’s home directory. However, not all users have a home directory in systems like Linux, so the home directory of a user can be optional. So it returns an Option type; Option<PathBuf>.

use std::env;
+
+fn main() {
+    let home_path = env::home_dir();
+    match home_path {
+        Some(p) => println!("{:?}", p), // This prints "/root", if you run this in Rust playground
+        None => println!("Can not find the home directory!"),
+    }
+}

⭐ However, when using optional arguments with functions, we have to pass None values for empty arguments while calling the function.

fn get_full_name(fname: &str, lname: &str, mname: Option<&str>) -> String { // middle name can be empty
+  match mname {
+    Some(n) => format!("{} {} {}", fname, n, lname),
+    None => format!("{} {}", fname, lname),
+  }
+}
+
+fn main() {
+  println!("{}", get_full_name("Galileo", "Galilei", None));
+  println!("{}", get_full_name("Leonardo", "Vinci", Some("Da")));
+}
+
+// 💡 Better create a struct as Person with fname, lname, mname fields and create a impl function as full_name()
+

🔎 Other than that, Option types are used with nullable pointers in Rust. Because there are no null pointers in Rust, the pointer types should point to a valid location. So if a pointer can be nullable, we have use Option<Box<T>> .

Basic usages of Result

If a function can produce an error, we have to use a Result type by combining the data type of the valid output and the data type of the error. For example, if the data type of the valid output is u64 and error type is String, the return type should be Result<u64, String>.

fn function_with_error() -> Result<u64, String> {
+  
+    //if error happens
+    return Err("The error message".to_string());
+
+    // else, return valid output
+    Ok(255)
+}

💭 As you know, we can use the pattern matching to catch the relevant return types (Ok/Err) via match. There is a function to fetch the value of any environment variable in std::env called var(). Its input is the environment variable name. This can produce an error if we pass a wrong environment variable or the program cannot extract the value of the environment variable while running. So, its return type is a Result type; Result<String, VarError>.

use std::env;
+
+fn main() {
+    let key = "HOME";
+    match env::var(key) {
+        Ok(v) => println!("{}", v), // This prints "/root", if you run this in Rust playground
+        Err(e) => println!("{}", e), // This prints "environment variable not found", if you give a nonexistent environment variable
+    }
+}

is_some(), is_none(), is_ok(), is_err()

Other than match expressions, Rust provides is_some() , is_none() and is_ok() , is_err() functions to identify the return type.

fn main() {
+    let x: Option<&str> = Some("Hello, world!");
+    assert_eq!(x.is_some(), true);
+    assert_eq!(x.is_none(), false);
+
+    let y: Result<i8, &str> = Ok(10);
+    assert_eq!(y.is_ok(), true);
+    assert_eq!(y.is_err(), false);
+}

ok(), err() for Result types

In addition to that, Rust provides ok() and err() for Result types. They convert the Ok<T> and Err<E> values of a Result type to Option types.

fn main() {
+    let o: Result<i8, &str> = Ok(8);
+    let e: Result<i8, &str> = Err("message");
+    
+    assert_eq!(o.ok(), Some(8)); // Ok(v) ok = Some(v)
+    assert_eq!(e.ok(), None);    // Err(v) ok = None
+    
+    assert_eq!(o.err(), None);            // Ok(v) err = None
+    assert_eq!(e.err(), Some("message")); // Err(v) err = Some(v)
+}
\ No newline at end of file diff --git a/docs/docs/option-and-result/og.jpg b/docs/docs/option-and-result/og.jpg new file mode 100644 index 00000000..d03b979c Binary files /dev/null and b/docs/docs/option-and-result/og.jpg differ diff --git a/docs/docs/overview/index.html b/docs/docs/overview/index.html new file mode 100644 index 00000000..9b63fe98 --- /dev/null +++ b/docs/docs/overview/index.html @@ -0,0 +1,28 @@ +Overview · Learning Rust

Overview

About me

🧑‍💻 I am an expat working in Singapore as a Go Backend and DevOps Engineer. Feel free to reach out if you find any mistakes or anything that needs to be changed, including spelling or grammar errors. Alternatively, you can create a pull request, open an issue, or share your awesome ideas in this gist. Good luck with learning Rust!

learning-rust.github.io +learning-cloud-native-go.github.io

github.com +buymeacoffee

Overview

This publication has its origins in the posts I authored on Medium at https://medium.com/learning-rust. However, please note that I have ceased updating the Medium posts. All current and future updates, new content, code, and grammar fixes will be exclusively maintained and released here, https://learning-rust.github.io.

Learning Rust @Medium

\ No newline at end of file diff --git a/docs/docs/overview/og.jpg b/docs/docs/overview/og.jpg new file mode 100644 index 00000000..0b73116f Binary files /dev/null and b/docs/docs/overview/og.jpg differ diff --git a/docs/docs/ownership/index.html b/docs/docs/ownership/index.html new file mode 100644 index 00000000..be380167 --- /dev/null +++ b/docs/docs/ownership/index.html @@ -0,0 +1,203 @@ +Ownership · Learning Rust

Ownership

We discussed in the Derive Traits, the usage of Copy marker trait and .clone() with the below code.

#[derive(Debug, Clone, Copy)]
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let a = Point { x: 0, y: 1 };
+    let b = a;
+
+    println!("{a:?}, {b:?}");
+}

If we try to remove Copy derive on Point and run, we will get the following error while compiling.

error[E0382]: borrow of moved value: `a`
+  --> src/main.rs:11:16
+   |
+ 8 |     let a = Point { x: 0, y: 1 };
+   |         - move occurs because `a` has type `Point`, which does not implement the `Copy` trait
+ 9 |     let b = a;
+   |             - value moved here

This is because of Ownership, which is used to achieve Rust’s memory safety.

Ownership

In Rust,

  • Each piece of data has a single owner, and data is only scoped to its owner (💯 if it is not borrowed).
    • This means that when the owner of the data goes out of scope, the bound resource will be dropped/ released from memory.
  • There can only be one owner at a time. Assigning a variable to another variable or passing it to a function (if not passed by referencing) triggers one of the following behaviors:
    • Copy (for the types that implement the Copy trait):
      • The value is duplicated.
      • Both variables become independent owners of their own data and remain accessible.
    • Move (default behavior/ if not implemented Copy trait):
      • Ownership transfers to the new variable.
      • The original variable is invalidated and can no longer be used.
+Important

In Rust, every assignment is technically a bitwise move (a memcpy/ byte-for-byte copy). The difference is that if a type implements the Copy trait, the compiler simply doesn’t invalidate the original variable afterward.

Copy Types

Rust Standard Library has implemented the Copy trait inside std for primitive types and most simpler types mainly,

  • Due to the orphan rule, we are not allowed to implement std traits for std types.
  • To avoid needing to call the .clone() method each time when assigning data to a new variable or passing it to a function.

Also as we discussed, the Copy marker trait can be added to custom types via a derive macro. However, the Rust compiler will fail to compile if the type contains any non-Copy members.

#[derive(Debug, Clone, Copy)]
+struct Person {
+    name: String, // 💡 String is not a Copy type. It's a Move type.
+}
+
+fn main() {
+    let a = Person { name: "Steve".to_string() };
+    dbg!(a);
+}
error[E0204]: the trait `Copy` cannot be implemented for this type
+ --> src/main.rs:1:24
+  |
+1 | #[derive(Debug, Clone, Copy)]
+  |                        ^^^^
+2 | struct Person {
+3 |     name: String,
+  |     ------------ this field does not implement `Copy`

Implementations in std

  • Primitive types, arrays of Copy types, tuples of Copy types, raw pointers *const T/ *mut T, shared references/ &T

    marker_impls! {
    +    #[stable(feature = "rust1", since = "1.0.0")]
    +    Copy for
    +        usize, u8, u16, u32, u64, u128,
    +        isize, i8, i16, i32, i64, i128,
    +        f16, f32, f64, f128,
    +        bool, char,
    +        {T: PointeeSized} *const T,
    +        {T: PointeeSized} *mut T,
    +}
    +
    +#[stable(feature = "copy_clone_array_lib", since = "1.58.0")]
    +impl<T: Copy, const N: usize> Copy for [T; N] {}
    +
    +/// Shared references can be copied, but mutable references *cannot*!
    +#[stable(feature = "rust1", since = "1.0.0")]
    +impl<T: PointeeSized> Copy for &T {}
  • When T, E, P are Copy types, then Option<T>, Result<T, E>, Pin<P>, Poll<T>

  • IpAddr, Ipv4Addr, Ipv6Addr, Ipv6MulticastScope, SocketAddr, SocketAddrV4, SocketAddrV6, SystemTime, Duration, Instant, PhantomData, and more

More Examples

#![allow(unused)]
+
+fn main() {
+    let a = [1, 2, 3]; // 💡 Array of Copy types
+    let b = a;
+
+    let c = (true, 'C', 32, 1.2); // 💡 Tuple of Copy types
+    let d = c;
+
+    let e = Color { red: 255, green: 0, blue: 0 }; // 💡 Type with Copy typed members + Copy marker
+    let f = e;
+
+    println!("{:?} {:?}", a, b); // [1, 2, 3] [1, 2, 3]
+    println!("{:?} {:?}", c, d); // (true, 'C', 32, 1.2) (true, 'C', 32, 1.2)
+    println!("{:?} {:?}", e, f); // Color { red: 255, green: 0, blue: 0 } Color { red: 255, green: 0, blue: 0 }
+}
+
+#[derive(Debug, Clone, Copy)]
+struct Color {
+    red: u8,
+    green: u8,
+    blue: u8,
+}
fn main() {
+    let x: [i32; 3];
+
+    {
+        let a = [1, 2, 3];
+        x = a;
+        println!("{:?}", a); // [1, 2, 3]
+        
+        // `a` is dropped here, but `x` holds its own independent copy
+    }
+
+    println!("{:?}", x); // [1, 2, 3]
+}

Move Types

By default, data in Rust follows move semantics unless the type implements the Copy marker trait.

Since Rust prohibits implementing Copy for types containing non-Copy members, we must manually call the .clone() method to duplicate the data.

Types like String, Vec<T>, and Box<T> in Rust store the unsized actual data on the heap and small, fixed-size metadata on the stack. When we assign these types to a new variable, only the stack metadata is copied, the original variable is invalidated, ownership of the heap data is transferred to the new variable, and only the new owner is responsible for freeing the heap memory.

However, when we call .clone() on these types, the heap data is duplicated (deep copy), unless types like Rc<T> and Arc<T>, which are specifically designed to be cloned efficiently by incrementing a reference count on the heap rather than duplicating the actual data in heap.

Move Types in std

  • Heap allocated collections like String, Vec<T>, VecDeque<T>, LinkedList<T>, HashMap<K, V>, HashSet<T>, BTreeMap<K, V>, BTreeSet<T>, BinaryHeap<T>
  • &mut T and most iterators
  • When T, E, P are non-Copy types, then Option<T>, Result<T, E>, Pin<P>, Poll<T>
  • PathBuf, File, TcpStream, TcpListener, JoinHandle, Child processes, channel Sender/Receiver, Mutex<T>, RwLock<T>
  • Smart Pointers like Box<T>, Rc<T>, Arc<T>, Pin<P>
  • Cell<T>, RefCell<T>

More Examples

fn main() {
+    let a = String::from("Hello");
+    let b = a.clone();
+
+    let c = vec![1, 2, 3];
+    let d = c.clone();
+
+    let e: [String; 2] = ["Steve".to_owned(), "Jony".to_owned()];
+    let f = e.clone();
+
+    let g = (128, "Steve".to_string());
+    let h = g.clone();
+
+    println!("{:?} {:?}", a, b); // "Hello" "Hello"
+    println!("{:?} {:?}", c, d); // [1, 2, 3] [1, 2, 3]
+    println!("{:?} {:?}", e, f); // ["Steve", "Jony"] ["Steve", "Jony"]
+    println!("{:?} {:?}", g, h); // (128, "Steve") (128, "Steve")
+}

Iterators created with into_iter() consume the collection and move ownership of its data into the iterator. In contrast, iter() and iter_mut() are used to iterate over shared and mutable references of the collection, without moving it.

fn main() {
+    let a = vec![1, 2, 3];
+
+    // ⭐️ By the way, we can use references without cloning which we'll discuss under borrowing
+    for x in a.clone() { // 👨‍🏫 Try remove `.clone()` 
+        println!("{x}");
+    }
+    
+    println!("{}", a.len());
+}
+
+// 💡`for x in a` uses `into_iter()`
+// 💯`for x in &a` uses `iter()` and `for x in &mut a` uses `iter_mut()`
+

Structs with non-Copy members are always moved. We need to explicitly call .clone() when we want to duplicate the data.

#![allow(unused)]
+
+#[derive(Debug, Clone)]
+struct Person {
+    name: String,
+    company_name: String,
+    age: f32,
+}
+
+fn main() {
+    let steve = Person{ name: "Steve Jobs".to_owned(), company_name: "Apple".to_owned(), age: 56.0 };
+    let steve_copy = steve.clone();
+
+    // ⭐️ copy fields values from another instance, steve_copy. steve_copy.company_name moves to wozniak
+    let wozniak = Person { name: "Steve Wozniak".to_owned(), .. steve_copy };
+
+    println!("{} {}" , steve_copy.name, steve_copy.age); // We can't access steve_copy.company_name here
+
+    dbg!(steve, wozniak);
+}
fn main() {
+    let x: String;
+
+    {
+        let a = String::from("Hello!");
+        x = a; // Ownership is transferred to `x` and `a` is invalidated
+        
+        // At the end of this block, `a` goes out of scope. 
+        // However, no heap data is freed because ownership was moved to `x`
+    }
+
+    println!("{x}"); // "Hello!"
+}

Box<T>

Box stores its value on the heap and provides ownership for the allocation.

fn main() {
+    let a = Box::new(5); // Data on the heap + Smart pointer Box<i32> on the stack
+
+    let b = a.clone(); // Deep copy: data on heap + smart pointer on the stack & new owner
+
+    let c = a; // Move ownership of original Box to c
+
+    println!("{b} {c}");
+}
  • Conceptually, Box::new(T) creates T on the stack before moving it to the heap, the compiler (via LLVM) often performs Stack-to-Heap “Move” Optimization in --release mode. This avoids a double move (Stack to Heap) by constructing the value directly in its final heap location rather than copying it from the stack. The concept is called “placement new” in some languages.

  • Conceptually, Box::new(String::from("Hello!")) involves two separate heap allocations/ double allocation. Original "Hello!" on one heap allocation and String structure on second heap allocation. If we use a Box<str>, it requires only one heap allocation.

use std::mem::size_of_val;
+
+fn main() {
+    let a = Box::new(String::from("Hello!"));
+
+    let b: Box<str> = "Hello!".into(); // 💡let b = Box::from("abc"); also works
+
+    println!("Sizes of Box<String>");
+    println!("1. Stack size (thin ptr): {} bytes", size_of_val(&a)); // 8 bytes
+    println!("2. Heap size (String struct): {} bytes", size_of_val(&*a)); // 24 bytes
+    println!("3. Heap size (actual data): {} bytes", size_of_val(&**a)); // 6 bytes
+
+    println!("\nSizes of Box<str>");
+    println!("1. Stack size (fat ptr): {} bytes", size_of_val(&b)); // 16 bytes
+    println!("2. Heap size (actual data): {} bytes", size_of_val(&*b)); // 6 bytes
+}
   STACK           HEAP (Alloc 1)       HEAP (Alloc 2)
+[ Box ptr ]  ->  [ String Struct ]  ->  [ "Hello!" ]
+ (Thin Ptr)       (ptr, len, cap)
+
+   STACK              HEAP (Alloc 1)
+[ Box ptr, len ]  ->  [ "Hello!" ]
+   (Fat Ptr)
+
+💡 A thin pointer is just a memory address: 8 bytes on 64-bit systems (1 machine word)
+💡 String: 24 bytes (3 words: ptr, len, cap)
+💡 Actualy data: N bytes on heap

👨‍🏫 Before going to the next…

  • A pointer is a memory address that refers to data stored elsewhere in memory (Heap, stack, or in the binary’s static memory).

    • If the pointer points to a Sized type, only the memory address is stored (&i32, Box<String>). We call this a thin pointer.
    • If the pointer points to an Unsized type (DST/ Dynamically Sized Type), it contains the memory address and some metadata such as a length (&str, &[T], Box<str>) or a vtable pointer (Box<dyn Trait>). We call this a fat pointer.
  • A smart pointer is a type that wraps a pointer and implements the Deref and Drop traits. This provides additional functionality such as heap management (Box<T>), shared ownership (Rc<T>, Arc<T>), etc.

\ No newline at end of file diff --git a/docs/docs/ownership/og.jpg b/docs/docs/ownership/og.jpg new file mode 100644 index 00000000..61cc0600 Binary files /dev/null and b/docs/docs/ownership/og.jpg differ diff --git a/docs/docs/panicking/index.html b/docs/docs/panicking/index.html new file mode 100644 index 00000000..d1e4303d --- /dev/null +++ b/docs/docs/panicking/index.html @@ -0,0 +1,157 @@ +Panicking · Learning Rust

Panicking

panic!()

  • In some cases, when an error occurs we can not do anything to handle it, if the error is something which should not have happened. In other words, if it’s an unrecoverable error.
  • Also when we are not using a feature-rich debugger or proper logs, sometimes we need to debug the code by quitting the program from a specific line of code by printing out a specific message or a value of a variable binding to understand the current flow of the program. +For above cases, we can use panic! macro.

panic!() runs thread based. One thread can be panicked, while other threads are running.

01. Quit from a specific line.

fn main() {
+    // some code
+
+    // if we need to debug in here
+    panic!();
+}
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'explicit panic', src/main.rs:5:5

02. Quit with a custom error message.

#[allow(unused_mut)] // 💡 A lint attribute used to suppress the warning; username variable does not need to be mutable
+fn main() {
+    let mut username = String::new();
+
+    // some code to get the name
+  
+    if username.is_empty() {
+        panic!("Username is empty!");
+    }
+
+    println!("{}", username);
+}
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'Username is empty!', src/main.rs:8:9

03. Quit with the value of code elements.

#[derive(Debug)] // 💡 A lint attribute which use to implement `std::fmt::Debug` to Color
+struct Color {
+    r: u8,
+    g: u8,
+    b: u8,
+}
+
+#[allow(unreachable_code)] // 💡 A lint attribute used to suppress the warning; unreachable statement
+fn main() {
+    let some_color: Color;
+    
+    // some code to get the color. ex
+    some_color = Color {r: 255, g: 255, b: 0};
+
+    // if we need to debug in here
+    panic!("{:?}", some_color);
+
+    println!(
+        "The color = rgb({},{},{})",
+        some_color.r, some_color.g, some_color.b
+    );
+}
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'Color { r: 255, g: 255, b: 0 }', src/main.rs:16:5

As you can see in the above examples panic!() supports println!() type style arguments. By default, it prints the error message, file path and line & column numbers where the error happens.

unimplemented!()

💡 If your code is having unfinished code sections, there is a standardized macro as unimplemented!() to mark those routes. The program will be panicked with a “not yet implemented” error message, if the program runs through those routes.

// error messages with panic!()
+thread 'main' panicked at 'explicit panic', src/main.rs:6:5
+thread 'main' panicked at 'Username is empty!', src/main.rs:9:9
+thread 'main' panicked at 'Color { r: 255, g: 255, b: 0 }', src/main.rs:17:5
+
+// error messages with unimplemented!()
+thread 'main' panicked at 'not yet implemented', src/main.rs:6:5
+thread 'main' panicked at 'not yet implemented: Username is empty!', src/main.rs:9:9
+thread 'main' panicked at 'not yet implemented: Color { r: 255, g: 255, b: 0 }', src/main.rs:17:5

unreachable!()

This is the standard macro to mark routes that the program should not enter. The program will be panicked with a “‘internal error: entered unreachable code’” error message, if the program entered those routes.

fn main() {
+    let level = 22;
+    let stage = match level {
+        1..=5 => "beginner",
+        6..=10 => "intermediate",
+        11..=20 => "expert",
+        _ => unreachable!(),
+    };
+    
+    println!("{}", stage);
+}
+
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'internal error: entered unreachable code', src/main.rs:7:20

We can set custom error messages for this as well.

// --- with a custom message ---
+_ => unreachable!("Custom message"),
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'internal error: entered unreachable code: Custom message', src/main.rs:7:20
+
+
+// --- with debug data ---
+_ => unreachable!("level is {}", level),
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'internal error: entered unreachable code: level is 22', src/main.rs:7:14

assert!(), assert_eq!(), assert_ne!()

These are standard macros which usually use with test assertions.

  • assert!() ensures that a boolean expression is true. It panics if the expression is false.
fn main() {
+    let f = false;
+    
+    assert!(f)
+}
+
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'assertion failed: f', src/main.rs:4:5
  • assert_eq!() ensures that two expressions are equal. It panics if the expressions are not equal.
fn main() {
+    let a = 10;
+    let b = 20;
+    
+    assert_eq!(a, b);
+}
+
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'assertion failed: `(left == right)`
+  left: `10`,
+ right: `20`', src/main.rs:5:5
  • assert_ne!() ensures that two expressions are not equal. It panics if the expressions are equal.
fn main() {
+    let a = 10;
+    let b = 10;
+    
+    assert_ne!(a, b);
+}
+
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'assertion failed: `(left != right)`
+  left: `10`,
+ right: `10`', src/main.rs:5:5

⭐ Expressions which use with assert_eq!() and assert_ne!() should return same data type.

We can set custom error messages for these macros as well. For examples,

  1. With a custom message for assert_eq!()
fn main() {
+    let a = 10;
+    let b = 20;
+    
+    assert_eq!(a, b, "a and b should be equal");
+}
+
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'assertion failed: `(left == right)`
+  left: `10`,
+ right: `20`: a and b should be equal', src/main.rs:5:5
  1. assert_eq!() with debug data
fn main() {
+    let a = 10;
+    let b = 20;
+
+    let c = 40;
+    
+    assert_eq!(a+b, c, "a = {} ; b = {}", a, b);
+}
+
+// -------------- Compile-time error --------------
+thread 'main' panicked at 'assertion failed: `(left == right)`
+  left: `30`,
+ right: `40`: a = 10 ; b = 20', src/main.rs:7:5

debug_assert!(), debug_assert_eq!(), debug_assert_ne!()

🔎 These are similar to above assert macros. But these statements are only enabled in non optimized builds by default. All these debug_assert macros will be omitted in release builds, unless we pass -C debug-assertions to the compiler.

\ No newline at end of file diff --git a/docs/docs/panicking/og.jpg b/docs/docs/panicking/og.jpg new file mode 100644 index 00000000..58e4b3c6 Binary files /dev/null and b/docs/docs/panicking/og.jpg differ diff --git a/docs/docs/primitive-data-types/index.html b/docs/docs/primitive-data-types/index.html new file mode 100644 index 00000000..25657dbc --- /dev/null +++ b/docs/docs/primitive-data-types/index.html @@ -0,0 +1,147 @@ +Primitive Data Types · Learning Rust

Primitive Data Types

bool

true or false

let a = true;
+let b: bool = false;
+
+// ⭐️ no TRUE, FALSE, 1, 0
+

bool is a single byte(8 bits) in size.

char

A single Unicode scalar value

let a = 'x';
+let b: char = '😎';
+
+// ⭐️ no "x", only single quotes
+

Because of Unicode support, char is not a single byte, but four(32 bits).

i8, i16, i32, i64, i128

8, 16, 32, 64 and 128 bit fixed sized signed(+/-) integer types

DATA TYPEMINMAX
i8-128127
i16-3276832767
i32-21474836482147483647
i64-92233720368547758089223372036854775807
i128-170141183460469231731687303715884105728170141183460469231731687303715884105727
+Tip

The min and max values are based on the following equation; from -(2ⁿ⁻¹) to 2ⁿ⁻¹-1. You can use MIN and MAX constants to find min and max of each integer type. ex.i8::MIN;

let a = 10; // ⭐️ The default integer type in Rust is i32
+let b: i8 = -128;

u8, u16, u32, u64, u128

8, 16, 32, 64 and 128 bit fixed sized unsigned(0/+) integer types

DATA TYPEMINMAX
u80255
u16065535
u3204294967295
u64018446744073709551615
u1280340282366920938463463374607431768211455
+Tip

The min and max values are based on the following equation; from 0 to 2ⁿ-1. Same way you can use MIN and MAX constants to find min and max of each integer type. ex.u8::MAX

isize, usize

Pointer sized signed and unsigned integer types

The actual bit size depends on the computer architecture you are compiling your program for. By default, the sizes are equal to 32 bits on 32-bit platforms and 64 bits on 64-bit platforms. You can use MIN and MAX constants to find min and max of each integer type. ex.isize::MAX.

f32, f64

32 and 64 bit sized floating point numbers(numbers with decimal points)

Rust follows IEEE Standard for Binary Floating-Point Arithmetic. The f32 type is similar to float(Single precision) in other languages, while f64 is similar to double(Double precision) in other languages.

let a = 1.5; // ⭐️ The default float type in Rust is f64
+let b: f64 = 2.0;
+Important

We should avoid using f32, unless you need to reduce memory consumption badly or if you are doing low-level optimization, when targeted hardware does not support for double-precision or when single-precision is faster than double-precision on it.

Array

Fixed size list of elements of same data type

let a = [1, 2, 3];
+
+let b: [i32; 3] = [1, 2, 3]; // with the data type 💡 [Type; NO of elements]
+// 💡let b: [i32; 3] = [1, 2]; // Compiling error : mismatched types : expected an array with a size of 3, found one with a size of 2
+
+let c: [i32; 0] = []; // An empty array
+
// Accessing and changing elements
+let mut a: [i32; 3] = [1, 2, 3];
+a[0] = 2;
+a[1] = 4;
+a[2] = 6;
+
+// Printing with debug and pretty-print debug specifiers
+println!("{a:?}"); // [2, 4, 6]
+println!("{a:#?}");
+//  [
+//      2,
+//      4,
+//      6,
+//  ]
+
let a = [0; 5];   // [0, 0, 0, 0, 0]
+let b = ["x"; 5]; // ["x", "x", "x", "x", "x"]
+

⭐️ Arrays are immutable by default and even with mut, its element count cannot be changed.

Tuple

Fixed size ordered list of elements of different(or same) data types

let a = (1, 1.5, true, 'a');
+
+let b: (i32, f64, bool, char) = (1, 1.5, true, 'a'); // With the data types
+
+let c = (0,); // single-element tuple
+
// Accessing and changing elements
+let mut a = (1, 1.5);
+a.0 = 2;
+a.1 = 3.0;
+
+// Printing with debug and pretty-print debug specifiers
+println!("{a:?}"); // (2, 3.0)
+println!("{a:#?}");
+// (
+//   2,
+//   3.0,
+// )
+
// Destructuring
+let a = (1, 1.5);
+let (b, c) = a; // b = 1, c = 1.5
+
+let a = (1, 1.5, true, 'a');
+let (b, _, _, c) = a; // b = 1, c = 'a'
+let (b, .., c) = a; // b = 1, c = 'a'
+
+let a = (1, 2, 3, true, false, 'a', 'b', 'c');
+let (b, ..) = a; // b = 1
+let (.., c) = a; // c = 'c'
+let (e, f, g, .., h, i, j) = a; // 1 2 3 'a' 'b' 'c'
+
// Nesting
+let a = (1, 1.5);
+let b = (a, (2, 4), 6); // ((1, 1.5), (2, 4), 6)
+

⭐️ Tuples are also immutable by default and even with mut, its element count cannot be changed. Also, if you want to change an element’s value, the new value should have the same data type of previous value.

Slice

Dynamically-sized reference to another data structure

Imagine you want to get/ pass a part of an array or any other data structure. Instead of copying it to another array (or same data structure), Rust allows for creating a view/ reference to access only that part of the data. This view/ reference can be mutable or immutable.

let a: [i32; 4] = [1, 2, 3, 4]; // Parent Array
+
+// Slice whole array
+let b: &[i32] = &a; // data type is optional
+let c = &a[0..4]; // From 0th position to 4th(excluding)
+let d = &a[..]; // high or low bounds are optional
+
+// Slicing part of the array
+let e = &a[1..3]; // [2, 3]
+let f = &a[1..]; // [2, 3, 4]
+let g = &a[..3]; // [1, 2, 3]
+
// directly creating a slice
+let h = &[1, 2];
+let h: &[i32] = &[1, 2]; // with the datatype
+

str

Unsized UTF-8 sequence of Unicode string slices

let a = "Hello, world."; // a: &'static str
+let b: &str = "こんにちは, 世界!";

str is a UTF-8 sequence of Unicode string slice; It’s unsized. You can’t create a variable of type str directly because Rust needs to know sizes at compile time. So, it must always be used behind a pointer — like &str, Box<str>, or Rc<str>.

&str is a fat pointer (a pointer to str and a length). So, it’s a sized reference that carries both a pointer and a length.

&'static str is an &str that is statically allocated directly to the read-only data segment in the program binary. The 'static lifetime means it lives for the entire program duration.

+String
  • String: Capital “S” as it’s a struct.
  • String type is a growable, heap-allocated, UTF-8 encoded string. It is a sized type.
  • String is the most common string type. In general, you should use String when you need ownership, and &str when you just need to borrow a string.
  • Can be generated from a &str type, via to_string(), to_owned() or String::from() methods. With as_str() method, a String type can be converted to a &str type.
let s: &str = "Hello"; // &str
+
+let a = s.to_string(); // String
+let b = s.to_owned(); // String
+let c = String::from(s); // String
+
let a = String::from("Hello"); // String
+let b = a.as_str(); // &str
+

Function

p1 is a function pointer to plus_one() in the following code.

fn main() {
+    let p1 = plus_one; // Without type declarations
+    let a = p1(5); // 6
+
+    let p1: fn(i32) -> i32 = plus_one; // With the type declarations
+    let b = p1(5); // 6
+}
+
+fn plus_one(i: i32) -> i32 {
+    i + 1
+}

Type Inference

The Rust compiler can infer the data type when we don’t explicitly specify it. For example, in Rust, the default integer type is i32 and the default float type is f64. However, the Rust compiler checks the usage and determines the best data type based on the context.

let a = 10;   // type inference: i32
+let b = 3.14; // type inference: f64
+let c = true; // type inference: bool
+
fn main() {
+    let a = 8; // type inference: i8
+    println!("{}", plus_one(a));
+}
+
+fn plus_one(i: i8) -> i8 {
+    i + 1
+}

Default Byte Sizes of Data Types

We can check the default byte sizes of data types via std::mem::size_of. For example: size_of::<bool>(), size_of::<f32>(), size_of::<&str>(), etc.

Data typebytesbits
bool18
char432
i8 i16 i32 i64 i1281, 2, 4, 8, 168, 16, 32, 64, 128
u8 u16 u32 u64 u1281, 2, 4, 8, 168, 16, 32, 64, 128
isize usize8, 864, 64
f32 f644, 832, 64
&str16128

👨‍🏫 Before going to the next…

  • Other than adding the type annotations to the variables, for numeric types, we can append the data type directly to the value as the suffix. Also, to improve the readability of long numbers, we can use _ as a divider.

    let a = 5i8; // Equals to `let a: i8 = 5;`
    +
    +let b = 100_000_000; // Equals to `let b = 100000000;`
    +// 💡 The placements of _s are not strict. ex. 10000_0000 is also valid.
    +
    +let pi = 3.141_592_653_59_f64; // Equals to `let pi: f64 = 3.14159265359`
    +// 💡 make sure group digits consistently by underscores avoid warnings
    +
    +const PI: f64 = 3.141_592_653_59; // In the constants and statics, the data type must be annotated in the beginning.
    +
\ No newline at end of file diff --git a/docs/docs/primitive-data-types/og.jpg b/docs/docs/primitive-data-types/og.jpg new file mode 100644 index 00000000..207ac39d Binary files /dev/null and b/docs/docs/primitive-data-types/og.jpg differ diff --git a/docs/docs/rust_playground.png b/docs/docs/rust_playground.png new file mode 100644 index 00000000..580f0049 Binary files /dev/null and b/docs/docs/rust_playground.png differ diff --git a/docs/docs/smart-compiler/index.html b/docs/docs/smart-compiler/index.html new file mode 100644 index 00000000..fbf283c1 --- /dev/null +++ b/docs/docs/smart-compiler/index.html @@ -0,0 +1,103 @@ +Smart Compiler · Learning Rust

Smart Compiler

+Caution

This needs a refresh!

Why Compiler?

The Rust compiler does the most significant job to prevent errors in Rust programs. It analyzes the code at compile-time and issues warnings, if the code does not follow memory management rules or lifetime annotations correctly.

For example,

#[allow(unused_variables)] //💡 A lint attribute used to suppress the warning; unused variable: `b`
+fn main() {
+    let a = vec![1, 2, 3];
+    let b = a;
+
+    println!("{:?}", a);
+}
+
+
+// ------ Compile-time error ------
+error[E0382]: use of moved value: `a`
+ --> src/main.rs:6:22
+  |
+3 |     let b = a;
+  |         - value moved here
+4 |
+5 |     println!("{:?}", a);
+  |                      ^ value used here after move
+  |
+  = note: move occurs because `a` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
+
+error: aborting due to previous error
+For more information about this error, try `rustc --explain E0382`.
+
+// ⭐ instead using #[allow(unused_variables)], consider using "let _b = a;" in line 4. 
+// Also you can use "let _ =" to completely ignore return values
+
+Recap

In the previous sections, we have discussed memory management concepts like ownership, borrowing, lifetimes and etc.

Rust compiler checks not only issues related with lifetimes or memory management and also common coding mistakes, like the following code.

struct Color {
+    r: u8,
+    g: u8,
+    b: u8,
+}
+
+fn main() {
+    let yellow = Color {
+        r: 255,
+        g: 255,
+        d: 0,
+    };
+
+    println!("Yellow = rgb({},{},{})", yellow.r, yellow.g, yellow.b);
+}
+
+
+// ------------ Compile-time error ------------
+error[E0560]: struct `Color` has no field named `d`
+  --> src/main.rs:11:9
+   |
+11 |         d: 0,
+   |         ^ field does not exist - did you mean `b`?
+
+error: aborting due to previous error
+For more information about this error, try `rustc --explain E0560`.

Explain Error Codes

Above error messages are very descriptive and we can easily see where is the error. But while we can not identify the issue via the error message, rustc --explain commands help us to identify the error type and how to solve it, by showing simple code samples which express the same problem and the solution we have to use.

For example, rustc --explain E0571 shows the following output in the console.

A `break` statement with an argument appeared in a non-`loop` loop.
+
+Example of erroneous code:
+```
+let result = while true {
+    if satisfied(i) {
+        break 2*i; // error: `break` with value from a `while` loop
+    }
+    i += 1;
+};
+```
+
+The `break` statement can take an argument (which will be the value of the loop
+expression if the `break` statement is executed) in `loop` loops, but not
+`for`, `while`, or `while let` loops.
+
+Make sure `break value;` statements only occur in `loop` loops:
+```
+let result = loop { // ok!
+    if satisfied(i) {
+        break 2*i;
+    }
+    i += 1;
+};
+```

💡 Also you can read the same explanations via Rust Compiler Error Index. For example to check the explanation of E0571 error, you can use https://doc.rust-lang.org/error-index.html#E0571.

\ No newline at end of file diff --git a/docs/docs/smart-compiler/og.jpg b/docs/docs/smart-compiler/og.jpg new file mode 100644 index 00000000..9e1bfa0a Binary files /dev/null and b/docs/docs/smart-compiler/og.jpg differ diff --git a/docs/docs/std-primitives-and-preludes/index.html b/docs/docs/std-primitives-and-preludes/index.html new file mode 100644 index 00000000..6381be9b --- /dev/null +++ b/docs/docs/std-primitives-and-preludes/index.html @@ -0,0 +1,114 @@ +STD, Primitives and Preludes · Learning Rust

STD, Primitives and Preludes

+Caution

This needs a refresh!

⭐️ In Rust, language elements are implemented by not only std library crate but also compiler as well. Examples,

  • Primitives: Defined by the compiler and methods are implemented by std library directly on primitives.
  • Standard Macros: Defined by both compiler and std

The std library has been divided into modules, according to the main areas each covered.

⭐️ While primitives are implemented by the compiler, the standard library implements the most useful methods directly on the primitive types. But some rarely useful language elements of some primitives are stored on relevant std modules. This is why you can see char, str and integer types on both primitives and std modules.

Primitives

// Primitives: Defined by the compiler and methods are directly implemented by std
+bool, char, slice, str
+
+i8, i16, i32, i64, i128, isize
+u8, u16, u32, u64, u128, usize
+
+f32, f64
+
+array, tuple
+
+pointer, fn, reference

Standard Macros

// Standard Macros also defined by both compiler and std
+print, println, eprint, eprintln
+format, format_args
+write, writeln
+
+concat, concat_idents, stringify // concat_idents: nightly-only experimental API
+
+include, include_bytes, include_str
+
+assert, assert_eq, assert_ne
+debug_assert, debug_assert_eq, debug_assert_ne
+
+try, panic, compile_error, unreachable, unimplemented
+
+file, line, column, module_path
+env, option_env
+cfg
+
+select, thread_local // select: nightly-only experimental API
+
+vec

Std Modules

// std modules
+char, str
+
+i8, i16, i32, i64, i128, isize
+u8, u16, u32 ,u64, u128, usize
+f32, f64
+num
+
+vec, slice, hash, heap, collections // heap: nightly-only experimental API
+
+string, ascii, fmt
+
+default
+
+marker, clone, convert, cmp, iter
+
+ops, ffi
+
+option, result, panic, error
+
+io
+fs, path
+mem, thread, sync
+process, env
+net
+time
+os
+
+ptr, boxed, borrow, cell, any, rc
+
+prelude
+
+intrinsics // intrinsics: nightly-only experimental API
+raw // raw: nightly-only experimental API
+

Few important std modules are,

  • std::io - Core I/O functionality
  • std::fs - Filesystem specific functionality
  • std::path - Cross-platform path specific functionality
  • std::env - Process’s environment related functionality
  • std::mem - Memory related functionality
  • std::net - TCP/UDP communication
  • std::os - OS specific functionality
  • std::thread - Native threads specific functionality
  • std::collections - Core Collection types
+Tip

Refer Rust Standard Library Documentation for more details.

Preludes

Even though Rust std contains many modules, by default it doesn’t load each and everything of std library on every rust program. Instead, it loads only the smallest list of things which require for almost every single Rust program. These are called preludes. They import only,

// Reexported core operators
+pub use marker::{Copy, Send, Sized, Sync};
+pub use ops::{Drop, Fn, FnMut, FnOnce};
+
+// Reexported functions
+pub use mem::drop;
+
+// Reexported types and traits
+pub use boxed::Box;
+pub use borrow::ToOwned;
+pub use clone::Clone;
+pub use cmp::{PartialEq, PartialOrd, Eq, Ord};
+pub use convert::{AsRef, AsMut, Into, From};
+pub use default::Default;
+pub use iter::{Iterator, Extend, IntoIterator};
+pub use iter::{DoubleEndedIterator, ExactSizeIterator};
+pub use option::Option::{self, Some, None};
+pub use result::Result::{self, Ok, Err};
+pub use slice::SliceConcatExt;
+pub use string::{String, ToString};
+pub use vec::Vec;

Preludes have been imported explicitly on libstd/lib.rs and the whole list can be seen on libstd/prelude/v1.rs.

⭐️ So technically, Rust inserts,

  • extern crate std; : into the crate root of every crate
  • use std::prelude::v1::*; : into every module +So you don’t need to import these each time.

The concept of preludes is quite common on Rust libraries. Even some modules inside std crate (ex.std::io) and many libraries (ex. Diesel) are having their own prelude modules.

⭐️ But preludes are used to create a single place to import all important components which are required while using the library. They do not load automatically unless you imported them manually. Only std::prelude imports automatically in every Rust programs.

\ No newline at end of file diff --git a/docs/docs/std-primitives-and-preludes/og.jpg b/docs/docs/std-primitives-and-preludes/og.jpg new file mode 100644 index 00000000..bf8fb014 Binary files /dev/null and b/docs/docs/std-primitives-and-preludes/og.jpg differ diff --git a/docs/docs/structs/index.html b/docs/docs/structs/index.html new file mode 100644 index 00000000..b5844849 --- /dev/null +++ b/docs/docs/structs/index.html @@ -0,0 +1,170 @@ +Structs · Learning Rust

Structs

  • Used to encapsulate related properties into one unified data type.
  • By convention, the name should follow PascalCase.
  • 3 variants,
    • C-like structs: One or more , separated name: value pairs enclosed in {}

      struct Color {
      +    red: u8,
      +    green: u8,
      +    blue: u8,
      +}
    • Tuple structs: One or more , separated values enclosed in ()

      struct Color(u8, u8, u8);
    • Unit structs: A struct with no fields/ members

      struct Black;
+Recap
  • In Rust, data (attributes) and behavior (associated functions and methods) are placed separately. Structs and Enums are used to group related data, and impls and traits are used to add associated and shared behavior to that data.
  • In Rust, the term “instantiation” is used to describe the act of creating a concrete instance of a type (struct or enum).
  • In Rust, the term “field” is used to describe a named component in a C-like struct & struct-like enum variant, and the term “element” is used to describe an unnamed component in a tuple struct & tuple-like enum variant. The term “member” is used to describe both.
  • More complex examples can be found on Impls and Traits, Lifetimes and Modules sections.

C-like Structs

  • Similar to classes (without its methods) in OOP languages.
  • Can access fields using the ./ dot notation and the field name.

Definition

struct Color {
+    red: u8,
+    green: u8,
+    blue: u8,
+}

Instantiation & Accessing Fields

struct Color {
+    red: u8,
+    green: u8,
+    blue: u8,
+}
+
+fn main() {
+    // 1. Instantiation
+    let white = Color {
+        red: 255,
+        green: 255,
+        blue: 255,
+    };
+
+    // 2. Instantiation without redundant field names, when using the same variable names
+    let (red, green, blue) = (0, 0, 0);
+    let black = Color { red, green, blue };
+
+    // 3. Instantiation + copy fields' values from another instance
+    let red = Color { red: 255, .. black }; // 💡 Copy green and blue from black
+    let green = Color { green: 255, .. black }; // 💡 Copy red and blue from black
+    let mut blue = Color { .. black }; // 💡 Copy all fields' values from black
+    blue.blue = 255;
+
+     println!("RGB({}, {}, {})", white.red, white.green, white.blue); // RGB(255, 255, 255)
+     println!("RGB({}, {}, {})", black.red, black.green, black.blue); // RGB(0, 0, 0)
+
+     println!("RGB({}, {}, {})", red.red, red.green, red.blue); // RGB(255, 0, 0)
+     println!("RGB({}, {}, {})", green.red, green.green, green.blue); // RGB(0, 255, 0)
+     println!("RGB({}, {}, {})", blue.red, blue.green, blue.blue); // RGB(0, 0, 255)
+}
// 4. Instantiation with default values
+
+#[derive(Default)]
+struct Person {
+    name: String,
+    age: f32,
+}
+
+fn main() {
+    let a = Person::default(); // Instantiation with default values
+
+    assert_eq!(a.name, ""); // String default value ""
+    assert_eq!(a.age, 0.0); // f32 default value 0.0
+}
+Tip

In Rust, the #[derive()] attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The std::default::Default trait allows us to create a new instance of a type with the Type::default() method.

💯 5. We can also use a constructor function inside an impl block to initialize a struct.

Destructuring

struct Person {
+    name: String,
+    company_name: String,
+}
+
+fn get_steve() -> Person {
+    Person {
+        name: "Steve Jobs".to_string(),
+        company_name: "Apple".to_string(),
+    }
+}
+
+fn main() {
+    let steve = Person {
+        name: "Steve Jobs".to_string(),
+        company_name: "Apple".to_string(),
+    };
+
+    let Person {name: a, company_name: b} = steve; // 1. Destructuring fields' values to a and b
+    println!("{a} {b}"); // Steve Jobs Apple
+
+    let Person {company_name: c, .. } = get_steve(); // 2. Destructuring only selected fields' values; directly from the function call
+    println!("{c}"); // Apple
+}
+
+// 💯 let Person {name: ref a, company_name: ref b} = steve; // add ref keyword, to pass a field's value as a reference
+

Tuple Structs

  • Looks like a named tuples.
  • Can access fields using the ./ dot notation and the index number of the field, like on tuples.
  • ⭐️ When a tuple struct has only one element, we call it newtype pattern. Because it helps to create a new type.

Definition

struct Color(u8, u8, u8);
+
+struct Department(String);

Instantiation & Accessing Elements

struct Color(u8, u8, u8);
+
+struct Department(String);
+
+fn main() {
+    let white = Color(255, 255, 255);
+    println!("RGB({}, {}, {})", white.0, white.1, white.2); // RGB(255, 255, 255)
+
+    let eng_department = Department("Engineering".to_string());
+    println!("{}", eng_department.0); // Engineering
+}

Destructuring

struct Color(u8, u8, u8);
+
+struct Department(String);
+
+fn get_department() -> Department {
+    Department("Engineering".to_string())
+}
+
+fn main() {
+    let white = Color(255, 255, 255);
+
+    let Color(red, green, blue) = white; // 💡 let Color(red, blue, .. ) = white; // Destructuring only selected field's value
+    println!("RGB({}, {}, {})", red, green, blue); // RGB(255, 255, 255)
+
+    let Department(name) = get_department();
+    println!("{}", name); // Engineering
+}

Unit Structs

  • It defines a new type, but it resembles an empty tuple, ()
  • This is rarely useful on its own. But in combination with other features (such as generics), it can become useful.

Definition & Instantiation

struct Electron;
+
+fn main() {
+    let x = Electron;
+}
+Tip

📖 ex: A library may ask you to create a structure that implements a certain trait to handle events. If you don’t have any data you need to store in the structure, you can create a unit-like struct.

Debug Printing and Pretty Debug Printing

In Rust, the #[derive()] attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The std::fmt::Debug trait allows us to format a value with {:?} or {:#?} in println! and similar macros.

#![allow(unused)] // 💡 skip unused warnings, as we don't read fields in the structs
+
+#[derive(Debug)]
+struct Electron;
+
+#[derive(Debug)]
+struct Department(String);
+
+#[derive(Debug)]
+struct Person {
+    name: String,
+    company_name: String,
+}
+
+fn main() {
+    let a = Electron;
+    println!("{a:?}"); // Electron // 💡{a:#?} prints the same
+
+    let b = Department("Engineering".to_string());
+    println!("{b:?}"); // Department("Engineering")
+    println!("{b:#?}");
+    // Department(
+    //     "Engineering",
+    // )
+    
+    let c = Person { name: "Steve Jobs".to_string(), company_name: "Apple".to_string() };
+    println!("{c:?}"); // Person { name: "Steve Jobs", company_name: "Apple" }
+    println!("{c:#?}");
+    // Person {
+    //     name: "Steve Jobs",
+    //     company_name: "Apple",
+    // }
+}
\ No newline at end of file diff --git a/docs/docs/structs/og.jpg b/docs/docs/structs/og.jpg new file mode 100644 index 00000000..6eabb228 Binary files /dev/null and b/docs/docs/structs/og.jpg differ diff --git a/docs/docs/traits/index.html b/docs/docs/traits/index.html new file mode 100644 index 00000000..5a1efa80 --- /dev/null +++ b/docs/docs/traits/index.html @@ -0,0 +1,442 @@ +Traits · Learning Rust

Traits

A trait is a contract that defines a set of behaviors or properties that a type must implement. It can contain associated types, constants, function or method signatures, and overridable default implementations.

Definition

With No Associates

pub trait Sized { }
+Tip

Mostly used to mark a type as having certain properties to allow in certain operations. Known as Marker Traits.

With the Declarations of Associates

trait Greet {
+    const PREFIX: &'static str;
+    type Item;
+    fn greet(&self) -> String;
+}

With the Default Implementations of Associates

trait Greet {
+    const PREFIX: &'static str = "Hello";
+    
+    fn greet(&self) -> String {
+        format!("{}!", String::from(Self::PREFIX))
+    }
+}

With Supertraits

A trait must have to be implemented first before implementing the current trait.

pub trait Copy: Clone { } // 💡 Any type that copyable should be clonable
+
+// 💡 Any type that clonable should be sized
+pub trait Clone: Sized {
+    fn clone(&self) -> Self;
+    fn clone_from(&mut self, source: &Self) { ... }
+}
+Tip

trait Subtrait: Super or trait Subtrait: SupertraitA + SupertraitB

With Generic Types

// 💡 Convert from one type to another. If successful, retrun Type; else return associated Error
+pub trait TryFrom<T>: Sized {
+    type Error;
+
+    fn try_from(value: T) -> Result<Self, Self::Error>;
+}

Trait Impls (Manual Implementation)

Manually implementing a shared behavior defined in a trait for a type via an impl block.

With Required Components

When a trait has only declarations of associated items, it’s required to implement all of them.

struct Person {
+    name: String
+}
+
+trait Greet {
+    const PREFIX: &'static str; // 💡 Required constant
+    fn greet(&self) -> String; // 💡 Required method
+}
+
+impl Greet for Person {
+    const PREFIX: &'static str = "Hello";
+    
+    fn greet(&self) -> String {
+        format!("{} {}!", Self::PREFIX.to_owned(), self.name)
+    }
+}
+
+fn main() {
+    let steve = Person { name: "Steve".to_string() };
+    println!("{}", steve.greet()); // Hello Steve!
+}

With Provided Components

When a trait has default values and default implementations, it’s possible to implement only some of them or override them.

struct Person {
+    name: String
+}
+
+trait Greet {
+    // 💡 Provided constant
+    const PREFIX: &'static str = "Hello";
+
+    // 💡 Provided method
+    fn greet(&self) -> String {
+        format!("{}!", String::from(Self::PREFIX))
+    }
+}
+
+impl Greet for Person {
+    // 💡 Overridden constant
+    const PREFIX: &'static str = "Good morning";
+    
+    // 💡 Overridden method
+    fn greet(&self) -> String {
+        format!("{} {}!", Self::PREFIX.to_owned(), self.name)
+    }
+}
+
+fn main() {
+    let steve = Person { name: "Steve".to_string() };
+    println!("{}", steve.greet()); // Good morning Steve!
+}

For Enum Types

#![allow(unused)]
+enum Shape {
+    Circle(f64),         // radius
+    Rectangle(u32, u32), // width, height
+}
+
+trait Area {
+    fn area(&self) -> f64;
+}
+
+impl Area for Shape {
+    fn area(&self) -> f64 {
+        match *self {
+            Shape::Circle(radius) => std::f64::consts::PI * radius * radius,
+            Shape::Rectangle(width, height) => (width * height) as f64,
+        }
+    }
+}
+
+fn main() {
+    let circle = Shape::Circle(7.0);
+    let rect = Shape::Rectangle(5, 5);
+
+    println!("{:?}", circle.area());
+    println!("{:?}", rect.area());
+}

For Generic Types

  1. impl<T> Trait for Type<T>

    #![allow(unused)]
    +struct Person<T> {
    +    name: String,
    +    age: T,
    +}
    +
    +trait Greet {
    +    fn greet(&self) -> String;
    +}
    +
    +impl<T> Greet for Person<T> {
    +    fn greet(&self) -> String {
    +        format!("Hello {}!", self.name)
    +    }
    +}
    +
    +fn main() {
    +    let steve = Person { name: "Steve".to_string(), age: 65 }; // 💡 age: i32
    +    let bill = Person { name: "Bill".to_string(), age: 7.5 }; // 💡 age: f64
    +
    +    println!("{:?}", steve.greet()); // Hello Steve!
    +    println!("{:?}", bill.greet()); // Hello Bill!
    +}
  2. impl<T> Trait<T> for Type<T>

    struct Point<T> {
    +    x: T,
    +    y: T,
    +}
    +
    +trait IntoTuple<T> {
    +    fn into_tuple(self) -> (T, T);
    +}
    +
    +impl<T> IntoTuple<T> for Point<T> {
    +    fn into_tuple(self) -> (T, T) {
    +        (self.x, self.y)
    +    }
    +}
    +
    +fn main() {
    +    let a = Point { x: 0, y: 1 }; // Point<i32>
    +    let b = Point { x: "2.0", y: "2.2" }; // Point<&str>
    +
    +    println!("{:?}", a.into_tuple()); // (0, 1)
    +    println!("{:?}", b.into_tuple()); // ("2.0", "2.2")
    +}
    +Assignment

    Update the above implementation to impl<T, U> Trait<T> for Type<U>.

Derive and Auto Traits (Autogenerate Implementations)

Derive Traits

The compiler generates the trait implementation automatically for you, based on the derived attributes.

#[derive(Debug, Clone, PartialEq)]
+struct Person {
+    name: String,
+}
+
+fn main() {
+    let steve = Person { name: "Steve".to_string() };
+    let bill = Person { name: "Bill".to_string() };
+
+    // Debug: 💡 allow debug print with {:?}
+    println!("{steve:?}"); // Person { name: "Steve" }
+
+    // Clone: 💡 allow duplicate data via .clone()
+    let gates = bill.clone();
+
+    // PartialEq: 💡 support equate the two instances via == and !=
+    println!("{}", steve == bill); // false
+    println!("{}", bill == gates); // true
+}

Check bellow Rust STD documentation pages, of these normal derive-traits.

TraitThe functionality (implement to the type at compile-time)
DebugEnables debug-printing of the internal state with {:?}
DefaultAllows creating a default initial value with ::default()
CloneAllows creating a deep copy explicitly with .clone()
PartialEq / EqAllows comparing instances with == and != operators
PartialOrd / OrdAllows comparing instances with <, <=, >, and >= operators
HashAllows the type to be used as a key in a HashMap or HashSet

Marker traits are the traits that have no associated constants, types, methods, etc. So, the compiler-generated implementation has only an empty impl Trait for Type { } block.

#![allow(unused)]
+
+#[derive(Debug, Clone, Copy)] // 💡 Copy trait needs the Clone trait as a supertrait
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let a = Point { x: 0, y: 1 };
+    let b = a; // Copy: 💡 duplicate data. a and b are separate instances
+
+    println!("{a:?}");
+    println!("{b:?}");
+}
+// 👨‍🏫 Try remove Copy derive on Point and explisitly copy via .clone()
+

Auto Traits

Implicitly bound/ automatically implemented by the compiler without any keywords.

pub trait Sized { }
+pub unsafe auto trait Sync { }
+pub auto trait Unpin { }
#![allow(unused)]
+
+#[derive(Debug)]
+struct Point { // 💡 Sized, Send, Sync: (Automatic) Implicitly bound for most simple types in Rust
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let a = Point { x: 0, y: 1 };
+
+    std::thread::scope(|s| {
+        s.spawn(|| println!("{a:?}"));
+        s.spawn(|| println!("{a:?}"));
+    });
+
+    std::thread::spawn(move || println!("{a:?}")).join().unwrap();
+}
TraitThe properties (set to the type automatically)
SizedAble to determine the size at compile time
UnpinSafe to move in memory
SendSafe to move ownership across threads
SyncSafe to shared references between threads
UnwindSafeSafe to use in panic unwindings
RefUnwindSafeSafe to use shared references in panic unwinding
+Recap

Sized/ Unsized

  • Most simple types in Rust have a fixed size known at compile time and automatically implement Sized marker trait.
  • Unsized types (or dynamically sized types, DSTs) cannot be used directly as local variables, function parameters, or return values. They must always be placed behind an indirection, such as a reference (& or &mut), a Box, Rc, or Arc. Common examples include slices ([T], &str) and trait objects (dyn Trait).
    • Pointers to these types are known as “wide pointers” or “fat pointers” because they contain both a pointer to the data and additional metadata. Metadata: for slices - length (number of elements) and for trait objects - a pointer to the vtable/ virtual function table. Ex: &[T], *mut str, Box<dyn Trait>
  • To allow a type to be unsized (relaxes the default Sized requirement), we can use the ?Sized bound.

Static Dispatch

+Note

Monomorphization is a compiler optimization and implementation strategy that transforms polymorphic (generic) code into specialized, monomorphic (single-type) versions for each unique set of concrete types used in a program. This increases the binary size.

Dispatch is the process of deciding which implementation of a polymorphic function to execute, either statically at compile time or dynamically at runtime (via a lookup in a vtable).

Rust compiler generates highly optimized code blocks for each type used for a generic function. So, function calls are statically resolved at compile time and no runtime overhead.

Trait Bounds

Specify the type constraints by traits in generics (<T: Trait>) rather than using exact types. Allowed in a field types & function return and argument.

struct Point { x: i32, y: i32 }
+impl Addr for Point {
+    fn addr(&self) -> String {
+        format!("{}, {}", self.x, self.y)
+    }
+}
+
+struct MapLocation(String, String); // latitude, longitude
+impl Addr for MapLocation {
+    fn addr(&self) -> String {
+        format!("{}, {}", self.0, self.1)
+    }
+}
+
+// --- ⭐️ main Trait & Type with Trait Bounds ⭐️
+trait Addr {
+    fn addr(&self) -> String;
+}
+
+struct Delivery<T: Addr> {
+    location: T,
+}
+
+impl<T: Addr> Delivery<T> {
+    fn new(location: T) -> Self {
+        Self { location }
+    }
+}
+
+// --- ⭐️ fn with Trait Bounds ⭐️
+fn location_info<T: Addr>(location: T) {
+    println!("Latitude/ Longitude: {}", location.addr())
+}
+
+// ---
+fn main() {
+    let ocean = Point { x: 35, y: 20 };
+    let tokyo = MapLocation("35.68951".to_string(), "139.69170".to_string());
+
+    let pkg1 = Delivery::new(ocean);
+    let pkg2 = Delivery::new(tokyo);
+
+    location_info(pkg1.location); // Latitude/ Longitude: 35, 20
+    location_info(pkg2.location); // Latitude/ Longitude: 35.68951, 139.69170
+}

Opaque Types

Specify the type constraints via traits (:impl Trait) without specifying full generic syntax. But only allowed as a function return or an argument.

struct Point { x: i32, y: i32 }
+impl Addr for Point {
+    fn addr(&self) -> String {
+        format!("{}, {}", self.x, self.y)
+    }
+}
+
+struct MapLocation(String, String); // latitude, longitude
+impl Addr for MapLocation {
+    fn addr(&self) -> String {
+        format!("{}, {}", self.0, self.1)
+    }
+}
+
+trait Addr {
+    fn addr(&self) -> String;
+}
+
+// --- ⭐️ Opaque Types are only allowed in function parameters 
+fn location_info(location: impl Addr) { // 💡 Argument-Position-Impl-Trait/ APIT
+    println!("Latitude/ Longitude: {}", location.addr())
+}
+
+fn tokyo_location() -> impl Addr { // 💡 Return-Position-Impl-Trait/ RPIT
+    MapLocation("35.68951".to_string(), "139.69170".to_string())
+}
+
+// ---
+fn main() {
+    let ocean = Point { x: 35, y: 20 };
+    let tokyo = tokyo_location();
+
+    location_info(ocean); // Latitude/ Longitude: 35, 20
+    location_info(tokyo); // Latitude/ Longitude: 35.68951, 139.69170
+}

Dynamic Dispatch

Dynamic dispatch is used when we need to handle multiple different concrete types (mix types) and resolve the trait implementation dynamically at runtime (because this is not possible with static dispatch).

Trait Objects

struct Point { x: i32, y: i32 }
+impl Addr for Point {
+    fn addr(&self) -> String {
+            format!("{}, {}", self.x, self.y)
+    }
+}
+
+struct MapLocation(String, String); // latitude, longitude
+impl Addr for MapLocation {
+    fn addr(&self) -> String {
+        format!("{}, {}", self.0, self.1)
+    }
+}
+
+trait Addr {
+    fn addr(&self) -> String;
+}
+
+// --- ⭐️ Trait Object, allow mix types and check dynamically at runtime
+fn locations_info(locations: &[Box<dyn Addr>]) { // 💡 `dyn Addr` is unsized (DST) and needs a pointer
+    for location in locations {
+        println!("Latitude/ Longitude: {}", location.addr())
+    }
+}
+
+// ---
+fn main() {
+    let ocean = Point { x: 35, y: 20 };
+    let tokyo = MapLocation("35.68951".to_string(), "139.69170".to_string());
+
+    let locations: Vec<Box<dyn Addr>> = vec![Box::new(ocean), Box::new(tokyo)];
+
+    locations_info(&locations);
+    // Latitude/ Longitude: 35, 20
+    // Latitude/ Longitude: 35.68951, 139.69170
+}

Blanket Impls (One-to-Many Manual Implementation)

Blanket impls are a special kind of trait implementation that applies to all types that have a certain type constraint/bound.

A Simple Blanket Impl

#![allow(unused)]
+
+// --- 💡 Two types with Greet implentation
+struct Person { name: String }
+impl Greet for Person {}
+
+struct Stranger {}
+impl Greet for Stranger {}
+
+trait Greet {
+    const PREFIX: &'static str = "Hello";
+
+    fn greet(&self) -> String {
+        format!("{}!", String::from(Self::PREFIX))
+    }
+}
+
+// --- New Farewell trait
+trait Farewell {
+    const PREFIX: &'static str = "Goodbye";
+
+    fn farewell(&self) -> String {
+        format!("{}!", String::from(Self::PREFIX))
+    }
+}
+
+// ⭐️ Any type that implements Greet gets this Farewell implementation
+impl<T: Greet> Farewell for T {} // 💡or impl<T> Farewell for T where T: Greet {}
+
+// ---
+fn main() {
+    let steve = Person { name: "Steve".to_string() };
+    let stranger = Stranger {};
+
+    println!("{}", steve.greet()); // Hello!
+    println!("{}", stranger.greet()); // Hello!
+
+    println!("{}", steve.farewell()); // Goodbye!
+    println!("{}", stranger.farewell()); // Goodbye!
+}

Useful Blanket Impls in STD

  1. When implement Display Rust STD automatically implements ToString to the type.

    use std::fmt;
    +
    +struct Person {
    +    name: String,
    +}
    +
    +impl fmt::Display for Person {
    +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    +        write!(f, "Person {{ name: {}}}", self.name) // 💡 {{ }} escaping curly braces
    +    }
    +}
    +
    +fn main() {
    +    let steve = Person {
    +        name: "Steve".to_string(),
    +    };
    +
    +    println!("{steve}"); // Person { name: Steve}
    +    println!("{}", steve.to_string()); // Person { name: Steve}
    +}
  2. When implement From<T> Rust STD automatically implements Into<U> to the type.

    #![allow(unused)]
    +
    +use std::convert::From;
    +
    +#[derive(Debug)]
    +struct Person {
    +    name: String,
    +}
    +
    +impl From<&str> for Person {
    +    fn from(name: &str) -> Self {
    +        Person {
    +            name: name.to_string(),
    +        }
    +    }
    +}
    +
    +fn main() {
    +    let steve = Person::from("Steve");
    +    let bill: Person = "Bill".into();
    +
    +    println!("{:?}", steve); // Person { name: "Steve" }
    +    println!("{:?}", bill); // Person { name: "Bill" }
    +}
+Assignment

Try to implement TryFrom and check the blanket implementation it provides.

Trait Coherence & Orphan Rule

Rust ensures there is only one implementation of a specific trait for a specific type in the global scope/ in the entire program (Only One Implementation Per Type). This is primarily done to prevent multiple conflicting implementations across different layers of an application. This is called Coherence (or Trait Coherence). If multiple implementations exist, the code will not compile.

To prevent overwriting an existing implementation in a different layer of the application, Rust prohibits implementing a foreign trait for a foreign type. In other words, either the trait or the type must be defined in the current crate. This is called the Orphan Rule.

Suppose you want to implement a std trait for a std/ foreign type but Rust not allow it due to the orphan rule. In that case, we can create a brand-new type that wraps the foreign type and implements the trait for the wrapper type. This is called Newtype Pattern.

use std::fmt;
+use std::time::{SystemTime, SystemTimeError, UNIX_EPOCH};
+
+struct Timestamp(SystemTime); // 💡 The newtype to wrap the std type
+
+impl Timestamp {
+    fn get_seconds(&self) -> Result<u64, SystemTimeError> {
+        self.0
+            .duration_since(UNIX_EPOCH)
+            .map(|duration| duration.as_secs())
+    }
+}
+
+// 💡 Implement the std trait Display for the newtype
+impl fmt::Display for Timestamp {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let secs = self.get_seconds().map_err(|_| fmt::Error)?;
+        write!(f, "{secs}")
+    }
+}
+
+fn main() -> Result<(), SystemTimeError> {
+    let now = Timestamp(SystemTime::now());
+    println!("Current time: {}", now);
+    Ok(())
+}
\ No newline at end of file diff --git a/docs/docs/traits/og.jpg b/docs/docs/traits/og.jpg new file mode 100644 index 00000000..cb190537 Binary files /dev/null and b/docs/docs/traits/og.jpg differ diff --git a/docs/docs/unwrap-and-expect/index.html b/docs/docs/unwrap-and-expect/index.html new file mode 100644 index 00000000..b07c3bc1 --- /dev/null +++ b/docs/docs/unwrap-and-expect/index.html @@ -0,0 +1,176 @@ +Unwrap and Expect · Learning Rust

Unwrap and Expect

unwrap()

  • If an Option type has Some value or a Result type has a Ok value, the value inside them passes to the next step.
  • If the Option type has None value or the Result type has Err value, program panics; If Err, panics with the error message.

The functionality is bit similar to the following codes, which are using match instead unwrap().

Example with Option and match, before using unwrap()

fn main() {
+    let x;
+    match get_an_optional_value() {
+        Some(v) => x = v, // if Some("abc"), set x to "abc"
+        None => panic!(), // if None, panic without any message
+    }
+
+    println!("{}", x); // "abc" ; if you change line 14 `false` to `true`
+}
+
+fn get_an_optional_value() -> Option<&'static str> {
+
+    //if the optional value is not empty
+    if false {
+        return Some("abc");
+    }
+    
+    //else
+    None
+}
+
+
+// --------------- Compile-time error ---------------
+thread 'main' panicked at 'explicit panic', src/main.rs:5:17

Example with Result and match, before using unwrap()

fn main() {
+    let x;
+    match function_with_error() {
+        Ok(v) => x = v, // if Ok(255), set x to 255
+        Err(e) => panic!(e), // if Err("some message"), panic with error message "some message"
+    }
+
+    println!("{}", x); // 255 ; if you change line 13 `true` to `false`
+}
+
+fn function_with_error() -> Result<u64, String> {
+    //if error happens
+    if true {
+        return Err("some message".to_string());
+    }
+
+    // else, return valid output
+    Ok(255)
+}
+
+
+// ---------- Compile-time error ----------
+thread 'main' panicked at 'some message', src/main.rs:5:19

Same codes in above main functions can be written with unwrap() using two lines.

// 01. unwrap error message for None
+fn main() {
+    let x = get_an_optional_value().unwrap();
+
+    println!("{}", x);
+}
+
+// --------------- Compile-time error ---------------
+thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', libcore/option.rs:345:21
+
+
+// 02. unwrap error message for Err
+fn main() {
+    let x = function_with_error().unwrap();
+
+    println!("{}", x);
+}
+
+// --------------- Compile-time error ---------------
+thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: "some message"', libcore/result.rs:945:5

⭐ But as you can see, when using unwrap() error messages are not showing the exact line numbers where the panic happens.

expect()

Similar to unwrap() but can set a custom message for the panics.

// 01. expect error message for None
+fn main() {
+    let n: Option<i8> = None;
+    
+    n.expect("empty value returned");
+}
+
+// --------------- Compile-time error ---------------
+thread 'main' panicked at 'empty value returned', libcore/option.rs:989:5
+
+
+// 02. expect error message for Err
+fn main() {
+    let e: Result<i8, &str> = Err("some message");
+
+    e.expect("expect error message");
+}
+
+// --------------- Compile-time error ---------------
+thread 'main' panicked at 'expect error message: "some message"', libcore/result.rs:945:5

unwrap_err() and expect_err() for Result types

The opposite case of unwrap() and expect(); Panics with Ok values, instead Err. Both print the value inside Ok on the error message.

💡 Usually use with tests.

// 01. unwrap_err error message for Ok
+fn main() {
+    let o: Result<i8, &str> = Ok(8);
+
+    o.unwrap_err();
+}
+
+// ---------- Compile-time error ----------
+thread 'main' panicked at 'called `Result::unwrap_err()` on an `Ok` value: 8', libcore/result.rs:945:5
+
+
+// 02. expect_err error message for Ok
+fn main() {
+    let o: Result<i8, &str> = Ok(8);
+
+    o.expect_err("Should not get Ok value");
+}
+
+// ---------- Compile-time error ----------
+thread 'main' panicked at 'Should not get Ok value: 8', libcore/result.rs:945:5

unwrap_or(), unwrap_or_default() and unwrap_or_else()

+Tip

These are bit similar to unwrap(), If an Option type has Some value or a Result type has a Ok value, the value inside them passes to the next step. But when having None or Err, the functionalities are bit different.

  • unwrap_or() : With None or Err, the value you passes to unwrap_or() is passing to the next step. But the data type of the value you passes should match with the data type of the relevant Some or Ok.
fn main() {
+    let v1 = 8;
+    let v2 = 16;
+
+    let s_v1 = Some(8);
+    let n = None;
+
+    assert_eq!(s_v1.unwrap_or(v2), v1); // Some(v1) unwrap_or v2 = v1
+    assert_eq!(n.unwrap_or(v2), v2);    // None unwrap_or v2 = v2
+
+    let o_v1: Result<i8, &str> = Ok(8);
+    let e: Result<i8, &str> = Err("error");
+
+    assert_eq!(o_v1.unwrap_or(v2), v1); // Ok(v1) unwrap_or v2 = v1
+    assert_eq!(e.unwrap_or(v2), v2);    // Err unwrap_or v2 = v2
+}
  • unwrap_or_default() : With None or Err, the default value of the data type of the relevant Some or Ok, is passing to the next step.
fn main() {
+    let v = 8;
+    let v_default = 0;
+
+    let s_v: Option<i8> = Some(8);
+    let n: Option<i8> = None;
+
+    assert_eq!(s_v.unwrap_or_default(), v);       // Some(v) unwrap_or_default = v
+    assert_eq!(n.unwrap_or_default(), v_default); // None unwrap_or_default = default value of v
+
+    let o_v: Result<i8, &str> = Ok(8);
+    let e: Result<i8, &str> = Err("error");
+
+    assert_eq!(o_v.unwrap_or_default(), v);       // Ok(v) unwrap_or_default = v
+    assert_eq!(e.unwrap_or_default(), v_default); // Err unwrap_or_default = default value of v
+}
  • unwrap_or_else() : Similar to unwrap_or(). The only difference is, instead of passing a value, you have to pass a closure which returns a value with the same data type of the relevant Some or Ok.
fn main() {
+    let v1 = 8;
+    let v2 = 16;
+
+    let s_v1 = Some(8);
+    let n = None;
+    let fn_v2_for_option = || 16;
+
+    assert_eq!(s_v1.unwrap_or_else(fn_v2_for_option), v1); // Some(v1) unwrap_or_else fn_v2 = v1
+    assert_eq!(n.unwrap_or_else(fn_v2_for_option), v2);    // None unwrap_or_else fn_v2 = v2
+
+    let o_v1: Result<i8, &str> = Ok(8);
+    let e: Result<i8, &str> = Err("error");
+    let fn_v2_for_result = |_| 16;
+
+    assert_eq!(o_v1.unwrap_or_else(fn_v2_for_result), v1); // Ok(v1) unwrap_or_else fn_v2 = v1
+    assert_eq!(e.unwrap_or_else(fn_v2_for_result), v2);    // Err unwrap_or_else fn_v2 = v2
+}
\ No newline at end of file diff --git a/docs/docs/unwrap-and-expect/og.jpg b/docs/docs/unwrap-and-expect/og.jpg new file mode 100644 index 00000000..16dada3c Binary files /dev/null and b/docs/docs/unwrap-and-expect/og.jpg differ diff --git a/docs/docs/use/index.html b/docs/docs/use/index.html new file mode 100644 index 00000000..0377a1b7 --- /dev/null +++ b/docs/docs/use/index.html @@ -0,0 +1,158 @@ +Use · Learning Rust

Use

  • A use statement is used to bring items (types, functions, traits, modules, etc.) into the current scope.
    • It doesn’t import anything; instead, it creates a local name (an alias) in the current scope.
  • This allows us:
    • to refer to items by shorter names instead of their full paths.
    • to re-export items (by publicly exposing items from another module or crate using pub use).

Path Prefixes

A use statement can start from:

  • The current crate:
    • use crate:: : Absolute path from the current crate.
    • use super:: : Relative path from the parent module.
    • use self:: : Relative path from the current module. Since Rust 2018, this is often optional.
  • External crates:
    • use my_app:: : The package name when having both main.rs/lib.rs (Cross-Crate). (my_app is the package name in the root Cargo.toml)
    • use uuid:: : Third-party crates. (uuid is a crate for UUID generation)
  • Rust library crates:
    • use std:: : Rust standard library.
    • use core:: : Rust core library. (contains functionality that does not require an operating system or a heap allocator)
    • use alloc:: : Rust core allocation and collections library. (Usually used in #![no_std] projects)

Item Selection

A use statement can select items from a path in different ways:

  • use path::item; : Bring a single item (type, function, trait, module, etc.) into scope.
  • use path::{item_a, item_b}; : Bring multiple specific items into scope.
  • use path::*; : Bring all public items from a module into scope.
  • use path::{self, item_a}; : Bring both the module itself and specific items into scope.
  • use path::nested::item; : Bring an item from a nested path into scope.
  • use path::{nested::item, nested2::item}; : Bring items from different nested modules under a shared base path in a single statement.
  • use path::item as custom_name; or use path::{self, item_a, item_b as custom_name}; : Rename items when bringing them into scope.

Re-exporting

  • Re-exporting publicly exposes items from another module or crate using pub use, allowing us to decouple the public API from internal module structure.
  • Re-exporting can be combined with visibility modifiers to control how far items are exposed within a crate.
    • pub use path::item; : Visible to anyone who can access the crate (fully public).
    • pub(crate) use path::item; : Visible anywhere inside the current crate.
    • pub(super) use path::item; : Visible only in the parent module.
    • pub(in crate::path) use path::item; : Visible only within the specified module path inside the crate.

Start From The Current Crate

use inline::nested::greet;
+use inline2::greet as greet2;
+use inline2::nested::greet as greet3;
+
+fn main() {
+    greet();
+    greet2();
+    greet3();
+}
+
+mod inline {
+    pub fn greet() {
+        println!("Inline");
+    }
+
+    pub mod nested {
+        pub fn greet() {
+            println!("Nested");
+        }
+    }
+}
+
+mod inline2 {
+    use crate::inline; // Bring `inline` into this module's scope.
+
+    pub fn greet() {
+        inline::greet();
+    }
+
+    pub mod nested {
+        pub use super::greet; // Re-export `inline2::greet` as `inline2::nested::greet`
+    }
+}
+// ⭐️ `use crate::inline;` can move directly inside `inline2::greet()` as well.
+

Start From External crates

+Lab
cargo new my_app && cd my_app
+touch src/lib.rs src/foo.rs
+cargo add uuid -F v7
Folder Structure
my_app
+├── Cargo.toml    (package name my_app)
+└── src
+    ├── main.rs
+    ├── lib.rs
+    └── foo.rs
foo.rs
src/foo.rs
use uuid::Uuid;
+
+pub fn new_id() -> Uuid {
+    Uuid::now_v7()
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use uuid::Version;
+
+    #[test]
+    fn test_new_id_is_version_7() {
+        let id = new_id();
+        assert_eq!(id.get_version(), Some(Version::SortRand));
+    }
+}
lib.rs
src/lib.rs
mod foo;
+pub use foo::new_id;
+
+pub struct Person {
+    pub name: String,
+}
+
+pub trait Greet {
+    const PREFIX: &'static str; // 💡 Required constant
+    fn greet(&self) -> String; // 💡 Required method
+}
+
+impl Greet for Person {
+    const PREFIX: &'static str = "Hello";
+
+    fn greet(&self) -> String {
+        format!("{} {}!", Self::PREFIX.to_owned(), self.name)
+    }
+}
main.rs
src/main.rs
use my_app::{Greet, Person as Employer, new_id};
+
+fn main() {
+    let steve = Employer { name: "Steve".to_string() };
+    let apple = Company { name: "Apple".to_string() };
+
+    println!("{}", steve.greet());
+    println!("{}", apple.greet());
+
+    print!("{}", new_id())
+}
+
+struct Company {
+    name: String,
+}
+
+impl Greet for Company {
+    const PREFIX: &'static str = "Welcome to";
+
+    fn greet(&self) -> String {
+        format!("{} {}!", Self::PREFIX.to_owned(), self.name)
+    }
+}

Start From Rust Library Crates

use std::fs::{self, File}; // Bringing the `fs` module and `fs::File` struct into scope
+
+fn main() {
+    fs::create_dir("some_dir").expect("Failed to create the directory!");
+    File::create("some_dir/empty.txt").expect("Failed to create the file!");
+}
use core::cmp::Ordering; // Bring Ordering into scope
+
+// Bring collections (HashMap, VecDeque), sleep, and Duration into scope
+use std::{collections::{HashMap, VecDeque}, thread::sleep, time::Duration };
+
+extern crate alloc; // Enable the alloc crate
+use alloc::vec::Vec; // Bring Vec into scope
+
+fn main() {
+    let two_sec = Duration::from_secs(2);
+    sleep(two_sec);
+
+    let mut map = HashMap::new();
+    map.insert("Key", 10);
+    println!("map: {:?}", map);
+
+    let mut queue = VecDeque::new();
+    queue.push_back(20);
+    println!("queue: {:?}", queue);
+
+    let vec: Vec<i32> = alloc::vec![1, 2, 3];
+    println!("vec: {:?}", vec);
+
+    let (x, y) = (5, 10);
+    match x.cmp(&y) {
+        Ordering::Less => println!("x is smaller"),
+        Ordering::Greater => println!("x is bigger"),
+        Ordering::Equal => println!("x and y are equal"),
+    }
+}
\ No newline at end of file diff --git a/docs/docs/use/og.jpg b/docs/docs/use/og.jpg new file mode 100644 index 00000000..e2e54432 Binary files /dev/null and b/docs/docs/use/og.jpg differ diff --git a/docs/docs/variable-bindings-constants-and-statics/index.html b/docs/docs/variable-bindings-constants-and-statics/index.html new file mode 100644 index 00000000..2592cecc --- /dev/null +++ b/docs/docs/variable-bindings-constants-and-statics/index.html @@ -0,0 +1,89 @@ +Variable bindings, Constants & Statics · Learning Rust

Variable bindings, Constants & Statics

  • Rust is a statically typed language; it checks data types at compile-time. But it doesn’t require you to actually type data types when declaring variable bindings. In that case, the compiler checks the usage and sets a better data type for it.
  • ⭐️ For constants and statics, we must annotate the data type.
  • Types come after a : (colon) sign.
  • The naming convention for the variable bindings is using the snake_case. But, for constants and statics, we should follow the SCREAMING_SNAKE_CASE.
+Recap

In the following examples, we will use data types like bool, i32, i64 and f64. Don’t worry about them for now; they’ll be discussed later.

Variable Bindings

  • The let keyword is used in binding expressions. We can bind a name to a value or a function. Also, because the left-hand side of a let expression is a “pattern”, you can bind multiple names to a set of values or function values.

  • In Rust, variables are immutable by default, so we call them Variable bindings. To make them mutable, the mut keyword is used.

Declaration & Assignment

let a; // Declaration; without data type
+a = 5; // Assignment
+
+let b: i8; // Declaration; with data type
+b = 5;
+
+let c = true;        // Declaration + assignment; without data type
+let d: bool = false; // Declaration + assignment; with data type
+
+let e = 4 + 2; // e = 6
+

Mutability

As mentioned, variable bindings are immutable by default. We need to add the mut keyword to make them mutable.

let mut a = 5; // a = 5
+a = a + 5; // a = 10
+

Multiple Declarations & Assignments

let (a, b); // Declaration
+(a, b) = (1, 2); // Assignment
+
// Declaration + assignment
+let (a, b) = (1, 2); // a = 1 and b = 2
+
// Declaration + assignment + mutability
+let (mut a, mut b) = (3, 4); // a = 3 and b = 4
+(a, b) = (a-b, a+b); // a = -1 and b = 7
+

Scope

let (a, b) = (1, 2); // a = 1 and b = 2
+
+let c = {
+    let a = 4; // affects inside wrapping {} only
+    let b = 6; // affects inside wrapping {} only
+
+    a + b
+}; // c = 10
+
+let d = { a + b }; // d = 3
+
+println!("{a} {b} {c} {d}"); // 1 2 10 3
+

Constants

The const keyword is used to define constants and after the assignment their values are not allowed to change. They live for the entire lifetime of a program but has no fixed address in the memory.

const N: i32 = 5;
+
+const DB_PORT: u16 = 5432;
+
+const SERVER_TIMEOUT: u32 = 60 * 5;

Statics

The static keyword is used to define a “global variable” type facility. There is only one instance for each value, and it’s at a fixed location in memory.

static N: i32 = 5;
+
+static DB_PORT: u16 = 5432;
+
+static SERVER_TIMEOUT: u32 = 60 * 5;
+Important

While you need constants, always use const, instead of static. It’s pretty rare that you actually want a memory location associated with your constant, and using a const allows for optimizations like constant propagation, not only in your crate but also in downstream crates.

Variable Shadowing

Sometimes, while dealing with data, initially we get them in one unit but need to transform them into another unit for further processing. In this situation, instead of using different variable names, Rust allows us to redeclare the same variable with a different data type and/ or with a different mutability setting. We call this Shadowing.

let s: &str = "hello"; // &str
+let s: String = s.to_uppercase(); // String
+println!("{s}"); // HELLO
+
let (a, b) = (1, 2);
+let (a, b) = (b, a); // swap variables via shadowing
+println!("{a} {b}"); // 2 1
+
fn main() {
+    let a: f64 = -20.48; // float
+    let a: i64 = a.floor() as i64; // int
+
+    println!("{a}"); // -21
+
+    {
+        let a = a + 26; // affects inside wrapping {} scope only
+        println!("{a}"); // 5 💡 -21 + 26
+    }
+
+    println!("{a}"); // -21 💡 outer a
+}

👨‍🏫 Before going to the next…

  • Usually, constants and statics are placed at the top of the code file, outside the functions (after module imports/ use declarations).

    const PI: f64 = 3.14159265359;
    +
    +fn main() {
    +    println!("π value is {}", PI);
    +}
\ No newline at end of file diff --git a/docs/docs/variable-bindings-constants-and-statics/og.jpg b/docs/docs/variable-bindings-constants-and-statics/og.jpg new file mode 100644 index 00000000..4551af7e Binary files /dev/null and b/docs/docs/variable-bindings-constants-and-statics/og.jpg differ diff --git a/docs/docs/vectors/index.html b/docs/docs/vectors/index.html new file mode 100644 index 00000000..69e16814 --- /dev/null +++ b/docs/docs/vectors/index.html @@ -0,0 +1,109 @@ +Vectors · Learning Rust

Vectors

If you remember, the array is a fixed-size list of elements, of the same data type. Even with mut, its element count cannot be changed. A vector is kind of a re-sizable array but all elements must be in the same type.

+Tip
  • Vec<T>: capital “V” as it’s a struct.
  • It’s a generic type, written as Vec<T>. T can have any type, ex. A vector of i32s is Vec<i32>. Also, Vectors always allocate their data in a dynamically allocated heap.

Creation

Empty Vector

let mut a = Vec::new(); // 1. With new() keyword
+let mut b = vec![]; // 2. Using the vec! macro (💡 usually create with values same time)
+
+// ⭐️ If you need an immutable empty vector, you must have to specify the data type.
+let a: Vec<i32> = Vec::new();
+let b: Vec<String> = vec![];

With Type Annotations

let a: Vec<i32> = Vec::new();
+let b: Vec<i32> = vec![];
+
+let c = vec![1i32, 2, 3]; // Suffixing 1st value with data type
+

With Values

let a = vec![1, 2, 3];
+let b: Vec<i32> = vec![1, 2, 3];
+let c  = vec![1i32, 2, 3];  
+
let a = vec![0; 10]; // Ten zeroes
+let b = vec![""; 10]; // Ten "" str
+

With a Capacity

let mut a: Vec<i32> = Vec::with_capacity(10);
+println!("Length: {}, Capacity : {}", a.len(), a.capacity()); // Length: 0, Capacity : 10
+
+Recap

We’ll discuss this in the Length and Capacity.

Accessing and Changing Elements

By Index

We can access and change elements of a vector, via the index (like we access/ change elements of an array).

let mut a = vec![1, 2, 3];
+
+println!("{} {} {}", a[0], a[1], a[2]); // 1 2 3
+
+a[0] = 4; // [4, 2, 3]
+(a[1], a[2]) = (a[2], a[1]); // Shuffle
+
+println!("{:?}", a); // [4, 3, 2]
+
+a[5] = 2; // 💥 panics at runtime; index out of bounds: the len is 3 but the index is 5
+

By the get Method

Similar to accessing elements via the index, but safer, as it always returns an Option<T>/ optional value.

let mut a = vec![1, 2, 3];
+
+let x = a.get(0); // Some(1)
+let y = a.get(5); // None
+
+Recap

Option<T> can be either Some value or None (no value). It is also a generic type, like Vec<T>. We’ll discuss more details in the Generics: Option. For the moment, focus on vectors.

By the push and pop Methods

let mut a: Vec<i32> = Vec::new();
+
+a.push(1); // Add 1 to the end; a = [1]
+a.push(2); // Add 2 to the end; a = [1, 2]
+
+a.pop(); // Remove 2 from the end; a = [1]
+
+let x = a.pop(); // Remove 1 from the end and assign it to x as Option<T>; a = []
+// x = Some(1)
+
+let y = a.pop(); // Remove nothing as a is empty; a = [] ⭐️ No panics
+// y = None
+

Length and Capacity

In Rust, most types have a fixed size known at compile time and implement the trait Sized. Vec<T> is also a sized type; A struct that internally stores,

  1. A pointer: points to the heap-allocated memory storing the elements contiguously, like a slice [T]
  2. Length: NO of elements currently have
  3. Capacity: Amount of space allocated for any future elements
+Tip

If the length of a vector exceeds its capacity, its capacity will be increased automatically. But its elements will be reallocated(which can be slow). So, always use Vec::with_capacity whenever it’s possible.

let mut e: Vec<i32> = Vec::with_capacity(10); // Length: 0, Capacity : 10
+
+// These are all done without reallocating...
+for i in 0..10 {
+    e.push(i);
+}
+
+// ...but this may make the vector reallocate as exceeded current capacity
+e.push(11);

👨‍🏫 Before going to the next…

  • Vectors can be used with iterators in three ways,

    let mut a = vec![1, 2, 3, 4, 5];
    +
    +for i in a {
    +    println!("Take ownership of the vector and its element {}", i);
    +}
    +
    +for i in &a {
    +    println!("A reference to {}", i);
    +}
    +
    +for i in &mut a {
    +    println!("A mutable reference to {}", i);
    +}
  • The String/ &str data types are UTF-8 encoded vectors. But you can not index into a String because of encoding.

    let a = String::from("Hello!");
    +print!("{}", a.chars().count()); // 6
    +// 💡 H   e   l   l   o   !
    +
    let a = "a̐éö̲\r\n";
    +for (i, v) in a.chars().enumerate() {
    +    println!("{i}: {v}");
    +}
    +
    +// 0: a
    +// 1: ̐
    +// 2: é
    +// 3: ö
    +// 4: ̲
    +// 5:  
    +// 6:  
    +
    +Important

    We can iterate over the characters of a string via the chars() method. But for more accurate results, you should use a crate like unicode_segmentation that follows more accurate Unicode text segmentation standards.

\ No newline at end of file diff --git a/docs/docs/vectors/og.jpg b/docs/docs/vectors/og.jpg new file mode 100644 index 00000000..c36be3be Binary files /dev/null and b/docs/docs/vectors/og.jpg differ diff --git a/docs/docs/why-rust/index.html b/docs/docs/why-rust/index.html new file mode 100644 index 00000000..fe263512 --- /dev/null +++ b/docs/docs/why-rust/index.html @@ -0,0 +1,27 @@ +Why Rust? · Learning Rust

Why Rust?

History of Rust

Rust was initially designed and developed by former Mozilla employee Graydon Hoare as a personal project. Mozilla began sponsoring the project in 2009 and announced it in 2010. But the first stable release, Rust 1.0, was released on May 15, 2015.

Since Rust 1.0, major updates have been released as Editions approximately every three years: Rust 2015 (with the release of Rust 1.0) , Rust 2018, Rust 2021, and Rust 2024, all maintaining backward compatibility.

Initial Goals

Rust is a systems programming language focused on three goals: safety, speed, and concurrency.
~ Rust Documentation

Rust is a modern, multi-platform, multi-paradigm, statically compiled, memory & thread safety–focused, systems programming language.

  • It uses LLVM on the backend and supports many different operating systems, architectures, targets, and cross-compiling.

  • It supports imperative procedural, concurrent actor, object-oriented, and pure functional styles. Rust also supports generic programming and metaprogramming, in both static and dynamic styles.

  • It doesn’t use a built-in runtime or an automated garbage collection system (GC).

    +Recap

    However, async Rust requires an async runtime, which is provided by community-maintained ecosystems like Compio, Embassy, smol, tokio etc. The async runtime will be bundled into the final executable.

  • One of Rust’s most unique and compelling features is Ownership, which is used to achieve memory safety. Rust creates memory pointers optimistically, checks memory pointers’ limited accesses at compile-time with the usage of References and Borrowing. And it does automatic compile-time memory management by checking the Lifetimes.

Influences

Its design elements came from a wide range of sources.

  • Abstract Machine Model: C
  • Data types: C, SML, OCaml, Lisp, Limbo
  • Optional Bindings: Swift
  • Hygienic Macros: Scheme
  • Functional Programming: Haskell, OCaml, F#
  • Attributes: ECMA-335
  • Memory Model and Memory Management: C++, ML Kit, Cyclone
  • Type Classes: Haskell
  • Crate: Assembly in the ECMA-335 CLI model
  • Channels and Concurrency: Newsqueak, Alef, Limbo
  • Message passing and Thread failure: Erlang

and etc.

Rust compiler observes the code at compile-time and helps to prevent many types of errors that are possible to write in C, C++ like programming languages.

👨‍🏫 Before going to the next…

\ No newline at end of file diff --git a/docs/docs/why-rust/og.jpg b/docs/docs/why-rust/og.jpg new file mode 100644 index 00000000..8e6df777 Binary files /dev/null and b/docs/docs/why-rust/og.jpg differ diff --git a/docs/docs/workspaces/index.html b/docs/docs/workspaces/index.html new file mode 100644 index 00000000..1a07830e --- /dev/null +++ b/docs/docs/workspaces/index.html @@ -0,0 +1,92 @@ +Workspaces · Learning Rust

Workspaces

Rust Workspaces

  • Rust workspaces provide a convenient way to manage multiple related crates together as a single project or monorepo.
  • A Rust workspace uses a shared Cargo.lock file and a common target directory to efficiently build and manage shared dependencies, reducing build times.
  • Cargo commands such as cargo test, cargo build, cargo check, cargo fmt, cargo clippy, and cargo clean can be run once from the workspace root to apply to all workspace members.

Simple

Let’s build a sample Rust workspace with two simple crates.

+Lab
mkdir my_project && cd my_project
+echo -e "[workspace]\nresolver = \"3\"" > ./Cargo.toml
+cargo new app_bar && cargo new app_buzz
+cargo add uuid -F v7 -p app_bar && cargo add uuid -F v7 -p app_buzz
+
+echo 'fn main() {
+\tprintln!("Bar ID: {}", uuid::Uuid::now_v7());
+}' > app_bar/src/main.rs
+
+echo 'fn main() {
+\tprintln!("Buzz ID: {}", uuid::Uuid::now_v7());
+}' > app_buzz/src/main.rs
Folder Structure
my_project
+├── app_bar
+│   ├── Cargo.toml
+│   └── src
+│       └── main.rs
+├── app_buzz
+│   ├── Cargo.toml
+│   └── src
+│       └── main.rs
+├── Cargo.lock
+└── Cargo.toml
app_bar → main.rs
my_project/app_bar/src/main.rs
fn main() {
+    println!("Bar ID: {}", uuid::Uuid::now_v7());
+}
app_buzz → main.rs
my_project/app_buzz/src/main.rs
fn main() {
+    println!("Buzz ID: {}", uuid::Uuid::now_v7());
+}
Cargo.toml
my_project/Cargo.toml
[workspace]
+resolver = "3"
+members = ["app_bar", "app_buzz"]

As you can see,

  • Cargo commands can automatically add workspace members to the Cargo.toml file in the workspace root(my_project).
  • Try running cargo build and observe how each dependency is compiled only once through the shared target directory in the workspace root.
+Assignment
  • Examine the Cargo.toml files in the workspace root and in each crate, and identify the main tables(sections) they contain.
  • Examine the Cargo.lock file in the workspace root and identify how the app_buzz and app_bar crates record their dependencies.
  • Run cargo run -p app_bar and cargo run -p app_buzz commands from the workspace root.

Advanced

+Lab
mkdir my_project2 && cd my_project2
+echo -e "[workspace]\nresolver = \"3\"\nmembers = [\"crates/*\"]" > ./Cargo.toml
+cargo new crates/bar && cargo new crates/buzz
+cargo new crates/common --lib
Folder Structure
my_project
+├── crates
+│   ├── bar
+│   │   ├── Cargo.toml
+│   │   └── src
+│   │       └── main.rs
+│   ├── buzz
+│   │   ├── Cargo.toml
+│   │   └── src
+│   │       └── main.rs
+│   └── common
+│       ├── Cargo.toml
+│       └── src
+│           └── lib.rs
+├── Cargo.toml
+└── Cargo.lock
  • To use the common crate from crates/bar and crates/buzz,
    • We have to add common crate as a dependency in both crates/bar/Cargo.toml and crates/buzz/Cargo.toml.
      [dependencies]
      +common = { path = "../common" }
    • Then, we can use the common crate like any other dependency in crates/bar and crates/buzz.
      fn main() {
      +    let x = common::add(1, 1);
      +    println!("{x}");
      +}
bar → Cargo.toml
my_project2/crates/bar/Cargo.toml
[package]
+name = "bar"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+common = { path = "../common" }
buzz → Cargo.toml
my_project2/crates/buzz/Cargo.toml
[package]
+name = "buzz"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+common = { path = "../common" }
my_project2 → Cargo.toml
my_project/Cargo.toml
[workspace]
+resolver = "3"
+members = ["crates/*"]

👨‍🏫 Before going to the next…

\ No newline at end of file diff --git a/docs/docs/workspaces/og.jpg b/docs/docs/workspaces/og.jpg new file mode 100644 index 00000000..c3423195 Binary files /dev/null and b/docs/docs/workspaces/og.jpg differ diff --git a/docs/favicon/android-chrome-192x192.png b/docs/favicon/android-chrome-192x192.png new file mode 100644 index 00000000..918f1d3b Binary files /dev/null and b/docs/favicon/android-chrome-192x192.png differ diff --git a/docs/favicon/android-chrome-512x512.png b/docs/favicon/android-chrome-512x512.png new file mode 100644 index 00000000..e46db077 Binary files /dev/null and b/docs/favicon/android-chrome-512x512.png differ diff --git a/docs/favicon/apple-touch-icon.png b/docs/favicon/apple-touch-icon.png new file mode 100644 index 00000000..84816ca1 Binary files /dev/null and b/docs/favicon/apple-touch-icon.png differ diff --git a/docs/favicon/favicon-16x16.png b/docs/favicon/favicon-16x16.png new file mode 100644 index 00000000..59a58e0f Binary files /dev/null and b/docs/favicon/favicon-16x16.png differ diff --git a/docs/favicon/favicon-32x32.png b/docs/favicon/favicon-32x32.png new file mode 100644 index 00000000..a680bb81 Binary files /dev/null and b/docs/favicon/favicon-32x32.png differ diff --git a/docs/favicon/favicon.ico b/docs/favicon/favicon.ico new file mode 100644 index 00000000..4f9e2b4e Binary files /dev/null and b/docs/favicon/favicon.ico differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..1c070997 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,99 @@ +Learning Rust · Rust Programming Language Tutorials for Everyone!

Made by a human · Made for a human

Rust Illuminated.
Grasp Quickly.

Rust Programming Language Tutorials for Everyone!

See the syntax in action

Let's explore a few syntaxes of the language.

Data and behavior
Structs, Impls and Traits
struct Person {
+    name: String,
+    company_name: String,
+}
+
+impl Person {
+    fn new(name: String, company_name: String) -> Self {
+        Self { name, company_name }
+    }
+
+    fn intro(&self) -> String {
+        format!("I'm {} from {}", self.name, self.company_name)
+    }
+}
+
+trait Greet {
+    const PREFIX: &'static str = "Hello";
+
+    fn greet(&self) -> String {
+        format!("{}!", String::from(Self::PREFIX))
+    }
+}
+impl Greet for Person {}
+
+fn main() {
+    let steve = Person::new("Steve".to_string(), "Apple".to_string());
+
+    println!("{}", steve.greet()); // Hello!
+    println!("{}", steve.intro()); // I'm Steve from Apple
+}
Functional
Iterators, Filters, and Maps
fn main() {
+    let marks = ["10", "20", "30", "0.4", "0.5", "invalid"];
+
+    let sum: i32 = marks
+        // Iterate through marks
+        .iter()
+        // Parse string to float, ignore if invalid
+        .filter_map(|&s| s.parse::<f64>().ok())
+        // Multiply decimals under 1.0 by 100
+        .map(|num| if num < 1.0 { num * 100.0 } else { num })
+        // Trace the values
+        .inspect(|&val| println!("Parsed: {val}"))
+        // Cast float to integer
+        .map(|num| num as i32)
+        // Aggregate
+        .sum();
+
+    println!("Final: {sum}"); // 150
+}
Advanced blueprint
Enums, Generics and HashMap
use std::collections::HashMap;
+
+enum Data<K, V> {
+    Value(V),
+    KeyValue(K, V),
+}
+
+fn main() {
+    let data: Vec<Data<&str, i32>> = vec![
+        Data::Value(10),
+        Data::KeyValue("Steve", 20),
+        Data::KeyValue("Tom", 30),
+        Data::Value(40),
+        Data::KeyValue("Mike", 50),
+    ];
+
+    let map: HashMap<String, i32> = data
+        .into_iter()
+        // Pattern matching and destructuring
+        .map(|item| match item {
+            Data::KeyValue(k, v) => {
+                println!("{}: {}", k, v);
+                (k.to_string(), v)
+            }
+            Data::Value(v) => {
+                println!("unknown: {}", v);
+                ("unknown".to_string(), v)
+            }
+        })
+        // Accumulate items into a map
+        .fold(HashMap::new(), |mut map, (key, value)| {
+            map.entry(key)
+                .and_modify(|existing| *existing += value)
+                .or_insert(value);
+            map
+        });
+
+    println!("Map: {:?}", map); // Map: {"unknown": 50, "Steve": 20, "Tom": 30, "Mike": 50}
+}

Contribution

Without them, we couldn't build this website

GoHugo

The world’s fastest framework for building websites.

Pagefind

Fully static search library that aims to perform well on large sites.

e25DX

Modern & modular technical documentation and blog setup.

Documentation

Help us to improve the documentation.

\ No newline at end of file diff --git a/docs/index.xml b/docs/index.xml new file mode 100644 index 00000000..1b3b029e --- /dev/null +++ b/docs/index.xml @@ -0,0 +1,948 @@ +Learning Rusthttps://learning-rust.github.io/Recent content on Learning RustHugoen-USBorrowinghttps://learning-rust.github.io/docs/borrowing/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/borrowing/<h2 id="borrowing">Borrowing</h2> +<ul> +<li> +<p>In a general sense, borrowing is taking something with the promise of returning it.</p> +</li> +<li> +<p>In Rust, borrowing is the act of creating a reference to a value without copying the data and without taking ownership.</p> +<ul> +<li>This is similar to the concept of &ldquo;passing by reference&rdquo; in other languages.</li> +<li>However in Rust, +<ul> +<li>References are immutable by default: shared, read-only/ immutable references: <code>&amp;T</code></li> +<li>To mutate the data, you must explicitly create a mutable reference: <code>&amp;mut T</code></li> +</ul> +</li> +</ul> +</li> +<li> +<p>Liveness</p>Cargo, Crates and Basic Project Structurehttps://learning-rust.github.io/docs/cargo-crates-and-basic-project-structure/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/cargo-crates-and-basic-project-structure/<h2 id="cargo">Cargo</h2> +<p>Cargo is Rust’s built-in package manager and build system. It also supports the following actions,</p> +<table> + <thead> + <tr> + <th>Command</th> + <th>Action</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>cargo new</code></td> + <td>Create a new project</td> + </tr> + <tr> + <td><code>cargo init</code></td> + <td>Create a new project in an existing directory</td> + </tr> + <tr> + <td><code>cargo check</code></td> + <td>Verify the project compiles without errors</td> + </tr> + <tr> + <td><code>cargo build</code></td> + <td>Build the executable</td> + </tr> + <tr> + <td><code>cargo run</code></td> + <td>Build the executable and run</td> + </tr> + <tr> + <td><code>cargo clean</code></td> + <td>Remove the build system directories/ <code>target</code> directory</td> + </tr> + </tbody> +</table> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <ul> +<li>The <code>cargo check</code> command verifies that the project compiles without errors, without producing an executable. +Thus, it is often faster than <code>cargo build</code>.</li> +<li>Cargo places executables compiled with <code>cargo build</code> or <code>cargo run</code> in the <code>target/debug/</code> directory. +But, while those built with <strong><code>cargo build --release</code></strong> for release purposes are stored in <code>target/release/</code> directory. +Release builds use more optimizations and remove some runtime safety checks to increase performance, although this comes at the cost of longer compile time.</li> +</ul> + </div> +</div><table> + <thead> + <tr> + <th>Command</th> + <th>Action</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>cargo add</code></td> + <td>Add a dependency crate to the project</td> + </tr> + <tr> + <td><code>cargo remove</code></td> + <td>Remove a dependency crate from the project</td> + </tr> + <tr> + <td><code>cargo fetch</code></td> + <td>Download the dependencies specified in Cargo.lock</td> + </tr> + <tr> + <td><code>cargo update</code></td> + <td>Update project dependencies</td> + </tr> + </tbody> +</table> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <ul> +<li>A crate is a package that can be shared via <a href="https://crates.io" target="_blank" >crates.io</a>, Rust community’s crate registry. +<code>cargo add</code>, <code>cargo remove</code>, <code>cargo fetch</code>, and <code>cargo update</code> commands manage project dependencies through the crate hosted on crates.io.</li> +<li>The <code>cargo add</code> command includes a specified crate in the <code>[dependencies]</code> section of <code>Cargo.toml</code>, while <code>cargo add --dev</code> adds a crate to the <code>[dev-dependencies]</code> section. This indicates that the crate is only used for development purposes like testing and will not be included in the final compiled code.</li> +</ul> + </div> +</div><table> + <thead> + <tr> + <th>Command</th> + <th>Action</th> + </tr> + </thead> + <tbody> + <tr> + <td><code>cargo test</code></td> + <td>Run tests</td> + </tr> + <tr> + <td><code>cargo bench</code></td> + <td>Run benchmarks</td> + </tr> + <tr> + <td><code>cargo doc</code></td> + <td>Generate the project documentation via <a href="https://doc.rust-lang.org/stable/rustdoc/" target="_blank" >rustdoc</a></td> + </tr> + </tbody> +</table> +<p>In addition, there are <code>cargo</code> commands to publish the project as a crate to <a href="https://crates.io/" target="_blank" >crates.io</a>.</p>Combinatorshttps://learning-rust.github.io/docs/combinators/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/combinators/<h2 id="what-is-a-combinator">What is a combinator?</h2> +<ul> +<li> +<p>One meaning of “combinator” is a more informal sense referring to the <strong>combinator pattern</strong>, a style of organizing libraries centered around the idea of combining things. Usually there is <strong>some type T</strong>, some <strong>functions for constructing “primitive” values of type T</strong>, and some “<strong>combinators</strong>” which can <strong>combine values of type T</strong> in various ways to <strong>build up more complex values of type T</strong>. The other definition is <strong>&ldquo;function with no free variables&rdquo;</strong>. +__ <a href="https://wiki.haskell.org/Combinator" target="_blank" >wiki.haskell.org</a></p>Comments and Documenting the codehttps://learning-rust.github.io/docs/comments-and-documenting-the-code/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/comments-and-documenting-the-code/<h2 id="comments">Comments</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// Line comments +</span></span></span><span class="line"><span class="cl"><span class="cm">/* Block comments */</span></span></span></code></pre></div><p>Nested block comments are supported.</p> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>By convention, try to avoid using block comments. Use line comments instead.</p>Control Flowshttps://learning-rust.github.io/docs/control-flows/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/control-flows/<h2 id="if---else-if---else">if - else if - else</h2> +<h3 id="if"><code>if</code></h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">13</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">18</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Hello, child!&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// The code prints this +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="if-else"><code>if</code> <code>else</code></h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">7</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">if</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Even&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Odd&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// The code prints this +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="with-let-statements">With <code>let</code> Statements</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">age</span>: <span class="kt">u8</span> <span class="o">=</span><span class="w"> </span><span class="mi">13</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">is_below_eighteen</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">age</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">18</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// true +</span></span></span></code></pre></div><h3 id="if-else-if-else"><code>if</code> <code>else if</code> <code>else</code></h3> +<p>i. A simple example,</p>Crateshttps://learning-rust.github.io/docs/crates/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/crates/<div class="alert alert-caution"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M480-300q15 0 25.5-10.5T516-336q0-15-10.5-25.5T480-372q-15 0-25.5 10.5T444-336q0 15 10.5 25.5T480-300Zm25.5-142.5Q516-453 516-468v-168q0-15-10.5-25.5T480-672q-15 0-25.5 10.5T444-636v168q0 15 10.5 25.5T480-432q15 0 25.5-10.5ZM371-144q-14 0-27-5t-24-16L165-321q-10-10-15.5-23.5T144-372v-217q0-14 5-27t16-24l155-155q11-11 24-16t27-5h218q14 0 27 5t24 16l155 155q11 11 16 24t5 27v218q0 14-5 27t-16 24L639-165q-10 10-23.5 15.5T588-144H371Zm0-72h218l155-155v-218L588-744H371L216-589v218l155 155Zm109-264Z"/></svg> + + + <span> + + Caution + + </span> + </div> + <div class="alert-body"> + <p>This needs a refresh!</p> + </div> +</div><p>💭 Crates are a bit similar to the packages in some other languages. Crates compile individually. If the crate has child file modules, those files will get merged with the crate file and compile as a single unit.</p>Custom Error Typeshttps://learning-rust.github.io/docs/custom-error-types/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/custom-error-types/<p>Rust allow us to create our own <code>Err</code> types. We call them “<em>Custom Error Types</em>”.</p> +<h2 id="error-trait">Error trait</h2> +<p>As you know <strong>traits define the functionality a type must provide</strong>. But we don’t always need to define new traits for common functionalities, because Rust <strong>standard library provides reusable traits</strong> which can be implemented on our own types. While creating custom error types the <a href="https://doc.rust-lang.org/std/error/trait.Error.html" target="_blank" ><code>std::error::Error</code> trait</a> helps us to convert any type to an <code>Err</code> type.</p>Enumshttps://learning-rust.github.io/docs/enums/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/enums/<ul> +<li>An enum is a single type that contains variants, which represent the possible values of the enum at any given time.</li> +<li>By convention, the enum name and its variants&rsquo; names should follow <a href="https://en.wikipedia.org/wiki/Camel_case" target="_blank" ><code>PascalCase</code></a>.</li> +<li>Can access the variants using the <code>::</code> notation and the variant name. ex. Day::Sunday</li> +</ul> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Day</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Sunday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Monday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Tuesday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Wednesday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Thursday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Friday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Saturday</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// 💡 Day is the enum. Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday are its variants. +</span></span></span></code></pre></div><ul> +<li>An enum variant can have either, +<ul> +<li>No data (a unit variant)</li> +<li>Unnamed ordered data (a tuple variant)</li> +<li>Named data/ fields (a struct variant)</li> +</ul> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">FlashMessage</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Success</span><span class="p">,</span><span class="w"> </span><span class="c1">// 💡 A unit variant (no data) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Error</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="p">),</span><span class="w"> </span><span class="c1">// 💡 A tuple variant (one or more , separated data) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Warning</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">field</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">message</span>: <span class="nb">String</span> <span class="p">},</span><span class="w"> </span><span class="c1">// 💡 A struct variant (one or more , separated name: value data) +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// 💡 FlashMessage is the emnum, Success, Error, Warning are its variants. +</span></span></span></code></pre></div></li> +</ul> + + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <ul> +<li>In Rust, the term &ldquo;instantiation&rdquo; is used to describe the act of creating a concrete instance of a type (struct or enum).</li> +<li>In Rust, the term &ldquo;field&rdquo; is used to describe a named component in a C-like struct &amp; struct-like enum variant, and the term &ldquo;element&rdquo; is used to describe an unnamed component in a tuple struct &amp; tuple-like enum variant. The term &ldquo;member&rdquo; is used to describe both.</li> +<li>More complex examples can be found on <a href="https://learning-rust.github.io/docs/generics/" >Generics</a>, <a href="https://learning-rust.github.io/docs/impls-and-traits" >Impls and Traits</a>, <a href="https://learning-rust.github.io/docs/lifetimes" >Lifetimes</a> and <a href="https://learning-rust.github.io/docs/modules" >Modules</a> sections.</li> +</ul> + </div> +</div><h2 id="instantiation">Instantiation</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#![allow(unused)]</span><span class="w"> </span><span class="c1">// 💡 skip unused warnings, as we don&#39;t read fields in the enums +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#[derive(Debug)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">FlashMessage</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="c1">// Definition +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Success</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Error</span><span class="p">(</span><span class="kt">u32</span><span class="p">,</span><span class="w"> </span><span class="nb">String</span><span class="p">),</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Warning</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">field</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> </span><span class="n">message</span>: <span class="nb">String</span> <span class="p">},</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 1. Instantiation with separate variable declaration and assignment +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">x</span>: <span class="nc">FlashMessage</span><span class="p">;</span><span class="w"> </span><span class="c1">// Declaration with the data type +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FlashMessage</span>::<span class="n">Success</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{x:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Success +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 2. Instantiation with a direct variable initialization +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FlashMessage</span>::<span class="n">Success</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FlashMessage</span>::<span class="n">Error</span><span class="p">(</span><span class="mi">401</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Unauthorized&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">());</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">FlashMessage</span>::<span class="n">Warning</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">field</span>: <span class="s">&#34;email&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">(),</span><span class="w"> </span><span class="n">message</span>: <span class="s">&#34;This is required&#34;</span><span class="p">.</span><span class="n">to_string</span><span class="p">()</span><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{a:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Success +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{b:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Error(401, &#34;Unauthorized&#34;) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{c:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Warning { field: &#34;email&#34;, message: &#34;This is required&#34; } +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// 3. Instantiation with a default variant +</span></span></span><span class="line"><span class="cl"><span class="cp">#![allow(unused)]</span><span class="w"> </span><span class="c1">// 💡 skip unused warnings, as we don&#39;t use the all variants of the enum +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#[derive(Debug, Default)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">enum</span> <span class="nc">Hand</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Left</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="cp">#[default]</span><span class="w"> </span><span class="c1">// 💡Set Right as the default variant +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">Right</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Hand</span>::<span class="n">default</span><span class="p">();</span><span class="w"> </span><span class="c1">// Instantiation with the default variant +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{a:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// Right +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>In Rust, the <code>#[derive()]</code> attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The <a href="https://doc.rust-lang.org/std/fmt/trait.Debug.html" target="_blank" ><code>std::fmt::Debug</code></a> trait allows us to format a value with <code>{:?}</code> or <code>{:#?}</code> in <code>println!</code> and similar macros. The <a href="https://doc.rust-lang.org/std/default/trait.Default.html" target="_blank" ><code>std::default::Default</code></a> trait allows us to create a new instance of a type with the <code>Type::default()</code> method.</p>Error and None Propagationhttps://learning-rust.github.io/docs/error-and-none-propagation/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/error-and-none-propagation/<p>We should use panics like <code>panic!()</code>, <code>unwrap()</code>, <code>expect()</code> only if we can not handle the situation in a better way. Also if a function contains expressions which can produce either <code>None</code> or <code>Err</code>,</p> +<ul> +<li>we can handle them inside the same function. Or,</li> +<li>we can return <code>None</code> and <code>Err</code> types immediately to the caller. So the caller can decide how to handle them.</li> +</ul> +<p>💡 <code>None</code> types no need to handle by the caller of the function always. But Rusts’ convention to handle <strong><code>Err</code></strong> types is, <strong>return them immediately to the caller to give more control to the caller to decide how to handle them.</strong></p>Functionshttps://learning-rust.github.io/docs/functions/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/functions/<h2 id="named-functions">Named Functions</h2> +<ul> +<li>Named functions are declared with the keyword <strong><code>fn</code></strong></li> +<li>When using <strong>arguments</strong>, we <strong>must declare the data types</strong>.</li> +<li>By default, functions <strong>return an empty <a href="https://learning-rust.github.io/docs/primitive-data-types/#tuple" >tuple</a>/ <code>()</code></strong>. If you want to return a value, the <strong>return type must be specified</strong> after <strong><code>-&gt;</code></strong></li> +</ul> +<h3 id="hello-world">Hello world</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Hello, world!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="passing-arguments">Passing Arguments</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">print_sum</span><span class="p">(</span><span class="n">a</span>: <span class="kt">i8</span><span class="p">,</span><span class="w"> </span><span class="n">b</span>: <span class="kt">i8</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;sum is: </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">b</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="returning-values">Returning Values</h3> +<ul> +<li> +<p>Without the <code>return</code> keyword. Only the last expression returns.</p>Genericshttps://learning-rust.github.io/docs/generics/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/generics/<ul> +<li>The core concept of generics is abstraction over types. They let us write one piece of code to operate with any data type without repeating ourselves to write separate versions for each type. At the compile time, Rust ensures the type safety and generates an optimized code for each concrete type used in the program.</li> +<li>Use an uppercase letter (<code>T</code>, <code>U</code>, &hellip;) or a <a href="https://en.wikipedia.org/wiki/Camel_case" target="_blank" ><code>PascalCase</code></a> identifier for the data type. +<ul> +<li>Instead of <code>x: u8</code> we use <code>x: T</code>.</li> +<li>Inform the compiler that <code>T</code> is a generic type by adding <code>&lt;T&gt;</code> at first.</li> +</ul> +</li> +</ul> +<h2 id="with-one-generic-type">With One Generic Type</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Point</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">y</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">to_tuple</span><span class="o">&lt;</span><span class="n">T</span><span class="o">&gt;</span><span class="p">(</span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">T</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="p">(</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="mi">1</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// a: Point&lt;i32&gt; +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">to_tuple</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">y</span><span class="p">);</span><span class="w"> </span><span class="c1">// (i32, i32) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{b:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// (0, 1) +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="nc">false</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">true</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// a: Point&lt;bool&gt; +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">to_tuple</span><span class="p">(</span><span class="n">c</span><span class="p">.</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">c</span><span class="p">.</span><span class="n">y</span><span class="p">);</span><span class="w"> </span><span class="c1">// (bool, bool) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{d:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// (false, true) +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="with-multiple-generic-types">With Multiple Generic Types</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Point</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">U</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">y</span>: <span class="nc">U</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">to_shuffled_tuple</span><span class="o">&lt;</span><span class="n">T</span><span class="p">,</span><span class="w"> </span><span class="n">U</span><span class="o">&gt;</span><span class="p">(</span><span class="n">x</span>: <span class="nc">T</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">U</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="p">(</span><span class="n">U</span><span class="p">,</span><span class="w"> </span><span class="n">T</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">(</span><span class="n">y</span><span class="p">,</span><span class="w"> </span><span class="n">x</span><span class="p">)</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="mi">1</span><span class="k">u8</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="nc">true</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// a: Point&lt;u8, bool&gt; +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">to_shuffled_tuple</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">x</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">y</span><span class="p">);</span><span class="w"> </span><span class="c1">// (bool, u8) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{b:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// (true, 1) +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>On some occasions, the compiler cannot inter the type, and we have to specify the type when using the generic type. By the way, it&rsquo;s good practice to specify the type on variables when using a generic implementation.</p>Hello Worldhttps://learning-rust.github.io/docs/hello-world/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/hello-world/<h2 id="hello-world">Hello, World!</h2> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Hello, world!&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p><code>fn</code> means function. The <code>main</code> function is the beginning of every Rust program. +<code>println!()</code> prints text to the console and its <code>!</code> indicates that it’s a <a href="https://doc.rust-lang.org/book/ch19-06-macros.html" target="_blank" >macro</a> rather than a function.</p> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>Rust files should have <code>.rs</code> file extension and if you’re using more than one word for the file name, follow the <a href="https://en.wikipedia.org/wiki/Snake_case" target="_blank" >snake_case</a> convention.</p>Implshttps://learning-rust.github.io/docs/impls/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/impls/<ul> +<li>Earlier, we discussed that structs and enums group related data, while impl blocks and traits add associated and shared behavior to the data.</li> +<li>Usage of <code>Self</code> vs <code>self</code> keywords: +<ul> +<li> +<p><code>Self</code>: Refers to the type itself (the blueprint).</p> +</li> +<li> +<p><code>self</code>: Refers to the instance of the type (the actual data).</p> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>This can be any form of <code>self</code>, <code>&amp;self</code>, <code>&amp;mut self</code>, <code>self: Box&lt;Self&gt;</code>, <code>self: Pin&lt;&amp;mut Self&gt;</code>, etc.</p>Installationhttps://learning-rust.github.io/docs/installation/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/installation/<h2 id="rustup">Rustup</h2> +<p>There are many ways to install Rust on your system. For the moment the official way to install Rust is using <a href="https://rustup.rs/" target="_blank" >Rustup</a>.</p> +<p><a href="https://rust-lang.github.io/rustup/index.html" target="_blank" >📖</a> Rustup installs The Rust Programming Language from the official release channels, enabling you to easily switch between <strong>stable, beta, and nightly</strong> compilers and keep them updated. It also makes cross-compiling simpler with binary builds of the standard library for common platforms.</p> +<p><a href="https://rust-lang.github.io/rustup/installation/index.html" target="_blank" >📖</a> Rustup installs <code>rustc</code>, <code>cargo</code>, <code>rustup</code> and other standard tools to <strong>Cargo&rsquo;s <code>bin</code> directory</strong>. On Unix it is located at <code>$HOME/.cargo/bin</code> and on Windows at <code>%USERPROFILE%\.cargo\bin</code>. This is the same directory that <code>cargo install</code> will install Rust programs and Cargo plugins.</p>Lifetimeshttps://learning-rust.github.io/docs/lifetimes/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/lifetimes/<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <ul> +<li>Ownership: Each piece of data has a single owner, and data is only scoped to its owner (unless it is borrowed).</li> +<li>In Rust, borrowing is the act of creating a reference to a value without copying the data and without taking ownership (move). While a reference exists, the original owner retains ownership, but its access to the data is restricted (or completely locked if <code>&amp;mut</code>) until that reference’s last use.</li> +<li>Liveness: +<ul> +<li>Owned values (<code>T</code>) are dropped at the end of their scope (unless moved early).</li> +<li>References (<code>&amp;T</code>, <code>&amp;mut T</code>) effectively end/ expire at their last use. This is known as Non-Lexical Lifetimes (NLL).</li> +</ul> +</li> +<li>Lifetimes are Rust&rsquo;s way of guaranteeing that a reference is valid within a discrete region of code at compile time.</li> +</ul> + </div> +</div><h2 id="lifetimes">Lifetimes</h2> +<ul> +<li>A lifetime is a construct the Rust compiler&rsquo;s borrow checker uses to track how long references remain valid and every reference in Rust has a lifetime.</li> +<li>However, most local lifetimes are implicitly figured out by the compiler through a mechanism called lifetime elision.</li> +<li>The primary purpose of lifetimes is to prevent dangling references, ensuring data is never dropped while a pointer still looks at it.</li> +</ul> +<p>Annotating a lifetime does not extend the lifespan of an actual value. It is simply a contract that tells the compiler, &ldquo;The lifespan of this reference is guaranteed to be tied to the lifespan of this data.&rdquo;</p>Moduleshttps://learning-rust.github.io/docs/modules/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/modules/<h2 id="rust-module-tree">Rust Module Tree</h2> +<ul> +<li>The Rust module tree is the hierarchical representation of all modules in a crate.</li> +<li>It defines the namespace structure and governs how items are referenced via paths at compile time.</li> +<li>Unlike some languages like Go and JavaScript, Rust&rsquo;s module tree is not purely based on the file system. +<ul> +<li> +<p>The root of the module tree begins with either <code>main.rs</code> (in a binary crate) or <code>lib.rs</code> (in a library crate).</p>Operatorshttps://learning-rust.github.io/docs/operators/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/operators/<h2 id="arithmetic">Arithmetic</h2> +<p><code>+ - * / %</code></p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="c1">// 6 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> </span><span class="c1">// 4 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="c1">// 10 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="c1">// ⭐️ 2 not 2.5 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="c1">// 1 +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mf">5.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mf">2.0</span><span class="p">;</span><span class="w"> </span><span class="c1">// 2.5 +</span></span></span></code></pre></div><h2 id="comparison">Comparison</h2> +<p><code>== != &lt; &gt; &lt;= &gt;=</code></p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"> </span><span class="c1">// false +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">b</span><span class="p">;</span><span class="w"> </span><span class="c1">// false +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">&lt;=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// 🔎 +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">j</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">&#39;a&#39;</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="sc">&#39;A&#39;</span><span class="p">;</span><span class="w"> </span><span class="c1">// true +</span></span></span></code></pre></div><h2 id="logical">Logical</h2> +<p><code>! &amp;&amp; ||</code></p>Option and Resulthttps://learning-rust.github.io/docs/option-and-result/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/option-and-result/<h2 id="why-option-and-result">Why Option and Result?</h2> +<p>Many languages use <strong><code>null</code>\ <code>nil</code>\ <code>undefined</code> types</strong> to represent empty outputs, and <strong><code>Exceptions</code></strong> to handle errors. Rust skips using both, especially to prevent issues like <strong>null pointer exceptions, sensitive data leakages through exceptions</strong>, etc. Instead, Rust provides two special <strong>generic enums</strong>;<code>Option</code> and <code>Result</code> to deal with above cases.</p> + + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <p>In the previous sections, we have discussed about the basics of <a href="https://learning-rust.github.io/docs/enums" >enums</a>, <a href="https://learning-rust.github.io/docs/generics" >generics</a> and <a href="https://learning-rust.github.io/docs/generics/#generalizing-enums" ><code>Result</code> &amp; <code>Option</code> types</a>.</p>Overviewhttps://learning-rust.github.io/docs/overview/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/overview/<h2 id="about-me">About me</h2> +<blockquote> +<p>🧑‍💻 I am an expat working in Singapore as a Go Backend and DevOps Engineer. Feel free to reach out if you find any mistakes or anything that needs to be changed, including spelling or grammar errors. Alternatively, you can create a pull request, open an issue, or <a href="https://gist.github.com/dumindu/00a0be2d175ed5ff3bc3c17bbf1ca5b6" target="_blank" >share your awesome ideas in this gist</a>. Good luck with learning Rust!</p> +</blockquote> +<p><a href="https://github.com/learning-rust/learning-rust.github.io" target="_blank" ><img src="https://img.shields.io/github/stars/learning-rust/learning-rust.github.io?style=for-the-badge&amp;logo=rust&amp;label=learning-rust.github.io&amp;logoColor=333333&amp;labelColor=f9f9f9&amp;color=F46623" alt="learning-rust.github.io"></a> +<a href="https://learning-cloud-native-go.github.io" target="_blank" ><img src="https://img.shields.io/github/stars/learning-cloud-native-go/learning-cloud-native-go.github.io?style=for-the-badge&amp;logo=go&amp;logoColor=333333&amp;label=learning-cloud-native-go.github.io&amp;labelColor=f9f9f9&amp;color=00ADD8" alt="learning-cloud-native-go.github.io"></a></p> +<p><a href="https://github.com/dumindu" target="_blank" ><img src="https://img.shields.io/badge/dumindu-866ee7?style=for-the-badge&amp;logo=GitHub&amp;logoColor=333333&amp;labelColor=f9f9f9" alt="github.com"></a> +<a href="https://www.buymeacoffee.com/dumindu" target="_blank" ><img src="https://img.shields.io/badge/Buy%20me%20a%20coffee-dumindu-FFDD00?style=for-the-badge&amp;logo=buymeacoffee&amp;logoColor=333333&amp;labelColor=f9f9f9" alt="buymeacoffee"></a></p> +<h2 id="overview">Overview</h2> +<p>This publication has its origins in the posts I authored on Medium at <a href="https://medium.com/learning-rust" target="_blank" >https://medium.com/learning-rust</a>. However, please note that I have ceased updating the Medium posts. All current and future updates, new content, code, and grammar fixes will be exclusively maintained and released here, <a href="https://learning-rust.github.io" target="_blank" >https://learning-rust.github.io</a>.</p>Ownershiphttps://learning-rust.github.io/docs/ownership/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/ownership/<p>We discussed in the <a href="https://learning-rust.github.io/docs/traits/#derive-traits" >Derive Traits</a>, the usage of <a href="https://doc.rust-lang.org/std/marker/trait.Copy.html" target="_blank" ><code>Copy</code> marker trait</a> and <a href="https://doc.rust-lang.org/std/clone/index.html" target="_blank" ><code>.clone()</code></a> with the below code.</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="cp">#[derive(Debug, Clone, Copy)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">x</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">y</span>: <span class="kt">i32</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="mi">1</span><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;</span><span class="si">{a:?}</span><span class="s">, </span><span class="si">{b:?}</span><span class="s">&#34;</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>If we try to remove <code>Copy</code> derive on <code>Point</code> and run, we will get the following error while compiling.</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="n">error</span><span class="p">[</span><span class="n">E0382</span><span class="p">]</span>: <span class="nc">borrow</span><span class="w"> </span><span class="n">of</span><span class="w"> </span><span class="n">moved</span><span class="w"> </span><span class="n">value</span>: <span class="err">`</span><span class="n">a</span><span class="err">`</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">-</span>-&gt; <span class="nc">src</span><span class="o">/</span><span class="n">main</span><span class="p">.</span><span class="n">rs</span>:<span class="mi">11</span>:<span class="mi">16</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">|</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="mi">8</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Point</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">x</span>: <span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">y</span>: <span class="mi">1</span><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="k">move</span><span class="w"> </span><span class="n">occurs</span><span class="w"> </span><span class="n">because</span><span class="w"> </span><span class="err">`</span><span class="n">a</span><span class="err">`</span><span class="w"> </span><span class="n">has</span><span class="w"> </span><span class="k">type</span> <span class="err">`</span><span class="n">Point</span><span class="err">`</span><span class="p">,</span><span class="w"> </span><span class="n">which</span><span class="w"> </span><span class="n">does</span><span class="w"> </span><span class="n">not</span><span class="w"> </span><span class="n">implement</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="err">`</span><span class="nb">Copy</span><span class="err">`</span><span class="w"> </span><span class="k">trait</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="mi">9</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">value</span><span class="w"> </span><span class="n">moved</span><span class="w"> </span><span class="n">here</span></span></span></code></pre></div><p>This is because of Ownership, which is used to achieve Rust&rsquo;s memory safety.</p>Panickinghttps://learning-rust.github.io/docs/panicking/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/panicking/<h2 id="panic">panic!()</h2> +<ul> +<li>In some cases, when an error occurs we can not do anything to handle it, <strong>if the error is something which should not have happened</strong>. In other words, if it’s an <strong>unrecoverable error</strong>.</li> +<li>Also <strong>when we are not using a feature-rich debugger or proper logs</strong>, sometimes we need to <strong>debug the code by quitting the program from a specific line of code</strong> by printing out a specific message or a value of a variable binding to understand the current flow of the program. +For above cases, we can use <code>panic!</code> macro.</li> +</ul> +<p>⭐ <code>panic!()</code> runs <strong>thread based</strong>. One thread can be panicked, while other threads are running.</p>Primitive Data Typeshttps://learning-rust.github.io/docs/primitive-data-types/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/primitive-data-types/<h2 id="bool">bool</h2> +<p>true or false</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">true</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="kt">bool</span> <span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// ⭐️ no TRUE, FALSE, 1, 0 +</span></span></span></code></pre></div><p>bool is a single byte(8 bits) in size.</p> +<h2 id="char">char</h2> +<p>A single Unicode scalar value</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="sc">&#39;x&#39;</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="kt">char</span> <span class="o">=</span><span class="w"> </span><span class="sc">&#39;😎&#39;</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// ⭐️ no &#34;x&#34;, only single quotes +</span></span></span></code></pre></div><p>Because of Unicode support, char is not a single byte, but four(32 bits).</p> +<h2 id="i8-i16-i32-i64-i128">i8, i16, i32, i64, i128</h2> +<p>8, 16, 32, 64 and 128 bit fixed sized signed(+/-) integer types</p>Smart Compilerhttps://learning-rust.github.io/docs/smart-compiler/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/smart-compiler/<div class="alert alert-caution"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M480-300q15 0 25.5-10.5T516-336q0-15-10.5-25.5T480-372q-15 0-25.5 10.5T444-336q0 15 10.5 25.5T480-300Zm25.5-142.5Q516-453 516-468v-168q0-15-10.5-25.5T480-672q-15 0-25.5 10.5T444-636v168q0 15 10.5 25.5T480-432q15 0 25.5-10.5ZM371-144q-14 0-27-5t-24-16L165-321q-10-10-15.5-23.5T144-372v-217q0-14 5-27t16-24l155-155q11-11 24-16t27-5h218q14 0 27 5t24 16l155 155q11 11 16 24t5 27v218q0 14-5 27t-16 24L639-165q-10 10-23.5 15.5T588-144H371Zm0-72h218l155-155v-218L588-744H371L216-589v218l155 155Zm109-264Z"/></svg> + + + <span> + + Caution + + </span> + </div> + <div class="alert-body"> + <p>This needs a refresh!</p> + </div> +</div><h2 id="why-compiler">Why Compiler?</h2> +<p>The Rust compiler does the most significant job to prevent errors in Rust programs. It <strong>analyzes the code at compile-time</strong> and issues warnings, if the code does not follow memory management rules or lifetime annotations correctly.</p>STD, Primitives and Preludeshttps://learning-rust.github.io/docs/std-primitives-and-preludes/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/std-primitives-and-preludes/<div class="alert alert-caution"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M480-300q15 0 25.5-10.5T516-336q0-15-10.5-25.5T480-372q-15 0-25.5 10.5T444-336q0 15 10.5 25.5T480-300Zm25.5-142.5Q516-453 516-468v-168q0-15-10.5-25.5T480-672q-15 0-25.5 10.5T444-636v168q0 15 10.5 25.5T480-432q15 0 25.5-10.5ZM371-144q-14 0-27-5t-24-16L165-321q-10-10-15.5-23.5T144-372v-217q0-14 5-27t16-24l155-155q11-11 24-16t27-5h218q14 0 27 5t24 16l155 155q11 11 16 24t5 27v218q0 14-5 27t-16 24L639-165q-10 10-23.5 15.5T588-144H371Zm0-72h218l155-155v-218L588-744H371L216-589v218l155 155Zm109-264Z"/></svg> + + + <span> + + Caution + + </span> + </div> + <div class="alert-body"> + <p>This needs a refresh!</p> + </div> +</div><p>⭐️ In Rust, language elements are implemented by not only <strong><code>std</code> library</strong> crate but also <strong>compiler</strong> as well. Examples,</p> +<ul> +<li><strong><a href="https://doc.rust-lang.org/std/#primitives" target="_blank" >Primitives</a></strong>: Defined by the compiler and methods are implemented by <code>std</code> library directly on primitives.</li> +<li><strong><a href="https://doc.rust-lang.org/std/#macros" target="_blank" >Standard Macros</a></strong>: Defined by both compiler and <code>std</code></li> +</ul> +<p>The <strong><code>std</code></strong> library has been divided into <strong><a href="https://doc.rust-lang.org/std/#modules" target="_blank" >modules</a></strong>, according to the main areas each covered.</p>Structshttps://learning-rust.github.io/docs/structs/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/structs/<ul> +<li>Used to <strong>encapsulate related properties into one unified data type</strong>.</li> +<li>By convention, the name should follow <a href="https://en.wikipedia.org/wiki/Camel_case" target="_blank" ><code>PascalCase</code></a>.</li> +<li>3 variants, +<ul> +<li> +<p>C-like structs: One or more <code>,</code> separated <code>name: value pairs</code> enclosed in <code>{}</code></p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">red</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">green</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></li> +<li> +<p>Tuple structs: One or more <code>,</code> separated <code>values</code> enclosed in <code>()</code></p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Color</span><span class="p">(</span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"> </span><span class="kt">u8</span><span class="p">);</span></span></span></code></pre></div></li> +<li> +<p>Unit structs: A struct with no fields/ members</p> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Black</span><span class="p">;</span></span></span></code></pre></div></li> +</ul> +</li> +</ul> + + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <ul> +<li>In Rust, data (attributes) and behavior (associated functions and methods) are placed separately. Structs and Enums are used to group related data, and impls and traits are used to add associated and shared behavior to that data.</li> +<li>In Rust, the term &ldquo;instantiation&rdquo; is used to describe the act of creating a concrete instance of a type (struct or enum).</li> +<li>In Rust, the term &ldquo;field&rdquo; is used to describe a named component in a C-like struct &amp; struct-like enum variant, and the term &ldquo;element&rdquo; is used to describe an unnamed component in a tuple struct &amp; tuple-like enum variant. The term &ldquo;member&rdquo; is used to describe both.</li> +<li>More complex examples can be found on <a href="https://learning-rust.github.io/docs/impls-and-traits" >Impls and Traits</a>, <a href="https://learning-rust.github.io/docs/lifetimes" >Lifetimes</a> and <a href="https://learning-rust.github.io/docs/modules" >Modules</a> sections.</li> +</ul> + </div> +</div><h2 id="c-like-structs">C-like Structs</h2> +<ul> +<li>Similar to classes (without its methods) in OOP languages.</li> +<li>Can access fields using the <code>.</code>/ dot notation and the field name.</li> +</ul> +<h3 id="definition">Definition</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">red</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">green</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div><h3 id="instantiation--accessing-fields">Instantiation &amp; Accessing Fields</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">red</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">green</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span>: <span class="kt">u8</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 1. Instantiation +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">white</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">red</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">green</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 2. Instantiation without redundant field names, when using the same variable names +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="p">};</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="c1">// 3. Instantiation + copy fields&#39; values from another instance +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">red</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">red</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// 💡 Copy green and blue from black +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">green</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">green</span>: <span class="mi">255</span><span class="p">,</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// 💡 Copy red and blue from black +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">blue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Color</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="o">..</span><span class="w"> </span><span class="n">black</span><span class="w"> </span><span class="p">};</span><span class="w"> </span><span class="c1">// 💡 Copy all fields&#39; values from black +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">blue</span><span class="p">.</span><span class="n">blue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">255</span><span class="p">;</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">white</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">white</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">white</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(255, 255, 255) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">black</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">black</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">black</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(0, 0, 0) +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">red</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">red</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">red</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(255, 0, 0) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">green</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(0, 255, 0) +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;RGB(</span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">, </span><span class="si">{}</span><span class="s">)&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="p">.</span><span class="n">red</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="p">.</span><span class="n">green</span><span class="p">,</span><span class="w"> </span><span class="n">blue</span><span class="p">.</span><span class="n">blue</span><span class="p">);</span><span class="w"> </span><span class="c1">// RGB(0, 0, 255) +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="c1">// 4. Instantiation with default values +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="cp">#[derive(Default)]</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">struct</span> <span class="nc">Person</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">name</span>: <span class="nb">String</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="n">age</span>: <span class="kt">f32</span><span class="p">,</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Person</span>::<span class="n">default</span><span class="p">();</span><span class="w"> </span><span class="c1">// Instantiation with default values +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="p">);</span><span class="w"> </span><span class="c1">// String default value &#34;&#34; +</span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="n">age</span><span class="p">,</span><span class="w"> </span><span class="mf">0.0</span><span class="p">);</span><span class="w"> </span><span class="c1">// f32 default value 0.0 +</span></span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div> + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>In Rust, the <code>#[derive()]</code> attribute is used to automatically generate an implementation of certain traits for a custom data structure (struct and enum), instead of you writing them by hand. The <a href="https://doc.rust-lang.org/std/default/trait.Default.html" target="_blank" ><code>std::default::Default</code></a> trait allows us to create a new instance of a type with the <code>Type::default()</code> method.</p>Traitshttps://learning-rust.github.io/docs/traits/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/traits/<p>A trait is a contract that defines a set of behaviors or properties that a type must implement. It can contain associated types, constants, function or method signatures, and overridable default implementations.</p> +<h2 id="definition">Definition</h2> +<h3 id="with-no-associates">With No Associates</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="k">pub</span><span class="w"> </span><span class="k">trait</span><span class="w"> </span><span class="nb">Sized</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">}</span></span></span></code></pre></div> + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <p>Mostly used to mark a type as having certain properties to allow in certain operations. Known as Marker Traits.</p>Unwrap and Expecthttps://learning-rust.github.io/docs/unwrap-and-expect/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/unwrap-and-expect/<h2 id="unwrap">unwrap()</h2> +<ul> +<li>If an <code>Option</code> type has <strong><code>Some</code></strong> value or a <code>Result</code> type has a <strong><code>Ok</code></strong> value, <strong>the value inside them</strong> passes to the next step.</li> +<li>If the <code>Option</code> type has <strong><code>None</code></strong> value or the <code>Result</code> type has <strong><code>Err</code></strong> value, <strong>program panics</strong>; If <code>Err</code>, panics with the error message.</li> +</ul> +<p>The functionality is bit similar to the following codes, which are using <code>match</code> instead <code>unwrap()</code>.</p> +<p>Example with <code>Option</code> and <code>match</code>, before using <code>unwrap()</code></p>Usehttps://learning-rust.github.io/docs/use/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/use/<ul> +<li>A <code>use</code> statement is used to bring items (types, functions, traits, modules, etc.) into the current scope. +<ul> +<li>It doesn&rsquo;t import anything; instead, it creates a local name (an alias) in the current scope.</li> +</ul> +</li> +<li>This allows us: +<ul> +<li>to refer to items by shorter names instead of their full paths.</li> +<li>to re-export items (by publicly exposing items from another module or crate using <code>pub use</code>).</li> +</ul> +</li> +</ul> +<h2 id="path-prefixes">Path Prefixes</h2> +<p>A <code>use</code> statement can start from:</p>Variable bindings, Constants & Staticshttps://learning-rust.github.io/docs/variable-bindings-constants-and-statics/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/variable-bindings-constants-and-statics/<ul> +<li>Rust is a statically typed language; it checks data types at compile-time. But it doesn’t require you to actually type data types when declaring variable bindings. In that case, the compiler checks the usage and sets a better data type for it.</li> +<li>⭐️ For <strong>constants and statics, we must annotate the data type</strong>.</li> +<li>Types come after a <code>:</code> (colon) sign.</li> +<li>The naming convention for the variable bindings is using the <a href="https://en.wikipedia.org/wiki/Snake_case" target="_blank" ><code>snake_case</code></a>. But, for constants and statics, we should follow the <a href="https://en.wikipedia.org/wiki/Snake_case" target="_blank" ><code>SCREAMING_SNAKE_CASE</code></a>.</li> +</ul> + + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <p>In the following examples, we will use <a href="https://learning-rust.github.io/docs/primitive-data-types" >data types</a> like <code>bool</code>, <code>i32</code>, <code>i64</code> and <code>f64</code>. Don&rsquo;t worry about them for now; they&rsquo;ll be discussed later.</p>Vectorshttps://learning-rust.github.io/docs/vectors/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/vectors/<p>If you remember, the array is a fixed-size list of elements, of the same data type. Even with mut, its element count cannot be changed. A vector is <strong>kind of a re-sizable array</strong> but <strong>all elements must be in the same type</strong>.</p> + + +<div class="alert alert-tip"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="M407.74-240Q378-240 357-261.15 336-282.3 336-312v-67q-57-37.3-88.5-95.65Q216-533 216-600q0-110.31 76.78-187.16 76.78-76.84 187-76.84T667-787.16q77 76.85 77 187.16 0 66.82-31.5 125.41T624-379v67q0 29.7-21.18 50.85Q581.65-240 551.91-240H407.74Zm.26-72h144v-86q0-9.29 4-17.64 4-8.36 12-12.92l17-10.93q41-25.51 64-68.69 23-43.18 23-91.82 0-79.68-56.23-135.84-56.22-56.16-136-56.16Q400-792 344-735.84 288-679.68 288-600q0 48.64 23 91.82 23 43.18 64 68.69l17 10.93q8 4.56 12 12.92 4 8.35 4 17.64v86ZM396-96q-15.3 0-25.65-10.29Q360-116.58 360-131.79t10.35-25.71Q380.7-168 396-168h168q15.3 0 25.65 10.29Q600-147.42 600-132.21t-10.35 25.71Q579.3-96 564-96H396Zm84-504Z"/></svg> + + + <span> + + Tip + + </span> + </div> + <div class="alert-body"> + <ul> +<li><code>Vec&lt;T&gt;</code>: capital “V” as <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html" target="_blank" >it’s a struct</a>.</li> +<li>It’s a generic type, written as <strong><code>Vec&lt;T&gt;</code></strong>. T can have any type, ex. A vector of i32s is <code>Vec&lt;i32&gt;</code>. Also, Vectors always allocate their data in a dynamically allocated heap.</li> +</ul> + </div> +</div><h2 id="creation">Creation</h2> +<h3 id="empty-vector">Empty Vector</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"> </span><span class="c1">// 1. With new() keyword +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[];</span><span class="w"> </span><span class="c1">// 2. Using the vec! macro (💡 usually create with values same time) +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="c1">// ⭐️ If you need an immutable empty vector, you must have to specify the data type. +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="nb">String</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[];</span></span></span></code></pre></div><h3 id="with-type-annotations">With Type Annotations</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="k">i32</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> </span><span class="c1">// Suffixing 1st value with data type +</span></span></span></code></pre></div><h3 id="with-values">With Values</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">1</span><span class="k">i32</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">3</span><span class="p">];</span><span class="w"> +</span></span></span></code></pre></div> + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="mi">10</span><span class="p">];</span><span class="w"> </span><span class="c1">// Ten zeroes +</span></span></span><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="fm">vec!</span><span class="p">[</span><span class="s">&#34;&#34;</span><span class="p">;</span><span class="w"> </span><span class="mi">10</span><span class="p">];</span><span class="w"> </span><span class="c1">// Ten &#34;&#34; str +</span></span></span></code></pre></div><h3 id="with-a-capacity">With a Capacity</h3> + + + +<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rust" data-lang="rust"><span class="line"><span class="cl"><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">a</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="kt">i32</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">with_capacity</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span><span class="w"> +</span></span></span><span class="line"><span class="cl"><span class="fm">println!</span><span class="p">(</span><span class="s">&#34;Length: </span><span class="si">{}</span><span class="s">, Capacity : </span><span class="si">{}</span><span class="s">&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">len</span><span class="p">(),</span><span class="w"> </span><span class="n">a</span><span class="p">.</span><span class="n">capacity</span><span class="p">());</span><span class="w"> </span><span class="c1">// Length: 0, Capacity : 10 +</span></span></span></code></pre></div> + +<div class="alert alert-recap"> + <div class="alert-header"> + + + + <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px"><path d="m687-273-78-160 95-95-79.77-79.77v-112.46H511.77L432-800l-79.77 79.77H239.77v112.46L160-528l79.77 79.77v112.46h112.46L432-256l95-95 160 78Zm104 104q-8 8-19 10.5t-22-3.5L541-264l-84 84q-10.64 11-24.82 11T407-180l-84.26-84H204q-15.3 0-25.65-10.35Q168-284.7 168-300v-118.74L84-503q-11-10.64-11-24.82T84-553l84-84.26V-756q0-15.3 10.35-25.65Q188.7-792 204-792h118.74L407-876q10.64-11 24.82-11T457-876l84.26 84H660q15.3 0 25.65 10.35Q696-771.3 696-756v118.74L780-553q11 10.64 11 24.82T780-503l-84 84 102 209q6 11 3.5 22T791-169ZM432-528Z"/></svg> + + + <span> + + Recap + + </span> + </div> + <div class="alert-body"> + <p>We&rsquo;ll discuss this in the <a href="https://learning-rust.github.io/docs/vectors/#length-and-capacity" >Length and Capacity</a>.</p>Why Rust?https://learning-rust.github.io/docs/why-rust/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/why-rust/<h2 id="history-of-rust">History of Rust</h2> +<p>Rust was initially designed and developed by former Mozilla employee <strong><a href="https://github.com/graydon" target="_blank" >Graydon Hoare</a></strong> as a personal project. Mozilla began sponsoring the project in 2009 and announced it in 2010. But the first stable release, Rust 1.0, was released on May 15, 2015.</p> +<p>Since Rust 1.0, major updates have been released as <a href="https://learning-rust.github.io/docs/cargo-crates-and-basic-project-structure/#rust-editions" ><code>Editions</code></a> approximately every three years: Rust 2015 (with the release of Rust 1.0) , Rust 2018, Rust 2021, and Rust 2024, all maintaining backward compatibility.</p>Workspaceshttps://learning-rust.github.io/docs/workspaces/Mon, 01 Jan 0001 00:00:00 +0000https://learning-rust.github.io/docs/workspaces/<h2 id="rust-workspaces">Rust Workspaces</h2> +<ul> +<li>Rust workspaces provide a convenient way to manage multiple related crates together as a single project or monorepo.</li> +<li>A Rust workspace uses a shared <code>Cargo.lock</code> file and a common <code>target</code> directory to efficiently build and manage shared dependencies, reducing build times.</li> +<li>Cargo commands such as <code>cargo test</code>, <code>cargo build</code>, <code>cargo check</code>, <code>cargo fmt</code>, <code>cargo clippy</code>, and <code>cargo clean</code> can be run once from the workspace root to apply to all workspace members.</li> +</ul> +<h2 id="simple">Simple</h2> +<p>Let&rsquo;s build a sample Rust workspace with two simple crates.</p> \ No newline at end of file diff --git a/docs/logo.svg b/docs/logo.svg new file mode 100644 index 00000000..cf3256de --- /dev/null +++ b/docs/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/manifest.json b/docs/manifest.json new file mode 100644 index 00000000..87493f4f --- /dev/null +++ b/docs/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "Learning Rust", + "short_name": "Learning Rust", + "description": "Rust Programming Language Tutorials for Everyone!", + "start_url": "/?source=pwa", + "display": "standalone", + "icons": [ + { + "src": "/favicon/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/favicon/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "background_color": "#866ee7", + "theme_color": "#866ee7" +} \ No newline at end of file diff --git a/docs/og.jpg b/docs/og.jpg new file mode 100644 index 00000000..a7e65376 Binary files /dev/null and b/docs/og.jpg differ diff --git a/docs/pagefind/fragment/en-us_037b612.pf_fragment b/docs/pagefind/fragment/en-us_037b612.pf_fragment new file mode 100644 index 00000000..949250e2 Binary files /dev/null and b/docs/pagefind/fragment/en-us_037b612.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_0bd509a.pf_fragment b/docs/pagefind/fragment/en-us_0bd509a.pf_fragment new file mode 100644 index 00000000..38450998 Binary files /dev/null and b/docs/pagefind/fragment/en-us_0bd509a.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_15da8a6.pf_fragment b/docs/pagefind/fragment/en-us_15da8a6.pf_fragment new file mode 100644 index 00000000..3f33de8b Binary files /dev/null and b/docs/pagefind/fragment/en-us_15da8a6.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_18ff739.pf_fragment b/docs/pagefind/fragment/en-us_18ff739.pf_fragment new file mode 100644 index 00000000..8049dacb Binary files /dev/null and b/docs/pagefind/fragment/en-us_18ff739.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_1cb75c4.pf_fragment b/docs/pagefind/fragment/en-us_1cb75c4.pf_fragment new file mode 100644 index 00000000..3cbe958b Binary files /dev/null and b/docs/pagefind/fragment/en-us_1cb75c4.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_24492d9.pf_fragment b/docs/pagefind/fragment/en-us_24492d9.pf_fragment new file mode 100644 index 00000000..f91a0ac2 Binary files /dev/null and b/docs/pagefind/fragment/en-us_24492d9.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_2a6ed42.pf_fragment b/docs/pagefind/fragment/en-us_2a6ed42.pf_fragment new file mode 100644 index 00000000..ad792734 Binary files /dev/null and b/docs/pagefind/fragment/en-us_2a6ed42.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_2acd441.pf_fragment b/docs/pagefind/fragment/en-us_2acd441.pf_fragment new file mode 100644 index 00000000..6ee774c1 Binary files /dev/null and b/docs/pagefind/fragment/en-us_2acd441.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_36c953b.pf_fragment b/docs/pagefind/fragment/en-us_36c953b.pf_fragment new file mode 100644 index 00000000..7a74ca9b Binary files /dev/null and b/docs/pagefind/fragment/en-us_36c953b.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_41f479a.pf_fragment b/docs/pagefind/fragment/en-us_41f479a.pf_fragment new file mode 100644 index 00000000..732f58e3 Binary files /dev/null and b/docs/pagefind/fragment/en-us_41f479a.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_4391b10.pf_fragment b/docs/pagefind/fragment/en-us_4391b10.pf_fragment new file mode 100644 index 00000000..64976abd Binary files /dev/null and b/docs/pagefind/fragment/en-us_4391b10.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_4be8a3c.pf_fragment b/docs/pagefind/fragment/en-us_4be8a3c.pf_fragment new file mode 100644 index 00000000..4a2de991 Binary files /dev/null and b/docs/pagefind/fragment/en-us_4be8a3c.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_5e87847.pf_fragment b/docs/pagefind/fragment/en-us_5e87847.pf_fragment new file mode 100644 index 00000000..88f8a715 Binary files /dev/null and b/docs/pagefind/fragment/en-us_5e87847.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_65bce44.pf_fragment b/docs/pagefind/fragment/en-us_65bce44.pf_fragment new file mode 100644 index 00000000..b401bde6 Binary files /dev/null and b/docs/pagefind/fragment/en-us_65bce44.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_6845397.pf_fragment b/docs/pagefind/fragment/en-us_6845397.pf_fragment new file mode 100644 index 00000000..3075e625 Binary files /dev/null and b/docs/pagefind/fragment/en-us_6845397.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_6fa9fe3.pf_fragment b/docs/pagefind/fragment/en-us_6fa9fe3.pf_fragment new file mode 100644 index 00000000..61661478 Binary files /dev/null and b/docs/pagefind/fragment/en-us_6fa9fe3.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_73301c8.pf_fragment b/docs/pagefind/fragment/en-us_73301c8.pf_fragment new file mode 100644 index 00000000..9f35a580 Binary files /dev/null and b/docs/pagefind/fragment/en-us_73301c8.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_7f35bdd.pf_fragment b/docs/pagefind/fragment/en-us_7f35bdd.pf_fragment new file mode 100644 index 00000000..65cb2394 Binary files /dev/null and b/docs/pagefind/fragment/en-us_7f35bdd.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_9280467.pf_fragment b/docs/pagefind/fragment/en-us_9280467.pf_fragment new file mode 100644 index 00000000..879b176a Binary files /dev/null and b/docs/pagefind/fragment/en-us_9280467.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_97e370c.pf_fragment b/docs/pagefind/fragment/en-us_97e370c.pf_fragment new file mode 100644 index 00000000..0e01448b Binary files /dev/null and b/docs/pagefind/fragment/en-us_97e370c.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_9ae84ea.pf_fragment b/docs/pagefind/fragment/en-us_9ae84ea.pf_fragment new file mode 100644 index 00000000..5da0d77b Binary files /dev/null and b/docs/pagefind/fragment/en-us_9ae84ea.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_a829b0a.pf_fragment b/docs/pagefind/fragment/en-us_a829b0a.pf_fragment new file mode 100644 index 00000000..cf2e0e10 Binary files /dev/null and b/docs/pagefind/fragment/en-us_a829b0a.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_a89afc7.pf_fragment b/docs/pagefind/fragment/en-us_a89afc7.pf_fragment new file mode 100644 index 00000000..430887d7 Binary files /dev/null and b/docs/pagefind/fragment/en-us_a89afc7.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_adbc73f.pf_fragment b/docs/pagefind/fragment/en-us_adbc73f.pf_fragment new file mode 100644 index 00000000..19934edd Binary files /dev/null and b/docs/pagefind/fragment/en-us_adbc73f.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_c677b46.pf_fragment b/docs/pagefind/fragment/en-us_c677b46.pf_fragment new file mode 100644 index 00000000..766d3240 Binary files /dev/null and b/docs/pagefind/fragment/en-us_c677b46.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_cf73769.pf_fragment b/docs/pagefind/fragment/en-us_cf73769.pf_fragment new file mode 100644 index 00000000..12a18fe5 Binary files /dev/null and b/docs/pagefind/fragment/en-us_cf73769.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_d5f780e.pf_fragment b/docs/pagefind/fragment/en-us_d5f780e.pf_fragment new file mode 100644 index 00000000..5893439f Binary files /dev/null and b/docs/pagefind/fragment/en-us_d5f780e.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_df7278e.pf_fragment b/docs/pagefind/fragment/en-us_df7278e.pf_fragment new file mode 100644 index 00000000..0f4c97f7 Binary files /dev/null and b/docs/pagefind/fragment/en-us_df7278e.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_e01cdcb.pf_fragment b/docs/pagefind/fragment/en-us_e01cdcb.pf_fragment new file mode 100644 index 00000000..5967e2c9 Binary files /dev/null and b/docs/pagefind/fragment/en-us_e01cdcb.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_f5ac963.pf_fragment b/docs/pagefind/fragment/en-us_f5ac963.pf_fragment new file mode 100644 index 00000000..d67a61ad Binary files /dev/null and b/docs/pagefind/fragment/en-us_f5ac963.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_fc3054b.pf_fragment b/docs/pagefind/fragment/en-us_fc3054b.pf_fragment new file mode 100644 index 00000000..cf7cc46b Binary files /dev/null and b/docs/pagefind/fragment/en-us_fc3054b.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_fdaaa5e.pf_fragment b/docs/pagefind/fragment/en-us_fdaaa5e.pf_fragment new file mode 100644 index 00000000..ca666725 Binary files /dev/null and b/docs/pagefind/fragment/en-us_fdaaa5e.pf_fragment differ diff --git a/docs/pagefind/fragment/en-us_ff4cc99.pf_fragment b/docs/pagefind/fragment/en-us_ff4cc99.pf_fragment new file mode 100644 index 00000000..961ee89b Binary files /dev/null and b/docs/pagefind/fragment/en-us_ff4cc99.pf_fragment differ diff --git a/docs/pagefind/index/en-us_30bca64.pf_index b/docs/pagefind/index/en-us_30bca64.pf_index new file mode 100644 index 00000000..25b8bb17 Binary files /dev/null and b/docs/pagefind/index/en-us_30bca64.pf_index differ diff --git a/docs/pagefind/index/en-us_47ea727.pf_index b/docs/pagefind/index/en-us_47ea727.pf_index new file mode 100644 index 00000000..f78ea1c6 Binary files /dev/null and b/docs/pagefind/index/en-us_47ea727.pf_index differ diff --git a/docs/pagefind/index/en-us_c636111.pf_index b/docs/pagefind/index/en-us_c636111.pf_index new file mode 100644 index 00000000..1aa19dfe Binary files /dev/null and b/docs/pagefind/index/en-us_c636111.pf_index differ diff --git a/docs/pagefind/pagefind-component-ui.css b/docs/pagefind/pagefind-component-ui.css new file mode 100644 index 00000000..5e9ef59a --- /dev/null +++ b/docs/pagefind/pagefind-component-ui.css @@ -0,0 +1,1509 @@ +/* + * Why all the :is(*, #\#) prefixes? + * + * These components are embedded in who-knows-what sites with unpredictable CSS. + * We need our styles to win against host page selectors like `.content p` or + * `article a:hover` without using !important everywhere. + * + * The :is(*, #\#) trick adds ID-level specificity (0,1,0) without requiring + * an actual ID in the DOM. Chaining it (2x or 3x) builds enough specificity + * to beat most host selectors. + * + * The #\# is an escaped # character, creating an invalid-but-harmless ID + * selector that never matches, but still contributes specificity via :is(). + * + * We also need these overrides to only affect the Component UI elements themselves. + * Users can provide custom templates to the results and searchbox components, + * and these must inherit their styles without having to fight ours. + * + * It's admittedly a bit of a hack but it does provide best effort styling consistency + * for the Component UI out in the wild. + */ + +:root { + --pf-text: #1a1a1a; + --pf-text-secondary: #666; + --pf-text-muted: #767676; + --pf-background: #fff; + --pf-border: #e0e0e0; + --pf-border-focus: #999; + --pf-skeleton: #eee; + --pf-skeleton-shine: #f5f5f5; + --pf-hover: #f5f5f5; + --pf-mark: #1a1a1a; + --pf-scroll-shadow: rgba(0, 0, 0, 0.08); + + --pf-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06); + --pf-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1); + --pf-shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.2); + + --pf-error-bg: #fef2f2; + --pf-error-border: #fecaca; + --pf-error-text: #dc2626; + --pf-error-text-secondary: #b91c1c; + + --pf-outline-focus: #0969da; + --pf-outline-width: 2px; + --pf-outline-offset: 2px; + + --pf-font: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, + sans-serif; + + --pf-input-height: 36px; + --pf-input-font-size: 16px; + --pf-summary-font-size: 12px; + --pf-result-title-font-size: 14px; + --pf-result-excerpt-font-size: 13px; + --pf-modal-backdrop: rgba(0, 0, 0, 0.5); + --pf-results-display: flex; + --pf-results-flex-direction: column; + --pf-results-flex-wrap: nowrap; + --pf-results-columns: none; + --pf-results-gap: 8px; + + --pf-border-radius: 6px; + --pf-image-width: 64px; + --pf-image-height: 48px; + + --pf-icon-search: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A"); + --pf-icon-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%23000' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); + + --pf-dropdown-z-index: 9999; + + --pf-modal-max-width: 560px; + --pf-modal-max-height: min(80dvh, 800px); + --pf-modal-top: 10dvh; + + --pf-searchbox-max-width: 480px; + --pf-searchbox-dropdown-max-height: 320px; + + --pf-dropdown-max-height: 280px; +} + +[data-pf-theme="dark"] { + --pf-text: #e5e5e5; + --pf-text-secondary: #a0a0a0; + --pf-text-muted: #949494; + --pf-background: #1a1a1a; + --pf-border: #333; + --pf-border-focus: #555; + --pf-skeleton: #2a2a2a; + --pf-skeleton-shine: #333; + --pf-hover: #252525; + --pf-mark: #e5e5e5; + --pf-scroll-shadow: rgba(255, 255, 255, 0.1); + + --pf-outline-focus: #58a6ff; + + --pf-shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.3); + --pf-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4); + --pf-shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.5); + + --pf-error-bg: #2a1a1a; + --pf-error-border: #5c2828; + --pf-error-text: #f87171; + --pf-error-text-secondary: #ef4444; + + --pf-modal-backdrop: rgba(0, 0, 0, 0.7); +} + +pagefind-config, +pagefind-filter-dropdown, +pagefind-filter-pane, +pagefind-input, +pagefind-keyboard-hints, +pagefind-modal, +pagefind-modal-body, +pagefind-modal-footer, +pagefind-modal-header, +pagefind-modal-trigger, +pagefind-results, +pagefind-searchbox, +pagefind-summary { + all: initial; + display: block; + box-sizing: border-box; + + /* Typography baseline */ + font-family: + var(--pf-font, + system-ui, + -apple-system, + BlinkMacSystemFont, + "Segoe UI", + Roboto, + sans-serif); + font-size: 16px; + line-height: 1.5; + color: CanvasText; + contain: layout style; +} + +pagefind-searchbox, +pagefind-filter-dropdown { + position: relative; +} + +pagefind-searchbox:has(.pf-searchbox.open), +pagefind-filter-dropdown:has(.pf-dropdown-trigger.open) { + z-index: var(--pf-dropdown-z-index); +} + +pagefind-config { + display: none; +} + +pagefind-modal-trigger { + display: inline-block; +} + +:is(*, #\#):is(*, #\#) :is([class^="pf-"], [class*=" pf-"]):not(svg, svg *) { + all: revert; + box-sizing: border-box; +} + +:is(*, #\#):is(*, #\#) :is([class^="pf-"], [class*=" pf-"]):not(svg, svg *)::before, +:is(*, #\#):is(*, #\#) :is([class^="pf-"], [class*=" pf-"]):not(svg, svg *)::after { + box-sizing: border-box; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) [class^="pf-"][hidden], +:is(*, #\#):is(*, #\#):is(*, #\#) [class*=" pf-"][hidden] { + display: none; +} + +[data-pf-hidden] { + display: none !important; +} + +[data-pf-suppressed] { + opacity: 0 !important; + pointer-events: none !important; +} + +[data-pf-sr-hidden] { + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + height: 1px !important; + overflow: hidden !important; + position: absolute !important; + white-space: nowrap !important; + width: 1px !important; +} + +/* +* Suppress native browser outlines. +* This is only okay because we exhaustively +* provide our own high-contrast custom focus styles. +*/ +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-clear:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-clear:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-link:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-link:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-link:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-link:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-checkbox-input:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-checkbox-input:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-btn:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-btn:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-close:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-close:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-options:focus, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-options:focus-visible { + outline: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-clear:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-clear:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-btn:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-close:focus-visible { + outline: var(--pf-outline-width) solid var(--pf-outline-focus); + outline-offset: var(--pf-outline-offset); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-key, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-key, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-footer-key, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-keyboard-key, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-footer-key { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0 4px; + background: var(--pf-hover); + border: 1px solid var(--pf-border); + border-radius: 3px; + font-weight: 500; + color: var(--pf-text-secondary); + font-family: var(--pf-font); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-key--sm, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-footer-key { + min-width: 16px; + height: 16px; + font-size: 9px; + padding: 0 3px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-wrapper { + position: relative; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-wrapper::before, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input-wrapper::before { + content: ""; + position: absolute; + background-color: var(--pf-text-muted); + width: 14px; + height: 14px; + top: calc((var(--pf-input-height) - 14px) / 2); + inset-inline-start: 10px; + -webkit-mask-image: var(--pf-icon-search); + mask-image: var(--pf-icon-search); + -webkit-mask-size: 100%; + mask-size: 100%; + pointer-events: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input-wrapper::before { + z-index: 1; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input { + width: 100%; + height: var(--pf-input-height); + background-color: var(--pf-background); + border: 1px solid var(--pf-border); + border-radius: var(--pf-border-radius); + font-family: var(--pf-font, inherit); + font-size: var(--pf-input-font-size); + color: var(--pf-text); + box-sizing: border-box; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input { + padding: 0; + padding-inline-start: 32px; + padding-inline-end: 36px; + font-weight: 400; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input:focus-visible, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input:focus-visible { + border-color: var(--pf-outline-focus); + box-shadow: 0 0 0 var(--pf-outline-width) var(--pf-outline-focus); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input::placeholder, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input::placeholder { + color: var(--pf-text-muted); +} + +/* Hide native search clear button - we have our own */ +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input::-webkit-search-decoration, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input::-webkit-search-cancel-button, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input::-webkit-search-results-button, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input::-webkit-search-results-decoration { + display: none; + appearance: none; + -webkit-appearance: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-clear { + position: absolute; + inset-inline-end: 2px; + top: 50%; + transform: translateY(-50%); + height: calc(100% - 4px); + min-width: 44px; + padding: 0 8px; + background: none; + border: none; + font-size: 12px; + color: var(--pf-text-muted); + cursor: pointer; + border-radius: 4px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-clear:hover { + background: var(--pf-hover); + color: var(--pf-text-secondary); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-clear[data-pf-suppressed] { + display: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-summary { + font-size: var(--pf-summary-font-size); + color: var(--pf-text-muted); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-results { + list-style: none; + padding: 0; + margin: 0; + display: var(--pf-results-display); + flex-direction: var(--pf-results-flex-direction); + flex-wrap: var(--pf-results-flex-wrap); + grid-template-columns: var(--pf-results-columns); + gap: var(--pf-results-gap); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result { + display: flex; + flex-direction: column; + gap: 8px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-card { + position: relative; + display: flex; + gap: 12px; + padding: 12px; + background: var(--pf-background); + border: 1px solid var(--pf-border); + border-radius: var(--pf-border-radius); + transition: + border-color 0.15s, + box-shadow 0.15s; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-card:hover { + border-color: var(--pf-border-focus); + box-shadow: var(--pf-shadow-sm); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-result-card:has(.pf-result-link:focus-visible) { + border-color: var(--pf-outline-focus); + box-shadow: 0 0 0 var(--pf-outline-width) var(--pf-outline-focus); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-card:has([data-pf-selected]) { + border-color: var(--pf-border-focus); + background: var(--pf-hover); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-image { + width: var(--pf-image-width); + height: var(--pf-image-height); + border-radius: 4px; + object-fit: cover; + background: var(--pf-skeleton); + flex-shrink: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-content { + flex: 1; + min-width: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-title { + font-size: var(--pf-result-title-font-size); + font-weight: 500; + margin: 0; + line-height: 1.4; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-link { + color: var(--pf-text); + text-decoration: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-link::after { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-link:hover { + text-decoration: underline; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-excerpt { + font-size: var(--pf-result-excerpt-font-size); + color: var(--pf-text-secondary); + margin: 4px 0 0 0; + line-height: 1.5; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-result-excerpt mark, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-excerpt mark, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result-excerpt mark { + background: transparent; + font-weight: 500; + color: var(--pf-mark); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-skeleton { + background: var(--pf-skeleton); + border-radius: 4px; +} + +@media (prefers-reduced-motion: no-preference) { + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-skeleton { + background: linear-gradient( + 90deg, + var(--pf-skeleton) 25%, + var(--pf-skeleton-shine) 50%, + var(--pf-skeleton) 75% + ); + background-size: 200% 100%; + animation: pf-shimmer 1.5s infinite; + } + + @keyframes pf-shimmer { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } + } +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-skeleton-title { + height: 14px; + width: 60%; + margin-bottom: 8px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-skeleton-excerpt { + height: 13px; + width: 90%; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-skeleton-image { + width: var(--pf-image-width); + height: var(--pf-image-height); + flex-shrink: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-empty { + text-align: center; + padding: 32px 16px; + color: var(--pf-text-muted); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-empty-icon { + font-size: 24px; + margin-bottom: 8px; + opacity: 0.4; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-empty-text { + font-size: 14px; + margin: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-error { + padding: 12px 16px; + background: var(--pf-error-bg); + border: 1px solid var(--pf-error-border); + border-radius: var(--pf-border-radius); + color: var(--pf-error-text); + font-size: 13px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-error strong { + font-weight: 600; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-error small { + display: block; + margin-top: 4px; + color: var(--pf-error-text-secondary); + font-size: 12px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-chips { + display: flex; + flex-direction: column; + gap: 6px; + margin: 0; + padding: 0; + padding-inline-start: 12px; + list-style: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-chip { + display: inline-flex; + flex-direction: column; + gap: 2px; + padding: 8px 10px; + background: var(--pf-background); + border: 1px solid var(--pf-border); + border-radius: 6px; + font-size: 12px; + transition: + border-color 0.15s, + box-shadow 0.15s; + position: relative; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-chip:hover { + border-color: var(--pf-border-focus); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-heading-chip:has(.pf-heading-link:focus-visible) { + border-color: var(--pf-outline-focus); + box-shadow: 0 0 0 var(--pf-outline-width) var(--pf-outline-focus); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-chip:has([data-pf-selected]) { + border-color: var(--pf-border-focus); + background: var(--pf-hover); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-link { + color: var(--pf-text); + font-weight: 500; + text-decoration: none; + line-height: 1.3; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-link::before { + content: "#"; + color: var(--pf-text-muted); + margin-inline-end: 4px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-link::after { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-link:hover { + text-decoration: underline; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-heading-excerpt { + margin: 0; + font-size: 12px; + color: var(--pf-text-secondary); + line-height: 1.4; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-pane { + display: flex; + flex-direction: column; + gap: 20px; + padding: 16px; + background: var(--pf-skeleton); + border-radius: var(--pf-border-radius); + border: 1px solid var(--pf-border); + overflow: hidden; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-group { + display: flex; + flex-direction: column; + gap: 8px; + border: none; + padding: 0; + margin: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) details.pf-filter-group { + display: block; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) details.pf-filter-group > .pf-filter-options { + margin-top: 8px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + details.pf-filter-group + > .pf-filter-fieldset { + margin-top: 8px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-fieldset { + border: none; + padding: 0; + margin: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-group-title { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.3px; + color: var(--pf-text-muted); + margin: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-group-name, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger-label, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-option-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + min-width: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) summary.pf-filter-group-title { + cursor: pointer; + list-style: none; + display: flex; + align-items: center; + padding: 4px 0; + margin: -4px 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + summary.pf-filter-group-title::-webkit-details-marker { + display: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) summary.pf-filter-group-title::after { + content: ""; + width: 10px; + height: 6px; + flex-shrink: 0; + margin-inline-start: 8px; + background: var(--pf-text-muted); + -webkit-mask-image: var(--pf-icon-arrow); + mask-image: var(--pf-icon-arrow); + -webkit-mask-size: 100% 100%; + mask-size: 100% 100%; + transition: transform 0.15s; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + details.pf-filter-group:not([open]) + > summary.pf-filter-group-title::after { + transform: rotate(-90deg); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + [dir="rtl"] + details.pf-filter-group:not([open]) + > summary.pf-filter-group-title::after { + transform: rotate(90deg); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) summary.pf-filter-group-title:hover { + color: var(--pf-text-secondary); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-group-count, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-selected-badge { + display: inline-flex; + align-items: center; + justify-content: center; + background: var(--pf-text); + color: var(--pf-background); + font-size: 11px; + font-weight: 500; + height: 18px; + min-width: 18px; + padding: 0 5px; + border-radius: 9px; + flex-shrink: 0; + box-sizing: border-box; + font-variant-numeric: tabular-nums; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-group-count { + margin-inline-start: auto; + text-transform: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#):is(*, #\#) + .pf-filter-group-count[data-pf-hidden], +:is(*, #\#):is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-selected-badge[data-pf-hidden] { + display: inline-flex !important; + visibility: hidden; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-options { + display: flex; + flex-direction: column; + gap: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-checkbox { + display: flex; + align-items: center; + gap: 8px; + min-height: 30px; + cursor: pointer; + font-size: 13px; + color: var(--pf-text-secondary); +} + +@media (pointer: coarse) { + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-checkbox { + min-height: 44px; + } +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-checkbox:hover { + color: var(--pf-text); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-checkbox-input { + width: 16px; + height: 16px; + margin: 0; + accent-color: var(--pf-text); + cursor: pointer; + color-scheme: light; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) [data-pf-theme="dark"] .pf-checkbox-input { + color-scheme: dark; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-filter-checkbox:has(.pf-checkbox-input:focus-visible) { + outline: var(--pf-outline-width) solid var(--pf-outline-focus); + outline-offset: var(--pf-outline-offset); + border-radius: 4px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-checkbox-count { + margin-inline-start: auto; + font-size: 11px; + color: var(--pf-text-muted); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-wrapper { + position: relative; + display: inline-flex; + align-items: center; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger { + display: inline-flex; + align-items: center; + gap: 8px; + height: var(--pf-input-height); + padding: 0 12px; + background: var(--pf-background); + border: 1px solid var(--pf-border); + border-radius: var(--pf-border-radius); + font-size: 13px; + color: var(--pf-text-secondary); + cursor: pointer; + transition: border-color 0.15s; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger:hover { + border-color: var(--pf-border-focus); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger.open { + border-color: var(--pf-border-focus); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger.wrap { + height: auto; + min-height: var(--pf-input-height); + padding-top: 6px; + padding-bottom: 6px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger-label.wrap, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-option-label.wrap { + white-space: normal; + line-height: 1.3; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-arrow { + width: 10px; + height: 6px; + flex-shrink: 0; + background: var(--pf-text-muted); + -webkit-mask-image: var(--pf-icon-arrow); + mask-image: var(--pf-icon-arrow); + -webkit-mask-size: 100% 100%; + mask-size: 100% 100%; + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger.open .pf-dropdown-arrow { + transform: rotate(180deg); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-menu { + position: absolute; + top: calc(100% + 4px); + inset-inline-start: 0; + min-width: 180px; + max-height: var(--pf-dropdown-max-height); + overflow-y: auto; + border: 1px solid var(--pf-border); + border-radius: var(--pf-border-radius); + box-shadow: var(--pf-shadow-md); + padding: 4px; + z-index: 100; + + background: + /* Top shadow cover (moves with content) */ + linear-gradient(var(--pf-background) 30%, transparent) center top, + /* Bottom shadow cover (moves with content) */ + linear-gradient(transparent, var(--pf-background) 70%) center bottom, + /* Top shadow (stays fixed) */ + linear-gradient(var(--pf-scroll-shadow), transparent) center top, + /* Bottom shadow (stays fixed) */ + linear-gradient(transparent, var(--pf-scroll-shadow)) center bottom, + var(--pf-background); + background-size: + 100% 40px, + 100% 40px, + 100% 14px, + 100% 14px, + 100% 100%; + background-repeat: no-repeat; + background-attachment: local, local, scroll, scroll, scroll; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-option { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 10px; + min-height: 44px; + font-size: 13px; + color: var(--pf-text-secondary); + border-radius: 4px; + cursor: pointer; + box-sizing: border-box; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-option:hover { + background: var(--pf-hover); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-option.pf-dropdown-option-focused { + outline: var(--pf-outline-width) solid var(--pf-outline-focus); + outline-offset: -2px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-option.wrap { + align-items: flex-start; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-checkbox { + width: 16px; + height: 16px; + flex-shrink: 0; + border: 1px solid var(--pf-border); + border-radius: 3px; + background: var(--pf-background); + position: relative; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-option.wrap + .pf-dropdown-checkbox { + margin-top: 2px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-option[aria-selected="true"] + .pf-dropdown-checkbox { + background: var(--pf-text); + border-color: var(--pf-text); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-option[aria-selected="true"] + .pf-dropdown-checkbox::after { + content: ""; + position: absolute; + inset-inline-start: 5px; + top: 2px; + width: 4px; + height: 8px; + border: solid var(--pf-background); + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-option-label { + flex: 1; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-option-count { + margin-inline-start: auto; + font-size: 11px; + color: var(--pf-text-muted); + flex-shrink: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-option.wrap + .pf-dropdown-option-count { + margin-top: 2px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-option-loading { + pointer-events: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-option-loading + .pf-dropdown-checkbox { + width: 16px; + height: 16px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-option-loading + .pf-dropdown-option-label { + height: 13px; + border-radius: 4px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-error { + padding: 12px 10px; + font-size: 13px; + color: var(--pf-error-text); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-clear { + display: inline-flex; + align-items: center; + height: 44px; + padding: 0 10px; + margin-inline-start: 4px; + background: transparent; + border: 1px solid transparent; + border-radius: var(--pf-border-radius); + font-size: 12px; + color: var(--pf-text-secondary); + cursor: pointer; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-clear:hover:not([aria-disabled="true"]) { + background: var(--pf-hover); + border-color: var(--pf-border); + color: var(--pf-text); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-clear[aria-disabled="true"] { + color: var(--pf-text-muted); + cursor: default; + opacity: 0.4; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-options { + max-height: var(--pf-dropdown-max-height); + overflow-y: auto; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-btn { + display: inline-flex; + align-items: center; + gap: 8px; + width: 100%; + height: var(--pf-input-height); + padding: 0 14px; + background: var(--pf-background); + border: 1px solid var(--pf-border); + border-radius: var(--pf-border-radius); + font-size: 14px; + color: var(--pf-text-muted); + cursor: pointer; + transition: + border-color 0.15s, + box-shadow 0.15s; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-btn:hover { + border-color: var(--pf-border-focus); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-icon { + width: 14px; + height: 14px; + background: var(--pf-text-muted); + -webkit-mask-image: var(--pf-icon-search); + mask-image: var(--pf-icon-search); + -webkit-mask-size: 100%; + mask-size: 100%; + flex-shrink: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-text { + flex: 1; + text-align: start; + color: var(--pf-text-muted); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-shortcut { + display: inline-flex; + align-items: center; + gap: 2px; + margin-inline-start: 8px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-key { + min-width: 20px; + height: 18px; + font-size: 11px; + border-radius: 4px; +} + +/* + NB: Background scrolling is prevented with the overscroll-behavior: contain + values below, plus overflow: hidden on the backdrop. + As of writing, this only works on Chrome 144. Tracking: + https://bugzilla.mozilla.org/show_bug.cgi?id=1837436 + https://bugs.webkit.org/show_bug.cgi?id=243452 +*/ + +:is(*, #\#):is(*, #\#):is(*, #\#) dialog.pf-modal { + position: fixed; + width: 100%; + max-width: var(--pf-modal-max-width); + max-height: var(--pf-modal-max-height); + margin: var(--pf-modal-top) auto; + padding: 0; + background: var(--pf-background); + border: none; + border-radius: 12px; + box-shadow: var(--pf-shadow-lg); + flex-direction: column; + overflow: hidden; + overscroll-behavior: contain; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) dialog.pf-modal::backdrop { + background: var(--pf-modal-backdrop); + overflow: hidden; + overscroll-behavior: contain; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) dialog.pf-modal[open] { + display: flex; +} + +@media (prefers-reduced-motion: no-preference) { + :is(*, #\#):is(*, #\#):is(*, #\#) dialog.pf-modal[open] { + animation: pf-modal-appear 0.15s ease-out; + } + + @keyframes pf-modal-appear { + from { + opacity: 0; + transform: scale(0.95); + } + to { + opacity: 1; + transform: scale(1); + } + } +} + +@media (max-width: 640px) { + :is(*, #\#):is(*, #\#):is(*, #\#) dialog.pf-modal[open] { + top: 0; + left: 0; + width: 100vw; + height: 100dvh; + max-width: none; + max-height: none; + margin: 0; + padding: 0; + border-radius: 0; + animation: none; + } + + :is(*, #\#):is(*, #\#):is(*, #\#) pagefind-modal-header, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-header { + padding-top: max(16px, env(safe-area-inset-top)); + padding-left: max(16px, env(safe-area-inset-left)); + padding-right: max(16px, env(safe-area-inset-right)); + } + + :is(*, #\#):is(*, #\#):is(*, #\#) pagefind-modal-body, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-body { + padding-left: max(16px, env(safe-area-inset-left)); + padding-right: max(16px, env(safe-area-inset-right)); + } + + :is(*, #\#):is(*, #\#):is(*, #\#) pagefind-modal-footer, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-footer { + padding-bottom: max(12px, env(safe-area-inset-bottom)); + padding-left: max(16px, env(safe-area-inset-left)); + padding-right: max(16px, env(safe-area-inset-right)); + } +} + +:is(*, #\#):is(*, #\#):is(*, #\#) pagefind-modal-header, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-header { + display: flex; + align-items: center; + gap: 8px; + padding: 16px; + border-bottom: 1px solid var(--pf-border); + flex-shrink: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-header-content { + flex: 1; + min-width: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-close { + display: none; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + padding: 0; + background: transparent; + border: none; + border-radius: 8px; + color: var(--pf-text-secondary); + cursor: pointer; + flex-shrink: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-close:hover { + background: var(--pf-hover); + color: var(--pf-text); +} + +@media (max-width: 640px) { + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-close { + display: flex; + } +} + +:is(*, #\#):is(*, #\#):is(*, #\#) + pagefind-modal-header + .pf-input-wrapper::before, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-header .pf-input-wrapper::before { + top: 50%; + transform: translateY(-50%); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) pagefind-modal-header .pf-input, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-header .pf-input { + border: none; + background: transparent; + font-size: var(--pf-input-font-size); + height: 40px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) pagefind-modal-body, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-body { + flex: 1 1 auto; + overflow-y: auto; + overscroll-behavior: contain; + padding: 8px 16px 16px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) pagefind-modal-body .pf-summary, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-body .pf-summary { + margin-bottom: 8px; +} + +/* Modal body uses the base card styles - only minor adjustments needed */ +:is(*, #\#):is(*, #\#):is(*, #\#) + pagefind-modal-body + .pf-result-card:has([data-pf-selected]), +:is(*, #\#):is(*, #\#):is(*, #\#) + .pf-modal-body + .pf-result-card:has([data-pf-selected]) { + background: var(--pf-skeleton); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) pagefind-modal-footer, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-footer { + padding: 10px 16px; + border-top: 1px solid var(--pf-border); + display: flex; + align-items: center; + gap: 16px; + font-size: 12px; + color: var(--pf-text-muted); + flex-shrink: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-footer-hint, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-keyboard-hint { + display: flex; + align-items: center; + gap: 6px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-footer-key, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-keyboard-key { + min-width: 18px; + height: 18px; + font-size: 10px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) pagefind-keyboard-hints, +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-keyboard-hints { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 16px; + font-size: 12px; + color: var(--pf-text-muted); + min-height: 20px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox { + position: relative; + display: inline-block; + width: 100%; + max-width: var(--pf-searchbox-max-width); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input-wrapper { + position: relative; +} + +/* Override trigger-shortcut positioning when inside searchbox */ +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input-wrapper .pf-trigger-shortcut { + position: absolute; + inset-inline-end: 12px; + top: 50%; + transform: translateY(-50%); + pointer-events: none; + margin-inline-start: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input { + padding: 0; + padding-inline-start: 32px; + padding-inline-end: 12px; +} + +/* Add padding when shortcut is present */ +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input-wrapper:has(.pf-trigger-shortcut) .pf-searchbox-input { + padding-inline-end: 72px; +} + +/* Hide shortcut badge when input is focused */ +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input:focus ~ .pf-trigger-shortcut { + display: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input:focus { + padding-inline-end: 12px; + border-color: var(--pf-border-focus); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox.open .pf-searchbox-input { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + border-bottom-color: var(--pf-border); +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-dropdown { + display: none; + flex-direction: column; + position: absolute; + top: 100%; + left: 0; + right: 0; + background: var(--pf-background); + border: 1px solid var(--pf-border); + border-top: none; + border-radius: 0 0 var(--pf-border-radius) var(--pf-border-radius); + box-shadow: var(--pf-shadow-md); + max-height: var(--pf-searchbox-dropdown-max-height); + z-index: 100; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox.open .pf-searchbox-dropdown { + display: flex; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-results { + list-style: none; + padding: 4px; + margin: 0; + flex: 1; + overflow-y: auto; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result { + display: block; + padding: 12px 10px; + min-height: 44px; + border-radius: 4px; + cursor: pointer; + text-decoration: none; + color: inherit; + box-sizing: border-box; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result:hover { + background: var(--pf-hover); +} + +/* Placeholder skeleton - non-interactive */ +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-placeholder { + cursor: default; + pointer-events: none; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-placeholder:hover { + background: transparent; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result[data-pf-selected] { + background: var(--pf-hover); + outline: var(--pf-outline-width) solid var(--pf-outline-focus); + outline-offset: -2px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result-title { + font-size: 13px; + font-weight: 500; + color: var(--pf-text); + margin: 0; + line-height: 1.3; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result-excerpt { + font-size: 12px; + color: var(--pf-text-secondary); + margin: 2px 0 0 0; + line-height: 1.4; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-subresult { + padding-inline-start: 20px; + border-inline-start: 2px solid var(--pf-border); + margin-inline-start: 10px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result-meta { + display: flex; + align-items: center; + gap: 6px; + margin-top: 4px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-footer { + padding: 8px 10px; + border-top: 1px solid var(--pf-border); + display: flex; + align-items: center; + justify-content: flex-end; + gap: 12px; + font-size: 11px; + color: var(--pf-text-muted); + flex-shrink: 0; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-footer-hint { + display: flex; + align-items: center; + gap: 4px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-empty { + padding: 20px 10px; + text-align: center; + color: var(--pf-text-muted); + font-size: 13px; +} + +:is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-loading { + padding: 16px 10px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + color: var(--pf-text-muted); + font-size: 13px; +} + +/* Ensure focus indicators are visible in Windows High Contrast Mode */ +@media (forced-colors: active) { + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-input:focus-visible, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-input:focus-visible, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-input-clear:focus-visible, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-trigger-btn:focus-visible, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-close:focus-visible, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-trigger:focus-visible, + :is(*, #\#):is(*, #\#):is(*, #\#) + .pf-result-card:has(.pf-result-link:focus-visible), + :is(*, #\#):is(*, #\#):is(*, #\#) + .pf-heading-chip:has(.pf-heading-link:focus-visible), + :is(*, #\#):is(*, #\#):is(*, #\#) + .pf-filter-checkbox:has(.pf-checkbox-input:focus-visible), + :is(*, #\#):is(*, #\#):is(*, #\#) + .pf-dropdown-option.pf-dropdown-option-focused, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-result[data-pf-selected] { + outline: 2px solid CanvasText; + outline-offset: 2px; + } +} + +/* Hide keyboard hints on touch-only devices (no hover capability) */ +@media (hover: none) { + :is(*, #\#):is(*, #\#):is(*, #\#) pagefind-keyboard-hints, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-keyboard-hints, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-footer, + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-modal-footer-hint { + display: none; + } +} + +@media (max-width: 640px) { + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-dropdown-menu { + min-width: unset; + width: max(180px, calc(100vw - 32px)); + max-width: calc(100vw - 32px); + inset-inline-start: unset; + left: 50%; + transform: translateX(-50%); + max-height: min(var(--pf-dropdown-max-height), 50vh); + } + + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-searchbox-dropdown { + max-height: min(var(--pf-searchbox-dropdown-max-height), 60vh); + } + + :is(*, #\#):is(*, #\#):is(*, #\#) .pf-filter-pane { + padding: 12px; + gap: 16px; + } +} diff --git a/docs/pagefind/pagefind-component-ui.js b/docs/pagefind/pagefind-component-ui.js new file mode 100644 index 00000000..d8c13515 --- /dev/null +++ b/docs/pagefind/pagefind-component-ui.js @@ -0,0 +1,56 @@ +"use strict";(()=>{var ls=Object.defineProperty;var h=(n,s)=>{for(var e in s)ls(n,e,{get:s[e],enumerable:!0})};var Bt={};h(Bt,{PagefindConfig:()=>Re,PagefindElement:()=>f,PagefindFilterDropdown:()=>Me,PagefindFilterPane:()=>Ne,PagefindInput:()=>ke,PagefindKeyboardHints:()=>Le,PagefindModal:()=>Oe,PagefindModalBody:()=>Ie,PagefindModalFooter:()=>Ue,PagefindModalHeader:()=>we,PagefindModalTrigger:()=>He,PagefindResults:()=>Se,PagefindSearchbox:()=>Fe,PagefindSummary:()=>Ae,configureInstance:()=>Qt,getInstanceManager:()=>Ce});var os="a[href], button, input, [tabindex]";function Kt(n){let s=n.querySelectorAll(os);for(let e of s)if(!(e.tabIndex<0)&&!e.disabled&&!e.hasAttribute("hidden")&&window.getComputedStyle(e).display!=="none")return!0;return!1}function $t(n,s){let e=null;for(let t of s)t.contains(n)||!(n.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_FOLLOWING)||Kt(t)&&(e===null||t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING)&&(e=t);return e}function Pe(n,s){let e=null;for(let t of s)t.contains(n)||!(n.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_PRECEDING)||Kt(t)&&(e===null||t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_PRECEDING)&&(e=t);return e}var Ve={};h(Ve,{comments:()=>us,default:()=>ps,direction:()=>ds,strings:()=>hs,thanks_to:()=>cs});var cs="Jan Claasen ",us="",ds="ltr",hs={placeholder:"Soek",clear_search:"Opruim",load_more:"Laai nog resultate",search_label:"Soek hierdie webwerf",filters_label:"Filters",zero_results:"Geen resultate vir [SEARCH_TERM]",many_results:"[COUNT] resultate vir [SEARCH_TERM]",one_result:"[COUNT] resultate vir [SEARCH_TERM]",total_zero_results:"Geen resultate",total_one_result:"[COUNT] resultaat",total_many_results:"[COUNT] resultate",alt_search:"Geen resultate vir [SEARCH_TERM]. Toon resultate vir [DIFFERENT_TERM] in plaas daarvan",search_suggestion:"Geen resultate vir [SEARCH_TERM]. Probeer eerder een van die volgende terme:",searching:"Soek vir [SEARCH_TERM]",results_label:"Soekresultate",keyboard_navigate:"navigeer",keyboard_select:"kies",keyboard_clear:"wis",keyboard_close:"sluit",keyboard_search:"soek",error_search:"Soek het misluk",filter_selected_one:"[COUNT] gekies",filter_selected_many:"[COUNT] gekies",input_hint:"Resultate sal verskyn terwyl jy tik",loading:"Laai"},ps={thanks_to:cs,comments:us,direction:ds,strings:hs};var qe={};h(qe,{comments:()=>_s,default:()=>Es,direction:()=>fs,strings:()=>gs,thanks_to:()=>ms});var ms="Jermanuts",_s="",fs="rtl",gs={placeholder:"\u0628\u062D\u062B",clear_search:"\u0627\u0645\u0633\u062D",load_more:"\u062D\u0645\u0651\u0650\u0644 \u0627\u0644\u0645\u0632\u064A\u062F \u0645\u0646 \u0627\u0644\u0646\u062A\u0627\u0626\u062C",search_label:"\u0627\u0628\u062D\u062B \u0641\u064A \u0647\u0630\u0627 \u0627\u0644\u0645\u0648\u0642\u0639",filters_label:"\u062A\u0635\u0641\u064A\u0627\u062A",zero_results:"\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C \u0644 [SEARCH_TERM]",many_results:"[COUNT] \u0646\u062A\u0627\u0626\u062C \u0644 [SEARCH_TERM]",one_result:"[COUNT] \u0646\u062A\u064A\u062C\u0629 \u0644 [SEARCH_TERM]",total_zero_results:"\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C",total_one_result:"[COUNT] \u0646\u062A\u064A\u062C\u0629",total_many_results:"[COUNT] \u0646\u062A\u0627\u0626\u062C",alt_search:"\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C \u0644 [SEARCH_TERM]. \u064A\u0639\u0631\u0636 \u0627\u0644\u0646\u062A\u0627\u0626\u062C \u0644 [DIFFERENT_TERM] \u0628\u062F\u0644\u0627\u064B \u0645\u0646 \u0630\u0644\u0643",search_suggestion:"\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C \u0644 [SEARCH_TERM]. \u062C\u0631\u0628 \u0623\u062D\u062F \u0639\u0645\u0644\u064A\u0627\u062A \u0627\u0644\u0628\u062D\u062B \u0627\u0644\u062A\u0627\u0644\u064A\u0629:",searching:"\u064A\u0628\u062D\u062B \u0639\u0646 [SEARCH_TERM]...",results_label:"\u0646\u062A\u0627\u0626\u062C \u0627\u0644\u0628\u062D\u062B",keyboard_navigate:"\u062A\u0646\u0642\u0644",keyboard_select:"\u0627\u062E\u062A\u064A\u0627\u0631",keyboard_clear:"\u0627\u0645\u0633\u062D",keyboard_close:"\u0625\u063A\u0644\u0627\u0642",keyboard_search:"\u0628\u062D\u062B",error_search:"\u0641\u0634\u0644 \u0627\u0644\u0628\u062D\u062B",filter_selected_one:"[COUNT] \u0645\u062D\u062F\u062F",filter_selected_many:"[COUNT] \u0645\u062D\u062F\u062F",input_hint:"\u0633\u062A\u0638\u0647\u0631 \u0627\u0644\u0646\u062A\u0627\u0626\u062C \u0623\u062B\u0646\u0627\u0621 \u0627\u0644\u0643\u062A\u0627\u0628\u0629",loading:"\u062C\u0627\u0631\u064D \u0627\u0644\u062A\u062D\u0645\u064A\u0644"},Es={thanks_to:ms,comments:_s,direction:fs,strings:gs};var Ge={};h(Ge,{comments:()=>Ts,default:()=>vs,direction:()=>Cs,strings:()=>ys,thanks_to:()=>bs});var bs="Maruf Alom ",Ts="",Cs="ltr",ys={placeholder:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",clear_search:"\u09AE\u09C1\u099B\u09C7 \u09AB\u09C7\u09B2\u09C1\u09A8",load_more:"\u0986\u09B0\u09CB \u09AB\u09B2\u09BE\u09AB\u09B2 \u09A6\u09C7\u0996\u09C1\u09A8",search_label:"\u098F\u0987 \u0993\u09AF\u09BC\u09C7\u09AC\u09B8\u09BE\u0987\u099F\u09C7 \u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",filters_label:"\u09AB\u09BF\u09B2\u09CD\u099F\u09BE\u09B0",zero_results:"[SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF",many_results:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u0997\u09BF\u09AF\u09BC\u09C7\u099B\u09C7 [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF",one_result:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u0997\u09BF\u09AF\u09BC\u09C7\u099B\u09C7 [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF",total_zero_results:"\u0995\u09CB\u09A8 \u09AB\u09B2\u09BE\u09AB\u09B2 \u09A8\u09C7\u0987",total_one_result:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2",total_many_results:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2",alt_search:"\u0995\u09CB\u09A8 \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF. \u09AA\u09B0\u09BF\u09AC\u09B0\u09CD\u09A4\u09C7 [DIFFERENT_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF \u09A6\u09C7\u0996\u09BE\u09A8\u09CB \u09B9\u099A\u09CD\u099B\u09C7",search_suggestion:"\u0995\u09CB\u09A8 \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF [SEARCH_TERM] \u098F\u09B0 \u09AC\u09BF\u09B7\u09AF\u09BC\u09C7. \u09A8\u09BF\u09A8\u09CD\u09AE\u09C7\u09B0 \u09AC\u09BF\u09B7\u09AF\u09BC\u09AC\u09B8\u09CD\u09A4\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09A6\u09C7\u0996\u09C1\u09A8:",searching:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u099A\u09B2\u099B\u09C7 [SEARCH_TERM]...",results_label:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8\u09C7\u09B0 \u09AB\u09B2\u09BE\u09AB\u09B2",keyboard_navigate:"\u09A8\u09C7\u09AD\u09BF\u0997\u09C7\u099F",keyboard_select:"\u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09A8",keyboard_clear:"\u09AE\u09C1\u099B\u09C1\u09A8",keyboard_close:"\u09AC\u09A8\u09CD\u09A7",keyboard_search:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8",error_search:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u09AC\u09CD\u09AF\u09B0\u09CD\u09A5",filter_selected_one:"[COUNT]-\u099F\u09BF \u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09BF\u09A4",filter_selected_many:"[COUNT]-\u099F\u09BF \u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09BF\u09A4",input_hint:"\u099F\u09BE\u0987\u09AA \u0995\u09B0\u09BE\u09B0 \u09B8\u09BE\u09A5\u09C7 \u09B8\u09BE\u09A5\u09C7 \u09AB\u09B2\u09BE\u09AB\u09B2 \u09A6\u09C7\u0996\u09BE \u09AF\u09BE\u09AC\u09C7",loading:"\u09B2\u09CB\u09A1 \u09B9\u099A\u09CD\u099B\u09C7"},vs={thanks_to:bs,comments:Ts,direction:Cs,strings:ys};var We={};h(We,{comments:()=>ks,default:()=>Ns,direction:()=>As,strings:()=>Ss,thanks_to:()=>Rs});var Rs="Pablo Villaverde ",ks="",As="ltr",Ss={placeholder:"Cerca",clear_search:"Netejar",load_more:"Veure m\xE9s resultats",search_label:"Cerca en aquest lloc",filters_label:"Filtres",zero_results:"No es van trobar resultats per [SEARCH_TERM]",many_results:"[COUNT] resultats trobats per [SEARCH_TERM]",one_result:"[COUNT] resultat trobat per [SEARCH_TERM]",total_zero_results:"Sense resultats",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultats",alt_search:"No es van trobar resultats per [SEARCH_TERM]. Mostrant al seu lloc resultats per [DIFFERENT_TERM]",search_suggestion:"No es van trobar resultats per [SEARCH_TERM]. Proveu una de les cerques seg\xFCents:",searching:"Cercant [SEARCH_TERM]...",results_label:"Resultats de la cerca",keyboard_navigate:"navegar",keyboard_select:"triar",keyboard_clear:"netejar",keyboard_close:"tancar",keyboard_search:"cercar",error_search:"Error en la cerca",filter_selected_one:"[COUNT] seleccionat",filter_selected_many:"[COUNT] seleccionats",input_hint:"Els resultats apareixeran mentre escriviu",loading:"Carregant"},Ns={thanks_to:Rs,comments:ks,direction:As,strings:Ss};var Ye={};h(Ye,{comments:()=>Os,default:()=>ws,direction:()=>xs,strings:()=>Hs,thanks_to:()=>Ms});var Ms="Dalibor Hon ",Os="",xs="ltr",Hs={placeholder:"Hledat",clear_search:"Smazat",load_more:"Na\u010D\xEDst dal\u0161\xED v\xFDsledky",search_label:"Prohledat tuto str\xE1nku",filters_label:"Filtry",zero_results:"\u017D\xE1dn\xE9 v\xFDsledky pro [SEARCH_TERM]",many_results:"[COUNT] v\xFDsledk\u016F pro [SEARCH_TERM]",one_result:"[COUNT] v\xFDsledek pro [SEARCH_TERM]",total_zero_results:"\u017D\xE1dn\xE9 v\xFDsledky",total_one_result:"[COUNT] v\xFDsledek",total_many_results:"[COUNT] v\xFDsledk\u016F",alt_search:"\u017D\xE1dn\xE9 v\xFDsledky pro [SEARCH_TERM]. Zobrazuj\xED se v\xFDsledky pro [DIFFERENT_TERM]",search_suggestion:"\u017D\xE1dn\xE9 v\xFDsledky pro [SEARCH_TERM]. Souvisej\xEDc\xED v\xFDsledky hled\xE1n\xED:",searching:"Hled\xE1m [SEARCH_TERM]...",results_label:"V\xFDsledky hled\xE1n\xED",keyboard_navigate:"navigovat",keyboard_select:"vybrat",keyboard_clear:"smazat",keyboard_close:"zav\u0159\xEDt",keyboard_search:"hledat",error_search:"Hled\xE1n\xED selhalo",filter_selected_one:"[COUNT] vybran\xFD",filter_selected_many:"[COUNT] vybran\xFDch",input_hint:"V\xFDsledky se zobraz\xED b\u011Bhem psan\xED",loading:"Na\u010D\xEDt\xE1n\xED"},ws={thanks_to:Ms,comments:Os,direction:xs,strings:Hs};var Je={};h(Je,{comments:()=>Us,default:()=>Ps,direction:()=>Ls,strings:()=>Fs,thanks_to:()=>Is});var Is="Jonas Smedegaard ",Us="",Ls="ltr",Fs={placeholder:"S\xF8g",clear_search:"Nulstil",load_more:"Indl\xE6s flere resultater",search_label:"S\xF8g p\xE5 dette website",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",total_zero_results:"Ingen resultater",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultater",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v et af disse s\xF8geord i stedet:",searching:"S\xF8ger efter [SEARCH_TERM]...",results_label:"S\xF8geresultater",keyboard_navigate:"naviger",keyboard_select:"v\xE6lg",keyboard_clear:"ryd",keyboard_close:"luk",keyboard_search:"s\xF8g",error_search:"S\xF8gning mislykkedes",filter_selected_one:"[COUNT] valgt",filter_selected_many:"[COUNT] valgte",input_hint:"Resultater vises mens du skriver",loading:"Indl\xE6ser"},Ps={thanks_to:Is,comments:Us,direction:Ls,strings:Fs};var Ze={};h(Ze,{comments:()=>Ds,default:()=>Ks,direction:()=>js,strings:()=>Bs,thanks_to:()=>zs});var zs="Jan Claasen ",Ds="",js="ltr",Bs={placeholder:"Suche",clear_search:"L\xF6schen",load_more:"Mehr Ergebnisse laden",search_label:"Suche diese Seite",filters_label:"Filter",zero_results:"Keine Ergebnisse f\xFCr [SEARCH_TERM]",many_results:"[COUNT] Ergebnisse f\xFCr [SEARCH_TERM]",one_result:"[COUNT] Ergebnis f\xFCr [SEARCH_TERM]",total_zero_results:"Keine Ergebnisse",total_one_result:"[COUNT] Ergebnis",total_many_results:"[COUNT] Ergebnisse",alt_search:"Keine Ergebnisse f\xFCr [SEARCH_TERM]. Stattdessen werden Ergebnisse f\xFCr [DIFFERENT_TERM] angezeigt",search_suggestion:"Keine Ergebnisse f\xFCr [SEARCH_TERM]. Versuchen Sie eine der folgenden Suchen:",searching:"Suche nach [SEARCH_TERM]\u202F\u2026",results_label:"Suchergebnisse",keyboard_navigate:"navigieren",keyboard_select:"ausw\xE4hlen",keyboard_clear:"l\xF6schen",keyboard_close:"schlie\xDFen",keyboard_search:"suchen",error_search:"Suche fehlgeschlagen",filter_selected_one:"[COUNT] ausgew\xE4hlt",filter_selected_many:"[COUNT] ausgew\xE4hlt",input_hint:"Ergebnisse werden w\xE4hrend der Eingabe angezeigt",loading:"Wird geladen"},Ks={thanks_to:zs,comments:Ds,direction:js,strings:Bs};var Xe={};h(Xe,{comments:()=>Vs,default:()=>Ws,direction:()=>qs,strings:()=>Gs,thanks_to:()=>$s});var $s="George Papadopoulos",Vs="",qs="ltr",Gs={placeholder:"\u0391\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7",clear_search:"\u039A\u03B1\u03B8\u03B1\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2",load_more:"\u03A6\u03CC\u03C1\u03C4\u03C9\u03C3\u03B7 \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03C9\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03B5\u03C3\u03BC\u03AC\u03C4\u03C9\u03BD",search_label:"\u0391\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7 \u03C3\u03B5 \u03B1\u03C5\u03C4\u03CC\u03BD \u03C4\u03BF\u03BD \u03B9\u03C3\u03C4\u03CC\u03C4\u03BF\u03C0\u03BF",filters_label:"\u03A6\u03AF\u03BB\u03C4\u03C1\u03B1",zero_results:"\u0394\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]",many_results:"[COUNT] \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]",one_result:"[COUNT] \u03B1\u03C0\u03BF\u03C4\u03AD\u03BB\u03B5\u03C3\u03BC\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]",total_zero_results:"\u0394\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1",total_one_result:"[COUNT] \u03B1\u03C0\u03BF\u03C4\u03AD\u03BB\u03B5\u03C3\u03BC\u03B1",total_many_results:"[COUNT] \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1",alt_search:"\u0394\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]. \u0395\u03BC\u03C6\u03B1\u03BD\u03AF\u03B6\u03BF\u03BD\u03C4\u03B1\u03B9 \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [DIFFERENT_TERM]",search_suggestion:"\u0394\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]. \u0394\u03BF\u03BA\u03B9\u03BC\u03AC\u03C3\u03C4\u03B5 \u03BC\u03AF\u03B1 \u03B1\u03C0\u03CC \u03C4\u03B9\u03C2 \u03C0\u03B1\u03C1\u03B1\u03BA\u03AC\u03C4\u03C9 \u03B1\u03BD\u03B1\u03B6\u03B7\u03C4\u03AE\u03C3\u03B5\u03B9\u03C2:",searching:"\u0391\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7 \u03B3\u03B9\u03B1 [SEARCH_TERM]...",results_label:"\u0391\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B1\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7\u03C2",keyboard_navigate:"\u03C0\u03BB\u03BF\u03AE\u03B3\u03B7\u03C3\u03B7",keyboard_select:"\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE",keyboard_clear:"\u03BA\u03B1\u03B8\u03B1\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2",keyboard_close:"\u03BA\u03BB\u03B5\u03AF\u03C3\u03B9\u03BC\u03BF",keyboard_search:"\u03B1\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7",error_search:"\u0397 \u03B1\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7 \u03B1\u03C0\u03AD\u03C4\u03C5\u03C7\u03B5",filter_selected_one:"[COUNT] \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF",filter_selected_many:"[COUNT] \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03B1",input_hint:"\u03A4\u03B1 \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B8\u03B1 \u03B5\u03BC\u03C6\u03B1\u03BD\u03AF\u03B6\u03BF\u03BD\u03C4\u03B1\u03B9 \u03BA\u03B1\u03B8\u03CE\u03C2 \u03C0\u03BB\u03B7\u03BA\u03C4\u03C1\u03BF\u03BB\u03BF\u03B3\u03B5\u03AF\u03C4\u03B5",loading:"\u03A6\u03CC\u03C1\u03C4\u03C9\u03C3\u03B7"},Ws={thanks_to:$s,comments:Vs,direction:qs,strings:Gs};var Qe={};h(Qe,{comments:()=>Js,default:()=>Qs,direction:()=>Zs,strings:()=>Xs,thanks_to:()=>Ys});var Ys="Liam Bigelow ",Js="",Zs="ltr",Xs={placeholder:"Search",clear_search:"Clear",load_more:"Load more results",search_label:"Search this site",filters_label:"Filters",zero_results:"No results for [SEARCH_TERM]",many_results:"[COUNT] results for [SEARCH_TERM]",one_result:"[COUNT] result for [SEARCH_TERM]",total_zero_results:"No results",total_one_result:"[COUNT] result",total_many_results:"[COUNT] results",alt_search:"No results for [SEARCH_TERM]. Showing results for [DIFFERENT_TERM] instead",search_suggestion:"No results for [SEARCH_TERM]. Try one of the following searches:",searching:"Searching for [SEARCH_TERM]...",results_label:"Search results",keyboard_navigate:"navigate",keyboard_select:"select",keyboard_clear:"clear",keyboard_close:"close",keyboard_search:"search",error_search:"Search failed",filter_selected_one:"[COUNT] selected",filter_selected_many:"[COUNT] selected",input_hint:"Results will appear as you type",loading:"Loading"},Qs={thanks_to:Ys,comments:Js,direction:Zs,strings:Xs};var et={};h(et,{comments:()=>tr,default:()=>ir,direction:()=>sr,strings:()=>rr,thanks_to:()=>er});var er="Pablo Villaverde ",tr="",sr="ltr",rr={placeholder:"Buscar",clear_search:"Limpiar",load_more:"Ver m\xE1s resultados",search_label:"Buscar en este sitio",filters_label:"Filtros",zero_results:"No se encontraron resultados para [SEARCH_TERM]",many_results:"[COUNT] resultados encontrados para [SEARCH_TERM]",one_result:"[COUNT] resultado encontrado para [SEARCH_TERM]",total_zero_results:"Sin resultados",total_one_result:"[COUNT] resultado",total_many_results:"[COUNT] resultados",alt_search:"No se encontraron resultados para [SEARCH_TERM]. Mostrando en su lugar resultados para [DIFFERENT_TERM]",search_suggestion:"No se encontraron resultados para [SEARCH_TERM]. Prueba una de las siguientes b\xFAsquedas:",searching:"Buscando [SEARCH_TERM]...",results_label:"Resultados de b\xFAsqueda",keyboard_navigate:"navegar",keyboard_select:"elegir",keyboard_clear:"limpiar",keyboard_close:"cerrar",keyboard_search:"buscar",error_search:"Error en la b\xFAsqueda",filter_selected_one:"[COUNT] seleccionado",filter_selected_many:"[COUNT] seleccionados",input_hint:"Los resultados aparecer\xE1n mientras escribe",loading:"Cargando"},ir={thanks_to:er,comments:tr,direction:sr,strings:rr};var tt={};h(tt,{comments:()=>ar,default:()=>cr,direction:()=>lr,strings:()=>or,thanks_to:()=>nr});var nr="Mikel Larreategi ",ar="",lr="ltr",or={placeholder:"Bilatu",clear_search:"Garbitu",load_more:"Kargatu emaitza gehiagi",search_label:"Bilatu",filters_label:"Iragazkiak",zero_results:"Ez dago emaitzarik [SEARCH_TERM] bilaketarentzat",many_results:"[COUNT] emaitza [SEARCH_TERM] bilaketarentzat",one_result:"Emaitza bat [COUNT] [SEARCH_TERM] bilaketarentzat",total_zero_results:"Emaitzarik ez",total_one_result:"[COUNT] emaitza",total_many_results:"[COUNT] emaitza",alt_search:"Ez dago emaitzarik [SEARCH_TERM] bilaketarentzat. [DIFFERENT_TERM] bilaketaren emaitzak erakusten",search_suggestion:"Ez dago emaitzarik [SEARCH_TERM] bilaketarentzat. Saiatu hauetako beste bateikin:",searching:"[SEARCH_TERM] bilatzen...",results_label:"Bilaketaren emaitzak",keyboard_navigate:"nabigatu",keyboard_select:"hautatu",keyboard_clear:"garbitu",keyboard_close:"itxi",keyboard_search:"bilatu",error_search:"Bilaketak huts egin du",filter_selected_one:"[COUNT] hautatuta",filter_selected_many:"[COUNT] hautatuta",input_hint:"Emaitzak idatzi ahala agertuko dira",loading:"Kargatzen"},cr={thanks_to:nr,comments:ar,direction:lr,strings:or};var st={};h(st,{comments:()=>dr,default:()=>mr,direction:()=>hr,strings:()=>pr,thanks_to:()=>ur});var ur="Ali Khaleqi Yekta ",dr="",hr="rtl",pr={placeholder:"\u062C\u0633\u062A\u062C\u0648",clear_search:"\u067E\u0627\u06A9\u0633\u0627\u0632\u06CC",load_more:"\u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC \u0646\u062A\u0627\u06CC\u062C \u0628\u06CC\u0634\u062A\u0631",search_label:"\u062C\u0633\u062A\u062C\u0648 \u062F\u0631 \u0633\u0627\u06CC\u062A",filters_label:"\u0641\u06CC\u0644\u062A\u0631\u0647\u0627",zero_results:"\u0646\u062A\u06CC\u062C\u0647\u200C\u0627\u06CC \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0646\u0634\u062F",many_results:"[COUNT] \u0646\u062A\u06CC\u062C\u0647 \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0634\u062F",one_result:"[COUNT] \u0646\u062A\u06CC\u062C\u0647 \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0634\u062F",total_zero_results:"\u0646\u062A\u06CC\u062C\u0647\u200C\u0627\u06CC \u06CC\u0627\u0641\u062A \u0646\u0634\u062F",total_one_result:"[COUNT] \u0646\u062A\u06CC\u062C\u0647",total_many_results:"[COUNT] \u0646\u062A\u06CC\u062C\u0647",alt_search:"\u0646\u062A\u06CC\u062C\u0647\u200C\u0627\u06CC \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0646\u0634\u062F. \u062F\u0631 \u0639\u0648\u0636 \u0646\u062A\u0627\u06CC\u062C \u0628\u0631\u0627\u06CC [DIFFERENT_TERM] \u0646\u0645\u0627\u06CC\u0634 \u062F\u0627\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F",search_suggestion:"\u0646\u062A\u06CC\u062C\u0647\u200C\u0627\u06CC \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0646\u0634\u062F. \u06CC\u06A9\u06CC \u0627\u0632 \u062C\u0633\u062A\u062C\u0648\u0647\u0627\u06CC \u0632\u06CC\u0631 \u0631\u0627 \u0627\u0645\u062A\u062D\u0627\u0646 \u06A9\u0646\u06CC\u062F:",searching:"\u062F\u0631 \u062D\u0627\u0644 \u062C\u0633\u062A\u062C\u0648\u06CC [SEARCH_TERM]...",results_label:"\u0646\u062A\u0627\u06CC\u062C \u062C\u0633\u062A\u062C\u0648",keyboard_navigate:"\u067E\u06CC\u0645\u0627\u06CC\u0634",keyboard_select:"\u0627\u0646\u062A\u062E\u0627\u0628",keyboard_clear:"\u067E\u0627\u06A9\u0633\u0627\u0632\u06CC",keyboard_close:"\u0628\u0633\u062A\u0646",keyboard_search:"\u062C\u0633\u062A\u062C\u0648",error_search:"\u062C\u0633\u062A\u062C\u0648 \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F",filter_selected_one:"[COUNT] \u0627\u0646\u062A\u062E\u0627\u0628 \u0634\u062F\u0647",filter_selected_many:"[COUNT] \u0627\u0646\u062A\u062E\u0627\u0628 \u0634\u062F\u0647",input_hint:"\u0646\u062A\u0627\u06CC\u062C \u0647\u0646\u06AF\u0627\u0645 \u062A\u0627\u06CC\u067E \u0646\u0645\u0627\u06CC\u0634 \u062F\u0627\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u0646\u062F",loading:"\u062F\u0631 \u062D\u0627\u0644 \u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC"},mr={thanks_to:ur,comments:dr,direction:hr,strings:pr};var rt={};h(rt,{comments:()=>fr,default:()=>br,direction:()=>gr,strings:()=>Er,thanks_to:()=>_r});var _r="Valtteri Laitinen ",fr="",gr="ltr",Er={placeholder:"Haku",clear_search:"Tyhjenn\xE4",load_more:"Lataa lis\xE4\xE4 tuloksia",search_label:"Hae t\xE4lt\xE4 sivustolta",filters_label:"Suodattimet",zero_results:"Ei tuloksia haulle [SEARCH_TERM]",many_results:"[COUNT] tulosta haulle [SEARCH_TERM]",one_result:"[COUNT] tulos haulle [SEARCH_TERM]",total_zero_results:"Ei tuloksia",total_one_result:"[COUNT] tulos",total_many_results:"[COUNT] tulosta",alt_search:"Ei tuloksia haulle [SEARCH_TERM]. N\xE4ytet\xE4\xE4n tulokset sen sijaan haulle [DIFFERENT_TERM]",search_suggestion:"Ei tuloksia haulle [SEARCH_TERM]. Kokeile jotain seuraavista:",searching:"Haetaan [SEARCH_TERM]...",results_label:"Hakutulokset",keyboard_navigate:"siirry",keyboard_select:"valitse",keyboard_clear:"tyhjenn\xE4",keyboard_close:"sulje",keyboard_search:"hae",error_search:"Haku ep\xE4onnistui",filter_selected_one:"[COUNT] valittu",filter_selected_many:"[COUNT] valittu",input_hint:"Tulokset n\xE4kyv\xE4t kirjoittaessasi",loading:"Ladataan"},br={thanks_to:_r,comments:fr,direction:gr,strings:Er};var it={};h(it,{comments:()=>Cr,default:()=>Rr,direction:()=>yr,strings:()=>vr,thanks_to:()=>Tr});var Tr="Nicolas Friedli ",Cr="",yr="ltr",vr={placeholder:"Rechercher",clear_search:"Nettoyer",load_more:"Charger plus de r\xE9sultats",search_label:"Recherche sur ce site",filters_label:"Filtres",zero_results:"Pas de r\xE9sultat pour [SEARCH_TERM]",many_results:"[COUNT] r\xE9sultats pour [SEARCH_TERM]",one_result:"[COUNT] r\xE9sultat pour [SEARCH_TERM]",total_zero_results:"Pas de r\xE9sultat",total_one_result:"[COUNT] r\xE9sultat",total_many_results:"[COUNT] r\xE9sultats",alt_search:"Pas de r\xE9sultat pour [SEARCH_TERM]. Montre les r\xE9sultats pour [DIFFERENT_TERM] \xE0 la place",search_suggestion:"Pas de r\xE9sultat pour [SEARCH_TERM]. Essayer une des recherches suivantes:",searching:"Recherche [SEARCH_TERM]...",results_label:"R\xE9sultats de recherche",keyboard_navigate:"naviguer",keyboard_select:"choisir",keyboard_clear:"effacer",keyboard_close:"fermer",keyboard_search:"rechercher",error_search:"\xC9chec de la recherche",filter_selected_one:"[COUNT] s\xE9lectionn\xE9",filter_selected_many:"[COUNT] s\xE9lectionn\xE9s",input_hint:"Les r\xE9sultats appara\xEEtront au fur et \xE0 mesure de la saisie",loading:"Chargement"},Rr={thanks_to:Tr,comments:Cr,direction:yr,strings:vr};var nt={};h(nt,{comments:()=>Ar,default:()=>Mr,direction:()=>Sr,strings:()=>Nr,thanks_to:()=>kr});var kr="Pablo Villaverde ",Ar="",Sr="ltr",Nr={placeholder:"Buscar",clear_search:"Limpar",load_more:"Ver m\xE1is resultados",search_label:"Buscar neste sitio",filters_label:"Filtros",zero_results:"Non se atoparon resultados para [SEARCH_TERM]",many_results:"[COUNT] resultados atopados para [SEARCH_TERM]",one_result:"[COUNT] resultado atopado para [SEARCH_TERM]",total_zero_results:"Sen resultados",total_one_result:"[COUNT] resultado",total_many_results:"[COUNT] resultados",alt_search:"Non se atoparon resultados para [SEARCH_TERM]. Amosando no seu lugar resultados para [DIFFERENT_TERM]",search_suggestion:"Non se atoparon resultados para [SEARCH_TERM]. Probe unha das seguintes pesquisas:",searching:"Buscando [SEARCH_TERM]...",results_label:"Resultados da busca",keyboard_navigate:"navegar",keyboard_select:"escoller",keyboard_clear:"limpar",keyboard_close:"pechar",keyboard_search:"buscar",error_search:"Erro na busca",filter_selected_one:"[COUNT] seleccionado",filter_selected_many:"[COUNT] seleccionados",input_hint:"Os resultados aparecer\xE1n mentres escribe",loading:"Cargando"},Mr={thanks_to:kr,comments:Ar,direction:Sr,strings:Nr};var at={};h(at,{comments:()=>xr,default:()=>Ir,direction:()=>Hr,strings:()=>wr,thanks_to:()=>Or});var Or="Nir Tamir ",xr="",Hr="rtl",wr={placeholder:"\u05D7\u05D9\u05E4\u05D5\u05E9",clear_search:"\u05E0\u05D9\u05E7\u05D5\u05D9",load_more:"\u05E2\u05D5\u05D3 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA",search_label:"\u05D7\u05D9\u05E4\u05D5\u05E9 \u05D1\u05D0\u05EA\u05E8 \u05D6\u05D4",filters_label:"\u05DE\u05E1\u05E0\u05E0\u05D9\u05DD",zero_results:"\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]",many_results:"\u05E0\u05DE\u05E6\u05D0\u05D5 [COUNT] \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]",one_result:"\u05E0\u05DE\u05E6\u05D0\u05D4 \u05EA\u05D5\u05E6\u05D0\u05D4 \u05D0\u05D7\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]",total_zero_results:"\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA",total_one_result:"\u05EA\u05D5\u05E6\u05D0\u05D4 [COUNT]",total_many_results:"[COUNT] \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA",alt_search:"\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]. \u05DE\u05D5\u05E6\u05D2\u05D5\u05EA \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [DIFFERENT_TERM]",search_suggestion:"\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]. \u05E0\u05E1\u05D5 \u05D0\u05D7\u05D3 \u05DE\u05D4\u05D7\u05D9\u05E4\u05D5\u05E9\u05D9\u05DD \u05D4\u05D1\u05D0\u05D9\u05DD:",searching:"\u05DE\u05D7\u05E4\u05E9 \u05D0\u05EA [SEARCH_TERM]...",results_label:"\u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05D7\u05D9\u05E4\u05D5\u05E9",keyboard_navigate:"\u05E0\u05D9\u05D5\u05D5\u05D8",keyboard_select:"\u05D1\u05D7\u05D9\u05E8\u05D4",keyboard_clear:"\u05E0\u05D9\u05E7\u05D5\u05D9",keyboard_close:"\u05E1\u05D2\u05D9\u05E8\u05D4",keyboard_search:"\u05D7\u05D9\u05E4\u05D5\u05E9",error_search:"\u05D4\u05D7\u05D9\u05E4\u05D5\u05E9 \u05E0\u05DB\u05E9\u05DC",filter_selected_one:"[COUNT] \u05E0\u05D1\u05D7\u05E8",filter_selected_many:"[COUNT] \u05E0\u05D1\u05D7\u05E8\u05D5",input_hint:"\u05D4\u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05D9\u05D5\u05E4\u05D9\u05E2\u05D5 \u05EA\u05D5\u05DA \u05DB\u05D3\u05D9 \u05D4\u05E7\u05DC\u05D3\u05D4",loading:"\u05D8\u05D5\u05E2\u05DF"},Ir={thanks_to:Or,comments:xr,direction:Hr,strings:wr};var lt={};h(lt,{comments:()=>Lr,default:()=>zr,direction:()=>Fr,strings:()=>Pr,thanks_to:()=>Ur});var Ur="Amit Yadav ",Lr="",Fr="ltr",Pr={placeholder:"\u0916\u094B\u091C\u0947\u0902",clear_search:"\u0938\u093E\u092B \u0915\u0930\u0947\u0902",load_more:"\u0914\u0930 \u0905\u0927\u093F\u0915 \u092A\u0930\u093F\u0923\u093E\u092E \u0932\u094B\u0921 \u0915\u0930\u0947\u0902",search_label:"\u0907\u0938 \u0938\u093E\u0907\u091F \u092E\u0947\u0902 \u0916\u094B\u091C\u0947\u0902",filters_label:"\u092B\u093C\u093F\u0932\u094D\u091F\u0930",zero_results:"\u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E",many_results:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u092E\u093F\u0932\u0947",one_result:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u092E\u093F\u0932\u093E",total_zero_results:"\u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902",total_one_result:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E",total_many_results:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E",alt_search:"[SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964 \u0907\u0938\u0915\u0947 \u092C\u091C\u093E\u092F [DIFFERENT_TERM] \u0915\u0947 \u0932\u093F\u090F \u092A\u0930\u093F\u0923\u093E\u092E \u0926\u093F\u0916\u093E \u0930\u0939\u093E \u0939\u0948",search_suggestion:"[SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964 \u0928\u093F\u092E\u094D\u0928\u0932\u093F\u0916\u093F\u0924 \u0916\u094B\u091C\u094B\u0902 \u092E\u0947\u0902 \u0938\u0947 \u0915\u094B\u0908 \u090F\u0915 \u0906\u091C\u093C\u092E\u093E\u090F\u0902:",searching:"[SEARCH_TERM] \u0915\u0940 \u0916\u094B\u091C \u0915\u0940 \u091C\u093E \u0930\u0939\u0940 \u0939\u0948...",results_label:"\u0916\u094B\u091C \u092A\u0930\u093F\u0923\u093E\u092E",keyboard_navigate:"\u0928\u0947\u0935\u093F\u0917\u0947\u091F",keyboard_select:"\u091A\u0941\u0928\u0947\u0902",keyboard_clear:"\u0938\u093E\u092B\u093C \u0915\u0930\u0947\u0902",keyboard_close:"\u092C\u0902\u0926 \u0915\u0930\u0947\u0902",keyboard_search:"\u0916\u094B\u091C\u0947\u0902",error_search:"\u0916\u094B\u091C \u0935\u093F\u092B\u0932",filter_selected_one:"[COUNT] \u091A\u092F\u0928\u093F\u0924",filter_selected_many:"[COUNT] \u091A\u092F\u0928\u093F\u0924",input_hint:"\u091F\u093E\u0907\u092A \u0915\u0930\u0924\u0947 \u0938\u092E\u092F \u092A\u0930\u093F\u0923\u093E\u092E \u0926\u093F\u0916\u093E\u0908 \u0926\u0947\u0902\u0917\u0947",loading:"\u0932\u094B\u0921 \u0939\u094B \u0930\u0939\u093E \u0939\u0948"},zr={thanks_to:Ur,comments:Lr,direction:Fr,strings:Pr};var ot={};h(ot,{comments:()=>jr,default:()=>$r,direction:()=>Br,strings:()=>Kr,thanks_to:()=>Dr});var Dr="Diomed ",jr="",Br="ltr",Kr={placeholder:"Tra\u017Ei",clear_search:"O\u010Disti",load_more:"U\u010Ditaj vi\u0161e rezultata",search_label:"Pretra\u017Ei ovu stranicu",filters_label:"Filteri",zero_results:"Nema rezultata za [SEARCH_TERM]",many_results:"[COUNT] rezultata za [SEARCH_TERM]",one_result:"[COUNT] rezultat za [SEARCH_TERM]",total_zero_results:"Nema rezultata",total_one_result:"[COUNT] rezultat",total_many_results:"[COUNT] rezultata",alt_search:"Nema rezultata za [SEARCH_TERM]. Prikazujem rezultate za [DIFFERENT_TERM]",search_suggestion:"Nema rezultata za [SEARCH_TERM]. Poku\u0161aj s jednom od ovih pretraga:",searching:"Pretra\u017Eujem [SEARCH_TERM]...",results_label:"Rezultati pretrage",keyboard_navigate:"navigiraj",keyboard_select:"odaberi",keyboard_clear:"o\u010Disti",keyboard_close:"zatvori",keyboard_search:"tra\u017Ei",error_search:"Pretraga nije uspjela",filter_selected_one:"[COUNT] odabran",filter_selected_many:"[COUNT] odabranih",input_hint:"Rezultati \u0107e se pojaviti dok tipkate",loading:"U\u010Ditavanje"},$r={thanks_to:Dr,comments:jr,direction:Br,strings:Kr};var ct={};h(ct,{comments:()=>qr,default:()=>Yr,direction:()=>Gr,strings:()=>Wr,thanks_to:()=>Vr});var Vr="Adam Laki ",qr="",Gr="ltr",Wr={placeholder:"Keres\xE9s",clear_search:"T\xF6rl\xE9s",load_more:"Tov\xE1bbi tal\xE1latok bet\xF6lt\xE9se",search_label:"Keres\xE9s az oldalon",filters_label:"Sz\u0171r\xE9s",zero_results:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",many_results:"[COUNT] db tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",one_result:"[COUNT] db tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",total_zero_results:"Nincs tal\xE1lat",total_one_result:"[COUNT] tal\xE1lat",total_many_results:"[COUNT] tal\xE1lat",alt_search:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre. Tal\xE1latok mutat\xE1sa ink\xE1bb a(z) [DIFFERENT_TERM] kifejez\xE9sre",search_suggestion:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre. Pr\xF3b\xE1ld meg a k\xF6vetkez\u0151 keres\xE9sek egyik\xE9t:",searching:"Keres\xE9s a(z) [SEARCH_TERM] kifejez\xE9sre...",results_label:"Keres\xE9si tal\xE1latok",keyboard_navigate:"navig\xE1l\xE1s",keyboard_select:"kiv\xE1laszt\xE1s",keyboard_clear:"t\xF6rl\xE9s",keyboard_close:"bez\xE1r\xE1s",keyboard_search:"keres\xE9s",error_search:"A keres\xE9s sikertelen",filter_selected_one:"[COUNT] kiv\xE1lasztva",filter_selected_many:"[COUNT] kiv\xE1lasztva",input_hint:"A tal\xE1latok g\xE9pel\xE9s k\xF6zben jelennek meg",loading:"Bet\xF6lt\xE9s"},Yr={thanks_to:Vr,comments:qr,direction:Gr,strings:Wr};var ut={};h(ut,{comments:()=>Zr,default:()=>ei,direction:()=>Xr,strings:()=>Qr,thanks_to:()=>Jr});var Jr="Nixentric",Zr="",Xr="ltr",Qr={placeholder:"Cari",clear_search:"Bersihkan",load_more:"Muat lebih banyak hasil",search_label:"Telusuri situs ini",filters_label:"Filter",zero_results:"[SEARCH_TERM] tidak ditemukan",many_results:"Ditemukan [COUNT] hasil untuk [SEARCH_TERM]",one_result:"Ditemukan [COUNT] hasil untuk [SEARCH_TERM]",total_zero_results:"Tidak ada hasil",total_one_result:"[COUNT] hasil",total_many_results:"[COUNT] hasil",alt_search:"[SEARCH_TERM] tidak ditemukan. Menampilkan hasil [DIFFERENT_TERM] sebagai gantinya",search_suggestion:"[SEARCH_TERM] tidak ditemukan. Coba salah satu pencarian berikut ini:",searching:"Mencari [SEARCH_TERM]...",results_label:"Hasil pencarian",keyboard_navigate:"navigasi",keyboard_select:"pilih",keyboard_clear:"bersihkan",keyboard_close:"tutup",keyboard_search:"cari",error_search:"Pencarian gagal",filter_selected_one:"[COUNT] dipilih",filter_selected_many:"[COUNT] dipilih",input_hint:"Hasil akan muncul saat Anda mengetik",loading:"Memuat"},ei={thanks_to:Jr,comments:Zr,direction:Xr,strings:Qr};var dt={};h(dt,{comments:()=>si,default:()=>ni,direction:()=>ri,strings:()=>ii,thanks_to:()=>ti});var ti="Cosette Bruhns Alonso, Andrew Janco ",si="",ri="ltr",ii={placeholder:"Cerca",clear_search:"Cancella la cronologia",load_more:"Mostra pi\xF9 risultati",search_label:"Cerca nel sito",filters_label:"Filtri di ricerca",zero_results:"Nessun risultato per [SEARCH_TERM]",many_results:"[COUNT] risultati per [SEARCH_TERM]",one_result:"[COUNT] risultato per [SEARCH_TERM]",total_zero_results:"Nessun risultato",total_one_result:"[COUNT] risultato",total_many_results:"[COUNT] risultati",alt_search:"Nessun risultato per [SEARCH_TERM]. Mostrando risultati per [DIFFERENT_TERM] come alternativa.",search_suggestion:"Nessun risultato per [SEARCH_TERM]. Prova una delle seguenti ricerche:",searching:"Cercando [SEARCH_TERM]...",results_label:"Risultati della ricerca",keyboard_navigate:"naviga",keyboard_select:"seleziona",keyboard_clear:"cancella",keyboard_close:"chiudi",keyboard_search:"cerca",error_search:"Ricerca fallita",filter_selected_one:"[COUNT] selezionato",filter_selected_many:"[COUNT] selezionati",input_hint:"I risultati appariranno durante la digitazione",loading:"Caricamento"},ni={thanks_to:ti,comments:si,direction:ri,strings:ii};var ht={};h(ht,{comments:()=>li,default:()=>ui,direction:()=>oi,strings:()=>ci,thanks_to:()=>ai});var ai="Tate",li="",oi="ltr",ci={placeholder:"\u691C\u7D22",clear_search:"\u30AF\u30EA\u30A2",load_more:"\u6B21\u3092\u8AAD\u307F\u8FBC\u3080",search_label:"\u3053\u306E\u30B5\u30A4\u30C8\u3092\u691C\u7D22",filters_label:"\u30D5\u30A3\u30EB\u30BF",zero_results:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u60C5\u5831\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F",many_results:"[SEARCH_TERM]\u306E[COUNT]\u4EF6\u306E\u691C\u7D22\u7D50\u679C",one_result:"[SEARCH_TERM]\u306E[COUNT]\u4EF6\u306E\u691C\u7D22\u7D50\u679C",total_zero_results:"\u7D50\u679C\u306A\u3057",total_one_result:"[COUNT]\u4EF6\u306E\u7D50\u679C",total_many_results:"[COUNT]\u4EF6\u306E\u7D50\u679C",alt_search:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u60C5\u5831\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002[DIFFERENT_TERM]\u306E\u691C\u7D22\u7D50\u679C\u3092\u8868\u793A\u3057\u3066\u3044\u307E\u3059",search_suggestion:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u60C5\u5831\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u6B21\u306E\u3044\u305A\u308C\u304B\u306E\u691C\u7D22\u3092\u8A66\u3057\u3066\u304F\u3060\u3055\u3044",searching:"[SEARCH_TERM]\u3092\u691C\u7D22\u3057\u3066\u3044\u307E\u3059",results_label:"\u691C\u7D22\u7D50\u679C",keyboard_navigate:"\u79FB\u52D5",keyboard_select:"\u9078\u629E",keyboard_clear:"\u30AF\u30EA\u30A2",keyboard_close:"\u9589\u3058\u308B",keyboard_search:"\u691C\u7D22",error_search:"\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F",filter_selected_one:"[COUNT]\u4EF6\u9078\u629E\u4E2D",filter_selected_many:"[COUNT]\u4EF6\u9078\u629E\u4E2D",input_hint:"\u5165\u529B\u4E2D\u306B\u691C\u7D22\u7D50\u679C\u304C\u8868\u793A\u3055\u308C\u307E\u3059",loading:"\u8AAD\u307F\u8FBC\u307F\u4E2D"},ui={thanks_to:ai,comments:li,direction:oi,strings:ci};var pt={};h(pt,{comments:()=>hi,default:()=>_i,direction:()=>pi,strings:()=>mi,thanks_to:()=>di});var di="Seokho Son ",hi="",pi="ltr",mi={placeholder:"\uAC80\uC0C9\uC5B4",clear_search:"\uBE44\uC6B0\uAE30",load_more:"\uAC80\uC0C9 \uACB0\uACFC \uB354 \uBCF4\uAE30",search_label:"\uC0AC\uC774\uD2B8 \uAC80\uC0C9",filters_label:"\uD544\uD130",zero_results:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC \uC5C6\uC74C",many_results:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC [COUNT]\uAC74",one_result:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC [COUNT]\uAC74",total_zero_results:"\uACB0\uACFC \uC5C6\uC74C",total_one_result:"\uACB0\uACFC [COUNT]\uAC74",total_many_results:"\uACB0\uACFC [COUNT]\uAC74",alt_search:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC \uC5C6\uC74C. [DIFFERENT_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC",search_suggestion:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC \uC5C6\uC74C. \uCD94\uCC9C \uAC80\uC0C9\uC5B4: ",searching:"[SEARCH_TERM] \uAC80\uC0C9 \uC911...",results_label:"\uAC80\uC0C9 \uACB0\uACFC",keyboard_navigate:"\uC774\uB3D9",keyboard_select:"\uC120\uD0DD",keyboard_clear:"\uBE44\uC6B0\uAE30",keyboard_close:"\uB2EB\uAE30",keyboard_search:"\uAC80\uC0C9",error_search:"\uAC80\uC0C9 \uC2E4\uD328",filter_selected_one:"[COUNT]\uAC1C \uC120\uD0DD\uB428",filter_selected_many:"[COUNT]\uAC1C \uC120\uD0DD\uB428",input_hint:"\uC785\uB825\uD558\uB294 \uB3D9\uC548 \uACB0\uACFC\uAC00 \uD45C\uC2DC\uB429\uB2C8\uB2E4",loading:"\uB85C\uB529 \uC911"},_i={thanks_to:di,comments:hi,direction:pi,strings:mi};var mt={};h(mt,{comments:()=>gi,default:()=>Ti,direction:()=>Ei,strings:()=>bi,thanks_to:()=>fi});var fi="",gi="",Ei="ltr",bi={placeholder:"Rapu",clear_search:"Whakakore",load_more:"Whakauta \u0113tahi otinga k\u0113",search_label:"Rapu",filters_label:"T\u0101tari",zero_results:"Otinga kore ki [SEARCH_TERM]",many_results:"[COUNT] otinga ki [SEARCH_TERM]",one_result:"[COUNT] otinga ki [SEARCH_TERM]",total_zero_results:"K\u0101ore he otinga",total_one_result:"[COUNT] otinga",total_many_results:"[COUNT] ng\u0101 otinga",alt_search:"Otinga kore ki [SEARCH_TERM]. Otinga k\u0113 ki [DIFFERENT_TERM]",search_suggestion:"Otinga kore ki [SEARCH_TERM]. whakam\u0101tau ki ng\u0101 mea atu:",searching:"Rapu ki [SEARCH_TERM]...",results_label:"Ng\u0101 otinga rapu",keyboard_navigate:"whakatere",keyboard_select:"t\u012Bpako",keyboard_clear:"whakakore",keyboard_close:"kati",keyboard_search:"rapu",error_search:"K\u0101ore i eke te rapu",filter_selected_one:"[COUNT] kua t\u012Bpakohia",filter_selected_many:"[COUNT] kua t\u012Bpakohia",input_hint:"Ka puta ng\u0101 otinga i a koe e patopato ana",loading:"E uta ana"},Ti={thanks_to:fi,comments:gi,direction:Ei,strings:bi};var _t={};h(_t,{comments:()=>yi,default:()=>ki,direction:()=>vi,strings:()=>Ri,thanks_to:()=>Ci});var Ci="Harry Min Khant ",yi="",vi="ltr",Ri={placeholder:"\u101B\u103E\u102C\u101B\u1014\u103A",clear_search:"\u101B\u103E\u102C\u1016\u103D\u1031\u1019\u103E\u102F\u1000\u102D\u102F \u101B\u103E\u1004\u103A\u1038\u101C\u1004\u103A\u1038\u1015\u102B\u104B",load_more:"\u1014\u1031\u102C\u1000\u103A\u1011\u1015\u103A\u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038\u1000\u102D\u102F \u1010\u1004\u103A\u1015\u102B\u104B",search_label:"\u1024\u1006\u102D\u102F\u1000\u103A\u1010\u103D\u1004\u103A\u101B\u103E\u102C\u1016\u103D\u1031\u1015\u102B\u104B",filters_label:"\u1005\u1005\u103A\u1011\u102F\u1010\u103A\u1019\u103E\u102F\u1019\u103B\u102C\u1038",zero_results:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038 \u1019\u101B\u103E\u102D\u1015\u102B",many_results:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A [COUNT] \u1001\u102F",one_result:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A [COUNT]",total_zero_results:"\u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038 \u1019\u101B\u103E\u102D\u1015\u102B",total_one_result:"\u101B\u101C\u1012\u103A [COUNT] \u1001\u102F",total_many_results:"\u101B\u101C\u1012\u103A [COUNT] \u1001\u102F",alt_search:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A\u1019\u101B\u103E\u102D\u1015\u102B\u104B \u104E\u1004\u103A\u1038\u1021\u1005\u102C\u1038 [DIFFERENT_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038\u1000\u102D\u102F \u1015\u103C\u101E\u101E\u100A\u103A\u104B",search_suggestion:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A\u1019\u101B\u103E\u102D\u1015\u102B\u104B \u1021\u1031\u102C\u1000\u103A\u1015\u102B\u101B\u103E\u102C\u1016\u103D\u1031\u1019\u103E\u102F\u1019\u103B\u102C\u1038\u1011\u1032\u1019\u103E \u1010\u1005\u103A\u1001\u102F\u1000\u102D\u102F \u1005\u1019\u103A\u1038\u1000\u103C\u100A\u1037\u103A\u1015\u102B:",searching:"[SEARCH_TERM] \u1000\u102D\u102F \u101B\u103E\u102C\u1016\u103D\u1031\u1014\u1031\u101E\u100A\u103A...",results_label:"\u101B\u103E\u102C\u1016\u103D\u1031\u1019\u103E\u102F \u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038",keyboard_navigate:"\u101C\u1019\u103A\u1038\u100A\u103D\u103E\u1014\u103A",keyboard_select:"\u101B\u103D\u1031\u1038\u1001\u103B\u101A\u103A",keyboard_clear:"\u101B\u103E\u1004\u103A\u1038\u101C\u1004\u103A\u1038",keyboard_close:"\u1015\u102D\u1010\u103A",keyboard_search:"\u101B\u103E\u102C\u101B\u1014\u103A",error_search:"\u101B\u103E\u102C\u1016\u103D\u1031\u1019\u103E\u102F \u1019\u1021\u1031\u102C\u1004\u103A\u1019\u103C\u1004\u103A\u1015\u102B",filter_selected_one:"[COUNT] \u1001\u102F \u101B\u103D\u1031\u1038\u1001\u103B\u101A\u103A\u1011\u102C\u1038\u101E\u100A\u103A",filter_selected_many:"[COUNT] \u1001\u102F \u101B\u103D\u1031\u1038\u1001\u103B\u101A\u103A\u1011\u102C\u1038\u101E\u100A\u103A",input_hint:"\u101B\u102D\u102F\u1000\u103A\u1014\u1031\u1005\u1009\u103A \u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038 \u1015\u1031\u102B\u103A\u101C\u102C\u1015\u102B\u1019\u100A\u103A",loading:"\u1010\u1004\u103A\u1014\u1031\u101E\u100A\u103A"},ki={thanks_to:Ci,comments:yi,direction:vi,strings:Ri};var ft={};h(ft,{comments:()=>Si,default:()=>Oi,direction:()=>Ni,strings:()=>Mi,thanks_to:()=>Ai});var Ai="Eirik Mikkelsen",Si="",Ni="ltr",Mi={placeholder:"S\xF8k",clear_search:"Fjern",load_more:"Last flere resultater",search_label:"S\xF8k p\xE5 denne siden",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",total_zero_results:"Ingen resultater",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultater",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v en av disse s\xF8keordene i stedet:",searching:"S\xF8ker etter [SEARCH_TERM]",results_label:"S\xF8keresultater",keyboard_navigate:"naviger",keyboard_select:"velg",keyboard_clear:"fjern",keyboard_close:"lukk",keyboard_search:"s\xF8k",error_search:"S\xF8k feilet",filter_selected_one:"[COUNT] valgt",filter_selected_many:"[COUNT] valgte",input_hint:"Resultater vises mens du skriver",loading:"Laster"},Oi={thanks_to:Ai,comments:Si,direction:Ni,strings:Mi};var gt={};h(gt,{comments:()=>Hi,default:()=>Ui,direction:()=>wi,strings:()=>Ii,thanks_to:()=>xi});var xi="Paul van Brouwershaven",Hi="",wi="ltr",Ii={placeholder:"Zoeken",clear_search:"Reset",load_more:"Meer resultaten laden",search_label:"Doorzoek deze site",filters_label:"Filters",zero_results:"Geen resultaten voor [SEARCH_TERM]",many_results:"[COUNT] resultaten voor [SEARCH_TERM]",one_result:"[COUNT] resultaat voor [SEARCH_TERM]",total_zero_results:"Geen resultaten",total_one_result:"[COUNT] resultaat",total_many_results:"[COUNT] resultaten",alt_search:"Geen resultaten voor [SEARCH_TERM]. In plaats daarvan worden resultaten voor [DIFFERENT_TERM] weergegeven",search_suggestion:"Geen resultaten voor [SEARCH_TERM]. Probeer een van de volgende zoekopdrachten:",searching:"Zoeken naar [SEARCH_TERM]...",results_label:"Zoekresultaten",keyboard_navigate:"navigeren",keyboard_select:"selecteren",keyboard_clear:"wissen",keyboard_close:"sluiten",keyboard_search:"zoeken",error_search:"Zoeken mislukt",filter_selected_one:"[COUNT] geselecteerd",filter_selected_many:"[COUNT] geselecteerd",input_hint:"Resultaten verschijnen terwijl u typt",loading:"Laden"},Ui={thanks_to:xi,comments:Hi,direction:wi,strings:Ii};var Et={};h(Et,{comments:()=>Fi,default:()=>Di,direction:()=>Pi,strings:()=>zi,thanks_to:()=>Li});var Li="Eirik Mikkelsen",Fi="",Pi="ltr",zi={placeholder:"S\xF8k",clear_search:"Fjern",load_more:"Last fleire resultat",search_label:"S\xF8k p\xE5 denne sida",filters_label:"Filter",zero_results:"Ingen resultat for [SEARCH_TERM]",many_results:"[COUNT] resultat for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",total_zero_results:"Ingen resultat",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultat",alt_search:"Ingen resultat for [SEARCH_TERM]. Viser resultat for [DIFFERENT_TERM] i staden",search_suggestion:"Ingen resultat for [SEARCH_TERM]. Pr\xF8v eitt av desse s\xF8keorda i staden:",searching:"S\xF8ker etter [SEARCH_TERM]",results_label:"S\xF8keresultat",keyboard_navigate:"naviger",keyboard_select:"vel",keyboard_clear:"fjern",keyboard_close:"lukk",keyboard_search:"s\xF8k",error_search:"S\xF8k feila",filter_selected_one:"[COUNT] vald",filter_selected_many:"[COUNT] valde",input_hint:"Resultat visast medan du skriv",loading:"Lastar"},Di={thanks_to:Li,comments:Fi,direction:Pi,strings:zi};var bt={};h(bt,{comments:()=>Bi,default:()=>Vi,direction:()=>Ki,strings:()=>$i,thanks_to:()=>ji});var ji="Christopher Wingate",Bi="",Ki="ltr",$i={placeholder:"S\xF8k",clear_search:"Fjern",load_more:"Last flere resultater",search_label:"S\xF8k p\xE5 denne siden",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",total_zero_results:"Ingen resultater",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultater",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v en av disse s\xF8keordene i stedet:",searching:"S\xF8ker etter [SEARCH_TERM]",results_label:"S\xF8keresultater",keyboard_navigate:"naviger",keyboard_select:"velg",keyboard_clear:"fjern",keyboard_close:"lukk",keyboard_search:"s\xF8k",error_search:"S\xF8k feilet",filter_selected_one:"[COUNT] valgt",filter_selected_many:"[COUNT] valgte",input_hint:"Resultater vises mens du skriver",loading:"Laster"},Vi={thanks_to:ji,comments:Bi,direction:Ki,strings:$i};var Tt={};h(Tt,{comments:()=>Gi,default:()=>Ji,direction:()=>Wi,strings:()=>Yi,thanks_to:()=>qi});var qi="",Gi="",Wi="ltr",Yi={placeholder:"Szukaj",clear_search:"Wyczy\u015B\u0107",load_more:"Za\u0142aduj wi\u0119cej",search_label:"Przeszukaj t\u0119 stron\u0119",filters_label:"Filtry",zero_results:"Brak wynik\xF3w dla [SEARCH_TERM]",many_results:"[COUNT] wynik\xF3w dla [SEARCH_TERM]",one_result:"[COUNT] wynik dla [SEARCH_TERM]",total_zero_results:"Brak wynik\xF3w",total_one_result:"[COUNT] wynik",total_many_results:"[COUNT] wynik\xF3w",alt_search:"Brak wynik\xF3w dla [SEARCH_TERM]. Wy\u015Bwietlam wyniki dla [DIFFERENT_TERM]",search_suggestion:"Brak wynik\xF3w dla [SEARCH_TERM]. Pokrewne wyniki wyszukiwania:",searching:"Szukam [SEARCH_TERM]...",results_label:"Wyniki wyszukiwania",keyboard_navigate:"nawiguj",keyboard_select:"wybierz",keyboard_clear:"wyczy\u015B\u0107",keyboard_close:"zamknij",keyboard_search:"szukaj",error_search:"Wyszukiwanie nie powiod\u0142o si\u0119",filter_selected_one:"[COUNT] wybrany",filter_selected_many:"[COUNT] wybranych",input_hint:"Wyniki pojawi\u0105 si\u0119 podczas pisania",loading:"\u0141adowanie"},Ji={thanks_to:qi,comments:Gi,direction:Wi,strings:Yi};var Ct={};h(Ct,{comments:()=>Xi,default:()=>tn,direction:()=>Qi,strings:()=>en,thanks_to:()=>Zi});var Zi="Jonatah",Xi="",Qi="ltr",en={placeholder:"Pesquisar",clear_search:"Limpar",load_more:"Ver mais resultados",search_label:"Pesquisar",filters_label:"Filtros",zero_results:"Nenhum resultado encontrado para [SEARCH_TERM]",many_results:"[COUNT] resultados encontrados para [SEARCH_TERM]",one_result:"[COUNT] resultado encontrado para [SEARCH_TERM]",total_zero_results:"Nenhum resultado",total_one_result:"[COUNT] resultado",total_many_results:"[COUNT] resultados",alt_search:"Nenhum resultado encontrado para [SEARCH_TERM]. Exibindo resultados para [DIFFERENT_TERM]",search_suggestion:"Nenhum resultado encontrado para [SEARCH_TERM]. Tente uma das seguintes pesquisas:",searching:"Pesquisando por [SEARCH_TERM]...",results_label:"Resultados da pesquisa",keyboard_navigate:"navegar",keyboard_select:"selecionar",keyboard_clear:"limpar",keyboard_close:"fechar",keyboard_search:"pesquisar",error_search:"Falha na pesquisa",filter_selected_one:"[COUNT] selecionado",filter_selected_many:"[COUNT] selecionados",input_hint:"Os resultados aparecer\xE3o enquanto voc\xEA digita",loading:"Carregando"},tn={thanks_to:Zi,comments:Xi,direction:Qi,strings:en};var yt={};h(yt,{comments:()=>rn,default:()=>ln,direction:()=>nn,strings:()=>an,thanks_to:()=>sn});var sn="Bogdan Mateescu ",rn="",nn="ltr",an={placeholder:"C\u0103utare",clear_search:"\u015Eterge\u0163i",load_more:"\xCEnc\u0103rca\u021Bi mai multe rezultate",search_label:"C\u0103uta\u021Bi \xEEn acest site",filters_label:"Filtre",zero_results:"Niciun rezultat pentru [SEARCH_TERM]",many_results:"[COUNT] rezultate pentru [SEARCH_TERM]",one_result:"[COUNT] rezultat pentru [SEARCH_TERM]",total_zero_results:"Niciun rezultat",total_one_result:"[COUNT] rezultat",total_many_results:"[COUNT] rezultate",alt_search:"Niciun rezultat pentru [SEARCH_TERM]. Se afi\u0219eaz\u0103 \xEEn schimb rezultatele pentru [DIFFERENT_TERM]",search_suggestion:"Niciun rezultat pentru [SEARCH_TERM]. \xCEncerca\u021Bi una dintre urm\u0103toarele c\u0103ut\u0103ri:",searching:"Se caut\u0103 dup\u0103: [SEARCH_TERM]...",results_label:"Rezultatele c\u0103ut\u0103rii",keyboard_navigate:"navigare",keyboard_select:"selectare",keyboard_clear:"\u0219tergere",keyboard_close:"\xEEnchidere",keyboard_search:"c\u0103utare",error_search:"C\u0103utarea a e\u0219uat",filter_selected_one:"[COUNT] selectat",filter_selected_many:"[COUNT] selectate",input_hint:"Rezultatele vor ap\u0103rea pe m\u0103sur\u0103 ce tasta\u021Bi",loading:"Se \xEEncarc\u0103"},ln={thanks_to:sn,comments:rn,direction:nn,strings:an};var vt={};h(vt,{comments:()=>cn,default:()=>hn,direction:()=>un,strings:()=>dn,thanks_to:()=>on});var on="Aleksandr Gordeev",cn="",un="ltr",dn={placeholder:"\u041F\u043E\u0438\u0441\u043A",clear_search:"\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u043F\u043E\u043B\u0435",load_more:"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0435",search_label:"\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0441\u0430\u0439\u0442\u0443",filters_label:"\u0424\u0438\u043B\u044C\u0442\u0440\u044B",zero_results:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u0432 \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",total_zero_results:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E",total_one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442",total_many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u0432",alt_search:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]. \u041F\u043E\u043A\u0430\u0437\u0430\u043D\u044B \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [DIFFERENT_TERM]",search_suggestion:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043E\u0434\u0438\u043D \u0438\u0437 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432",searching:"\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",results_label:"\u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u043F\u043E\u0438\u0441\u043A\u0430",keyboard_navigate:"\u043D\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044F",keyboard_select:"\u0432\u044B\u0431\u0440\u0430\u0442\u044C",keyboard_clear:"\u043E\u0447\u0438\u0441\u0442\u0438\u0442\u044C",keyboard_close:"\u0437\u0430\u043A\u0440\u044B\u0442\u044C",keyboard_search:"\u043F\u043E\u0438\u0441\u043A",error_search:"\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u043E\u0438\u0441\u043A\u0430",filter_selected_one:"[COUNT] \u0432\u044B\u0431\u0440\u0430\u043D",filter_selected_many:"[COUNT] \u0432\u044B\u0431\u0440\u0430\u043D\u043E",input_hint:"\u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u044F\u0432\u043B\u044F\u0442\u044C\u0441\u044F \u043F\u043E \u043C\u0435\u0440\u0435 \u0432\u0432\u043E\u0434\u0430",loading:"\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430"},hn={thanks_to:on,comments:cn,direction:un,strings:dn};var Rt={};h(Rt,{comments:()=>mn,default:()=>gn,direction:()=>_n,strings:()=>fn,thanks_to:()=>pn});var pn="Andrija Sagicc",mn="",_n="ltr",fn={placeholder:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430",clear_search:"\u0411\u0440\u0438\u0441\u0430\u045A\u0435",load_more:"\u041F\u0440\u0438\u043A\u0430\u0437 \u0432\u0438\u0448\u0435 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430",search_label:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u0441\u0430\u0458\u0442\u0430",filters_label:"\u0424\u0438\u043B\u0442\u0435\u0440\u0438",zero_results:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",total_zero_results:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430",total_one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442",total_many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430",alt_search:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]. \u041F\u0440\u0438\u043A\u0430\u0437 \u0434\u043E\u0434\u0430\u0442\u043D\u0438\u043A \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [DIFFERENT_TERM]",search_suggestion:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]. \u041F\u043E\u043A\u0443\u0448\u0430\u0458\u0442\u0435 \u0441\u0430 \u043D\u0435\u043A\u043E\u043C \u043E\u0434 \u0441\u043B\u0435\u0434\u0435\u045B\u0438\u0445 \u043F\u0440\u0435\u0442\u0440\u0430\u0433\u0430:",searching:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u0442\u0435\u0440\u043C\u0438\u043D\u0430 [SEARCH_TERM]...",results_label:"\u0420\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0438 \u043F\u0440\u0435\u0442\u0440\u0430\u0433\u0435",keyboard_navigate:"\u043D\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0458\u0430",keyboard_select:"\u0438\u0437\u0430\u0431\u0435\u0440\u0438",keyboard_clear:"\u043E\u0431\u0440\u0438\u0448\u0438",keyboard_close:"\u0437\u0430\u0442\u0432\u043E\u0440\u0438",keyboard_search:"\u043F\u0440\u0435\u0442\u0440\u0430\u0433\u0430",error_search:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u043D\u0438\u0458\u0435 \u0443\u0441\u043F\u0435\u043B\u0430",filter_selected_one:"[COUNT] \u0438\u0437\u0430\u0431\u0440\u0430\u043D",filter_selected_many:"[COUNT] \u0438\u0437\u0430\u0431\u0440\u0430\u043D\u0438\u0445",input_hint:"\u0420\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0438 \u045B\u0435 \u0441\u0435 \u043F\u043E\u0458\u0430\u0432\u0459\u0438\u0432\u0430\u0442\u0438 \u0434\u043E\u043A \u043A\u0443\u0446\u0430\u0442\u0435",loading:"\u0423\u0447\u0438\u0442\u0430\u0432\u0430\u045A\u0435"},gn={thanks_to:pn,comments:mn,direction:_n,strings:fn};var kt={};h(kt,{comments:()=>bn,default:()=>yn,direction:()=>Tn,strings:()=>Cn,thanks_to:()=>En});var En="Montazar Al-Jaber ",bn="",Tn="ltr",Cn={placeholder:"S\xF6k",clear_search:"Rensa",load_more:"Visa fler tr\xE4ffar",search_label:"S\xF6k p\xE5 denna sida",filters_label:"Filter",zero_results:"[SEARCH_TERM] gav inga tr\xE4ffar",many_results:"[SEARCH_TERM] gav [COUNT] tr\xE4ffar",one_result:"[SEARCH_TERM] gav [COUNT] tr\xE4ff",total_zero_results:"Inga tr\xE4ffar",total_one_result:"[COUNT] tr\xE4ff",total_many_results:"[COUNT] tr\xE4ffar",alt_search:"[SEARCH_TERM] gav inga tr\xE4ffar. Visar resultat f\xF6r [DIFFERENT_TERM] ist\xE4llet",search_suggestion:"[SEARCH_TERM] gav inga tr\xE4ffar. F\xF6rs\xF6k igen med en av f\xF6ljande s\xF6kord:",searching:"S\xF6ker efter [SEARCH_TERM]...",results_label:"S\xF6kresultat",keyboard_navigate:"navigera",keyboard_select:"v\xE4lj",keyboard_clear:"rensa",keyboard_close:"st\xE4ng",keyboard_search:"s\xF6k",error_search:"S\xF6kningen misslyckades",filter_selected_one:"[COUNT] vald",filter_selected_many:"[COUNT] valda",input_hint:"Resultat visas medan du skriver",loading:"L\xE4ser in"},yn={thanks_to:En,comments:bn,direction:Tn,strings:Cn};var At={};h(At,{comments:()=>Rn,default:()=>Sn,direction:()=>kn,strings:()=>An,thanks_to:()=>vn});var vn="Anonymous",Rn="",kn="ltr",An={placeholder:"Tafuta",clear_search:"Futa",load_more:"Pakia matokeo zaidi",search_label:"Tafuta tovuti hii",filters_label:"Vichujio",zero_results:"Hakuna matokeo ya [SEARCH_TERM]",many_results:"Matokeo [COUNT] ya [SEARCH_TERM]",one_result:"Tokeo [COUNT] la [SEARCH_TERM]",total_zero_results:"Hakuna matokeo",total_one_result:"Tokeo [COUNT]",total_many_results:"Matokeo [COUNT]",alt_search:"Hakuna mayokeo ya [SEARCH_TERM]. Badala yake, inaonyesha matokeo ya [DIFFERENT_TERM]",search_suggestion:"Hakuna matokeo ya [SEARCH_TERM]. Jaribu mojawapo ya utafutaji ufuatao:",searching:"Kutafuta [SEARCH_TERM]...",results_label:"Matokeo ya utafutaji",keyboard_navigate:"sogeza",keyboard_select:"chagua",keyboard_clear:"futa",keyboard_close:"funga",keyboard_search:"tafuta",error_search:"Utafutaji umeshindwa",filter_selected_one:"[COUNT] imechaguliwa",filter_selected_many:"[COUNT] zimechaguliwa",input_hint:"Matokeo yataonekana unapoandika",loading:"Inapakia"},Sn={thanks_to:vn,comments:Rn,direction:kn,strings:An};var St={};h(St,{comments:()=>Mn,default:()=>Hn,direction:()=>On,strings:()=>xn,thanks_to:()=>Nn});var Nn="",Mn="",On="ltr",xn={placeholder:"\u0BA4\u0BC7\u0B9F\u0BC1\u0B95",clear_search:"\u0B85\u0BB4\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0B95",load_more:"\u0BAE\u0BC7\u0BB2\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BC8\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1\u0B95",search_label:"\u0B87\u0BA8\u0BCD\u0BA4 \u0BA4\u0BB3\u0BA4\u0BCD\u0BA4\u0BBF\u0BB2\u0BCD \u0BA4\u0BC7\u0B9F\u0BC1\u0B95",filters_label:"\u0BB5\u0B9F\u0BBF\u0B95\u0B9F\u0BCD\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BCD",zero_results:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8",many_results:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 [COUNT] \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",one_result:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1",total_zero_results:"\u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8",total_one_result:"[COUNT] \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1",total_many_results:"[COUNT] \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",alt_search:"[SEARCH_TERM] \u0B87\u0BA4\u0BCD\u0BA4\u0BC7\u0B9F\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8, \u0B87\u0BA8\u0BCD\u0BA4 \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0B92\u0BA4\u0BCD\u0BA4 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD [DIFFERENT_TERM]",search_suggestion:"[SEARCH_TERM] \u0B87\u0BA4\u0BCD \u0BA4\u0BC7\u0B9F\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8.\u0B87\u0BA4\u0BB1\u0BCD\u0B95\u0BC1 \u0BAA\u0BA4\u0BBF\u0BB2\u0BC0\u0B9F\u0BBE\u0BA9 \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BC8 \u0BA4\u0BC7\u0B9F\u0BC1\u0B95:",searching:"[SEARCH_TERM] \u0BA4\u0BC7\u0B9F\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0B95\u0BBF\u0BA9\u0BCD\u0BB1\u0BA4\u0BC1",results_label:"\u0BA4\u0BC7\u0B9F\u0BB2\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",keyboard_navigate:"\u0BB5\u0BB4\u0BBF\u0BA8\u0B9F\u0BA4\u0BCD\u0BA4\u0BC1",keyboard_select:"\u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1",keyboard_clear:"\u0B85\u0BB4\u0BBF",keyboard_close:"\u0BAE\u0BC2\u0B9F\u0BC1",keyboard_search:"\u0BA4\u0BC7\u0B9F\u0BC1",error_search:"\u0BA4\u0BC7\u0B9F\u0BB2\u0BCD \u0BA4\u0BCB\u0BB2\u0BCD\u0BB5\u0BBF",filter_selected_one:"[COUNT] \u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1",filter_selected_many:"[COUNT] \u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA9",input_hint:"\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BA4\u0B9F\u0BCD\u0B9F\u0B9A\u0BCD\u0B9A\u0BC1 \u0B9A\u0BC6\u0BAF\u0BCD\u0BAF\u0BC1\u0BAE\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0BA4\u0BCB\u0BA9\u0BCD\u0BB1\u0BC1\u0BAE\u0BCD",loading:"\u0B8F\u0BB1\u0BCD\u0BB1\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1"},Hn={thanks_to:Nn,comments:Mn,direction:On,strings:xn};var Nt={};h(Nt,{comments:()=>In,default:()=>Fn,direction:()=>Un,strings:()=>Ln,thanks_to:()=>wn});var wn="Patiphon Loetsuthakun ",In="",Un="ltr",Ln={placeholder:"\u0E04\u0E49\u0E19\u0E2B\u0E32",clear_search:"\u0E25\u0E49\u0E32\u0E07",load_more:"\u0E42\u0E2B\u0E25\u0E14\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E40\u0E15\u0E34\u0E21",search_label:"\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E1A\u0E19\u0E40\u0E27\u0E47\u0E1A\u0E44\u0E0B\u0E15\u0E4C",filters_label:"\u0E15\u0E31\u0E27\u0E01\u0E23\u0E2D\u0E07",zero_results:"\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM]",many_results:"\u0E1E\u0E1A [COUNT] \u0E1C\u0E25\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM]",one_result:"\u0E1E\u0E1A [COUNT] \u0E1C\u0E25\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM]",total_zero_results:"\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C",total_one_result:"[COUNT] \u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C",total_many_results:"[COUNT] \u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C",alt_search:"\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM] \u0E41\u0E2A\u0E14\u0E07\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E08\u0E32\u0E01\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32 [DIFFERENT_TERM] \u0E41\u0E17\u0E19",search_suggestion:"\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM] \u0E25\u0E2D\u0E07\u0E04\u0E33\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E40\u0E2B\u0E25\u0E48\u0E32\u0E19\u0E35\u0E49\u0E41\u0E17\u0E19:",searching:"\u0E01\u0E33\u0E25\u0E31\u0E07\u0E04\u0E49\u0E19\u0E2B\u0E32 [SEARCH_TERM]...",results_label:"\u0E1C\u0E25\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32",keyboard_navigate:"\u0E19\u0E33\u0E17\u0E32\u0E07",keyboard_select:"\u0E40\u0E25\u0E37\u0E2D\u0E01",keyboard_clear:"\u0E25\u0E49\u0E32\u0E07",keyboard_close:"\u0E1B\u0E34\u0E14",keyboard_search:"\u0E04\u0E49\u0E19\u0E2B\u0E32",error_search:"\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E25\u0E49\u0E21\u0E40\u0E2B\u0E25\u0E27",filter_selected_one:"\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E41\u0E25\u0E49\u0E27 [COUNT] \u0E23\u0E32\u0E22\u0E01\u0E32\u0E23",filter_selected_many:"\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E41\u0E25\u0E49\u0E27 [COUNT] \u0E23\u0E32\u0E22\u0E01\u0E32\u0E23",input_hint:"\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E08\u0E30\u0E1B\u0E23\u0E32\u0E01\u0E0F\u0E02\u0E13\u0E30\u0E17\u0E35\u0E48\u0E04\u0E38\u0E13\u0E1E\u0E34\u0E21\u0E1E\u0E4C",loading:"\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14"},Fn={thanks_to:wn,comments:In,direction:Un,strings:Ln};var Mt={};h(Mt,{comments:()=>zn,default:()=>Bn,direction:()=>Dn,strings:()=>jn,thanks_to:()=>Pn});var Pn="Taylan \xD6zg\xFCr Bildik",zn="",Dn="ltr",jn={placeholder:"Ara\u015Ft\u0131r",clear_search:"Temizle",load_more:"Daha fazla sonu\xE7",search_label:"Site genelinde arama",filters_label:"Filtreler",zero_results:"[SEARCH_TERM] i\xE7in sonu\xE7 yok",many_results:"[SEARCH_TERM] i\xE7in [COUNT] sonu\xE7 bulundu",one_result:"[SEARCH_TERM] i\xE7in [COUNT] sonu\xE7 bulundu",total_zero_results:"Sonu\xE7 yok",total_one_result:"[COUNT] sonu\xE7",total_many_results:"[COUNT] sonu\xE7",alt_search:"[SEARCH_TERM] i\xE7in sonu\xE7 yok. Bunun yerine [DIFFERENT_TERM] i\xE7in sonu\xE7lar g\xF6steriliyor",search_suggestion:"[SEARCH_TERM] i\xE7in sonu\xE7 yok. Alternatif olarak a\u015Fa\u011F\u0131daki kelimelerden birini deneyebilirsiniz:",searching:"[SEARCH_TERM] ara\u015Ft\u0131r\u0131l\u0131yor...",results_label:"Arama sonu\xE7lar\u0131",keyboard_navigate:"gezin",keyboard_select:"se\xE7",keyboard_clear:"temizle",keyboard_close:"kapat",keyboard_search:"ara",error_search:"Arama ba\u015Far\u0131s\u0131z",filter_selected_one:"[COUNT] se\xE7ili",filter_selected_many:"[COUNT] se\xE7ili",input_hint:"Sonu\xE7lar siz yazarken g\xF6r\xFCnecektir",loading:"Y\xFCkleniyor"},Bn={thanks_to:Pn,comments:zn,direction:Dn,strings:jn};var Ot={};h(Ot,{comments:()=>$n,default:()=>Gn,direction:()=>Vn,strings:()=>qn,thanks_to:()=>Kn});var Kn="Vladyslav Lyshenko ",$n="",Vn="ltr",qn={placeholder:"\u041F\u043E\u0448\u0443\u043A",clear_search:"\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u043F\u043E\u043B\u0435",load_more:"\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0449\u0435",search_label:"\u041F\u043E\u0448\u0443\u043A \u043F\u043E \u0441\u0430\u0439\u0442\u0443",filters_label:"\u0424\u0456\u043B\u044C\u0442\u0440\u0438",zero_results:"\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E \u0437\u0430 \u0437\u0430\u043F\u0438\u0442\u043E\u043C: [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0456\u0432 \u043D\u0430 \u0437\u0430\u043F\u0438\u0442: [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u0437\u0430 \u0437\u0430\u043F\u0438\u0442\u043E\u043C: [SEARCH_TERM]",total_zero_results:"\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E",total_one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442",total_many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0456\u0432",alt_search:"\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043D\u0430 \u0437\u0430\u043F\u0438\u0442: [SEARCH_TERM]. \u041F\u043E\u043A\u0430\u0437\u0430\u043D\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0438 \u043D\u0430 \u0437\u0430\u043F\u0438\u0442: [DIFFERENT_TERM]",search_suggestion:"\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043D\u0430 \u0437\u0430\u043F\u0438\u0442: [SEARCH_TERM]. \u0421\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043E\u0434\u0438\u043D \u0456\u0437 \u0442\u0430\u043A\u0438\u0445 \u0432\u0430\u0440\u0456\u0430\u043D\u0442\u0456\u0432",searching:"\u041F\u043E\u0448\u0443\u043A \u0437\u0430 \u0437\u0430\u043F\u0438\u0442\u043E\u043C: [SEARCH_TERM]",results_label:"\u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0438 \u043F\u043E\u0448\u0443\u043A\u0443",keyboard_navigate:"\u043D\u0430\u0432\u0456\u0433\u0430\u0446\u0456\u044F",keyboard_select:"\u0432\u0438\u0431\u0440\u0430\u0442\u0438",keyboard_clear:"\u043E\u0447\u0438\u0441\u0442\u0438\u0442\u0438",keyboard_close:"\u0437\u0430\u043A\u0440\u0438\u0442\u0438",keyboard_search:"\u043F\u043E\u0448\u0443\u043A",error_search:"\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u043F\u043E\u0448\u0443\u043A\u0443",filter_selected_one:"[COUNT] \u0432\u0438\u0431\u0440\u0430\u043D\u043E",filter_selected_many:"[COUNT] \u0432\u0438\u0431\u0440\u0430\u043D\u043E",input_hint:"\u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0438 \u0437'\u044F\u0432\u043B\u044F\u0442\u0438\u043C\u0443\u0442\u044C\u0441\u044F \u043F\u0456\u0434 \u0447\u0430\u0441 \u0432\u0432\u0435\u0434\u0435\u043D\u043D\u044F",loading:"\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F"},Gn={thanks_to:Kn,comments:$n,direction:Vn,strings:qn};var xt={};h(xt,{comments:()=>Yn,default:()=>Xn,direction:()=>Jn,strings:()=>Zn,thanks_to:()=>Wn});var Wn="Long Nhat Nguyen",Yn="",Jn="ltr",Zn={placeholder:"T\xECm ki\u1EBFm",clear_search:"X\xF3a",load_more:"Nhi\u1EC1u k\u1EBFt qu\u1EA3 h\u01A1n",search_label:"T\xECm ki\u1EBFm trong trang n\xE0y",filters_label:"B\u1ED9 l\u1ECDc",zero_results:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",many_results:"[COUNT] k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",one_result:"[COUNT] k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",total_zero_results:"Kh\xF4ng c\xF3 k\u1EBFt qu\u1EA3",total_one_result:"[COUNT] k\u1EBFt qu\u1EA3",total_many_results:"[COUNT] k\u1EBFt qu\u1EA3",alt_search:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]. Ki\u1EC3m th\u1ECB k\u1EBFt qu\u1EA3 thay th\u1EBF v\u1EDBi [DIFFERENT_TERM]",search_suggestion:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]. Th\u1EED m\u1ED9t trong c\xE1c t\xECm ki\u1EBFm:",searching:"\u0110ang t\xECm ki\u1EBFm cho [SEARCH_TERM]...",results_label:"K\u1EBFt qu\u1EA3 t\xECm ki\u1EBFm",keyboard_navigate:"chuy\u1EC3n",keyboard_select:"ch\u1ECDn",keyboard_clear:"x\xF3a",keyboard_close:"\u0111\xF3ng",keyboard_search:"t\xECm ki\u1EBFm",error_search:"T\xECm ki\u1EBFm th\u1EA5t b\u1EA1i",filter_selected_one:"\u0110\xE3 ch\u1ECDn [COUNT]",filter_selected_many:"\u0110\xE3 ch\u1ECDn [COUNT]",input_hint:"K\u1EBFt qu\u1EA3 s\u1EBD xu\u1EA5t hi\u1EC7n khi b\u1EA1n nh\u1EADp",loading:"\u0110ang t\u1EA3i"},Xn={thanks_to:Wn,comments:Yn,direction:Jn,strings:Zn};var Ht={};h(Ht,{comments:()=>ea,default:()=>ra,direction:()=>ta,strings:()=>sa,thanks_to:()=>Qn});var Qn="Amber Song",ea="",ta="ltr",sa={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F7D\u66F4\u591A\u7ED3\u679C",search_label:"\u7AD9\u5185\u641C\u7D22",filters_label:"\u7B5B\u9009",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",many_results:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",one_result:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",total_zero_results:"\u65E0\u7ED3\u679C",total_one_result:"[COUNT] \u4E2A\u7ED3\u679C",total_many_results:"[COUNT] \u4E2A\u7ED3\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u6539\u4E3A\u663E\u793A [DIFFERENT_TERM] \u7684\u76F8\u5173\u7ED3\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u8BF7\u5C1D\u8BD5\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]...",results_label:"\u641C\u7D22\u7ED3\u679C",keyboard_navigate:"\u5BFC\u822A",keyboard_select:"\u9009\u62E9",keyboard_clear:"\u6E05\u9664",keyboard_close:"\u5173\u95ED",keyboard_search:"\u641C\u7D22",error_search:"\u641C\u7D22\u5931\u8D25",filter_selected_one:"\u5DF2\u9009\u62E9 [COUNT] \u4E2A",filter_selected_many:"\u5DF2\u9009\u62E9 [COUNT] \u4E2A",input_hint:"\u8F93\u5165\u65F6\u5C06\u663E\u793A\u7ED3\u679C",loading:"\u52A0\u8F7D\u4E2D"},ra={thanks_to:Qn,comments:ea,direction:ta,strings:sa};var wt={};h(wt,{comments:()=>na,default:()=>oa,direction:()=>aa,strings:()=>la,thanks_to:()=>ia});var ia="Amber Song",na="",aa="ltr",la={placeholder:"\u641C\u5C0B",clear_search:"\u6E05\u9664",load_more:"\u8F09\u5165\u66F4\u591A\u7D50\u679C",search_label:"\u7AD9\u5167\u641C\u5C0B",filters_label:"\u7BE9\u9078",zero_results:"\u627E\u4E0D\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",many_results:"\u627E\u5230 [COUNT] \u500B [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",one_result:"\u627E\u5230 [COUNT] \u500B [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",total_zero_results:"\u7121\u7D50\u679C",total_one_result:"[COUNT] \u500B\u7D50\u679C",total_many_results:"[COUNT] \u500B\u7D50\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C\u3002\u6539\u70BA\u986F\u793A [DIFFERENT_TERM] \u7684\u76F8\u95DC\u7D50\u679C",search_suggestion:"\u627E\u4E0D\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C\u3002\u8ACB\u5617\u8A66\u4EE5\u4E0B\u7684\u5EFA\u8B70\u4E4B\u4E00\u3002",searching:"\u6B63\u5728\u641C\u5C0B[SEARCH_TERM]...",results_label:"\u641C\u5C0B\u7D50\u679C",keyboard_navigate:"\u5C0E\u89BD",keyboard_select:"\u9078\u64C7",keyboard_clear:"\u6E05\u9664",keyboard_close:"\u95DC\u9589",keyboard_search:"\u641C\u5C0B",error_search:"\u641C\u5C0B\u5931\u6557",filter_selected_one:"\u5DF2\u9078\u64C7 [COUNT] \u500B",filter_selected_many:"\u5DF2\u9078\u64C7 [COUNT] \u500B",input_hint:"\u8F38\u5165\u6642\u5C07\u986F\u793A\u7D50\u679C",loading:"\u8F09\u5165\u4E2D"},oa={thanks_to:ia,comments:na,direction:aa,strings:la};var It={};h(It,{comments:()=>ua,default:()=>pa,direction:()=>da,strings:()=>ha,thanks_to:()=>ca});var ca="Amber Song",ua="",da="ltr",ha={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F7D\u66F4\u591A\u7ED3\u679C",search_label:"\u7AD9\u5185\u641C\u7D22",filters_label:"\u7B5B\u9009",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",many_results:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",one_result:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",total_zero_results:"\u65E0\u7ED3\u679C",total_one_result:"[COUNT] \u4E2A\u7ED3\u679C",total_many_results:"[COUNT] \u4E2A\u7ED3\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u6539\u4E3A\u663E\u793A [DIFFERENT_TERM] \u7684\u76F8\u5173\u7ED3\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u8BF7\u5C1D\u8BD5\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]...",results_label:"\u641C\u7D22\u7ED3\u679C",keyboard_navigate:"\u5BFC\u822A",keyboard_select:"\u9009\u62E9",keyboard_clear:"\u6E05\u9664",keyboard_close:"\u5173\u95ED",keyboard_search:"\u641C\u7D22",error_search:"\u641C\u7D22\u5931\u8D25",filter_selected_one:"\u5DF2\u9009\u62E9 [COUNT] \u4E2A",filter_selected_many:"\u5DF2\u9009\u62E9 [COUNT] \u4E2A",input_hint:"\u8F93\u5165\u65F6\u5C06\u663E\u793A\u7ED3\u679C",loading:"\u52A0\u8F7D\u4E2D"},pa={thanks_to:ca,comments:ua,direction:da,strings:ha};var ma=[Ve,qe,Ge,We,Ye,Je,Ze,Xe,Qe,et,tt,st,rt,it,nt,at,lt,ot,ct,ut,dt,ht,pt,mt,_t,ft,gt,Et,bt,Tt,Ct,yt,vt,Rt,kt,At,St,Nt,Mt,Ot,xt,Ht,wt,It],Vt=ma,qt=["../../translations/af.json","../../translations/ar.json","../../translations/bn.json","../../translations/ca.json","../../translations/cs.json","../../translations/da.json","../../translations/de.json","../../translations/el.json","../../translations/en.json","../../translations/es.json","../../translations/eu.json","../../translations/fa.json","../../translations/fi.json","../../translations/fr.json","../../translations/gl.json","../../translations/he.json","../../translations/hi.json","../../translations/hr.json","../../translations/hu.json","../../translations/id.json","../../translations/it.json","../../translations/ja.json","../../translations/ko.json","../../translations/mi.json","../../translations/my.json","../../translations/nb.json","../../translations/nl.json","../../translations/nn.json","../../translations/no.json","../../translations/pl.json","../../translations/pt.json","../../translations/ro.json","../../translations/ru.json","../../translations/sr.json","../../translations/sv.json","../../translations/sw.json","../../translations/ta.json","../../translations/th.json","../../translations/tr.json","../../translations/uk.json","../../translations/vi.json","../../translations/zh-cn.json","../../translations/zh-tw.json","../../translations/zh.json"];function b(n){let s=typeof n=="string"?n.charCodeAt(0):n;return s>=97&&s<=122||s>=65&&s<=90}function A(n){let s=typeof n=="string"?n.charCodeAt(0):n;return s>=48&&s<=57}function k(n){return b(n)||A(n)}var Gt=["art-lojban","cel-gaulish","no-bok","no-nyn","zh-guoyu","zh-hakka","zh-min","zh-min-nan","zh-xiang"];var Ut={"en-gb-oed":"en-GB-oxendict","i-ami":"ami","i-bnn":"bnn","i-default":null,"i-enochian":null,"i-hak":"hak","i-klingon":"tlh","i-lux":"lb","i-mingo":null,"i-navajo":"nv","i-pwn":"pwn","i-tao":"tao","i-tay":"tay","i-tsu":"tsu","sgn-be-fr":"sfb","sgn-be-nl":"vgt","sgn-ch-de":"sgg","art-lojban":"jbo","cel-gaulish":null,"no-bok":"nb","no-nyn":"nn","zh-guoyu":"cmn","zh-hakka":"hak","zh-min":null,"zh-min-nan":"nan","zh-xiang":"hsn"};var fa={}.hasOwnProperty;function ze(n,s={}){let e=Wt(),t=String(n),r=t.toLowerCase(),i=0;if(n==null)throw new Error("Expected string, got `"+n+"`");if(fa.call(Ut,r)){let l=Ut[r];return(s.normalize===void 0||s.normalize===null||s.normalize)&&typeof l=="string"?ze(l):(e[Gt.includes(r)?"regular":"irregular"]=t,e)}for(;b(r.charCodeAt(i))&&i<9;)i++;if(i>1&&i<9){if(e.language=t.slice(0,i),i<4){let l=0;for(;r.charCodeAt(i)===45&&b(r.charCodeAt(i+1))&&b(r.charCodeAt(i+2))&&b(r.charCodeAt(i+3))&&!b(r.charCodeAt(i+4));){if(l>2)return a(i,3,"Too many extended language subtags, expected at most 3 subtags");e.extendedLanguageSubtags.push(t.slice(i+1,i+4)),i+=4,l++}}for(r.charCodeAt(i)===45&&b(r.charCodeAt(i+1))&&b(r.charCodeAt(i+2))&&b(r.charCodeAt(i+3))&&b(r.charCodeAt(i+4))&&!b(r.charCodeAt(i+5))&&(e.script=t.slice(i+1,i+5),i+=5),r.charCodeAt(i)===45&&(b(r.charCodeAt(i+1))&&b(r.charCodeAt(i+2))&&!b(r.charCodeAt(i+3))?(e.region=t.slice(i+1,i+3),i+=3):A(r.charCodeAt(i+1))&&A(r.charCodeAt(i+2))&&A(r.charCodeAt(i+3))&&!A(r.charCodeAt(i+4))&&(e.region=t.slice(i+1,i+4),i+=4));r.charCodeAt(i)===45;){let l=i+1,o=l;for(;k(r.charCodeAt(o));){if(o-l>7)return a(o,1,"Too long variant, expected at most 8 characters");o++}if(o-l>4||o-l>3&&A(r.charCodeAt(l)))e.variants.push(t.slice(l,o)),i=o;else break}for(;r.charCodeAt(i)===45&&!(r.charCodeAt(i+1)===120||!k(r.charCodeAt(i+1))||r.charCodeAt(i+2)!==45||!k(r.charCodeAt(i+3)));){let l=i+2,o=0;for(;r.charCodeAt(l)===45&&k(r.charCodeAt(l+1))&&k(r.charCodeAt(l+2));){let c=l+1;for(l=c+2,o++;k(r.charCodeAt(l));){if(l-c>7)return a(l,2,"Too long extension, expected at most 8 characters");l++}}if(!o)return a(l,4,"Empty extension, extensions must have at least 2 characters of content");e.extensions.push({singleton:t.charAt(i+1),extensions:t.slice(i+3,l).split("-")}),i=l}}else i=0;if(i===0&&r.charCodeAt(i)===120||r.charCodeAt(i)===45&&r.charCodeAt(i+1)===120){i=i?i+2:1;let l=i;for(;r.charCodeAt(l)===45&&k(r.charCodeAt(l+1));){let o=i+1;for(l=o;k(r.charCodeAt(l));){if(l-o>7)return a(l,5,"Too long private-use area, expected at most 8 characters");l++}e.privateuse.push(t.slice(i+1,l)),i=l}}if(i!==t.length)return a(i,6,"Found superfluous content after tag");return e;function a(l,o,c){return s.warning&&s.warning(c,o,l),s.forgiving?e:Wt()}}function Wt(){return{language:null,extendedLanguageSubtags:[],script:null,region:null,variants:[],extensions:[],privateuse:[],irregular:null,regular:null}}var be={},Yt=qt,Jt=Vt;for(let n=0;n"u")return;let s=document.createElement("div");s.id=this.containerId,s.setAttribute("data-pagefind-announcer","");let e=t=>{let r=[];for(let i=0;i<2;i++){let a=document.createElement("div");a.id=this.idGenerator(`pf-${t}-region`),a.setAttribute("role","status"),a.setAttribute("aria-live",t),a.setAttribute("aria-atomic","true"),a.setAttribute("data-pf-sr-hidden",""),s.appendChild(a),r.push(a)}return r};this.regions={polite:e("polite"),assertive:e("assertive")},document.body.appendChild(s)}announce(s,e="polite"){if(!this.regions||!s)return;this.clearTimeoutId&&(clearTimeout(this.clearTimeoutId),this.clearTimeoutId=null);let t=e==="polite"?this.politeIndex:this.assertiveIndex,r=this.regions[e][t];e==="polite"?this.politeIndex=t===0?1:0:this.assertiveIndex=t===0?1:0;let i=e==="polite"?this.politeIndex:this.assertiveIndex;this.regions[e][i].textContent="",setTimeout(()=>{r.textContent=s,this.clearTimeoutId=setTimeout(()=>{r.textContent="",this.clearTimeoutId=null},350)},100)}clear(){if(this.regions){this.clearTimeoutId&&(clearTimeout(this.clearTimeoutId),this.clearTimeoutId=null);for(let s of["polite","assertive"])for(let e of this.regions[s])e.textContent=""}}destroy(){if(this.clear(),typeof document<"u"){let s=document.getElementById(this.containerId);s&&s.remove()}this.regions=null}};var Lt;try{if(document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"){let n=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind[-_])?component[-_]?ui.js.*$/);n&&(Lt=n[1])}}catch{Lt="/pagefind/"}var Te=class{constructor(s,e={}){this.__pagefind__=null;this.__loadPromise__=null;this.__searchID__=0;this._translations=null;this._userTranslations={};this._direction="ltr";this._languageSet=!1;this.components=[];this.componentsByType={};this.searchTerm="";this.searchFilters={};this.searchResult={results:[]};this.availableFilters=null;this.totalFilters=null;this.activeShortcuts=[];this.faceted=!1;this.generatedIds=new Set;this.name=s,this.__hooks__={search:[],filters:[],loading:[],results:[],error:[],translations:[]},this.options={bundlePath:e.bundlePath??Lt??"/pagefind/",mergeIndex:e.mergeIndex??[]};let t={...e};delete t.bundlePath,delete t.mergeIndex,this.pagefindOptions=t,this._announcer=new De(this.generateId.bind(this))}generateId(s,e=2){let t="abcdef",r=(o=3)=>{let c="";for(let u=0;ur()).join("-"),l=`${s}-${i}${a}`;return this.generatedIds.has(l)||document.getElementById(l)?this.generateId(s,e+1):(this.generatedIds.add(l),l)}add(s){s?.register?.(this),this.components.push(s)}registerInput(s,e={}){this._registerComponent(s,"input",null,e)}registerResults(s,e={}){this._registerComponent(s,"results",null,e)}registerSummary(s,e={}){this._registerComponent(s,"summary",null,e)}registerFilter(s,e={}){this._registerComponent(s,"filter",null,e)}registerSort(s,e={}){this._registerComponent(s,"sort",null,e)}registerUtility(s,e=null,t={}){this._registerComponent(s,"utility",e,t)}_registerComponent(s,e,t=null,r={}){if(this.componentsByType[e]||(this.componentsByType[e]=[]),this._languageSet||this.setLanguage(),this.components.includes(s)){s.capabilities=r,this.reconcileAria();return}s.componentType=e,s.componentSubtype=t,s.capabilities=r,this.componentsByType[e].push(s),this.components.push(s),this.reconcileAria()}getInputs(s=null){let e=this.componentsByType.input||[];return s?e.filter(t=>t.capabilities?.[s]):e}getResults(s=null){let e=this.componentsByType.results||[];return s?e.filter(t=>t.capabilities?.[s]):e}getSummaries(s=null){let e=this.componentsByType.summary||[];return s?e.filter(t=>t.capabilities?.[s]):e}getFilters(s=null){let e=this.componentsByType.filter||[];return s?e.filter(t=>t.capabilities?.[s]):e}getSorts(s=null){let e=this.componentsByType.sort||[];return s?e.filter(t=>t.capabilities?.[s]):e}getUtilities(s=null,e=null){let t=this.componentsByType.utility||[];return s!==null&&(t=t.filter(r=>r.componentSubtype===s)),e&&(t=t.filter(r=>r.capabilities?.[e])),t}hasAnnouncementCapability(){return this.components.some(s=>s.capabilities?.announcements===!0)}registerShortcut(s,e){let t={...s,owner:e};this.activeShortcuts.push(t),this.notifyShortcutsChanged()}deregisterShortcut(s,e){this.activeShortcuts=this.activeShortcuts.filter(t=>!(t.label===s&&t.owner===e)),this.notifyShortcutsChanged()}deregisterAllShortcuts(s){this.activeShortcuts=this.activeShortcuts.filter(e=>e.owner!==s),this.notifyShortcutsChanged()}getActiveShortcuts(){return this.activeShortcuts}notifyShortcutsChanged(){this.getUtilities("keyboard-hints").forEach(e=>e.render?.())}focusNextResults(s){let e=this.getResults("keyboardNavigation"),t=$t(s,e);if(!t)return!1;let r=t.querySelector("a");return r?(r.focus(),!0):!1}focusPreviousInput(s){let e=this.getInputs("keyboardNavigation"),t=Pe(s,e);if(!t)return!1;if(t.focus)return t.focus(),!0;let r=t.querySelector("input");return r?(r.focus(),!0):!1}focusInputAndType(s,e){let t=this.getInputs("keyboardNavigation"),r=Pe(s,t),i=r?.inputEl||r?.querySelector("input");i&&(i.value+=e,i.focus(),i.dispatchEvent(new Event("input",{bubbles:!0})))}focusInputAndDelete(s){let e=this.getInputs("keyboardNavigation"),t=Pe(s,e),r=t?.inputEl||t?.querySelector("input");r&&(r.value=r.value.slice(0,-1),r.focus(),r.dispatchEvent(new Event("input",{bubbles:!0})))}reconcileAria(){this.components.forEach(s=>s.reconcileAria?.())}get direction(){return this._direction}setLanguage(s){s||(s=document?.documentElement?.lang||"en"),this._translations=Zt(s),this._direction=this._translations.direction||"ltr",this._languageSet=!0,this.__dispatch__("translations",this._translations,this._direction)}setTranslations(s){this._userTranslations={...this._userTranslations,...s},this.__dispatch__("translations",this._translations,this._direction)}translate(s,e={}){let t=this._userTranslations[s]??this._translations?.[s];return Xt(typeof t=="string"?t:void 0,e,this._translations?.language)}announce(s,e={},t="polite"){let r=this.translate(s,e);r&&this._announcer.announce(r,t)}announceRaw(s,e="polite"){this._announcer.announce(s,e)}clearAnnouncements(){this._announcer.clear()}on(s,e,t=null){if(!this.__hooks__[s]){let r=Object.keys(this.__hooks__).join(", ");console.error(`[Pagefind Component UI]: Unknown event type ${s}. Supported events: [${r}]`);return}if(typeof e!="function"){console.error(`[Pagefind Component UI]: Expected callback to be a function, received ${typeof e}`);return}if(t){let r=this.__hooks__[s].findIndex(i=>typeof i=="object"&&i.owner===t);if(r!==-1){this.__hooks__[s][r]={callback:e,owner:t};return}this.__hooks__[s].push({callback:e,owner:t})}else this.__hooks__[s].push(e)}triggerLoad(){return this.__load__()}triggerSearch(s){this.searchTerm=s,this.__dispatch__("search",s,this.searchFilters),this.__search__(s,this.searchFilters)}triggerSearchWithFilters(s,e){this.searchTerm=s,this.searchFilters=e,this.__dispatch__("search",s,e),this.__search__(s,e)}triggerFilters(s){this.searchFilters=s,this.__dispatch__("search",this.searchTerm,s),this.__search__(this.searchTerm,s)}triggerFilter(s,e){this.searchFilters=this.searchFilters||{},this.searchFilters[s]=e,this.__dispatch__("search",this.searchTerm,this.searchFilters),this.__search__(this.searchTerm,this.searchFilters)}__dispatch__(s,...e){this.__hooks__[s]?.forEach(t=>{typeof t=="function"?t(...e):t?.callback&&t.callback(...e)})}async __clear__(){this.__dispatch__("results",{results:[],unfilteredTotalCount:0}),this.__pagefind__&&(this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters}))}async __search__(s,e){this.__dispatch__("loading"),await this.__load__();let t=++this.__searchID__;if((!s||!s.length)&&!this.faceted)return this.__clear__();if(!this.__pagefind__)return;let r=s&&s.length?s:null,i=await this.__pagefind__.search(r,{filters:e});if(i&&this.__searchID__===t&&(i.filters&&Object.keys(i.filters)?.length&&(this.availableFilters=i.filters,this.totalFilters=i.totalFilters??null,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})),this.searchResult=i,this.__dispatch__("results",this.searchResult),!this.hasAnnouncementCapability()&&s)){let a=i.results?.length??0,l=a===0?"zero_results":a===1?"one_result":"many_results",o=a===0?"assertive":"polite";this.announce(l,{SEARCH_TERM:s,COUNT:a},o)}}async __load__(){if(!this.__pagefind__){if(this.__loadPromise__)return this.__loadPromise__;this.__loadPromise__=this.__doLoad__();try{await this.__loadPromise__}finally{this.__loadPromise__=null}}}async __doLoad__(){if(this.__pagefind__)return;let s;try{s=await import(`${this.options.bundlePath}pagefind.js`)}catch(t){console.error(t),console.error([`Pagefind couldn't be loaded from ${this.options.bundlePath}pagefind.js`,"You can configure this by passing a bundlePath option to the Pagefind Component UI"].join(` +`)),document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?console.error(`[DEBUG: Loaded from ${document.currentScript?.src??"bad script location"}]`):console.error("no known script location"),this.__dispatch__("error",{type:"bundle_load_failed",message:"Could not load search bundle",bundlePath:this.options.bundlePath,error:t}),this.hasAnnouncementCapability()||this.announce("error_search",{},"assertive");return}let e=s.createInstance(this.pagefindOptions||{});for(let t of this.options.mergeIndex){if(!t.bundlePath)throw new Error("mergeIndex requires a bundlePath parameter");let{bundlePath:r,...i}=t;await e.mergeIndex(r,i)}this.__pagefind__=e,this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters}),this.faceted&&this.__searchID__===0&&this.triggerSearch("")}thinSubResults(s,e=3){if(s.length<=e)return s;let t=[...s].sort((r,i)=>(i.locations?.length??0)-(r.locations?.length??0)).slice(0,e).map(r=>r.url);return s.filter(r=>t.includes(r.url))}getDisplaySubResults(s,e=3){if(!Array.isArray(s.sub_results))return[];let r=s.sub_results[0]?.url===(s.meta?.url||s.url)?s.sub_results.slice(1):s.sub_results;return this.thinSubResults(r,e)}};var Pt=class{constructor(){this.instances=new Map;this.defaultOptions={bundlePath:this.detectBundlePath()}}detectBundlePath(){try{if(document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"){let s=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind[-_])?.*\.js.*$/);if(s)return s[1]}}catch{}return"/pagefind/"}getInstance(s="default",e={}){let t=this.instances.get(s);if(t)return t;let r={...this.defaultOptions,...e},i=new Te(s,r);return this.instances.set(s,i),i}hasInstance(s){return this.instances.has(s)}removeInstance(s){this.instances.delete(s)}getInstanceNames(){return Array.from(this.instances.keys())}},Ft=null;function Ce(){return Ft||(Ft=new Pt),Ft}function Qt(n,s){let e=Ce();return e.hasInstance(n)?(console.warn(`[Pagefind Component UI]: Instance "${n}" already exists, configuration ignored`),e.getInstance(n)):e.getInstance(n,s)}var ye=n=>!(n==null||n===!1||n===0||n===""||Number.isNaN(n)||Array.isArray(n)&&n.length===0||typeof n=="object"&&n!==null&&!Array.isArray(n)&&Object.keys(n).length===0),T=(n,s,e)=>n.lengthT(s,2,"eq")??m(s[0],n)===m(s[1],n),ne:(n,...s)=>T(s,2,"ne")??m(s[0],n)!==m(s[1],n),gt:(n,...s)=>T(s,2,"gt")??Number(m(s[0],n))>Number(m(s[1],n)),lt:(n,...s)=>T(s,2,"lt")??Number(m(s[0],n))T(s,2,"gte")??Number(m(s[0],n))>=Number(m(s[1],n)),lte:(n,...s)=>T(s,2,"lte")??Number(m(s[0],n))<=Number(m(s[1],n)),and:(n,...s)=>{let e=!0;for(let t of s)if(e=m(t,n),!ye(e))return e;return e},or:(n,...s)=>{let e=!1;for(let t of s)if(e=m(t,n),ye(e))return e;return e},not:(n,...s)=>T(s,1,"not")??!ye(m(s[0],n)),lowercase:(n,...s)=>String(m(s[0],n)).toLowerCase(),uppercase:(n,...s)=>String(m(s[0],n)).toUpperCase(),trim:(n,...s)=>String(m(s[0],n)).trim(),truncate:(n,...s)=>{let e=T(s,2,"truncate");if(e)return e;let t=String(m(s[0],n)),r=Number(m(s[1],n)),i=s[2]?String(m(s[2],n)):"...";return t.length>r?t.slice(0,r)+i:t},replace:(n,...s)=>T(s,3,"replace")??String(m(s[0],n)).split(String(m(s[1],n))).join(String(m(s[2],n))),limit:(n,...s)=>{let e=T(s,2,"limit");if(e)return e;let t=m(s[0],n),r=m(s[1],n);return Array.isArray(t)?t.slice(0,r<0?0:r):t},first:(n,...s)=>{let e=T(s,1,"first");if(e)return e;let t=m(s[0],n);return Array.isArray(t)?t[0]:t},last:(n,...s)=>{let e=T(s,1,"last");if(e)return e;let t=m(s[0],n);return Array.isArray(t)?t[t.length-1]:t},length:(n,...s)=>{let e=T(s,1,"length");if(e)return e;let t=m(s[0],n);return Array.isArray(t)?t.length:String(t).length},join:(n,...s)=>T(s,2,"join")??(e=>Array.isArray(e)?e.join(String(m(s[1],n))):String(e))(m(s[0],n)),default:(n,...s)=>{let e=T(s,2,"default");if(e)return e;let t=m(s[0],n);return ye(t)?t:m(s[1],n)},safeUrl:(n,...s)=>{let e=String(m(s[0],n)??"").trim();return e&&/^(?:\.{0,2}\/|[#?]|(?:https?|ftp):\/\/|(?:mailto|tel):)/i.test(e)?e:""}},m=(n,s)=>{if(!n)return;if(n.t==="L")return n.val;if(n.t==="V"){let t=(i,a)=>Object.prototype.hasOwnProperty.call(i,a),r=s;for(let i of n.path){if(r==null||!t(r,i))return;r=r[i]}return r}let e=es[n.fn];return e?n.t==="C"?e(s,...n.args):e(s,n.left,...n.args):`[Error: unknown ${n.fn}()]`},ga=n=>n.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'"),ve=(n,s)=>{let e="";for(let t of n){if(t.t==="T"){e+=t.val;continue}if(t.t==="I"){let r=m(t.expr,s);if(Array.isArray(r))e+="[Error: use #each for arrays]";else if(typeof r=="object"&&r!==null)e+="[Error: cannot render object]";else{let i=String(r??"");e+=t.raw?i:ga(i)}continue}if(t.t==="F"){let r=!1;for(let i of t.branches)if(ye(m(i.cond,s))){e+=ve(i.body,s),r=!0;break}!r&&t.else&&(e+=ve(t.else,s));continue}if(t.t==="E"){let r=m(t.arr,s);if(!Array.isArray(r)){e+="[Error: #each needs array]";continue}if(!r.length&&t.else)e+=ve(t.else,s);else for(let i=0;i{let s=n,e=0,t=()=>{for(;es.slice(e,e+u.length)===u,i=u=>{r(u)&&(e+=u.length)},a=()=>{let u="";for(;e{t();let u=e,d,_=s[e];if(_==='"'||_==="'"){let p=s[e++],g="";for(;e{let d=[];e:for(;eve(c,u)};var ts=(n,s)=>{es[n]=(e,...t)=>s(...t.map(r=>m(r,e)))};var f=class extends HTMLElement{constructor(){super();this.instance=null;this._initialized=!1}connectedCallback(){if(this._initialized)return;this._initialized=!0;let e=this.getAttribute("instance")||"default",t=Ce();this.instance=t.getInstance(e),this.init(),this.register&&typeof this.register=="function"&&this.register(this.instance)}disconnectedCallback(){this.cleanup&&typeof this.cleanup=="function"&&this.cleanup(),this._initialized=!1}attributeChangedCallback(e,t,r){if(!this._initialized||t===r)return;let i=this.kebabToCamel(e);r==="false"?this[i]=!1:r==="true"?this[i]=!0:r==null?this[i]=!1:this[i]=r,this.update&&typeof this.update=="function"&&this.update()}kebabToCamel(e){return e.replace(/-([a-z])/g,t=>t[1].toUpperCase())}ensureId(e="pagefind"){return!this.id&&this.instance&&(this.id=this.instance.generateId(e)),this.id}init(){}reconcileAria(){}register(e){}cleanup(){}update(){}showError(e){let t=document.createElement("div");t.className="pf-error",t.innerHTML=` + Pagefind Error: ${this.escapeHtml(e.message||"Unknown error")} + ${e.details?`
${this.escapeHtml(e.details)}`:""} + `,this.appendChild(t),this.dispatchEvent(new CustomEvent("pagefind-error",{detail:e,bubbles:!0,composed:!0}))}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}};var Re=class extends f{init(){this.setAttribute("hidden","")}register(s){s.registerUtility(this);let e=this.getAttribute("bundle-path");e&&(s.options.bundlePath=e);let t=this.getAttribute("base-url");t&&(s.pagefindOptions.baseUrl=t);let r=this.getAttribute("excerpt-length");r&&(s.pagefindOptions.excerptLength=parseInt(r,10));let i=this.getAttribute("lang");i&&s.setLanguage(i);let a=this.getAttribute("meta-cache-tag");a&&(s.pagefindOptions.metaCacheTag=a);let l=this.getAttribute("highlight-param");l&&(s.pagefindOptions.highlightParam=l),this.hasAttribute("exact-diacritics")&&(s.pagefindOptions.exactDiacritics=!0),this.hasAttribute("no-worker")&&(s.pagefindOptions.noWorker=!0),this.hasAttribute("faceted")&&(s.faceted=!0),this.hasAttribute("preload")&&s.triggerLoad()}};customElements.get("pagefind-config")||customElements.define("pagefind-config",Re);var Ea=(n=100)=>new Promise(s=>setTimeout(s,n)),ke=class extends f{constructor(){super();this.inputEl=null;this.clearEl=null;this.searchID=0;this.placeholder="";this.debounce=300;this.autofocus=!1}static get observedAttributes(){return["placeholder","debounce","autofocus"]}readAttributes(){this.hasAttribute("placeholder")&&(this.placeholder=this.getAttribute("placeholder")||""),this.hasAttribute("debounce")&&(this.debounce=parseInt(this.getAttribute("debounce")||"300",10)||300),this.hasAttribute("autofocus")&&(this.autofocus=this.hasAttribute("autofocus"))}init(){this.readAttributes(),this.render()}render(){this.innerHTML="";let e=this.instance.generateId("pfmod-input"),t=this.instance?.translate("search_label")||"Search this site",r=this.instance?.translate("clear_search")||"Clear",i=this.placeholder||this.instance?.translate("placeholder")||"Search";this.instance?.direction==="rtl"?this.setAttribute("dir","rtl"):this.removeAttribute("dir");let a=document.createElement("search");a.className="pf-input-wrapper",a.setAttribute("role","search"),a.setAttribute("aria-label",t);let l=document.createElement("label");l.setAttribute("for",e),l.setAttribute("data-pf-sr-hidden","true"),l.textContent=t,a.appendChild(l),this.inputEl=document.createElement("input"),this.inputEl.id=e,this.inputEl.className="pf-input",this.inputEl.setAttribute("type","search"),this.inputEl.setAttribute("autocomplete","off"),this.inputEl.setAttribute("autocapitalize","none"),this.inputEl.setAttribute("enterkeyhint","search"),this.inputEl.setAttribute("placeholder",i),this.autofocus&&this.inputEl.setAttribute("autofocus","autofocus");let o=this.instance.generateId("pf-input-hint"),c=this.instance?.translate("input_hint")||"Results will appear as you type",u=document.createElement("span");u.id=o,u.setAttribute("data-pf-sr-hidden","true"),u.textContent=c,this.inputEl.setAttribute("aria-describedby",o),a.appendChild(this.inputEl),a.appendChild(u),this.clearEl=document.createElement("button"),this.clearEl.className="pf-input-clear",this.clearEl.setAttribute("type","button"),this.clearEl.setAttribute("data-pf-suppressed","true"),this.clearEl.textContent=r,a.appendChild(this.clearEl),this.appendChild(a),this.setupEventHandlers()}setupEventHandlers(){!this.inputEl||!this.clearEl||(this.inputEl.addEventListener("input",async e=>{let t=e.target;if(this.instance&&typeof t?.value=="string"){this.updateState(t.value);let r=++this.searchID;if(await Ea(this.debounce),r!==this.searchID)return;this.instance?.triggerSearch(t.value)}}),this.inputEl.addEventListener("keydown",e=>{e.key==="Escape"&&(++this.searchID,this.inputEl&&(this.inputEl.value=""),this.instance?.triggerSearch(""),this.updateState("")),e.key==="ArrowDown"&&(e.preventDefault(),this.inputEl&&this.instance?.focusNextResults(this.inputEl))}),this.inputEl.addEventListener("focus",()=>{this.instance?.triggerLoad();let e=this.instance?.translate("keyboard_navigate")||"navigate",t=this.instance?.translate("keyboard_clear")||"clear";this.instance?.registerShortcut({label:"\u2193",description:e},this),this.instance?.registerShortcut({label:"esc",description:t},this)}),this.inputEl.addEventListener("blur",()=>{this.instance?.deregisterAllShortcuts(this)}),this.clearEl.addEventListener("click",()=>{this.inputEl&&(this.inputEl.value="",this.instance?.triggerSearch(""),this.updateState(""),this.inputEl.focus())}))}updateState(e){this.clearEl&&(e&&e?.length?this.clearEl.removeAttribute("data-pf-suppressed"):this.clearEl.setAttribute("data-pf-suppressed","true"))}register(e){e.registerInput(this,{keyboardNavigation:!0}),e.on("search",t=>{this.inputEl&&document.activeElement!==this.inputEl&&(this.inputEl.value=t,this.updateState(t))},this),e.on("error",t=>{let r=t;this.showError({message:r.message||"Search initialization failed",details:r.bundlePath?`Bundle path: ${r.bundlePath}`:void 0})},this),e.on("translations",()=>{let t=this.inputEl?.value||"";this.render(),this.inputEl&&t&&(this.inputEl.value=t,this.updateState(t))},this)}update(){this.render()}focus(){this.inputEl&&this.inputEl.focus()}};customElements.get("pagefind-input")||customElements.define("pagefind-input",ke);var Ae=class extends f{constructor(){super();this.containerEl=null;this.term="";this.defaultMessage=""}static get observedAttributes(){return["default-message"]}init(){this.hasAttribute("default-message")&&(this.defaultMessage=this.getAttribute("default-message")||""),this.render()}render(){this.innerHTML="",this.instance?.direction==="rtl"?this.setAttribute("dir","rtl"):this.removeAttribute("dir"),this.containerEl=document.createElement("div"),this.containerEl.className="pf-summary",this.containerEl.textContent=this.defaultMessage,this.appendChild(this.containerEl)}reconcileAria(){}register(e){e.registerSummary(this),e.on("search",t=>{this.term=t},this),e.on("results",t=>{if(!this.containerEl||!t)return;let i=t?.results?.length??0;if(!this.term){if(e.faceted){let o=i===0?"total_zero_results":i===1?"total_one_result":"total_many_results",c=e.translate(o,{COUNT:i});this.containerEl.textContent=c||`${i} result${i===1?"":"s"}`}else this.containerEl.textContent=this.defaultMessage;return}let a=i===0?"zero_results":i===1?"one_result":"many_results",l=e.translate(a,{SEARCH_TERM:this.term,COUNT:i});this.containerEl.textContent=l||`${i} result${i===1?"":"s"} for ${this.term}`},this),e.on("loading",()=>{if(!this.containerEl)return;let t=e.translate("searching",{SEARCH_TERM:this.term});this.containerEl.textContent=t||`Searching for ${this.term}...`},this),e.on("error",t=>{if(!this.containerEl)return;let r=t,i=e.translate("error_search")||"Search failed";this.containerEl.textContent=`Error: ${r.message||i}`},this),e.on("translations",()=>{this.render()},this)}update(){this.hasAttribute("default-message")&&(this.defaultMessage=this.getAttribute("default-message")||"",!this.term&&this.containerEl&&(this.containerEl.textContent=this.defaultMessage))}};customElements.get("pagefind-summary")||customElements.define("pagefind-summary",Ae);var ss=n=>{if(n instanceof Element)return[n];if(Array.isArray(n)&&n.every(s=>s instanceof Element))return n;if(typeof n=="string"||n instanceof String){let s=document.createElement("div");return s.innerHTML=n,[...s.childNodes]}return console.error(`[Pagefind Results]: Expected template to return HTML element or string, got ${typeof n}`),[]},ba=`
  • +
    + {{#if and(options.show_images, meta.image)}} + {{ meta.image_alt | default(meta.title) }} + {{/if}} +
    +

    + {{ meta.title }} +

    + {{#if excerpt}} +

    {{+ excerpt +}}

    + {{/if}} +
    +
    + {{#if sub_results}} +
      + {{#each sub_results as sub}} +
    • + {{ sub.title }} +

      {{+ sub.excerpt +}}

      +
    • + {{/each}} +
    + {{/if}} +
  • `,Ta=``,Ca=R(ba),ya=R(Ta),rs=(n,s)=>{for(let e of n)if(e instanceof Element){e.setAttribute("data-pf-result-index",String(s));break}},zt=n=>{if(!(n instanceof HTMLElement))return null;let s=window.getComputedStyle(n).overflowY;return s!=="visible"&&s!=="hidden"?n:zt(n.parentNode)},Dt=class{constructor(s){this.result=null;this.loading=!1;this.observer=null;this.rawResult=s.result,this.index=s.index,this.placeholderNodes=s.placeholderNodes,this.resultFn=s.resultFn,this.intersectionEl=s.intersectionEl,this.showImages=s.showImages,this.showSubResults=s.showSubResults,this.maxSubResults=s.maxSubResults,this.linkTarget=s.linkTarget,this.onLoad=s.onLoad,this.setupObserver()}setupObserver(){if(this.result!==null||this.observer!==null||!this.placeholderNodes?.length)return;let s={root:this.intersectionEl,rootMargin:"50px",threshold:.01};this.observer=new IntersectionObserver((e,t)=>{this.result===null&&e?.[0]?.isIntersecting&&(this.load(),t.disconnect(),this.observer=null)},s),this.observer.observe(this.placeholderNodes[0])}async load(){if(this.placeholderNodes?.length&&!(this.result!==null||this.loading)){this.loading=!0;try{this.result=await this.rawResult.data();let s=this.resultFn(this.result,{showImages:this.showImages,showSubResults:this.showSubResults,maxSubResults:this.maxSubResults,linkTarget:this.linkTarget}),e=ss(s);for(rs(e,this.index);this.placeholderNodes.length>1;){let r=this.placeholderNodes.pop();r instanceof Element&&r.remove()}let t=this.placeholderNodes[0];t instanceof Element&&t.replaceWith(...e)}catch{this.loading=!1}this.onLoad?.()}}cleanup(){this.observer&&(this.observer.disconnect(),this.observer=null)}},Se=class extends f{constructor(){super();this.containerEl=null;this.intersectionEl=document.body;this.results=[];this.showImages=!1;this.hideSubResults=!1;this.maxSubResults=3;this.maxResults=0;this.linkTarget=null;this.resultTemplate=null;this.compiledResultTemplate=null;this.compiledPlaceholderTemplate=null;this.selectedIndex=-1;this.selectedAnchor=null;this.loadingAnnouncementTimeout=null}static get observedAttributes(){return["show-images","hide-sub-results","max-sub-results","max-results","link-target"]}init(){this.hasAttribute("show-images")&&(this.showImages=this.getAttribute("show-images")!=="false"),this.hasAttribute("hide-sub-results")&&(this.hideSubResults=this.getAttribute("hide-sub-results")!=="false"),this.hasAttribute("max-sub-results")&&(this.maxSubResults=parseInt(this.getAttribute("max-sub-results")||"3",10)||3),this.hasAttribute("max-results")&&(this.maxResults=parseInt(this.getAttribute("max-results")||"0",10)),this.hasAttribute("link-target")&&(this.linkTarget=this.getAttribute("link-target")),this.checkForTemplates(),this.render()}checkForTemplates(){let e=this.querySelector('script[type="text/pagefind-template"]:not([data-template]), script[type="text/pagefind-template"][data-template="result"]');e&&(this.compiledResultTemplate=R((e.textContent||"").trim()));let t=this.querySelector('script[type="text/pagefind-template"][data-template="placeholder"]');t&&(this.compiledPlaceholderTemplate=R((t.textContent||"").trim()))}buildTemplateData(e,t){let r=t.showSubResults?this.instance.getDisplaySubResults(e,t.maxSubResults):[];return{meta:e.meta||{},excerpt:e.excerpt||"",url:e.url||"",sub_results:r.map(i=>({title:i.title,url:i.url,excerpt:i.excerpt})),options:{link_target:t.linkTarget,show_images:t.showImages}}}getResultRenderer(){if(this.resultTemplate){let e=this.resultTemplate;return(t,r)=>e(t)}if(this.compiledResultTemplate){let e=this.compiledResultTemplate;return(t,r)=>{let i=this.buildTemplateData(t,r);return e(i)}}return(e,t)=>{let r=this.buildTemplateData(e,t);return Ca(r)}}getPlaceholder(){return this.compiledPlaceholderTemplate?this.compiledPlaceholderTemplate({}):ya({})}render(){let e=[];this.querySelectorAll('script[type="text/pagefind-template"]').forEach(r=>{e.push(r)}),this.innerHTML="",e.forEach(r=>this.appendChild(r));let t=this.instance?.translate("results_label")||"Search results";this.instance?.direction==="rtl"?this.setAttribute("dir","rtl"):this.removeAttribute("dir"),this.containerEl=document.createElement("ul"),this.containerEl.className="pf-results",this.containerEl.setAttribute("aria-label",t),this.containerEl.setAttribute("aria-busy","false"),this.appendChild(this.containerEl),this.setupKeyboardHandlers()}appendResults(e){if(this.containerEl)for(let t of e)this.containerEl.appendChild(t)}register(e){e.registerResults(this,{keyboardNavigation:!0,announcements:!0}),e.on("results",t=>{if(!this.containerEl)return;let r=t;for(let c of this.results)c.cleanup();this.containerEl.innerHTML="",this.containerEl.setAttribute("aria-busy","false"),this.intersectionEl=zt(this.containerEl),this.selectedIndex=-1,this.selectedAnchor=null;let i=this.maxResults>0?r.results.slice(0,this.maxResults):r.results,a=i.length,l=e.searchTerm;if(l){let c=a===0?"zero_results":a===1?"one_result":"many_results",u=a===0?"assertive":"polite";e.announce(c,{SEARCH_TERM:l,COUNT:a},u)}else if(e.faceted){let c=a===0?"total_zero_results":a===1?"total_one_result":"total_many_results",u=a===0?"assertive":"polite";e.announce(c,{COUNT:a},u)}let o=this.getResultRenderer();this.results=i.map((c,u)=>{let d=ss(this.getPlaceholder());rs(d,u),this.appendResults(d);let _=new Dt({result:c,index:u,placeholderNodes:d,resultFn:o,intersectionEl:this.intersectionEl,showImages:this.showImages,showSubResults:!this.hideSubResults,maxSubResults:this.maxSubResults,linkTarget:this.linkTarget,onLoad:()=>{_.result&&this.clearLoadingAnnouncement()}});return _})},this),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerHTML="",this.containerEl.setAttribute("aria-busy","true"),this.selectedIndex=-1,this.selectedAnchor=null)},this),e.on("error",t=>{let r=t;this.containerEl&&this.containerEl.setAttribute("aria-busy","false"),e.announce("error_search",{},"assertive"),this.showError({message:r.message||e.translate("error_search")||"Failed to load search results",details:r.bundlePath?`Bundle path: ${r.bundlePath}`:void 0})},this),e.on("translations",()=>{this.render()},this)}findNeighborAnchor(e,t){if(!this.containerEl)return null;let r=document.createTreeWalker(this.containerEl,NodeFilter.SHOW_ELEMENT,{acceptNode:l=>l.tagName==="A"?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP});r.currentNode=e;let i=t>0?r.nextNode():r.previousNode();if(!i||!(i instanceof HTMLAnchorElement))return null;let a=this.resultIndexForNode(i);return{anchor:i,resultIndex:a}}resultIndexForNode(e){if(!this.containerEl)return-1;let t=e;for(;t&&t.parentNode!==this.containerEl;)t=t.parentNode;if(!t||!(t instanceof Element))return-1;let r=t.getAttribute("data-pf-result-index");if(r===null)return-1;let i=parseInt(r,10);return Number.isNaN(i)?-1:i}setupKeyboardHandlers(){this.containerEl&&(this.containerEl.addEventListener("keydown",e=>{let t=e.target.closest("a");if(t)if(e.key==="ArrowDown"){e.preventDefault();let r=this.findNeighborAnchor(t,1);if(r)r.anchor.focus(),this.scrollToCenter(r.anchor,e.repeat),r.resultIndex!==-1&&this.preloadAhead(r.resultIndex,1);else{let a=this.resultIndexForNode(t)+1;if(a>0&&a{let t=e.target.closest("a");if(!t)return;this.clearSelection(),t.setAttribute("data-pf-selected",""),this.selectedAnchor=t;let r=this.instance?.translate("keyboard_navigate")||"navigate",i=this.instance?.translate("keyboard_select")||"select",a=this.instance?.translate("keyboard_search")||"search";this.instance?.registerShortcut({label:"\u2191\u2193",description:r},this),this.instance?.registerShortcut({label:"\u21B5",description:i},this),this.instance?.registerShortcut({label:"/",description:a},this)}),this.containerEl.addEventListener("focusout",e=>{let t=e;this.containerEl?.contains(t.relatedTarget)||(this.clearSelection(),this.instance?.deregisterAllShortcuts(this))}))}scrollToCenter(e,t=!1){let r=this.intersectionEl||zt(e);if(!r||!(r instanceof HTMLElement)||r===document.body||r===document.documentElement)return;let i=e.getBoundingClientRect(),a=r.getBoundingClientRect(),o=i.top-a.top+r.scrollTop-r.clientHeight/2+e.offsetHeight/2;r.scrollTo({top:o,behavior:t?"instant":"smooth"})}preloadAhead(e,t){let r=t>0?1:-1;for(let i=1;i<=3;i++){let a=e+r*i;if(a>=0&&a{this.loadingAnnouncementTimeout=null,this.instance?.announce("loading",{},"polite")},800))}clearLoadingAnnouncement(){this.loadingAnnouncementTimeout&&(clearTimeout(this.loadingAnnouncementTimeout),this.loadingAnnouncementTimeout=null)}clearSelection(){this.selectedAnchor&&(this.selectedAnchor.removeAttribute("data-pf-selected"),this.selectedAnchor=null)}cleanup(){this.clearLoadingAnnouncement();for(let e of this.results)e.cleanup();this.results=[],this.selectedAnchor=null}update(){this.render()}};customElements.get("pagefind-results")||customElements.define("pagefind-results",Se);var Ne=class extends f{constructor(){super();this.containerEl=null;this.showEmpty=!1;this.expanded=!1;this.openFilters=[];this.sortOption="default";this.autoOpenThreshold=6;this.selectedFilters={};this.availableFilters=null;this.totalFilters=null;this.filterElements=new Map;this.groupElements=new Map;this.groupVisibleCounts=new Map;this.isRendered=!1}static get observedAttributes(){return["show-empty","expanded","open","sort","auto-open-threshold"]}init(){if(this.hasAttribute("show-empty")&&(this.showEmpty=this.getAttribute("show-empty")!=="false"),this.hasAttribute("expanded")&&(this.expanded=this.getAttribute("expanded")!=="false"),this.hasAttribute("open")&&(this.openFilters=(this.getAttribute("open")||"").split(",").map(e=>e.trim().toLowerCase()).filter(e=>e.length>0)),this.hasAttribute("sort")){let e=this.getAttribute("sort");["default","alphabetical","count-desc","count-asc"].includes(e)&&(this.sortOption=e)}this.hasAttribute("auto-open-threshold")&&(this.autoOpenThreshold=parseInt(this.getAttribute("auto-open-threshold")||"6",10)),this.render()}sortValues(e,t){if(this.sortOption==="default")return e;let r=[...e];switch(this.sortOption){case"alphabetical":r.sort((i,a)=>i[0].localeCompare(a[0]));break;case"count-desc":r.sort((i,a)=>{let l=t[i[0]]??i[1];return(t[a[0]]??a[1])-l});break;case"count-asc":r.sort((i,a)=>{let l=t[i[0]]??i[1],o=t[a[0]]??a[1];return l-o});break}return r}render(){this.innerHTML="",this.instance?.direction==="rtl"?this.setAttribute("dir","rtl"):this.removeAttribute("dir"),this.containerEl=document.createElement("div"),this.containerEl.className="pf-filter-pane",this.appendChild(this.containerEl)}getSelectedText(e){return String(e)}shouldGroupStartOpen(e,t,r){return this.openFilters.length>0?this.openFilters.includes(e.toLowerCase()):this.autoOpenThreshold>0&&r===1&&t<=this.autoOpenThreshold}hasStructureChanged(){if(!this.totalFilters)return!1;let e=new Set(Object.keys(this.totalFilters)),t=new Set(this.groupElements.keys());if(e.size!==t.size)return!0;for(let r of e)if(!t.has(r))return!0;for(let[r,i]of Object.entries(this.totalFilters)){let a=new Set(Object.keys(i));for(let l of a)if(!this.filterElements.has(`${r}:${l}`))return!0}return!1}handleFiltersUpdate(){if(!this.containerEl||!this.totalFilters)return;if(Object.keys(this.totalFilters).length===0){this.containerEl.setAttribute("data-pf-hidden","true");return}this.containerEl.removeAttribute("data-pf-hidden"),!this.isRendered||this.hasStructureChanged()?this.renderFilters():this.updateFilters()}renderFilters(){if(!this.containerEl||!this.totalFilters)return;this.containerEl.innerHTML="",this.filterElements.clear(),this.groupElements.clear(),this.groupVisibleCounts.clear();let e=Object.keys(this.totalFilters);for(let t of e){let r=this.totalFilters[t],i=this.availableFilters?.[t]||{},a=this.renderFilterGroup(t,r,i,e.length);a&&this.containerEl.appendChild(a)}this.isRendered=!0}updateFilters(){for(let[e,t]of this.filterElements){let r=e.indexOf(":"),i=e.slice(0,r),a=e.slice(r+1),l=this.availableFilters?.[i]?.[a]??0,o=this.totalFilters?.[i]?.[a]??0,c=this.selectedFilters[i]?.has(a),u=c?o:l;t.countSpan.textContent=String(u);let d=this.showEmpty||l>0||c,_=t.label.hasAttribute("data-pf-hidden");t.label.toggleAttribute("data-pf-hidden",!d),d&&_?this.groupVisibleCounts.set(i,(this.groupVisibleCounts.get(i)??0)+1):!d&&!_&&this.groupVisibleCounts.set(i,(this.groupVisibleCounts.get(i)??1)-1),t.checkbox.checked=c||!1}for(let[e,t]of this.groupElements){let r=this.selectedFilters[e]?.size||0;t.selectedCountSpan&&(r>0?(t.selectedCountSpan.textContent=this.getSelectedText(r),t.selectedCountSpan.removeAttribute("data-pf-hidden")):t.selectedCountSpan.setAttribute("data-pf-hidden","true"));let i=this.groupVisibleCounts.get(e)??0;t.group.toggleAttribute("data-pf-hidden",i===0)}}renderFilterGroup(e,t,r,i){let a=Object.entries(t);if(a.length===0)return null;let l=this.sortValues(a,r),o=e.charAt(0).toUpperCase()+e.slice(1),c=this.selectedFilters[e]?.size||0,u=this.expanded||this.shouldGroupStartOpen(e,l.length,i),d,_,p=null;if(this.expanded){d=document.createElement("fieldset"),d.className="pf-filter-group";let E=document.createElement("legend");E.className="pf-filter-group-title";let C=document.createElement("span");C.className="pf-filter-group-name",C.textContent=o,E.appendChild(C),d.appendChild(E),_=document.createElement("div"),_.className="pf-filter-options",d.appendChild(_)}else{d=document.createElement("details"),d.className="pf-filter-group",d.dataset.filterName=e,u&&(d.open=!0);let E=document.createElement("summary");E.className="pf-filter-group-title";let C=document.createElement("span");C.className="pf-filter-group-name",C.textContent=o,E.appendChild(C),p=document.createElement("span"),p.className="pf-filter-group-count",p.setAttribute("aria-hidden","true"),c>0?p.textContent=this.getSelectedText(c):p.setAttribute("data-pf-hidden","true"),E.appendChild(p),d.appendChild(E);let y=document.createElement("fieldset");y.className="pf-filter-fieldset";let v=document.createElement("legend");v.setAttribute("data-pf-sr-hidden",""),v.textContent=o,y.appendChild(v),_=document.createElement("div"),_.className="pf-filter-options",y.appendChild(_),d.appendChild(y)}this.groupElements.set(e,{group:d,optionsContainer:_,selectedCountSpan:p});let g=0;for(let[E,C]of l){let y=r[E]??0,v=this.selectedFilters[e]?.has(E)||!1,$e=v?C:y,N=this.showEmpty||y>0||v;N&&g++,this.renderCheckbox(_,e,E,$e,v,N)}return this.groupVisibleCounts.set(e,g),d}renderCheckbox(e,t,r,i,a,l){let o=this.instance.generateId(`pf-filter-${t}-${r}`),c=document.createElement("label");c.className="pf-filter-checkbox",c.setAttribute("for",o),l||c.setAttribute("data-pf-hidden","true");let u=document.createElement("input");u.type="checkbox",u.className="pf-checkbox-input",u.id=o,u.name=t,u.value=r,u.checked=a,u.addEventListener("change",p=>{this.handleCheckboxChange(t,r,p.target.checked)}),c.appendChild(u);let d=document.createTextNode(r);c.appendChild(d);let _=document.createElement("span");_.className="pf-filter-checkbox-count",_.textContent=String(i),c.appendChild(_),e.appendChild(c),this.filterElements.set(`${t}:${r}`,{label:c,countSpan:_,checkbox:u})}handleCheckboxChange(e,t,r){this.selectedFilters[e]||(this.selectedFilters[e]=new Set),r?this.selectedFilters[e].add(t):this.selectedFilters[e].delete(t);let i=this.groupElements.get(e);if(i?.selectedCountSpan){let l=this.selectedFilters[e].size;l>0?(i.selectedCountSpan.textContent=this.getSelectedText(l),i.selectedCountSpan.removeAttribute("data-pf-hidden")):i.selectedCountSpan.setAttribute("data-pf-hidden","true")}let a=Array.from(this.selectedFilters[e]);if(a.length===0){delete this.selectedFilters[e];let l={};for(let[o,c]of Object.entries(this.selectedFilters))l[o]=Array.from(c);this.instance?.triggerFilters(l)}else this.instance?.triggerFilter(e,a)}register(e){e.registerFilter(this),e.on("filters",t=>{let r=t;this.availableFilters=r.available,this.totalFilters=r.total,this.handleFiltersUpdate()},this),e.on("search",(t,r)=>{this.selectedFilters={};let i=r;if(i)for(let[a,l]of Object.entries(i))Array.isArray(l)&&l.length>0&&(this.selectedFilters[a]=new Set(l));this.isRendered&&this.updateFilters()},this),e.on("error",t=>{let r=t;this.showError({message:r.message||"Failed to load filters",details:r.bundlePath?`Bundle path: ${r.bundlePath}`:void 0})},this),e.on("translations",()=>{this.render(),this.isRendered=!1,this.handleFiltersUpdate()},this)}update(){this.hasAttribute("show-empty")&&(this.showEmpty=this.getAttribute("show-empty")!=="false"),this.hasAttribute("expanded")&&(this.expanded=this.getAttribute("expanded")!=="false"),this.hasAttribute("open")&&(this.openFilters=(this.getAttribute("open")||"").split(",").map(e=>e.trim().toLowerCase()).filter(e=>e.length>0)),this.isRendered&&(this.isRendered=!1,this.handleFiltersUpdate())}};customElements.get("pagefind-filter-pane")||customElements.define("pagefind-filter-pane",Ne);var Me=class extends f{constructor(){super();this.isOpen=!1;this.activeIndex=-1;this.selectedValues=new Set;this.isRendered=!1;this.filtersLoaded=!1;this.filterName=null;this.availableFilters={};this.totalFilters={};this.singleSelect=!1;this.showEmpty=!1;this.wrapLabels=!1;this.hideClear=!1;this.sortOption="default";this.wrapperEl=null;this.triggerEl=null;this.menuEl=null;this.optionsEl=null;this.clearEl=null;this.badgeEl=null;this.optionElements=[];this.focusedOptionEl=null;this.typeAheadBuffer="";this.typeAheadTimeout=null;this._handleClickOutside=this._handleClickOutside.bind(this)}static get observedAttributes(){return["filter","label","single-select","show-empty","wrap","sort","hide-clear"]}init(){if(this.filterName=this.getAttribute("filter"),!this.filterName){this.showError({message:"filter attribute is required on "});return}if(this.singleSelect=this.hasAttribute("single-select"),this.showEmpty=this.hasAttribute("show-empty"),this.wrapLabels=this.hasAttribute("wrap"),this.hideClear=this.hasAttribute("hide-clear"),this.hasAttribute("sort")){let e=this.getAttribute("sort");["default","alphabetical","count-desc","count-asc"].includes(e)&&(this.sortOption=e)}this.render()}sortValues(e){if(this.sortOption==="default")return e;let t=[...e];switch(this.sortOption){case"alphabetical":t.sort((r,i)=>r.localeCompare(i));break;case"count-desc":t.sort((r,i)=>{let a=this.availableFilters[r]??this.totalFilters[r]??0;return(this.availableFilters[i]??this.totalFilters[i]??0)-a});break;case"count-asc":t.sort((r,i)=>{let a=this.availableFilters[r]??this.totalFilters[r]??0,l=this.availableFilters[i]??this.totalFilters[i]??0;return a-l});break}return t}render(){this.innerHTML="";let e=this.ensureId("pf-dropdown"),t=`${e}-trigger`,r=`${e}-menu`;this.wrapperEl=document.createElement("div"),this.wrapperEl.className="pf-dropdown-wrapper",this.triggerEl=document.createElement("button"),this.triggerEl.type="button",this.triggerEl.id=t,this.triggerEl.className="pf-dropdown-trigger",this.wrapLabels&&this.triggerEl.classList.add("wrap"),this.triggerEl.setAttribute("role","combobox"),this.triggerEl.setAttribute("aria-haspopup","listbox"),this.triggerEl.setAttribute("aria-expanded","false"),this.triggerEl.setAttribute("aria-controls",r);let i=document.createElement("span");i.className="pf-dropdown-trigger-label",this.wrapLabels&&i.classList.add("wrap"),i.textContent=this.getAttribute("label")||this.filterName||"",this.triggerEl.appendChild(i),this.badgeEl=document.createElement("span"),this.badgeEl.className="pf-dropdown-selected-badge",this.badgeEl.setAttribute("data-pf-hidden","true"),this.badgeEl.setAttribute("aria-hidden","true"),this.badgeEl.textContent="0",this.triggerEl.appendChild(this.badgeEl);let a=document.createElement("span");a.className="pf-dropdown-arrow",a.setAttribute("aria-hidden","true"),this.triggerEl.appendChild(a),this.wrapperEl.appendChild(this.triggerEl),this.menuEl=document.createElement("div"),this.menuEl.id=r,this.menuEl.className="pf-dropdown-menu",this.menuEl.hidden=!0,this.optionsEl=document.createElement("div"),this.optionsEl.className="pf-dropdown-options",this.optionsEl.setAttribute("role","listbox"),this.optionsEl.setAttribute("aria-multiselectable",this.singleSelect?"false":"true"),this.optionsEl.setAttribute("aria-labelledby",t),this.menuEl.appendChild(this.optionsEl),this.wrapperEl.appendChild(this.menuEl),this.hideClear||(this.clearEl=document.createElement("button"),this.clearEl.type="button",this.clearEl.className="pf-dropdown-clear",this.clearEl.setAttribute("aria-disabled","true"),this.clearEl.setAttribute("aria-label",(this.instance?.translate("clear_search")||"Clear")+" "+(this.getAttribute("label")||this.filterName||"")),this.clearEl.textContent=this.instance?.translate("clear_search")||"Clear",this.wrapperEl.appendChild(this.clearEl),this.clearEl.addEventListener("click",()=>this.clearAll())),this.appendChild(this.wrapperEl),this.triggerEl.addEventListener("click",()=>this.toggle()),this.triggerEl.addEventListener("focus",()=>this.instance?.triggerLoad()),this.triggerEl.addEventListener("keydown",l=>{this.isOpen?this.handleMenuKeydown(l):this.handleTriggerKeydown(l)}),this.isRendered=!0}toggle(){this.isOpen?this.close():this.open()}open(){if(this.instance?.triggerLoad(),this.isOpen||!this.menuEl||!this.triggerEl||!this.optionsEl)return;if(this.isOpen=!0,this.filtersLoaded||this.showLoadingState(),this.menuEl.hidden=!1,this.triggerEl.setAttribute("aria-expanded","true"),this.triggerEl.classList.add("open"),this.optionElements.length>0){let i=this.activeIndex>=0?this.activeIndex:0;this.setActiveIndex(i)}let e=this.instance?.translate("keyboard_navigate")||"navigate",t=this.instance?.translate("keyboard_select")||"select",r=this.instance?.translate("keyboard_close")||"close";this.instance?.registerShortcut({label:"\u2191\u2193",description:e},this),this.instance?.registerShortcut({label:"\u21B5",description:t},this),this.instance?.registerShortcut({label:"esc",description:r},this),setTimeout(()=>{document.addEventListener("click",this._handleClickOutside)},0)}close(e=!0){!this.isOpen||!this.menuEl||!this.triggerEl||!this.optionsEl||(this.isOpen=!1,this.menuEl.hidden=!0,this.triggerEl.setAttribute("aria-expanded","false"),this.triggerEl.classList.remove("open"),this.triggerEl?.removeAttribute("aria-activedescendant"),this.focusedOptionEl&&(this.focusedOptionEl.classList.remove("pf-dropdown-option-focused"),this.focusedOptionEl=null),this.instance?.deregisterAllShortcuts(this),document.removeEventListener("click",this._handleClickOutside),e&&this.triggerEl.focus())}_handleClickOutside(e){this.wrapperEl&&!this.wrapperEl.contains(e.target)&&this.close(!1)}handleTriggerKeydown(e){switch(e.key){case"Enter":case" ":e.preventDefault(),this.open();break;case"ArrowDown":e.preventDefault(),this.open(),this.setActiveIndex(0);break;case"ArrowUp":e.preventDefault(),this.open(),this.setActiveIndex(this.optionElements.length-1);break}}handleMenuKeydown(e){switch(e.key){case"ArrowDown":e.preventDefault(),this.moveActiveIndex(1);break;case"ArrowUp":e.preventDefault(),this.moveActiveIndex(-1);break;case"Home":e.preventDefault(),this.setActiveIndex(0);break;case"End":e.preventDefault(),this.setActiveIndex(this.optionElements.length-1);break;case"Enter":case" ":if(e.preventDefault(),this.activeIndex>=0&&this.activeIndex=this.optionElements.length||!this.optionsEl)return;this.focusedOptionEl&&this.focusedOptionEl.classList.remove("pf-dropdown-option-focused"),this.activeIndex=e;let t=this.optionElements[e];t.el.classList.add("pf-dropdown-option-focused"),this.focusedOptionEl=t.el,this.triggerEl?.setAttribute("aria-activedescendant",t.el.id),this.scrollToCenter(t.el)}scrollToCenter(e){if(!this.optionsEl)return;let t=this.optionsEl,r=e.offsetTop,i=e.offsetHeight,a=t.clientHeight,l=r-a/2+i/2;t.scrollTo({top:l,behavior:"smooth"})}moveActiveIndex(e){let t=this.activeIndex+e;t<0?t=this.optionElements.length-1:t>=this.optionElements.length&&(t=0),this.setActiveIndex(t)}handleTypeAhead(e){this.typeAheadBuffer+=e.toLowerCase(),this.typeAheadTimeout&&clearTimeout(this.typeAheadTimeout);let t=this.optionElements.findIndex(({value:r})=>r.toLowerCase().startsWith(this.typeAheadBuffer));t>=0&&this.setActiveIndex(t),this.typeAheadTimeout=setTimeout(()=>{this.typeAheadBuffer=""},500)}showLoadingState(){if(!this.optionsEl)return;this.optionsEl.innerHTML="",this.optionsEl.setAttribute("aria-busy","true");let e=document.createElement("div");e.setAttribute("data-pf-sr-hidden","true"),e.textContent="Loading filter options...",this.optionsEl.appendChild(e);for(let t=0;t<3;t++){let r=document.createElement("div");r.className="pf-dropdown-option pf-dropdown-option-loading",r.setAttribute("aria-hidden","true");let i=document.createElement("span");i.className="pf-dropdown-checkbox pf-skeleton",r.appendChild(i);let a=document.createElement("span");a.className="pf-dropdown-option-label pf-skeleton",a.style.width=`${60+t*15}%`,a.innerHTML=" ",r.appendChild(a),this.optionsEl.appendChild(r)}}updateOptions(){if(!this.optionsEl)return;this.filtersLoaded=!0,this.optionsEl.removeAttribute("aria-busy");let e=Object.keys(this.totalFilters||{}),t=this.sortValues(e);if(e.length===0){this.optionsEl.innerHTML="";let i=document.createElement("div");i.className="pf-dropdown-error",i.setAttribute("role","alert"),i.textContent=`No filter "${this.filterName}" found`,this.optionsEl.appendChild(i),this.optionElements=[],this.focusedOptionEl=null;return}this.wrapperEl?.removeAttribute("data-pf-hidden"),this.optionsEl.innerHTML="",this.optionElements=[],this.focusedOptionEl=null;let r=this.id||this.ensureId("pf-dropdown");t.forEach((i,a)=>{let l=this.availableFilters?.[i]??0,o=this.totalFilters[i]??0,c=this.selectedValues.has(i);if(!(this.showEmpty||l>0||c))return;let d=c?o:l,_=`${r}-option-${a}`,p=this.createOption(_,i,d,c);this.optionsEl.appendChild(p),this.optionElements.push({el:p,value:i})}),this.isOpen&&this.optionElements.length>0&&(this.activeIndex>=this.optionElements.length?this.setActiveIndex(this.optionElements.length-1):this.activeIndex<0?this.setActiveIndex(0):this.setActiveIndex(this.activeIndex)),this.updateBadge()}createOption(e,t,r,i){let a=document.createElement("div");a.id=e,a.className="pf-dropdown-option",this.wrapLabels&&a.classList.add("wrap"),a.setAttribute("role","option"),a.setAttribute("aria-selected",String(i)),a.dataset.value=t;let l=document.createElement("span");l.className="pf-dropdown-checkbox",l.setAttribute("aria-hidden","true"),a.appendChild(l);let o=document.createElement("span");o.className="pf-dropdown-option-label",this.wrapLabels&&o.classList.add("wrap"),o.textContent=t,a.appendChild(o);let c=document.createElement("span");c.className="pf-dropdown-option-count",c.setAttribute("aria-hidden","true"),c.textContent=String(r),a.appendChild(c);let u=r===1?"result":"results";return a.setAttribute("aria-label",`${t}, ${r} ${u}`),a.addEventListener("click",d=>{d.stopPropagation(),this.toggleOption(t)}),a}toggleOption(e){let t=this.selectedValues.has(e);this.singleSelect?(this.selectedValues.has(e)?this.selectedValues.clear():(this.selectedValues.clear(),this.selectedValues.add(e)),this.close()):this.selectedValues.has(e)?this.selectedValues.delete(e):this.selectedValues.add(e);let r=this.selectedValues.has(e);if(r!==t){let i=r?"selected":"deselected";this.instance?.announceRaw(`${e} ${i}`)}this.updateOptionStates(),this.updateBadge(),this.dispatchFilterChange()}clearAll(){this.selectedValues.size!==0&&(this.selectedValues.clear(),this.updateOptionStates(),this.updateBadge(),this.dispatchFilterChange())}dispatchFilterChange(){if(!this.filterName)return;let e=Array.from(this.selectedValues);e.length===0?this.instance?.triggerFilter(this.filterName,[]):this.instance?.triggerFilter(this.filterName,e)}updateBadge(){if(!this.badgeEl||!this.triggerEl)return;let e=this.selectedValues.size;if(e>0){this.badgeEl.textContent=String(e),this.badgeEl.removeAttribute("data-pf-hidden");let t=this.getAttribute("label")||this.filterName||"",r=e===1?"filter":"filters";this.triggerEl.setAttribute("aria-label",`${t}, ${e} ${r} selected`),this.clearEl&&this.clearEl.removeAttribute("aria-disabled")}else this.badgeEl.setAttribute("data-pf-hidden","true"),this.triggerEl.removeAttribute("aria-label"),this.clearEl&&this.clearEl.setAttribute("aria-disabled","true")}updateOptionStates(){for(let{el:e,value:t}of this.optionElements){let r=this.selectedValues.has(t);e.setAttribute("aria-selected",String(r))}}register(e){this.filterName&&(e.registerFilter(this),e.on("filters",t=>{let r=t;this.availableFilters=r.available?.[this.filterName]||{},this.totalFilters=r.total?.[this.filterName]||{},this.isRendered&&this.updateOptions()},this),e.on("search",(t,r)=>{let a=r?.[this.filterName]||[];this.selectedValues=new Set(a),this.isRendered&&(this.updateOptionStates(),this.updateBadge())},this),e.on("error",t=>{let r=t;this.showError({message:r.message||"Failed to load filters",details:r.bundlePath?`Bundle path: ${r.bundlePath}`:void 0})},this))}update(){let e=this.getAttribute("filter");if(e!==this.filterName&&(this.filterName=e,this.selectedValues.clear(),this.updateOptions()),this.singleSelect=this.hasAttribute("single-select"),this.showEmpty=this.hasAttribute("show-empty"),this.wrapLabels=this.hasAttribute("wrap"),this.hideClear=this.hasAttribute("hide-clear"),this.hasAttribute("sort")){let r=this.getAttribute("sort");["default","alphabetical","count-desc","count-asc"].includes(r)&&(this.sortOption=r)}else this.sortOption="default";this.optionsEl&&this.optionsEl.setAttribute("aria-multiselectable",this.singleSelect?"false":"true");let t=this.triggerEl?.querySelector(".pf-dropdown-trigger-label");t&&(t.textContent=this.getAttribute("label")||this.filterName||""),this.updateOptions()}cleanup(){document.removeEventListener("click",this._handleClickOutside),this.instance?.deregisterAllShortcuts(this),this.focusedOptionEl=null,this.typeAheadTimeout&&clearTimeout(this.typeAheadTimeout)}};customElements.get("pagefind-filter-dropdown")||customElements.define("pagefind-filter-dropdown",Me);var Oe=class extends f{constructor(){super();this.dialogEl=null;this.resetOnClose=!1;this._isOpen=!1;this._closeHandler=null}static get observedAttributes(){return["reset-on-close"]}init(){this.hasAttribute("reset-on-close")&&(this.resetOnClose=this.getAttribute("reset-on-close")!=="false"),this.render()}render(){let e=this.children.length>0,t=e?Array.from(this.children):null;this.innerHTML="";let r=this.id||this.instance.generateId("pagefind-modal"),i=this.instance?.translate("keyboard_search")||"search";if(this.instance?.direction==="rtl"?this.setAttribute("dir","rtl"):this.removeAttribute("dir"),this.dialogEl=document.createElement("dialog"),this.dialogEl.className="pf-modal",this.dialogEl.id=r,this.dialogEl.setAttribute("aria-label",i),e&&t)t.forEach(a=>this.dialogEl.appendChild(a));else{let a=this.getAttribute("instance"),l=document.createElement("pagefind-modal-header"),o=document.createElement("pagefind-input");a&&o.setAttribute("instance",a),l.appendChild(o);let c=document.createElement("pagefind-modal-body"),u=document.createElement("pagefind-summary"),d=document.createElement("pagefind-results");a&&(u.setAttribute("instance",a),d.setAttribute("instance",a)),c.append(u,d);let _=document.createElement("pagefind-modal-footer"),p=document.createElement("pagefind-keyboard-hints");a&&p.setAttribute("instance",a),_.appendChild(p),this.dialogEl.append(l,c,_)}this.appendChild(this.dialogEl),this.setupEventHandlers()}setupEventHandlers(){this.dialogEl&&(this._closeHandler=()=>{this._isOpen=!1,this.handleClose()},this.dialogEl.addEventListener("close",this._closeHandler),this.dialogEl.addEventListener("keydown",e=>{e.key==="Escape"&&(e.preventDefault(),e.stopPropagation(),this.close())},!0),this.dialogEl.addEventListener("click",e=>{e.target===this.dialogEl&&this.close()}))}open(){if(this._isOpen||!this.dialogEl)return;this._isOpen=!0,this.dialogEl.showModal();let e=this.instance?.translate("keyboard_close")||"close";this.instance?.registerShortcut({label:"esc",description:e},this),requestAnimationFrame(()=>{let r=this.querySelector("pagefind-input");if(r&&typeof r.focus=="function")r.focus();else{let i=this.querySelector("input");i&&i.focus()}}),(this.instance?.getUtilities("modal-trigger")||[]).forEach(r=>r.buttonEl?.setAttribute("aria-expanded","true"))}close(){!this._isOpen||!this.dialogEl||this.dialogEl.close()}handleClose(){this.instance?.deregisterAllShortcuts(this),this.resetOnClose&&this.instance&&this.instance.triggerSearch("");let t=(this.instance?.getUtilities("modal-trigger")||[])[0];t&&typeof t.handleModalClose=="function"&&t.handleModalClose()}get isOpen(){return this._isOpen}register(e){e.registerUtility(this,"modal"),e.on("translations",()=>{let t=this._isOpen;this.render(),t&&this.open()},this)}reconcileAria(){(this.instance?.getUtilities("modal-trigger")||[]).forEach(t=>{t.buttonEl&&this.dialogEl?.id&&t.buttonEl.setAttribute("aria-controls",this.dialogEl.id)})}cleanup(){this.dialogEl&&this._closeHandler&&this.dialogEl.removeEventListener("close",this._closeHandler),this.instance?.deregisterAllShortcuts(this)}update(){this.hasAttribute("reset-on-close")&&(this.resetOnClose=this.getAttribute("reset-on-close")!=="false")}};customElements.get("pagefind-modal")||customElements.define("pagefind-modal",Oe);var S=null;function is(){if(S!==null)return S;try{let n=navigator.userAgentData;if(n?.platform)return S=n.platform.toLowerCase().includes("mac"),S}catch{}return S=/mac/i.test(navigator.userAgent),S}function je(n){let s=n.toLowerCase().split("+"),e={mod:!1,ctrl:!1,shift:!1,alt:!1,meta:!1,key:""};for(let t of s)switch(t){case"mod":e.mod=!0;break;case"ctrl":e.ctrl=!0;break;case"shift":e.shift=!0;break;case"alt":e.alt=!0;break;case"meta":case"cmd":case"command":e.meta=!0;break;default:e.key=t}return e}function Be(n,s){let e=is(),t=s.key.toLowerCase()===n.key,r=n.mod?!e:n.ctrl,i=n.mod?e:n.meta,a=r?s.ctrlKey:!s.ctrlKey,l=i?s.metaKey:!s.metaKey,o=n.shift?s.shiftKey:!s.shiftKey,c=n.alt?s.altKey:!s.altKey;return t&&a&&l&&o&&c}function xe(n){let s=is(),e=[],t=[];return n.mod&&(e.push(s?"\u2318":"Ctrl"),t.push(s?"Meta":"Control")),n.meta&&(e.push(s?"\u2318":"Win"),t.push("Meta")),n.ctrl&&(e.push("Ctrl"),t.push("Control")),n.shift&&(e.push("Shift"),t.push("Shift")),n.alt&&(e.push("Alt"),t.push("Alt")),e.push(n.key.toUpperCase()),t.push(n.key),{keys:e,aria:t.join("+")}}var He=class extends f{constructor(){super();this.buttonEl=null;this._userPlaceholder=null;this.shortcut="mod+k";this.hideShortcut=!1;this.compact=!1;this._keydownHandler=null;this._keyBinding=null}static get observedAttributes(){return["placeholder","shortcut","hide-shortcut","compact"]}get placeholder(){return this._userPlaceholder||this.instance?.translate("keyboard_search")||"Search"}init(){this.readAttributes(),this.render(),this.setupKeyboardShortcut()}readAttributes(){this.hasAttribute("placeholder")&&(this._userPlaceholder=this.getAttribute("placeholder")),this.hasAttribute("shortcut")&&(this.shortcut=this.getAttribute("shortcut")||"mod+k"),this.hasAttribute("hide-shortcut")&&(this.hideShortcut=this.getAttribute("hide-shortcut")!=="false"),this.hasAttribute("compact")&&(this.compact=this.getAttribute("compact")!=="false"),this._keyBinding=je(this.shortcut)}render(){if(this.innerHTML="",this.instance?.direction==="rtl"?this.setAttribute("dir","rtl"):this.removeAttribute("dir"),this.buttonEl=document.createElement("button"),this.buttonEl.className="pf-trigger-btn",this.buttonEl.type="button",this.buttonEl.setAttribute("aria-haspopup","dialog"),this.buttonEl.setAttribute("aria-expanded","false"),this.buttonEl.setAttribute("aria-label",this.placeholder||"Search"),this._keyBinding){let t=xe(this._keyBinding);this.buttonEl.setAttribute("aria-keyshortcuts",t.aria)}let e=document.createElement("span");if(e.className="pf-trigger-icon",e.setAttribute("aria-hidden","true"),this.buttonEl.appendChild(e),!this.compact){let t=document.createElement("span");t.className="pf-trigger-text",t.textContent=this.placeholder,this.buttonEl.appendChild(t)}if(!this.hideShortcut&&this._keyBinding){let t=document.createElement("span");t.className="pf-trigger-shortcut",t.setAttribute("aria-hidden","true");let r=xe(this._keyBinding);for(let i of r.keys){let a=document.createElement("span");a.className="pf-trigger-key",a.textContent=i,t.appendChild(a)}this.buttonEl.appendChild(t)}this.appendChild(this.buttonEl),this.buttonEl.addEventListener("click",()=>{this.openModal()})}setupKeyboardShortcut(){this._keydownHandler=e=>{if(!this._keyBinding||!Be(this._keyBinding,e))return;let t=document.activeElement;t&&(t.tagName==="INPUT"||t.tagName==="TEXTAREA"||t.isContentEditable)||(e.preventDefault(),this.openModal())},document.addEventListener("keydown",this._keydownHandler)}openModal(){let t=(this.instance?.getUtilities("modal")||[])[0];t&&typeof t.open=="function"&&(t.open(),this.buttonEl&&this.buttonEl.setAttribute("aria-expanded","true"))}handleModalClose(){this.buttonEl&&(this.buttonEl.setAttribute("aria-expanded","false"),this.buttonEl.focus())}register(e){e.registerUtility(this,"modal-trigger"),e.on("translations",()=>{this.render()},this)}reconcileAria(){let t=(this.instance?.getUtilities("modal")||[])[0];t?.dialogEl?.id&&this.buttonEl&&this.buttonEl.setAttribute("aria-controls",t.dialogEl.id)}cleanup(){this._keydownHandler&&(document.removeEventListener("keydown",this._keydownHandler),this._keydownHandler=null)}update(){this.readAttributes(),this.render()}};customElements.get("pagefind-modal-trigger")||customElements.define("pagefind-modal-trigger",He);var we=class extends f{constructor(){super(...arguments);this.closeBtn=null}init(){this.classList.add("pf-modal-header");let e=document.createElement("div");for(e.className="pf-modal-header-content";this.firstChild;)e.appendChild(this.firstChild);this.closeBtn=document.createElement("button"),this.closeBtn.type="button",this.closeBtn.className="pf-modal-close",this.closeBtn.setAttribute("aria-label",this.instance?.translate("keyboard_close")||"Close"),this.closeBtn.innerHTML='',this.closeBtn.addEventListener("click",()=>{let t=this.closest("pagefind-modal");t&&typeof t.close=="function"&&t.close()}),this.append(e,this.closeBtn)}register(e){e.registerUtility(this,"modal-header"),e.on("translations",()=>{this.closeBtn&&this.closeBtn.setAttribute("aria-label",e.translate("keyboard_close")||"Close")},this)}};customElements.get("pagefind-modal-header")||customElements.define("pagefind-modal-header",we);var Ie=class extends f{init(){this.classList.add("pf-modal-body"),this.setAttribute("tabindex","-1")}register(s){}};customElements.get("pagefind-modal-body")||customElements.define("pagefind-modal-body",Ie);var Ue=class extends f{init(){this.classList.add("pf-modal-footer")}register(s){}};customElements.get("pagefind-modal-footer")||customElements.define("pagefind-modal-footer",Ue);var Le=class extends f{init(){this.classList.add("pf-keyboard-hints"),this.setAttribute("aria-hidden","true")}render(){this.innerHTML="",this.instance?.direction==="rtl"?this.setAttribute("dir","rtl"):this.removeAttribute("dir");let s=this.instance?.getActiveShortcuts()||[];if(s.length===0)return;let e=new Set;for(let t of s){if(e.has(t.label))continue;e.add(t.label);let r=document.createElement("div");r.className="pf-keyboard-hint";let i=document.createElement("kbd");i.className="pf-keyboard-key",i.textContent=t.label,r.appendChild(i),r.appendChild(document.createTextNode(` ${t.description}`)),this.appendChild(r)}}register(s){s.registerUtility(this,"keyboard-hints"),this.render(),s.on("translations",()=>{this.render()},this)}};customElements.get("pagefind-keyboard-hints")||customElements.define("pagefind-keyboard-hints",Le);var va=(n=100)=>new Promise(s=>setTimeout(s,n)),as=(n,s)=>{let e=n.getAttribute("role")==="option"?[n]:Array.from(n.querySelectorAll('[role="option"]'));for(let t=0;t{if(n instanceof Element)return[n];if(Array.isArray(n)&&n.every(s=>s instanceof Element))return n;if(typeof n=="string"||n instanceof String){let s=document.createElement("div");return s.innerHTML=n,[...s.childNodes]}return console.error(`[Pagefind Searchbox]: Expected template to return HTML element or string, got ${typeof n}`),[]},Ra=`{{#if and(options.show_sub_results, sub_results)}}{{/if}}`,ns=R(Ra),ka=``,Aa=R(ka),jt=class{constructor(s){this.data=null;this.cachedOptions=null;this.loading=!1;this.retryDelay=0;this.observer=null;this.rawResult=s.rawResult,this.placeholderEl=s.placeholderEl,this.renderFn=s.renderFn,this.intersectionRoot=s.intersectionRoot,this.index=s.index,this.onLoad=s.onLoad,this.setupObserver()}setupObserver(){if(this.data!==null||this.observer!==null)return;let s={root:this.intersectionRoot,rootMargin:"50px",threshold:.01};this.observer=new IntersectionObserver((e,t)=>{this.data===null&&e?.[0]?.isIntersecting&&(this.load(),t.disconnect(),this.observer=null)},s),this.observer.observe(this.placeholderEl)}async load(){if(!(this.data!==null||this.loading)){this.loading=!0;try{this.data=await this.rawResult.data();let s=this.renderFn(this.data),e=Ke(s);if(e.length>0&&this.placeholderEl.parentNode){let t=e.find(r=>r instanceof Element);this.placeholderEl.replaceWith(...e),t instanceof Element&&(this.placeholderEl=t,as(t,this.index),this.cacheOptions())}}catch{await new Promise(s=>setTimeout(s,this.retryDelay||100)),this.retryDelay=Math.min((this.retryDelay||100)*2,1e4),this.loading=!1}this.onLoad?.()}}cacheOptions(){if(!this.data||!this.placeholderEl){this.cachedOptions=null;return}this.placeholderEl.getAttribute("role")==="group"?this.cachedOptions=Array.from(this.placeholderEl.querySelectorAll('[role="option"]')):this.placeholderEl.getAttribute("role")==="option"?this.cachedOptions=[this.placeholderEl]:this.cachedOptions=[]}cleanup(){this.observer&&(this.observer.disconnect(),this.observer=null),this.cachedOptions=null}},Fe=class extends f{constructor(){super();this.containerEl=null;this.inputEl=null;this.dropdownEl=null;this.resultsEl=null;this.statusEl=null;this.footerEl=null;this.isOpen=!1;this.isLoading=!1;this.results=[];this.activeIndex=-1;this.activeOptionOffset=0;this.searchID=0;this.searchTerm="";this.pendingNavigation=0;this.loadingAnnouncementTimeout=null;this.selectedEl=null;this._userPlaceholder=null;this.debounce=150;this.autofocus=!1;this.showSubResults=!1;this.maxResults=0;this.showKeyboardHints=!0;this.shortcut="mod+k";this.hideShortcut=!1;this.resultTemplate=null;this.compiledResultTemplate=null;this.compiledPlaceholderTemplate=null;this._documentClickHandler=null;this._shortcutKeyHandler=null;this._keyBinding=null;this._shortcutEl=null}static get observedAttributes(){return["placeholder","debounce","autofocus","show-sub-results","max-results","show-keyboard-hints","shortcut","hide-shortcut"]}get placeholder(){return this._userPlaceholder||this.instance?.translate("placeholder")||"Search..."}readAttributes(){this.hasAttribute("placeholder")&&(this._userPlaceholder=this.getAttribute("placeholder")),this.hasAttribute("debounce")&&(this.debounce=parseInt(this.getAttribute("debounce")||"150",10)||150),this.hasAttribute("autofocus")&&(this.autofocus=this.hasAttribute("autofocus")),this.hasAttribute("show-sub-results")&&(this.showSubResults=this.getAttribute("show-sub-results")!=="false"),this.hasAttribute("max-results")&&(this.maxResults=parseInt(this.getAttribute("max-results")||"0",10)),this.hasAttribute("show-keyboard-hints")&&(this.showKeyboardHints=this.getAttribute("show-keyboard-hints")!=="false"),this.hasAttribute("shortcut")&&(this.shortcut=this.getAttribute("shortcut")||"mod+k"),this.hasAttribute("hide-shortcut")&&(this.hideShortcut=this.getAttribute("hide-shortcut")!=="false"),this._keyBinding=je(this.shortcut)}init(){this.readAttributes(),this.checkForTemplates(),this.render(),this.setupOutsideClickHandler(),this.setupShortcutHandler()}checkForTemplates(){let e=this.querySelector('script[type="text/pagefind-template"]:not([data-template]), script[type="text/pagefind-template"][data-template="result"]');e&&(this.compiledResultTemplate=R((e.textContent||"").trim()));let t=this.querySelector('script[type="text/pagefind-template"][data-template="placeholder"]');t&&(this.compiledPlaceholderTemplate=R((t.textContent||"").trim()))}getPlaceholder(){return this.compiledPlaceholderTemplate?this.compiledPlaceholderTemplate({}):Aa({})}render(){let e=[];this.querySelectorAll('script[type="text/pagefind-template"]').forEach(l=>{e.push(l)}),this.innerHTML="",e.forEach(l=>this.appendChild(l));let t=this.instance.generateId("pf-sb-input"),r=this.instance.generateId("pf-sb-results");this.containerEl=document.createElement("div"),this.containerEl.className="pf-searchbox",this.appendChild(this.containerEl);let i=document.createElement("div");if(i.className="pf-searchbox-input-wrapper",this.containerEl.appendChild(i),this.inputEl=document.createElement("input"),this.inputEl.id=t,this.inputEl.className="pf-searchbox-input",this.inputEl.type="text",this.inputEl.setAttribute("role","combobox"),this.inputEl.setAttribute("aria-autocomplete","list"),this.inputEl.setAttribute("aria-controls",r),this.inputEl.setAttribute("aria-expanded","false"),this.inputEl.setAttribute("autocomplete","off"),this.inputEl.setAttribute("autocapitalize","none"),this.inputEl.placeholder=this.placeholder,this.autofocus&&this.inputEl.setAttribute("autofocus","autofocus"),i.appendChild(this.inputEl),!this.hideShortcut&&this._keyBinding){this._shortcutEl=document.createElement("span"),this._shortcutEl.className="pf-trigger-shortcut",this._shortcutEl.setAttribute("aria-hidden","true");let l=xe(this._keyBinding);for(let o of l.keys){let c=document.createElement("span");c.className="pf-trigger-key",c.textContent=o,this._shortcutEl.appendChild(c)}i.appendChild(this._shortcutEl),this.inputEl.setAttribute("aria-keyshortcuts",l.aria)}this.dropdownEl=document.createElement("div"),this.dropdownEl.className="pf-searchbox-dropdown",this.containerEl.appendChild(this.dropdownEl);let a=this.instance?.translate("results_label")||"Search results";this.instance?.direction==="rtl"?this.setAttribute("dir","rtl"):this.removeAttribute("dir"),this.resultsEl=document.createElement("div"),this.resultsEl.id=r,this.resultsEl.className="pf-searchbox-results",this.resultsEl.setAttribute("role","listbox"),this.resultsEl.setAttribute("aria-label",a),this.dropdownEl.appendChild(this.resultsEl),this.statusEl=document.createElement("div"),this.statusEl.className="pf-searchbox-status",this.statusEl.hidden=!0,this.dropdownEl.appendChild(this.statusEl),this.showKeyboardHints&&(this.footerEl=document.createElement("div"),this.footerEl.className="pf-searchbox-footer",this.footerEl.setAttribute("aria-hidden","true"),this.dropdownEl.appendChild(this.footerEl),this.renderFooterHints()),this.setupEventHandlers()}renderFooterHints(){if(!this.footerEl)return;this.footerEl.innerHTML="";let e=this.instance?.translate("keyboard_navigate")||"navigate",t=this.instance?.translate("keyboard_select")||"select",r=this.instance?.translate("keyboard_close")||"close",i=document.createElement("div");i.className="pf-searchbox-footer-hint";let a=document.createElement("span");a.className="pf-searchbox-footer-key",a.textContent="\u2191",i.appendChild(a);let l=document.createElement("span");l.className="pf-searchbox-footer-key",l.textContent="\u2193",i.appendChild(l),i.appendChild(document.createTextNode(` ${e}`)),this.footerEl.appendChild(i);let o=document.createElement("div");o.className="pf-searchbox-footer-hint";let c=document.createElement("span");c.className="pf-searchbox-footer-key",c.textContent="\u21B5",o.appendChild(c),o.appendChild(document.createTextNode(` ${t}`)),this.footerEl.appendChild(o);let u=document.createElement("div");u.className="pf-searchbox-footer-hint";let d=document.createElement("span");d.className="pf-searchbox-footer-key",d.textContent="esc",u.appendChild(d),u.appendChild(document.createTextNode(` ${r}`)),this.footerEl.appendChild(u)}setupEventHandlers(){!this.inputEl||!this.resultsEl||(this.inputEl.addEventListener("input",async e=>{let t=e.target.value;if(this.searchTerm=t,!t||!t.trim()){this.closeDropdown(),this.results=[],this.instance?.triggerSearch("");return}this.openDropdown(),this.showLoadingState();let r=++this.searchID;await va(this.debounce),r===this.searchID&&this.instance?.triggerSearch(t)}),this.inputEl.addEventListener("keydown",e=>{switch(e.key){case"ArrowDown":e.preventDefault(),!this.isOpen&&this.inputEl?.value.trim()&&this.openDropdown(),this.isOpen&&this.results.length>0&&this.moveSelection(1);break;case"ArrowUp":e.preventDefault(),this.isOpen&&this.results.length>0&&this.moveSelection(-1);break;case"Enter":this.isOpen&&this.activeIndex>=0?(e.preventDefault(),this.activateCurrentSelection(e)):!this.isOpen&&this.inputEl?.value.trim()&&(e.preventDefault(),this.openDropdown(),this.results.length>0?(this.rerenderLoadedResults(),this.activeIndex=0,this.activeOptionOffset=0,this.updateSelectionUI()):this.instance?.triggerSearch(this.inputEl.value));break;case"Escape":this.pendingNavigation=0,this.clearLoadingAnnouncement(),this.isOpen&&(e.preventDefault(),this.closeDropdown());break;case"Tab":this.pendingNavigation=0,this.clearLoadingAnnouncement(),this.isOpen&&this.closeDropdown();break}}),this.inputEl.addEventListener("focus",()=>{this.instance?.triggerLoad()}),this.resultsEl.addEventListener("click",e=>{e.target.closest("a")&&this.closeDropdown()}),this.resultsEl.addEventListener("mousemove",e=>{let t=e.target.closest("a");if(t){let r=this.getResultAndOffsetFromElement(t);r&&(r.resultIndex!==this.activeIndex||r.optionOffset!==this.activeOptionOffset)&&(this.activeIndex=r.resultIndex,this.activeOptionOffset=r.optionOffset,this.updateSelectionUI(!1))}}))}setupOutsideClickHandler(){this._documentClickHandler=e=>{this.isOpen&&!this.contains(e.target)&&this.closeDropdown()},document.addEventListener("click",this._documentClickHandler)}setupShortcutHandler(){this._keyBinding&&(this._shortcutKeyHandler=e=>{if(!this._keyBinding||!Be(this._keyBinding,e))return;let t=document.activeElement;t&&(t.tagName==="INPUT"||t.tagName==="TEXTAREA"||t.isContentEditable)||(e.preventDefault(),this.inputEl?.focus())},document.addEventListener("keydown",this._shortcutKeyHandler))}openDropdown(){this.isOpen||!this.containerEl||!this.inputEl||(this.isOpen=!0,this.containerEl.classList.add("open"),this.inputEl.setAttribute("aria-expanded","true"))}closeDropdown(){!this.isOpen||!this.containerEl||!this.inputEl||(this.isOpen=!1,this.pendingNavigation=0,this.clearLoadingAnnouncement(),this.containerEl.classList.remove("open"),this.inputEl.setAttribute("aria-expanded","false"),this.inputEl.removeAttribute("aria-activedescendant"),this.activeIndex=-1,this.activeOptionOffset=0,this.selectedEl=null)}showLoadingState(){if(!this.resultsEl||!this.statusEl)return;this.isLoading=!0,this.resultsEl.innerHTML="",this.selectedEl=null,this.resultsEl.setAttribute("aria-busy","true");let e=this.instance?.translate("searching",{SEARCH_TERM:this.searchTerm})||"Searching...";this.statusEl.textContent=e,this.statusEl.className="pf-searchbox-status pf-searchbox-loading",this.statusEl.hidden=!1}showEmptyState(){if(!this.resultsEl||!this.statusEl)return;this.resultsEl.innerHTML="",this.selectedEl=null,this.resultsEl.removeAttribute("aria-busy");let e=this.instance?.translate("zero_results",{SEARCH_TERM:this.searchTerm})||`No results for "${this.searchTerm}"`;this.statusEl.textContent=e,this.statusEl.className="pf-searchbox-status pf-searchbox-empty",this.statusEl.hidden=!1,this.instance?.announce("zero_results",{SEARCH_TERM:this.searchTerm},"assertive")}getOptionsForResult(e){return e.cachedOptions!==null?e.cachedOptions:!e.data||!e.placeholderEl?[]:e.placeholderEl.getAttribute("role")==="group"?Array.from(e.placeholderEl.querySelectorAll('[role="option"]')):e.placeholderEl.getAttribute("role")==="option"?[e.placeholderEl]:[]}moveSelection(e){let t=this.results.length;if(t===0)return;if(e<0){if(this.activeIndex===-1)return;if(this.activeOptionOffset>0){this.activeOptionOffset--,this.pendingNavigation=0,this.clearLoadingAnnouncement(),this.updateSelectionUI(!0);return}let o=this.activeIndex-1;if(o<0){this.pendingNavigation=0,this.clearLoadingAnnouncement(),this.activeIndex=-1,this.activeOptionOffset=0,this.updateSelectionUI(!0);return}let c=this.results[o];if(!c||!c.data)return;let u=this.getOptionsForResult(c);this.activeIndex=o,this.activeOptionOffset=Math.max(0,u.length-1),this.pendingNavigation=0,this.clearLoadingAnnouncement(),this.updateSelectionUI(!0),this.preloadAhead(o,e);return}if(this.activeIndex===-1){if(this.results[0]&&!this.results[0].data){this.pendingNavigation+=e,this.results[0].load(),this.scheduleLoadingAnnouncement(),this.preloadAhead(0,e);return}this.activeIndex=0,this.activeOptionOffset=0,this.pendingNavigation=0,this.clearLoadingAnnouncement(),this.updateSelectionUI(!0),this.preloadAhead(0,e);return}let r=this.results[this.activeIndex];if(!r?.data){r&&(this.pendingNavigation+=e,r.load(),this.scheduleLoadingAnnouncement(),this.preloadAhead(this.activeIndex,e));return}let i=this.getOptionsForResult(r);if(this.activeOptionOffset=t)return;let l=this.results[a];if(l&&!l.data){this.pendingNavigation+=e,l.load(),this.scheduleLoadingAnnouncement(),this.preloadAhead(a,e);return}this.activeIndex=a,this.activeOptionOffset=0,this.pendingNavigation=0,this.clearLoadingAnnouncement(),this.updateSelectionUI(!0),this.preloadAhead(a,e)}preloadAhead(e,t){let r=t>0?1:-1,i=Math.abs(this.pendingNavigation)+3;for(let a=1;a<=i;a++){let l=e+r*a;if(l>=0&&l{this.loadingAnnouncementTimeout=null,this.instance?.announce("loading",{},"polite")},800))}clearLoadingAnnouncement(){this.loadingAnnouncementTimeout&&(clearTimeout(this.loadingAnnouncementTimeout),this.loadingAnnouncementTimeout=null)}handleResultLoaded(){if(this.clearLoadingAnnouncement(),this.pendingNavigation===0){this.updateSelectionUI();return}let e=this.pendingNavigation>0?1:-1,t=this.activeIndex,r=this.activeOptionOffset;for(;this.pendingNavigation!==0;)if(e>0){let i=this.results[t];if(i?.data){let o=this.getOptionsForResult(i);if(r=this.results.length){this.pendingNavigation=0;break}let l=this.results[a];if(l?.data)t=a,r=0,this.pendingNavigation--;else{l&&(l.load(),this.scheduleLoadingAnnouncement(),this.preloadAhead(a,e));break}}else{if(r>0){r--,this.pendingNavigation++;continue}let i=t-1;if(i<0){this.pendingNavigation=0;break}let a=this.results[i];if(a?.data){let l=this.getOptionsForResult(a);t=i,r=Math.max(0,l.length-1),this.pendingNavigation++}else break}(t!==this.activeIndex||r!==this.activeOptionOffset)&&(this.activeIndex=t,this.activeOptionOffset=r,this.updateSelectionUI(!0))}updateSelectionUI(e=!1){if(!this.resultsEl||!this.inputEl)return;this.selectedEl&&(this.selectedEl.removeAttribute("data-pf-selected"),this.selectedEl.setAttribute("aria-selected","false"),this.selectedEl=null);let t=this.activeIndex>=0?this.results[this.activeIndex]:null,i=(t?this.getOptionsForResult(t):[])[this.activeOptionOffset];i?(i.setAttribute("data-pf-selected",""),i.setAttribute("aria-selected","true"),this.selectedEl=i,this.inputEl.setAttribute("aria-activedescendant",i.id),e&&this.scrollToCenter(i)):this.inputEl.removeAttribute("aria-activedescendant")}scrollToCenter(e){if(!this.resultsEl)return;let t=this.resultsEl,r=e.offsetTop,i=e.offsetHeight,a=t.clientHeight,l=r-a/2+i/2;t.scrollTo({top:l,behavior:"smooth"})}getResultAndOffsetFromElement(e){let t=e.closest("[data-pf-result-index]");if(!t)return null;let r=parseInt(t.getAttribute("data-pf-result-index"),10),i=parseInt(t.getAttribute("data-pf-option-offset")||"0",10);return Number.isNaN(r)||Number.isNaN(i)?null:{resultIndex:r,optionOffset:i}}activateCurrentSelection(e){if(this.activeIndex<0||this.activeIndex>=this.results.length)return;let t=this.results[this.activeIndex];if(!t||!t.data)return;let i=this.getOptionsForResult(t)[this.activeOptionOffset];!i||!i.href||(e.metaKey||e.ctrlKey||e.shiftKey?window.open(i.href,"_blank"):window.location.href=i.href,this.closeDropdown())}handleResults(e){this.isLoading=!1,this.resultsEl&&this.resultsEl.removeAttribute("aria-busy"),this.statusEl&&(this.statusEl.hidden=!0);for(let i of this.results)i.cleanup();if(this.pendingNavigation=0,this.clearLoadingAnnouncement(),!e.results||e.results.length===0){this.results=[],this.showEmptyState();return}let t=this.maxResults>0?e.results.slice(0,this.maxResults):e.results;this.resultsEl&&(this.resultsEl.innerHTML="",this.selectedEl=null);let r=this.getResultRenderer();this.results=t.map((i,a)=>{let l=this.getPlaceholder(),c=Ke(l)[0];this.resultsEl&&c&&this.resultsEl.appendChild(c);let u=new jt({rawResult:i,placeholderEl:c,renderFn:r,intersectionRoot:this.resultsEl,index:a,onLoad:()=>{this.results[a]===u&&this.handleResultLoaded()}});return u}),this.activeIndex=0,this.activeOptionOffset=0,this.updateSelectionUI(),this.announceResults()}buildTemplateData(e){let t=this.showSubResults?this.instance.getDisplaySubResults(e):[],r=this.instance.generateId("pf-sb-result");return{meta:e.meta||{},excerpt:e.excerpt||"",url:e.url||"",sub_results:t.map(i=>{let a=this.instance.generateId("pf-sb-result");return{title:i.title,url:i.url,excerpt:i.excerpt,aria:{result_id:a,title_id:`${a}-title`,excerpt_id:`${a}-excerpt`}}}),options:{show_sub_results:this.showSubResults},aria:{result_id:r,title_id:`${r}-title`,excerpt_id:`${r}-excerpt`}}}getResultRenderer(){if(this.resultTemplate)return this.resultTemplate;if(this.compiledResultTemplate){let e=this.compiledResultTemplate;return t=>{let r=this.buildTemplateData(t);return e(r)}}return e=>{let t=this.buildTemplateData(e);return ns(t)}}rerenderLoadedResults(){if(this.resultsEl){this.resultsEl.innerHTML="",this.selectedEl=null;for(let e=0;e{this.searchTerm&&this.searchTerm.trim()&&(this.openDropdown(),this.showLoadingState())},this),e.on("results",t=>{this.handleResults(t)},this),e.on("error",t=>{let r=t;this.isLoading=!1;let i=e.translate("error_search")||"Search failed";this.showError({message:r.message||i,details:r.bundlePath?`Bundle path: ${r.bundlePath}`:void 0}),e.announce("error_search",{},"assertive")},this),e.on("search",t=>{this.inputEl&&document.activeElement!==this.inputEl&&(this.inputEl.value=t,this.searchTerm=t)},this),e.on("translations",()=>{let t=this.inputEl?.value||"",r=this.isOpen;this.render(),this.inputEl&&t&&(this.inputEl.value=t),r&&(this.openDropdown(),this.results.length>0&&(this.rerenderLoadedResults(),this.updateSelectionUI()))},this)}cleanup(){this.clearLoadingAnnouncement();for(let e of this.results)e.cleanup();this.results=[],this.selectedEl=null,this._documentClickHandler&&(document.removeEventListener("click",this._documentClickHandler),this._documentClickHandler=null),this._shortcutKeyHandler&&(document.removeEventListener("keydown",this._shortcutKeyHandler),this._shortcutKeyHandler=null)}update(){this.readAttributes(),this._documentClickHandler&&(document.removeEventListener("click",this._documentClickHandler),this._documentClickHandler=null),this._shortcutKeyHandler&&(document.removeEventListener("keydown",this._shortcutKeyHandler),this._shortcutKeyHandler=null),this.render(),this.setupOutsideClickHandler(),this.setupShortcutHandler()}focus(){this.inputEl&&this.inputEl.focus()}};customElements.get("pagefind-searchbox")||customElements.define("pagefind-searchbox",Fe);ts("resolveUrl",(n,s)=>{let e=String(n??"");if(!e||/^[a-z][a-z0-9+.-]*:/i.test(e)||/^\/\//.test(e)||e.startsWith("/"))return e;try{return new URL(e,new URL(String(s??"/"),"https://p")).pathname}catch{return e}});typeof window<"u"&&(window.PagefindComponents=Bt);})(); diff --git a/docs/pagefind/pagefind-entry.json b/docs/pagefind/pagefind-entry.json new file mode 100644 index 00000000..311d8f03 --- /dev/null +++ b/docs/pagefind/pagefind-entry.json @@ -0,0 +1 @@ +{"version":"1.5.2","languages":{"en-us":{"hash":"en-us_27757699fa6c6","wasm":"en-us","page_count":33}},"include_characters":["_","‿","⁀","⁔","︳","︴","﹍","﹎","﹏","_"]} \ No newline at end of file diff --git a/docs/pagefind/pagefind-highlight.js b/docs/pagefind/pagefind-highlight.js new file mode 100644 index 00000000..0e538a5f --- /dev/null +++ b/docs/pagefind/pagefind-highlight.js @@ -0,0 +1,1070 @@ +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); + +// node_modules/mark.js/dist/mark.js +var require_mark = __commonJS({ + "node_modules/mark.js/dist/mark.js"(exports, module) { + (function(global, factory) { + typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : global.Mark = factory(); + })(exports, (function() { + "use strict"; + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) { + return typeof obj; + } : function(obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + var classCallCheck = function(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + }; + var createClass = /* @__PURE__ */ (function() { + function defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + return function(Constructor, protoProps, staticProps) { + if (protoProps) defineProperties(Constructor.prototype, protoProps); + if (staticProps) defineProperties(Constructor, staticProps); + return Constructor; + }; + })(); + var _extends = Object.assign || function(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }; + var DOMIterator = (function() { + function DOMIterator2(ctx) { + var iframes = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : true; + var exclude = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : []; + var iframesTimeout = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : 5e3; + classCallCheck(this, DOMIterator2); + this.ctx = ctx; + this.iframes = iframes; + this.exclude = exclude; + this.iframesTimeout = iframesTimeout; + } + createClass(DOMIterator2, [{ + key: "getContexts", + value: function getContexts() { + var ctx = void 0, filteredCtx = []; + if (typeof this.ctx === "undefined" || !this.ctx) { + ctx = []; + } else if (NodeList.prototype.isPrototypeOf(this.ctx)) { + ctx = Array.prototype.slice.call(this.ctx); + } else if (Array.isArray(this.ctx)) { + ctx = this.ctx; + } else if (typeof this.ctx === "string") { + ctx = Array.prototype.slice.call(document.querySelectorAll(this.ctx)); + } else { + ctx = [this.ctx]; + } + ctx.forEach(function(ctx2) { + var isDescendant = filteredCtx.filter(function(contexts) { + return contexts.contains(ctx2); + }).length > 0; + if (filteredCtx.indexOf(ctx2) === -1 && !isDescendant) { + filteredCtx.push(ctx2); + } + }); + return filteredCtx; + } + }, { + key: "getIframeContents", + value: function getIframeContents(ifr, successFn) { + var errorFn = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : function() { + }; + var doc = void 0; + try { + var ifrWin = ifr.contentWindow; + doc = ifrWin.document; + if (!ifrWin || !doc) { + throw new Error("iframe inaccessible"); + } + } catch (e) { + errorFn(); + } + if (doc) { + successFn(doc); + } + } + }, { + key: "isIframeBlank", + value: function isIframeBlank(ifr) { + var bl = "about:blank", src = ifr.getAttribute("src").trim(), href = ifr.contentWindow.location.href; + return href === bl && src !== bl && src; + } + }, { + key: "observeIframeLoad", + value: function observeIframeLoad(ifr, successFn, errorFn) { + var _this = this; + var called = false, tout = null; + var listener = function listener2() { + if (called) { + return; + } + called = true; + clearTimeout(tout); + try { + if (!_this.isIframeBlank(ifr)) { + ifr.removeEventListener("load", listener2); + _this.getIframeContents(ifr, successFn, errorFn); + } + } catch (e) { + errorFn(); + } + }; + ifr.addEventListener("load", listener); + tout = setTimeout(listener, this.iframesTimeout); + } + }, { + key: "onIframeReady", + value: function onIframeReady(ifr, successFn, errorFn) { + try { + if (ifr.contentWindow.document.readyState === "complete") { + if (this.isIframeBlank(ifr)) { + this.observeIframeLoad(ifr, successFn, errorFn); + } else { + this.getIframeContents(ifr, successFn, errorFn); + } + } else { + this.observeIframeLoad(ifr, successFn, errorFn); + } + } catch (e) { + errorFn(); + } + } + }, { + key: "waitForIframes", + value: function waitForIframes(ctx, done) { + var _this2 = this; + var eachCalled = 0; + this.forEachIframe(ctx, function() { + return true; + }, function(ifr) { + eachCalled++; + _this2.waitForIframes(ifr.querySelector("html"), function() { + if (!--eachCalled) { + done(); + } + }); + }, function(handled) { + if (!handled) { + done(); + } + }); + } + }, { + key: "forEachIframe", + value: function forEachIframe(ctx, filter, each) { + var _this3 = this; + var end = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : function() { + }; + var ifr = ctx.querySelectorAll("iframe"), open = ifr.length, handled = 0; + ifr = Array.prototype.slice.call(ifr); + var checkEnd = function checkEnd2() { + if (--open <= 0) { + end(handled); + } + }; + if (!open) { + checkEnd(); + } + ifr.forEach(function(ifr2) { + if (DOMIterator2.matches(ifr2, _this3.exclude)) { + checkEnd(); + } else { + _this3.onIframeReady(ifr2, function(con) { + if (filter(ifr2)) { + handled++; + each(con); + } + checkEnd(); + }, checkEnd); + } + }); + } + }, { + key: "createIterator", + value: function createIterator(ctx, whatToShow, filter) { + return document.createNodeIterator(ctx, whatToShow, filter, false); + } + }, { + key: "createInstanceOnIframe", + value: function createInstanceOnIframe(contents) { + return new DOMIterator2(contents.querySelector("html"), this.iframes); + } + }, { + key: "compareNodeIframe", + value: function compareNodeIframe(node, prevNode, ifr) { + var compCurr = node.compareDocumentPosition(ifr), prev = Node.DOCUMENT_POSITION_PRECEDING; + if (compCurr & prev) { + if (prevNode !== null) { + var compPrev = prevNode.compareDocumentPosition(ifr), after = Node.DOCUMENT_POSITION_FOLLOWING; + if (compPrev & after) { + return true; + } + } else { + return true; + } + } + return false; + } + }, { + key: "getIteratorNode", + value: function getIteratorNode(itr) { + var prevNode = itr.previousNode(); + var node = void 0; + if (prevNode === null) { + node = itr.nextNode(); + } else { + node = itr.nextNode() && itr.nextNode(); + } + return { + prevNode, + node + }; + } + }, { + key: "checkIframeFilter", + value: function checkIframeFilter(node, prevNode, currIfr, ifr) { + var key = false, handled = false; + ifr.forEach(function(ifrDict, i) { + if (ifrDict.val === currIfr) { + key = i; + handled = ifrDict.handled; + } + }); + if (this.compareNodeIframe(node, prevNode, currIfr)) { + if (key === false && !handled) { + ifr.push({ + val: currIfr, + handled: true + }); + } else if (key !== false && !handled) { + ifr[key].handled = true; + } + return true; + } + if (key === false) { + ifr.push({ + val: currIfr, + handled: false + }); + } + return false; + } + }, { + key: "handleOpenIframes", + value: function handleOpenIframes(ifr, whatToShow, eCb, fCb) { + var _this4 = this; + ifr.forEach(function(ifrDict) { + if (!ifrDict.handled) { + _this4.getIframeContents(ifrDict.val, function(con) { + _this4.createInstanceOnIframe(con).forEachNode(whatToShow, eCb, fCb); + }); + } + }); + } + }, { + key: "iterateThroughNodes", + value: function iterateThroughNodes(whatToShow, ctx, eachCb, filterCb, doneCb) { + var _this5 = this; + var itr = this.createIterator(ctx, whatToShow, filterCb); + var ifr = [], elements = [], node = void 0, prevNode = void 0, retrieveNodes = function retrieveNodes2() { + var _getIteratorNode = _this5.getIteratorNode(itr); + prevNode = _getIteratorNode.prevNode; + node = _getIteratorNode.node; + return node; + }; + while (retrieveNodes()) { + if (this.iframes) { + this.forEachIframe(ctx, function(currIfr) { + return _this5.checkIframeFilter(node, prevNode, currIfr, ifr); + }, function(con) { + _this5.createInstanceOnIframe(con).forEachNode(whatToShow, function(ifrNode) { + return elements.push(ifrNode); + }, filterCb); + }); + } + elements.push(node); + } + elements.forEach(function(node2) { + eachCb(node2); + }); + if (this.iframes) { + this.handleOpenIframes(ifr, whatToShow, eachCb, filterCb); + } + doneCb(); + } + }, { + key: "forEachNode", + value: function forEachNode(whatToShow, each, filter) { + var _this6 = this; + var done = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : function() { + }; + var contexts = this.getContexts(); + var open = contexts.length; + if (!open) { + done(); + } + contexts.forEach(function(ctx) { + var ready = function ready2() { + _this6.iterateThroughNodes(whatToShow, ctx, each, filter, function() { + if (--open <= 0) { + done(); + } + }); + }; + if (_this6.iframes) { + _this6.waitForIframes(ctx, ready); + } else { + ready(); + } + }); + } + }], [{ + key: "matches", + value: function matches(element, selector) { + var selectors = typeof selector === "string" ? [selector] : selector, fn = element.matches || element.matchesSelector || element.msMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.webkitMatchesSelector; + if (fn) { + var match = false; + selectors.every(function(sel) { + if (fn.call(element, sel)) { + match = true; + return false; + } + return true; + }); + return match; + } else { + return false; + } + } + }]); + return DOMIterator2; + })(); + var Mark$1 = (function() { + function Mark3(ctx) { + classCallCheck(this, Mark3); + this.ctx = ctx; + this.ie = false; + var ua = window.navigator.userAgent; + if (ua.indexOf("MSIE") > -1 || ua.indexOf("Trident") > -1) { + this.ie = true; + } + } + createClass(Mark3, [{ + key: "log", + value: function log(msg) { + var level = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "debug"; + var log2 = this.opt.log; + if (!this.opt.debug) { + return; + } + if ((typeof log2 === "undefined" ? "undefined" : _typeof(log2)) === "object" && typeof log2[level] === "function") { + log2[level]("mark.js: " + msg); + } + } + }, { + key: "escapeStr", + value: function escapeStr(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + } + }, { + key: "createRegExp", + value: function createRegExp(str) { + if (this.opt.wildcards !== "disabled") { + str = this.setupWildcardsRegExp(str); + } + str = this.escapeStr(str); + if (Object.keys(this.opt.synonyms).length) { + str = this.createSynonymsRegExp(str); + } + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.setupIgnoreJoinersRegExp(str); + } + if (this.opt.diacritics) { + str = this.createDiacriticsRegExp(str); + } + str = this.createMergedBlanksRegExp(str); + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.createJoinersRegExp(str); + } + if (this.opt.wildcards !== "disabled") { + str = this.createWildcardsRegExp(str); + } + str = this.createAccuracyRegExp(str); + return str; + } + }, { + key: "createSynonymsRegExp", + value: function createSynonymsRegExp(str) { + var syn = this.opt.synonyms, sens = this.opt.caseSensitive ? "" : "i", joinerPlaceholder = this.opt.ignoreJoiners || this.opt.ignorePunctuation.length ? "\0" : ""; + for (var index in syn) { + if (syn.hasOwnProperty(index)) { + var value = syn[index], k1 = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(index) : this.escapeStr(index), k2 = this.opt.wildcards !== "disabled" ? this.setupWildcardsRegExp(value) : this.escapeStr(value); + if (k1 !== "" && k2 !== "") { + str = str.replace(new RegExp("(" + this.escapeStr(k1) + "|" + this.escapeStr(k2) + ")", "gm" + sens), joinerPlaceholder + ("(" + this.processSynomyms(k1) + "|") + (this.processSynomyms(k2) + ")") + joinerPlaceholder); + } + } + } + return str; + } + }, { + key: "processSynomyms", + value: function processSynomyms(str) { + if (this.opt.ignoreJoiners || this.opt.ignorePunctuation.length) { + str = this.setupIgnoreJoinersRegExp(str); + } + return str; + } + }, { + key: "setupWildcardsRegExp", + value: function setupWildcardsRegExp(str) { + str = str.replace(/(?:\\)*\?/g, function(val) { + return val.charAt(0) === "\\" ? "?" : ""; + }); + return str.replace(/(?:\\)*\*/g, function(val) { + return val.charAt(0) === "\\" ? "*" : ""; + }); + } + }, { + key: "createWildcardsRegExp", + value: function createWildcardsRegExp(str) { + var spaces = this.opt.wildcards === "withSpaces"; + return str.replace(/\u0001/g, spaces ? "[\\S\\s]?" : "\\S?").replace(/\u0002/g, spaces ? "[\\S\\s]*?" : "\\S*"); + } + }, { + key: "setupIgnoreJoinersRegExp", + value: function setupIgnoreJoinersRegExp(str) { + return str.replace(/[^(|)\\]/g, function(val, indx, original) { + var nextChar = original.charAt(indx + 1); + if (/[(|)\\]/.test(nextChar) || nextChar === "") { + return val; + } else { + return val + "\0"; + } + }); + } + }, { + key: "createJoinersRegExp", + value: function createJoinersRegExp(str) { + var joiner = []; + var ignorePunctuation = this.opt.ignorePunctuation; + if (Array.isArray(ignorePunctuation) && ignorePunctuation.length) { + joiner.push(this.escapeStr(ignorePunctuation.join(""))); + } + if (this.opt.ignoreJoiners) { + joiner.push("\\u00ad\\u200b\\u200c\\u200d"); + } + return joiner.length ? str.split(/\u0000+/).join("[" + joiner.join("") + "]*") : str; + } + }, { + key: "createDiacriticsRegExp", + value: function createDiacriticsRegExp(str) { + var sens = this.opt.caseSensitive ? "" : "i", dct = this.opt.caseSensitive ? ["a\xE0\xE1\u1EA3\xE3\u1EA1\u0103\u1EB1\u1EAF\u1EB3\u1EB5\u1EB7\xE2\u1EA7\u1EA5\u1EA9\u1EAB\u1EAD\xE4\xE5\u0101\u0105", "A\xC0\xC1\u1EA2\xC3\u1EA0\u0102\u1EB0\u1EAE\u1EB2\u1EB4\u1EB6\xC2\u1EA6\u1EA4\u1EA8\u1EAA\u1EAC\xC4\xC5\u0100\u0104", "c\xE7\u0107\u010D", "C\xC7\u0106\u010C", "d\u0111\u010F", "D\u0110\u010E", "e\xE8\xE9\u1EBB\u1EBD\u1EB9\xEA\u1EC1\u1EBF\u1EC3\u1EC5\u1EC7\xEB\u011B\u0113\u0119", "E\xC8\xC9\u1EBA\u1EBC\u1EB8\xCA\u1EC0\u1EBE\u1EC2\u1EC4\u1EC6\xCB\u011A\u0112\u0118", "i\xEC\xED\u1EC9\u0129\u1ECB\xEE\xEF\u012B", "I\xCC\xCD\u1EC8\u0128\u1ECA\xCE\xCF\u012A", "l\u0142", "L\u0141", "n\xF1\u0148\u0144", "N\xD1\u0147\u0143", "o\xF2\xF3\u1ECF\xF5\u1ECD\xF4\u1ED3\u1ED1\u1ED5\u1ED7\u1ED9\u01A1\u1EDF\u1EE1\u1EDB\u1EDD\u1EE3\xF6\xF8\u014D", "O\xD2\xD3\u1ECE\xD5\u1ECC\xD4\u1ED2\u1ED0\u1ED4\u1ED6\u1ED8\u01A0\u1EDE\u1EE0\u1EDA\u1EDC\u1EE2\xD6\xD8\u014C", "r\u0159", "R\u0158", "s\u0161\u015B\u0219\u015F", "S\u0160\u015A\u0218\u015E", "t\u0165\u021B\u0163", "T\u0164\u021A\u0162", "u\xF9\xFA\u1EE7\u0169\u1EE5\u01B0\u1EEB\u1EE9\u1EED\u1EEF\u1EF1\xFB\xFC\u016F\u016B", "U\xD9\xDA\u1EE6\u0168\u1EE4\u01AF\u1EEA\u1EE8\u1EEC\u1EEE\u1EF0\xDB\xDC\u016E\u016A", "y\xFD\u1EF3\u1EF7\u1EF9\u1EF5\xFF", "Y\xDD\u1EF2\u1EF6\u1EF8\u1EF4\u0178", "z\u017E\u017C\u017A", "Z\u017D\u017B\u0179"] : ["a\xE0\xE1\u1EA3\xE3\u1EA1\u0103\u1EB1\u1EAF\u1EB3\u1EB5\u1EB7\xE2\u1EA7\u1EA5\u1EA9\u1EAB\u1EAD\xE4\xE5\u0101\u0105A\xC0\xC1\u1EA2\xC3\u1EA0\u0102\u1EB0\u1EAE\u1EB2\u1EB4\u1EB6\xC2\u1EA6\u1EA4\u1EA8\u1EAA\u1EAC\xC4\xC5\u0100\u0104", "c\xE7\u0107\u010DC\xC7\u0106\u010C", "d\u0111\u010FD\u0110\u010E", "e\xE8\xE9\u1EBB\u1EBD\u1EB9\xEA\u1EC1\u1EBF\u1EC3\u1EC5\u1EC7\xEB\u011B\u0113\u0119E\xC8\xC9\u1EBA\u1EBC\u1EB8\xCA\u1EC0\u1EBE\u1EC2\u1EC4\u1EC6\xCB\u011A\u0112\u0118", "i\xEC\xED\u1EC9\u0129\u1ECB\xEE\xEF\u012BI\xCC\xCD\u1EC8\u0128\u1ECA\xCE\xCF\u012A", "l\u0142L\u0141", "n\xF1\u0148\u0144N\xD1\u0147\u0143", "o\xF2\xF3\u1ECF\xF5\u1ECD\xF4\u1ED3\u1ED1\u1ED5\u1ED7\u1ED9\u01A1\u1EDF\u1EE1\u1EDB\u1EDD\u1EE3\xF6\xF8\u014DO\xD2\xD3\u1ECE\xD5\u1ECC\xD4\u1ED2\u1ED0\u1ED4\u1ED6\u1ED8\u01A0\u1EDE\u1EE0\u1EDA\u1EDC\u1EE2\xD6\xD8\u014C", "r\u0159R\u0158", "s\u0161\u015B\u0219\u015FS\u0160\u015A\u0218\u015E", "t\u0165\u021B\u0163T\u0164\u021A\u0162", "u\xF9\xFA\u1EE7\u0169\u1EE5\u01B0\u1EEB\u1EE9\u1EED\u1EEF\u1EF1\xFB\xFC\u016F\u016BU\xD9\xDA\u1EE6\u0168\u1EE4\u01AF\u1EEA\u1EE8\u1EEC\u1EEE\u1EF0\xDB\xDC\u016E\u016A", "y\xFD\u1EF3\u1EF7\u1EF9\u1EF5\xFFY\xDD\u1EF2\u1EF6\u1EF8\u1EF4\u0178", "z\u017E\u017C\u017AZ\u017D\u017B\u0179"]; + var handled = []; + str.split("").forEach(function(ch) { + dct.every(function(dct2) { + if (dct2.indexOf(ch) !== -1) { + if (handled.indexOf(dct2) > -1) { + return false; + } + str = str.replace(new RegExp("[" + dct2 + "]", "gm" + sens), "[" + dct2 + "]"); + handled.push(dct2); + } + return true; + }); + }); + return str; + } + }, { + key: "createMergedBlanksRegExp", + value: function createMergedBlanksRegExp(str) { + return str.replace(/[\s]+/gmi, "[\\s]+"); + } + }, { + key: "createAccuracyRegExp", + value: function createAccuracyRegExp(str) { + var _this = this; + var chars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\xA1\xBF"; + var acc = this.opt.accuracy, val = typeof acc === "string" ? acc : acc.value, ls = typeof acc === "string" ? [] : acc.limiters, lsJoin = ""; + ls.forEach(function(limiter) { + lsJoin += "|" + _this.escapeStr(limiter); + }); + switch (val) { + case "partially": + default: + return "()(" + str + ")"; + case "complementary": + lsJoin = "\\s" + (lsJoin ? lsJoin : this.escapeStr(chars)); + return "()([^" + lsJoin + "]*" + str + "[^" + lsJoin + "]*)"; + case "exactly": + return "(^|\\s" + lsJoin + ")(" + str + ")(?=$|\\s" + lsJoin + ")"; + } + } + }, { + key: "getSeparatedKeywords", + value: function getSeparatedKeywords(sv) { + var _this2 = this; + var stack = []; + sv.forEach(function(kw) { + if (!_this2.opt.separateWordSearch) { + if (kw.trim() && stack.indexOf(kw) === -1) { + stack.push(kw); + } + } else { + kw.split(" ").forEach(function(kwSplitted) { + if (kwSplitted.trim() && stack.indexOf(kwSplitted) === -1) { + stack.push(kwSplitted); + } + }); + } + }); + return { + "keywords": stack.sort(function(a, b) { + return b.length - a.length; + }), + "length": stack.length + }; + } + }, { + key: "isNumeric", + value: function isNumeric(value) { + return Number(parseFloat(value)) == value; + } + }, { + key: "checkRanges", + value: function checkRanges(array) { + var _this3 = this; + if (!Array.isArray(array) || Object.prototype.toString.call(array[0]) !== "[object Object]") { + this.log("markRanges() will only accept an array of objects"); + this.opt.noMatch(array); + return []; + } + var stack = []; + var last = 0; + array.sort(function(a, b) { + return a.start - b.start; + }).forEach(function(item) { + var _callNoMatchOnInvalid = _this3.callNoMatchOnInvalidRanges(item, last), start = _callNoMatchOnInvalid.start, end = _callNoMatchOnInvalid.end, valid = _callNoMatchOnInvalid.valid; + if (valid) { + item.start = start; + item.length = end - start; + stack.push(item); + last = end; + } + }); + return stack; + } + }, { + key: "callNoMatchOnInvalidRanges", + value: function callNoMatchOnInvalidRanges(range, last) { + var start = void 0, end = void 0, valid = false; + if (range && typeof range.start !== "undefined") { + start = parseInt(range.start, 10); + end = start + parseInt(range.length, 10); + if (this.isNumeric(range.start) && this.isNumeric(range.length) && end - last > 0 && end - start > 0) { + valid = true; + } else { + this.log("Ignoring invalid or overlapping range: " + ("" + JSON.stringify(range))); + this.opt.noMatch(range); + } + } else { + this.log("Ignoring invalid range: " + JSON.stringify(range)); + this.opt.noMatch(range); + } + return { + start, + end, + valid + }; + } + }, { + key: "checkWhitespaceRanges", + value: function checkWhitespaceRanges(range, originalLength, string) { + var end = void 0, valid = true, max = string.length, offset = originalLength - max, start = parseInt(range.start, 10) - offset; + start = start > max ? max : start; + end = start + parseInt(range.length, 10); + if (end > max) { + end = max; + this.log("End range automatically set to the max value of " + max); + } + if (start < 0 || end - start < 0 || start > max || end > max) { + valid = false; + this.log("Invalid range: " + JSON.stringify(range)); + this.opt.noMatch(range); + } else if (string.substring(start, end).replace(/\s+/g, "") === "") { + valid = false; + this.log("Skipping whitespace only range: " + JSON.stringify(range)); + this.opt.noMatch(range); + } + return { + start, + end, + valid + }; + } + }, { + key: "getTextNodes", + value: function getTextNodes(cb) { + var _this4 = this; + var val = "", nodes = []; + this.iterator.forEachNode(NodeFilter.SHOW_TEXT, function(node) { + nodes.push({ + start: val.length, + end: (val += node.textContent).length, + node + }); + }, function(node) { + if (_this4.matchesExclude(node.parentNode)) { + return NodeFilter.FILTER_REJECT; + } else { + return NodeFilter.FILTER_ACCEPT; + } + }, function() { + cb({ + value: val, + nodes + }); + }); + } + }, { + key: "matchesExclude", + value: function matchesExclude(el) { + return DOMIterator.matches(el, this.opt.exclude.concat(["script", "style", "title", "head", "html"])); + } + }, { + key: "wrapRangeInTextNode", + value: function wrapRangeInTextNode(node, start, end) { + var hEl = !this.opt.element ? "mark" : this.opt.element, startNode = node.splitText(start), ret = startNode.splitText(end - start); + var repl = document.createElement(hEl); + repl.setAttribute("data-markjs", "true"); + if (this.opt.className) { + repl.setAttribute("class", this.opt.className); + } + repl.textContent = startNode.textContent; + startNode.parentNode.replaceChild(repl, startNode); + return ret; + } + }, { + key: "wrapRangeInMappedTextNode", + value: function wrapRangeInMappedTextNode(dict, start, end, filterCb, eachCb) { + var _this5 = this; + dict.nodes.every(function(n, i) { + var sibl = dict.nodes[i + 1]; + if (typeof sibl === "undefined" || sibl.start > start) { + if (!filterCb(n.node)) { + return false; + } + var s = start - n.start, e = (end > n.end ? n.end : end) - n.start, startStr = dict.value.substr(0, n.start), endStr = dict.value.substr(e + n.start); + n.node = _this5.wrapRangeInTextNode(n.node, s, e); + dict.value = startStr + endStr; + dict.nodes.forEach(function(k, j) { + if (j >= i) { + if (dict.nodes[j].start > 0 && j !== i) { + dict.nodes[j].start -= e; + } + dict.nodes[j].end -= e; + } + }); + end -= e; + eachCb(n.node.previousSibling, n.start); + if (end > n.end) { + start = n.end; + } else { + return false; + } + } + return true; + }); + } + }, { + key: "wrapMatches", + value: function wrapMatches(regex, ignoreGroups, filterCb, eachCb, endCb) { + var _this6 = this; + var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1; + this.getTextNodes(function(dict) { + dict.nodes.forEach(function(node) { + node = node.node; + var match = void 0; + while ((match = regex.exec(node.textContent)) !== null && match[matchIdx] !== "") { + if (!filterCb(match[matchIdx], node)) { + continue; + } + var pos = match.index; + if (matchIdx !== 0) { + for (var i = 1; i < matchIdx; i++) { + pos += match[i].length; + } + } + node = _this6.wrapRangeInTextNode(node, pos, pos + match[matchIdx].length); + eachCb(node.previousSibling); + regex.lastIndex = 0; + } + }); + endCb(); + }); + } + }, { + key: "wrapMatchesAcrossElements", + value: function wrapMatchesAcrossElements(regex, ignoreGroups, filterCb, eachCb, endCb) { + var _this7 = this; + var matchIdx = ignoreGroups === 0 ? 0 : ignoreGroups + 1; + this.getTextNodes(function(dict) { + var match = void 0; + while ((match = regex.exec(dict.value)) !== null && match[matchIdx] !== "") { + var start = match.index; + if (matchIdx !== 0) { + for (var i = 1; i < matchIdx; i++) { + start += match[i].length; + } + } + var end = start + match[matchIdx].length; + _this7.wrapRangeInMappedTextNode(dict, start, end, function(node) { + return filterCb(match[matchIdx], node); + }, function(node, lastIndex) { + regex.lastIndex = lastIndex; + eachCb(node); + }); + } + endCb(); + }); + } + }, { + key: "wrapRangeFromIndex", + value: function wrapRangeFromIndex(ranges, filterCb, eachCb, endCb) { + var _this8 = this; + this.getTextNodes(function(dict) { + var originalLength = dict.value.length; + ranges.forEach(function(range, counter) { + var _checkWhitespaceRange = _this8.checkWhitespaceRanges(range, originalLength, dict.value), start = _checkWhitespaceRange.start, end = _checkWhitespaceRange.end, valid = _checkWhitespaceRange.valid; + if (valid) { + _this8.wrapRangeInMappedTextNode(dict, start, end, function(node) { + return filterCb(node, range, dict.value.substring(start, end), counter); + }, function(node) { + eachCb(node, range); + }); + } + }); + endCb(); + }); + } + }, { + key: "unwrapMatches", + value: function unwrapMatches(node) { + var parent = node.parentNode; + var docFrag = document.createDocumentFragment(); + while (node.firstChild) { + docFrag.appendChild(node.removeChild(node.firstChild)); + } + parent.replaceChild(docFrag, node); + if (!this.ie) { + parent.normalize(); + } else { + this.normalizeTextNode(parent); + } + } + }, { + key: "normalizeTextNode", + value: function normalizeTextNode(node) { + if (!node) { + return; + } + if (node.nodeType === 3) { + while (node.nextSibling && node.nextSibling.nodeType === 3) { + node.nodeValue += node.nextSibling.nodeValue; + node.parentNode.removeChild(node.nextSibling); + } + } else { + this.normalizeTextNode(node.firstChild); + } + this.normalizeTextNode(node.nextSibling); + } + }, { + key: "markRegExp", + value: function markRegExp(regexp, opt) { + var _this9 = this; + this.opt = opt; + this.log('Searching with expression "' + regexp + '"'); + var totalMatches = 0, fn = "wrapMatches"; + var eachCb = function eachCb2(element) { + totalMatches++; + _this9.opt.each(element); + }; + if (this.opt.acrossElements) { + fn = "wrapMatchesAcrossElements"; + } + this[fn](regexp, this.opt.ignoreGroups, function(match, node) { + return _this9.opt.filter(node, match, totalMatches); + }, eachCb, function() { + if (totalMatches === 0) { + _this9.opt.noMatch(regexp); + } + _this9.opt.done(totalMatches); + }); + } + }, { + key: "mark", + value: function mark(sv, opt) { + var _this10 = this; + this.opt = opt; + var totalMatches = 0, fn = "wrapMatches"; + var _getSeparatedKeywords = this.getSeparatedKeywords(typeof sv === "string" ? [sv] : sv), kwArr = _getSeparatedKeywords.keywords, kwArrLen = _getSeparatedKeywords.length, sens = this.opt.caseSensitive ? "" : "i", handler = function handler2(kw) { + var regex = new RegExp(_this10.createRegExp(kw), "gm" + sens), matches = 0; + _this10.log('Searching with expression "' + regex + '"'); + _this10[fn](regex, 1, function(term, node) { + return _this10.opt.filter(node, kw, totalMatches, matches); + }, function(element) { + matches++; + totalMatches++; + _this10.opt.each(element); + }, function() { + if (matches === 0) { + _this10.opt.noMatch(kw); + } + if (kwArr[kwArrLen - 1] === kw) { + _this10.opt.done(totalMatches); + } else { + handler2(kwArr[kwArr.indexOf(kw) + 1]); + } + }); + }; + if (this.opt.acrossElements) { + fn = "wrapMatchesAcrossElements"; + } + if (kwArrLen === 0) { + this.opt.done(totalMatches); + } else { + handler(kwArr[0]); + } + } + }, { + key: "markRanges", + value: function markRanges(rawRanges, opt) { + var _this11 = this; + this.opt = opt; + var totalMatches = 0, ranges = this.checkRanges(rawRanges); + if (ranges && ranges.length) { + this.log("Starting to mark with the following ranges: " + JSON.stringify(ranges)); + this.wrapRangeFromIndex(ranges, function(node, range, match, counter) { + return _this11.opt.filter(node, range, match, counter); + }, function(element, range) { + totalMatches++; + _this11.opt.each(element, range); + }, function() { + _this11.opt.done(totalMatches); + }); + } else { + this.opt.done(totalMatches); + } + } + }, { + key: "unmark", + value: function unmark(opt) { + var _this12 = this; + this.opt = opt; + var sel = this.opt.element ? this.opt.element : "*"; + sel += "[data-markjs]"; + if (this.opt.className) { + sel += "." + this.opt.className; + } + this.log('Removal selector "' + sel + '"'); + this.iterator.forEachNode(NodeFilter.SHOW_ELEMENT, function(node) { + _this12.unwrapMatches(node); + }, function(node) { + var matchesSel = DOMIterator.matches(node, sel), matchesExclude = _this12.matchesExclude(node); + if (!matchesSel || matchesExclude) { + return NodeFilter.FILTER_REJECT; + } else { + return NodeFilter.FILTER_ACCEPT; + } + }, this.opt.done); + } + }, { + key: "opt", + set: function set$$1(val) { + this._opt = _extends({}, { + "element": "", + "className": "", + "exclude": [], + "iframes": false, + "iframesTimeout": 5e3, + "separateWordSearch": true, + "diacritics": true, + "synonyms": {}, + "accuracy": "partially", + "acrossElements": false, + "caseSensitive": false, + "ignoreJoiners": false, + "ignoreGroups": 0, + "ignorePunctuation": [], + "wildcards": "disabled", + "each": function each() { + }, + "noMatch": function noMatch() { + }, + "filter": function filter() { + return true; + }, + "done": function done() { + }, + "debug": false, + "log": window.console + }, val); + }, + get: function get$$1() { + return this._opt; + } + }, { + key: "iterator", + get: function get$$1() { + return new DOMIterator(this.ctx, this.opt.iframes, this.opt.exclude, this.opt.iframesTimeout); + } + }]); + return Mark3; + })(); + function Mark2(ctx) { + var _this = this; + var instance = new Mark$1(ctx); + this.mark = function(sv, opt) { + instance.mark(sv, opt); + return _this; + }; + this.markRegExp = function(sv, opt) { + instance.markRegExp(sv, opt); + return _this; + }; + this.markRanges = function(sv, opt) { + instance.markRanges(sv, opt); + return _this; + }; + this.unmark = function(opt) { + instance.unmark(opt); + return _this; + }; + return this; + } + return Mark2; + })); + } +}); + +// lib/highlight.ts +var import_mark = __toESM(require_mark(), 1); +var PagefindHighlight = class { + constructor(options = { + markContext: null, + highlightParam: "pagefind-highlight", + markOptions: { + className: "pagefind-highlight", + exclude: ["[data-pagefind-ignore]", "[data-pagefind-ignore] *"] + }, + addStyles: true + }) { + __publicField(this, "highlightParam"); + __publicField(this, "markContext"); + __publicField(this, "markOptions"); + __publicField(this, "addStyles"); + var _a, _b; + const { highlightParam, markContext, markOptions, addStyles } = options; + this.highlightParam = highlightParam ?? "pagefind-highlight"; + this.addStyles = addStyles ?? true; + this.markContext = markContext !== void 0 ? markContext : null; + this.markOptions = markOptions !== void 0 ? markOptions : { + className: "pagefind-highlight", + exclude: ["[data-pagefind-ignore]", "[data-pagefind-ignore] *"] + }; + (_a = this.markOptions).className ?? (_a.className = "pagefind__highlight"); + (_b = this.markOptions).exclude ?? (_b.exclude = [ + "[data-pagefind-ignore]", + "[data-pagefind-ignore] *" + ]); + this.markOptions.separateWordSearch = false; + this.highlight(); + } + getHighlightParams(paramName) { + const urlParams = new URLSearchParams(window.location.search); + return urlParams.getAll(paramName); + } + // Inline styles might be too hard to override + addHighlightStyles(className) { + if (!className) return; + const styleElement = document.createElement("style"); + styleElement.innerText = `:where(.${className}) { background-color: yellow; color: black; }`; + document.head.appendChild(styleElement); + } + createMarkInstance() { + if (this.markContext) { + return new import_mark.default(this.markContext); + } + const pagefindBody = document.querySelectorAll("[data-pagefind-body]"); + if (pagefindBody.length !== 0) { + return new import_mark.default(pagefindBody); + } else { + return new import_mark.default(document.body); + } + } + markText(instance, text) { + instance.mark(text, this.markOptions); + } + highlight() { + const params = this.getHighlightParams(this.highlightParam); + if (!params || params.length === 0) return; + this.addStyles && this.addHighlightStyles(this.markOptions.className); + const markInstance = this.createMarkInstance(); + this.markText(markInstance, params); + } +}; +window.PagefindHighlight = PagefindHighlight; +export { + PagefindHighlight as default +}; +/*! Bundled license information: + +mark.js/dist/mark.js: + (*!*************************************************** + * mark.js v8.11.1 + * https://markjs.io/ + * Copyright (c) 2014–2018, Julian Kühnel + * Released under the MIT license https://git.io/vwTVl + *****************************************************) +*/ diff --git a/docs/pagefind/pagefind-modular-ui.css b/docs/pagefind/pagefind-modular-ui.css new file mode 100644 index 00000000..9c6793ed --- /dev/null +++ b/docs/pagefind/pagefind-modular-ui.css @@ -0,0 +1,214 @@ +:root { + --pagefind-ui-scale: 0.8; + --pagefind-ui-primary: #034AD8; + --pagefind-ui-fade: #707070; + --pagefind-ui-text: #393939; + --pagefind-ui-background: #ffffff; + --pagefind-ui-border: #eeeeee; + --pagefind-ui-tag: #eeeeee; + --pagefind-ui-border-width: 2px; + --pagefind-ui-border-radius: 8px; + --pagefind-ui-image-border-radius: 8px; + --pagefind-ui-image-box-ratio: 3 / 2; + --pagefind-ui-font: system, -apple-system, ".SFNSText-Regular", + "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", + "Lucida Grande", sans-serif; +} + +[data-pfmod-hidden] { + display: none !important; +} + +[data-pfmod-suppressed] { + opacity: 0 !important; + pointer-events: none !important; +} + +[data-pfmod-sr-hidden] { + -webkit-clip: rect(0 0 0 0) !important; + clip: rect(0 0 0 0) !important; + -webkit-clip-path: inset(100%) !important; + clip-path: inset(100%) !important; + height: 1px !important; + overflow: hidden !important; + overflow: clip !important; + position: absolute !important; + white-space: nowrap !important; + width: 1px !important; +} + +[data-pfmod-loading] { + color: var(--pagefind-ui-text); + background-color: var(--pagefind-ui-text); + border-radius: var(--pagefind-ui-border-radius); + opacity: 0.1; + pointer-events: none; +} + +/* Input */ + +.pagefind-modular-input-wrapper { + position: relative; +} + +.pagefind-modular-input-wrapper::before { + background-color: var(--pagefind-ui-text); + width: calc(18px * var(--pagefind-ui-scale)); + height: calc(18px * var(--pagefind-ui-scale)); + top: calc(23px * var(--pagefind-ui-scale)); + left: calc(20px * var(--pagefind-ui-scale)); + content: ""; + position: absolute; + display: block; + opacity: 0.7; + -webkit-mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A"); + mask-image: url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A"); + -webkit-mask-size: 100%; + mask-size: 100%; + z-index: 9; + pointer-events: none; +} + +.pagefind-modular-input { + height: calc(64px * var(--pagefind-ui-scale)); + padding: 0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale)); + background-color: var(--pagefind-ui-background); + border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border); + border-radius: var(--pagefind-ui-border-radius); + font-size: calc(21px * var(--pagefind-ui-scale)); + position: relative; + appearance: none; + -webkit-appearance: none; + display: flex; + width: 100%; + box-sizing: border-box; + font-weight: 700; +} + +.pagefind-modular-input::placeholder { + opacity: 0.2; +} + +.pagefind-modular-input-clear { + position: absolute; + top: calc(2px * var(--pagefind-ui-scale)); + right: calc(2px * var(--pagefind-ui-scale)); + height: calc(60px * var(--pagefind-ui-scale)); + border-radius: var(--pagefind-ui-border-radius); + padding: 0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale)); + color: var(--pagefind-ui-text); + font-size: calc(14px * var(--pagefind-ui-scale)); + cursor: pointer; + background-color: var(--pagefind-ui-background); + border: none; + appearance: none; +} + +/* ResultList */ + +.pagefind-modular-list-result { + list-style-type: none; + display: flex; + align-items: flex-start; + gap: min(calc(40px * var(--pagefind-ui-scale)), 3%); + padding: calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale)); + border-top: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border); +} + +.pagefind-modular-list-result:last-of-type { + border-bottom: solid var(--pagefind-ui-border-width) var(--pagefind-ui-border); +} + +.pagefind-modular-list-thumb { + width: min(30%, + calc((30% - (100px * var(--pagefind-ui-scale))) * 100000)); + max-width: calc(120px * var(--pagefind-ui-scale)); + margin-top: calc(10px * var(--pagefind-ui-scale)); + aspect-ratio: var(--pagefind-ui-image-box-ratio); + position: relative; +} + +.pagefind-modular-list-image { + display: block; + position: absolute; + left: 50%; + transform: translateX(-50%); + font-size: 0; + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; + border-radius: var(--pagefind-ui-image-border-radius); +} + +.pagefind-modular-list-inner { + flex: 1; + display: flex; + flex-direction: column; + align-items: flex-start; + margin-top: calc(10px * var(--pagefind-ui-scale)); +} + +.pagefind-modular-list-title { + display: inline-block; + font-weight: 700; + font-size: calc(21px * var(--pagefind-ui-scale)); + margin-top: 0; + margin-bottom: 0; +} + +.pagefind-modular-list-link { + color: var(--pagefind-ui-text); + text-decoration: none; +} + +.pagefind-modular-list-link:hover { + text-decoration: underline; +} + +.pagefind-modular-list-excerpt { + display: inline-block; + font-weight: 400; + font-size: calc(16px * var(--pagefind-ui-scale)); + margin-top: calc(4px * var(--pagefind-ui-scale)); + margin-bottom: 0; + min-width: calc(250px * var(--pagefind-ui-scale)); +} + +/* FilterPills */ + +.pagefind-modular-filter-pills-wrapper { + overflow-x: scroll; + padding: 15px 0; +} + +.pagefind-modular-filter-pills { + display: flex; + gap: 6px; +} + +.pagefind-modular-filter-pill { + display: flex; + justify-content: center; + align-items: center; + border: none; + appearance: none; + padding: 0 calc(24px * var(--pagefind-ui-scale)); + background-color: var(--pagefind-ui-background); + color: var(--pagefind-ui-fade); + border: var(--pagefind-ui-border-width) solid var(--pagefind-ui-border); + border-radius: calc(25px * var(--pagefind-ui-scale)); + font-size: calc(18px * var(--pagefind-ui-scale)); + height: calc(50px * var(--pagefind-ui-scale)); + cursor: pointer; + white-space: nowrap; +} + +.pagefind-modular-filter-pill:hover { + border-color: var(--pagefind-ui-primary); +} + +.pagefind-modular-filter-pill[aria-pressed="true"] { + border-color: var(--pagefind-ui-primary); + color: var(--pagefind-ui-primary); +} \ No newline at end of file diff --git a/docs/pagefind/pagefind-modular-ui.js b/docs/pagefind/pagefind-modular-ui.js new file mode 100644 index 00000000..6caacd6a --- /dev/null +++ b/docs/pagefind/pagefind-modular-ui.js @@ -0,0 +1,8 @@ +(()=>{var w=Object.defineProperty;var b=(i,e)=>{for(var t in e)w(i,t,{get:e[t],enumerable:!0})};var f={};b(f,{FilterPills:()=>c,Input:()=>a,Instance:()=>p,ResultList:()=>o,Summary:()=>h});var r=class i{constructor(e){this.element=document.createElement(e)}id(e){return this.element.id=e,this}class(e){return this.element.classList.add(e),this}attrs(e){for(let[t,s]of Object.entries(e))this.element.setAttribute(t,s);return this}text(e){return this.element.innerText=e,this}html(e){return this.element.innerHTML=e,this}handle(e,t){return this.element.addEventListener(e,t),this}addTo(e){return e instanceof i?e.element.appendChild(this.element):e.appendChild(this.element),this.element}};var T=async(i=100)=>new Promise(e=>setTimeout(e,i)),a=class{constructor(e={}){if(this.inputEl=null,this.clearEl=null,this.instance=null,this.searchID=0,this.debounceTimeoutMs=e.debounceTimeoutMs??300,e.inputElement){if(e.containerElement){console.warn("[Pagefind Input component]: inputElement and containerElement both supplied. Ignoring the container option.");return}this.initExisting(e.inputElement)}else if(e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind Input component]: No selector supplied for containerElement or inputElement");return}this.inputEl.addEventListener("input",async t=>{if(this.instance&&typeof t?.target?.value=="string"){this.updateState(t.target.value);let s=++this.searchID;if(await T(this.debounceTimeoutMs),s!==this.searchID)return null;this.instance?.triggerSearch(t.target.value)}}),this.inputEl.addEventListener("keydown",t=>{t.key==="Escape"&&(++this.searchID,this.inputEl.value="",this.instance?.triggerSearch(""),this.updateState("")),t.key==="Enter"&&t.preventDefault()}),this.inputEl.addEventListener("focus",()=>{this.instance?.triggerLoad()})}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Input component]: No container found for ${e} selector`);return}if(t.tagName==="INPUT")console.warn(`[Pagefind Input component]: Encountered input element for ${e} when a container was expected`),console.warn("[Pagefind Input component]: Treating containerElement option as inputElement and proceeding"),this.initExisting(e);else{t.innerHTML="";let s=0;for(;document.querySelector(`#pfmod-input-${s}`);)s+=1;let n=new r("form").class("pagefind-modular-input-wrapper").attrs({role:"search","aria-label":"Search this site",action:"javascript:void(0);"});new r("label").attrs({for:`pfmod-input-${s}`,"data-pfmod-sr-hidden":"true"}).text("Search this site").addTo(n),this.inputEl=new r("input").id(`pfmod-input-${s}`).class("pagefind-modular-input").attrs({autocapitalize:"none",enterkeyhint:"search"}).addTo(n),this.clearEl=new r("button").class("pagefind-modular-input-clear").attrs({"data-pfmod-suppressed":"true"}).text("Clear").handle("click",()=>{this.inputEl.value="",this.instance.triggerSearch(""),this.updateState("")}).addTo(n),n.addTo(t)}}initExisting(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Input component]: No input element found for ${e} selector`);return}if(t.tagName!=="INPUT"){console.error(`[Pagefind Input component]: Expected ${e} to be an element`);return}this.inputEl=t}updateState(e){this.clearEl&&(e&&e?.length?this.clearEl.removeAttribute("data-pfmod-suppressed"):this.clearEl.setAttribute("data-pfmod-suppressed","true"))}register(e){this.instance=e,this.instance.on("search",(t,s)=>{this.inputEl&&document.activeElement!==this.inputEl&&(this.inputEl.value=t,this.updateState(t))})}focus(){this.inputEl&&this.inputEl.focus()}};var g=i=>{if(i instanceof Element)return[i];if(Array.isArray(i)&&i.every(e=>e instanceof Element))return i;if(typeof i=="string"||i instanceof String){let e=document.createElement("div");return e.innerHTML=i,[...e.childNodes]}else return console.error(`[Pagefind ResultList component]: Expected template function to return an HTML element or string, got ${typeof i}`),[]},v=()=>{let i=(e=30)=>". ".repeat(Math.floor(10+Math.random()*e));return`
  • +
    +
    +

    ${i(30)}

    +

    ${i(40)}

    +
    +
  • `},y=(i,e)=>{let t=new r("li").class("pagefind-modular-list-result");if(e){let l=new r("div").class("pagefind-modular-list-thumb").addTo(t);i?.meta?.image&&new r("img").class("pagefind-modular-list-image").attrs({src:i.meta.image,alt:i.meta.image_alt||i.meta.title}).addTo(l)}let s=new r("div").class("pagefind-modular-list-inner").addTo(t),n=new r("p").class("pagefind-modular-list-title").addTo(s);return new r("a").class("pagefind-modular-list-link").text(i.meta?.title).attrs({href:i.meta?.url||i.url}).addTo(n),new r("p").class("pagefind-modular-list-excerpt").html(i.excerpt).addTo(s),t.element},E=i=>{if(!(i instanceof HTMLElement))return null;let e=window.getComputedStyle(i).overflowY;return e!=="visible"&&e!=="hidden"?i:E(i.parentNode)},d=class{constructor(e={}){this.rawResult=e.result,this.placeholderNodes=e.placeholderNodes,this.resultFn=e.resultFn,this.intersectionEl=e.intersectionEl,this.showImages=e.showImages,this.result=null,this.waitForIntersection()}waitForIntersection(){if(!this.placeholderNodes?.length)return;let e={root:this.intersectionEl,rootMargin:"0px",threshold:.01};new IntersectionObserver((s,n)=>{this.result===null&&s?.[0]?.isIntersecting&&(this.load(),n.disconnect())},e).observe(this.placeholderNodes[0])}async load(){if(!this.placeholderNodes?.length)return;this.result=await this.rawResult.data();let e=this.resultFn(this.result,this.showImages),t=g(e);for(;this.placeholderNodes.length>1;)this.placeholderNodes.pop().remove();this.placeholderNodes[0].replaceWith(...t)}},o=class{constructor(e){if(this.intersectionEl=document.body,this.containerEl=null,this.results=[],this.placeholderTemplate=e.placeholderTemplate??v,this.resultTemplate=e.resultTemplate??y,this.showImages=e.showImages??!0,e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind ResultList component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind ResultList component]: No container found for ${e} selector`);return}this.containerEl=t}append(e){for(let t of e)this.containerEl.appendChild(t)}register(e){e.on("results",t=>{this.containerEl&&(this.containerEl.innerHTML="",this.intersectionEl=E(this.containerEl),this.results=t.results.map(s=>{let n=g(this.placeholderTemplate());return this.append(n),new d({result:s,placeholderNodes:n,resultFn:this.resultTemplate,intersectionEl:this.intersectionEl,showImages:this.showImages})}))}),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerHTML="")})}};var h=class{constructor(e={}){if(this.containerEl=null,this.defaultMessage=e.defaultMessage??"",this.term="",e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind Summary component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind Summary component]: No container found for ${e} selector`);return}this.containerEl=t,this.containerEl.innerText=this.defaultMessage}register(e){e.on("search",(t,s)=>{this.term=t}),e.on("results",t=>{if(!this.containerEl||!t)return;if(!this.term){this.containerEl.innerText=this.defaultMessage;return}let s=t?.results?.length??0;this.containerEl.innerText=`${s} result${s===1?"":"s"} for ${this.term}`}),e.on("loading",()=>{this.containerEl&&(this.containerEl.innerText=`Searching for ${this.term}...`)})}};var c=class{constructor(e={}){if(this.instance=null,this.wrapper=null,this.pillContainer=null,this.available={},this.selected=["All"],this.total=0,this.filterMemo="",this.filter=e.filter,this.ordering=e.ordering??null,this.alwaysShow=e.alwaysShow??!1,this.selectMultiple=e.selectMultiple??!1,!this.filter?.length){console.error("[Pagefind FilterPills component]: No filter option supplied, nothing to display");return}if(e.containerElement)this.initContainer(e.containerElement);else{console.error("[Pagefind FilterPills component]: No selector supplied for containerElement");return}}initContainer(e){let t=document.querySelector(e);if(!t){console.error(`[Pagefind FilterPills component]: No container found for ${e} selector`);return}t.innerHTML="";let s=`pagefind_modular_filter_pills_${this.filter}`,n=new r("div").class("pagefind-modular-filter-pills-wrapper").attrs({role:"group","aria-labelledby":s});this.alwaysShow||n.attrs({"data-pfmod-hidden":!0}),new r("div").id(s).class("pagefind-modular-filter-pills-label").attrs({"data-pfmod-sr-hidden":!0}).text(`Filter results by ${this.filter}`).addTo(n),this.pillContainer=new r("div").class("pagefind-modular-filter-pills").addTo(n),this.wrapper=n.addTo(t)}update(){let e=this.available.map(t=>t[0]).join("~");e==this.filterMemo?this.updateExisting():(this.renderNew(),this.filterMemo=e)}pushFilters(){let e=this.selected.filter(t=>t!=="All");this.instance.triggerFilter(this.filter,e)}pillInner(e,t){return this.total?`${e} (${t})`:`${e}`}renderNew(){this.available.forEach(([e,t])=>{new r("button").class("pagefind-modular-filter-pill").html(this.pillInner(e,t)).attrs({"aria-pressed":this.selected.includes(e),type:"button"}).handle("click",()=>{e==="All"?this.selected=["All"]:this.selected.includes(e)?this.selected=this.selected.filter(s=>s!==e):this.selectMultiple?this.selected.push(e):this.selected=[e],this.selected?.length?this.selected?.length>1&&(this.selected=this.selected.filter(s=>s!=="All")):this.selected=["All"],this.update(),this.pushFilters()}).addTo(this.pillContainer)})}updateExisting(){let e=[...this.pillContainer.childNodes];this.available.forEach(([t,s],n)=>{e[n].innerHTML=this.pillInner(t,s),e[n].setAttribute("aria-pressed",this.selected.includes(t))})}register(e){this.instance=e,this.instance.on("filters",t=>{if(!this.pillContainer)return;this.selectMultiple?t=t.available:t=t.total;let s=t[this.filter];if(!s){console.warn(`[Pagefind FilterPills component]: No possible values found for the ${this.filter} filter`);return}this.available=Object.entries(s),Array.isArray(this.ordering)?this.available.sort((n,l)=>{let m=this.ordering.indexOf(n[0]),_=this.ordering.indexOf(l[0]);return(m===-1?1/0:m)-(_===-1?1/0:_)}):this.available.sort((n,l)=>n[0].localeCompare(l[0])),this.available.unshift(["All",this.total]),this.update()}),e.on("results",t=>{this.pillContainer&&(this.total=t?.unfilteredResultCount||0,this.available?.[0]?.[0]==="All"&&(this.available[0][1]=this.total),this.total||this.alwaysShow?this.wrapper.removeAttribute("data-pfmod-hidden"):this.wrapper.setAttribute("data-pfmod-hidden","true"),this.update())})}};var P=async(i=50)=>await new Promise(e=>setTimeout(e,i)),u;try{document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"&&(u=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind-)?modular-ui.js.*$/)[1])}catch{u="/pagefind/"}var p=class{constructor(e={}){this.__pagefind__=null,this.__initializing__=null,this.__searchID__=0,this.__hooks__={search:[],filters:[],loading:[],results:[]},this.components=[],this.searchTerm="",this.searchFilters={},this.searchResult={},this.availableFilters=null,this.totalFilters=null,this.options={bundlePath:e.bundlePath??u,mergeIndex:e.mergeIndex??[]},delete e.bundlePath,delete e.resetStyles,delete e.processResult,delete e.processTerm,delete e.debounceTimeoutMs,delete e.mergeIndex,delete e.translations,this.pagefindOptions=e}add(e){e?.register?.(this),this.components.push(e)}on(e,t){if(!this.__hooks__[e]){let s=Object.keys(this.__hooks__).join(", ");console.error(`[Pagefind Composable]: Unknown event type ${e}. Supported events: [${s}]`);return}if(typeof t!="function"){console.error(`[Pagefind Composable]: Expected callback to be a function, received ${typeof t}`);return}this.__hooks__[e].push(t)}triggerLoad(){this.__load__()}triggerSearch(e){this.searchTerm=e,this.__dispatch__("search",e,this.searchFilters),this.__search__(e,this.searchFilters)}triggerSearchWithFilters(e,t){this.searchTerm=e,this.searchFilters=t,this.__dispatch__("search",e,t),this.__search__(e,t)}triggerFilters(e){this.searchFilters=e,this.__dispatch__("search",this.searchTerm,e),this.__search__(this.searchTerm,e)}triggerFilter(e,t){this.searchFilters=this.searchFilters||{},this.searchFilters[e]=t,this.__dispatch__("search",this.searchTerm,this.searchFilters),this.__search__(this.searchTerm,this.searchFilters)}__dispatch__(e,...t){this.__hooks__[e]?.forEach(s=>s?.(...t))}async __clear__(){this.__dispatch__("results",{results:[],unfilteredTotalCount:0}),this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})}async __search__(e,t){this.__dispatch__("loading"),await this.__load__();let s=++this.__searchID__;if(!e||!e.length)return this.__clear__();let n=await this.__pagefind__.search(e,{filters:t});n&&this.__searchID__===s&&(n.filters&&Object.keys(n.filters)?.length&&(this.availableFilters=n.filters,this.totalFilters=n.totalFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})),this.searchResult=n,this.__dispatch__("results",this.searchResult))}async __load__(){if(this.__initializing__){for(;!this.__pagefind__;)await P(50);return}if(this.__initializing__=!0,!this.__pagefind__){let e;try{e=await import(`${this.options.bundlePath}pagefind.js`)}catch(t){console.error(t),console.error([`Pagefind couldn't be loaded from ${this.options.bundlePath}pagefind.js`,"You can configure this by passing a bundlePath option to PagefindComposable Instance"].join(` +`)),document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?console.error(`[DEBUG: Loaded from ${document.currentScript?.src??"bad script location"}]`):console.error("no known script location")}await e.options(this.pagefindOptions||{});for(let t of this.options.mergeIndex){if(!t.bundlePath)throw new Error("mergeIndex requires a bundlePath parameter");let s=t.bundlePath;delete t.bundlePath,await e.mergeIndex(s,t)}this.__pagefind__=e}this.availableFilters=await this.__pagefind__.filters(),this.totalFilters=this.availableFilters,this.__dispatch__("filters",{available:this.availableFilters,total:this.totalFilters})}};window.PagefindModularUI=f;})(); diff --git a/docs/pagefind/pagefind-ui.css b/docs/pagefind/pagefind-ui.css new file mode 100644 index 00000000..c17a1d49 --- /dev/null +++ b/docs/pagefind/pagefind-ui.css @@ -0,0 +1 @@ +.pagefind-ui__result.svelte-j9e30.svelte-j9e30{list-style-type:none;display:flex;align-items:flex-start;gap:min(calc(40px * var(--pagefind-ui-scale)),3%);padding:calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));border-top:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result.svelte-j9e30.svelte-j9e30:last-of-type{border-bottom:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result-thumb.svelte-j9e30.svelte-j9e30{width:min(30%,calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));max-width:calc(120px * var(--pagefind-ui-scale));margin-top:calc(10px * var(--pagefind-ui-scale));aspect-ratio:var(--pagefind-ui-image-box-ratio);position:relative}.pagefind-ui__result-image.svelte-j9e30.svelte-j9e30{display:block;position:absolute;left:50%;transform:translate(-50%);font-size:0;width:auto;height:auto;max-width:100%;max-height:100%;border-radius:var(--pagefind-ui-image-border-radius)}.pagefind-ui__result-inner.svelte-j9e30.svelte-j9e30{flex:1;display:flex;flex-direction:column;align-items:flex-start;margin-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-title.svelte-j9e30.svelte-j9e30{display:inline-block;font-weight:700;font-size:calc(21px * var(--pagefind-ui-scale));margin-top:0;margin-bottom:0}.pagefind-ui__result-title.svelte-j9e30 .pagefind-ui__result-link.svelte-j9e30{color:var(--pagefind-ui-text);text-decoration:none}.pagefind-ui__result-title.svelte-j9e30 .pagefind-ui__result-link.svelte-j9e30:hover{text-decoration:underline}.pagefind-ui__result-excerpt.svelte-j9e30.svelte-j9e30{display:inline-block;font-weight:400;font-size:calc(16px * var(--pagefind-ui-scale));margin-top:calc(4px * var(--pagefind-ui-scale));margin-bottom:0;min-width:calc(250px * var(--pagefind-ui-scale))}.pagefind-ui__loading.svelte-j9e30.svelte-j9e30{color:var(--pagefind-ui-text);background-color:var(--pagefind-ui-text);border-radius:var(--pagefind-ui-border-radius);opacity:.1;pointer-events:none}.pagefind-ui__result-tags.svelte-j9e30.svelte-j9e30{list-style-type:none;padding:0;display:flex;gap:calc(20px * var(--pagefind-ui-scale));flex-wrap:wrap;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-tag.svelte-j9e30.svelte-j9e30{padding:calc(4px * var(--pagefind-ui-scale)) calc(8px * var(--pagefind-ui-scale));font-size:calc(14px * var(--pagefind-ui-scale));border-radius:var(--pagefind-ui-border-radius);background-color:var(--pagefind-ui-tag)}.pagefind-ui__result.svelte-4xnkmf.svelte-4xnkmf{list-style-type:none;display:flex;align-items:flex-start;gap:min(calc(40px * var(--pagefind-ui-scale)),3%);padding:calc(30px * var(--pagefind-ui-scale)) 0 calc(40px * var(--pagefind-ui-scale));border-top:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result.svelte-4xnkmf.svelte-4xnkmf:last-of-type{border-bottom:solid var(--pagefind-ui-border-width) var(--pagefind-ui-border)}.pagefind-ui__result-nested.svelte-4xnkmf.svelte-4xnkmf{display:flex;flex-direction:column;padding-left:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-nested.svelte-4xnkmf.svelte-4xnkmf:first-of-type{padding-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-nested.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf{font-size:.9em;position:relative}.pagefind-ui__result-nested.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf:before{content:"\2937 ";position:absolute;top:0;right:calc(100% + .1em)}.pagefind-ui__result-thumb.svelte-4xnkmf.svelte-4xnkmf{width:min(30%,calc((30% - (100px * var(--pagefind-ui-scale))) * 100000));max-width:calc(120px * var(--pagefind-ui-scale));margin-top:calc(10px * var(--pagefind-ui-scale));aspect-ratio:var(--pagefind-ui-image-box-ratio);position:relative}.pagefind-ui__result-image.svelte-4xnkmf.svelte-4xnkmf{display:block;position:absolute;left:50%;transform:translate(-50%);font-size:0;width:auto;height:auto;max-width:100%;max-height:100%;border-radius:var(--pagefind-ui-image-border-radius)}.pagefind-ui__result-inner.svelte-4xnkmf.svelte-4xnkmf{flex:1;display:flex;flex-direction:column;align-items:flex-start;margin-top:calc(10px * var(--pagefind-ui-scale))}.pagefind-ui__result-title.svelte-4xnkmf.svelte-4xnkmf{display:inline-block;font-weight:700;font-size:calc(21px * var(--pagefind-ui-scale));margin-top:0;margin-bottom:0}.pagefind-ui__result-title.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf{color:var(--pagefind-ui-text);text-decoration:none}.pagefind-ui__result-title.svelte-4xnkmf .pagefind-ui__result-link.svelte-4xnkmf:hover{text-decoration:underline}.pagefind-ui__result-excerpt.svelte-4xnkmf.svelte-4xnkmf{display:inline-block;font-weight:400;font-size:calc(16px * var(--pagefind-ui-scale));margin-top:calc(4px * var(--pagefind-ui-scale));margin-bottom:0;min-width:calc(250px * var(--pagefind-ui-scale))}.pagefind-ui__loading.svelte-4xnkmf.svelte-4xnkmf{color:var(--pagefind-ui-text);background-color:var(--pagefind-ui-text);border-radius:var(--pagefind-ui-border-radius);opacity:.1;pointer-events:none}.pagefind-ui__result-tags.svelte-4xnkmf.svelte-4xnkmf{list-style-type:none;padding:0;display:flex;gap:calc(20px * var(--pagefind-ui-scale));flex-wrap:wrap;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__result-tag.svelte-4xnkmf.svelte-4xnkmf{padding:calc(4px * var(--pagefind-ui-scale)) calc(8px * var(--pagefind-ui-scale));font-size:calc(14px * var(--pagefind-ui-scale));border-radius:var(--pagefind-ui-border-radius);background-color:var(--pagefind-ui-tag)}legend.svelte-1v2r7ls.svelte-1v2r7ls{position:absolute;clip:rect(0 0 0 0)}.pagefind-ui__filter-panel.svelte-1v2r7ls.svelte-1v2r7ls{min-width:min(calc(260px * var(--pagefind-ui-scale)),100%);flex:1;display:flex;flex-direction:column;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__filter-group.svelte-1v2r7ls.svelte-1v2r7ls{border:0;padding:0}.pagefind-ui__filter-block.svelte-1v2r7ls.svelte-1v2r7ls{padding:0;display:block;border-bottom:solid calc(2px * var(--pagefind-ui-scale)) var(--pagefind-ui-border);padding:calc(20px * var(--pagefind-ui-scale)) 0}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls{font-size:calc(16px * var(--pagefind-ui-scale));position:relative;display:flex;align-items:center;list-style:none;font-weight:700;cursor:pointer;height:calc(24px * var(--pagefind-ui-scale))}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls::-webkit-details-marker{display:none}.pagefind-ui__filter-name.svelte-1v2r7ls.svelte-1v2r7ls:after{position:absolute;content:"";right:calc(6px * var(--pagefind-ui-scale));top:50%;width:calc(8px * var(--pagefind-ui-scale));height:calc(8px * var(--pagefind-ui-scale));border:solid calc(2px * var(--pagefind-ui-scale)) currentColor;border-right:0;border-top:0;transform:translateY(-70%) rotate(-45deg)}.pagefind-ui__filter-block[open].svelte-1v2r7ls .pagefind-ui__filter-name.svelte-1v2r7ls:after{transform:translateY(-70%) rotate(-225deg)}.pagefind-ui__filter-group.svelte-1v2r7ls.svelte-1v2r7ls{display:flex;flex-direction:column;gap:calc(20px * var(--pagefind-ui-scale));padding-top:calc(30px * var(--pagefind-ui-scale))}.pagefind-ui__filter-value.svelte-1v2r7ls.svelte-1v2r7ls{position:relative;display:flex;align-items:center;gap:calc(8px * var(--pagefind-ui-scale))}.pagefind-ui__filter-value.svelte-1v2r7ls.svelte-1v2r7ls:before{position:absolute;content:"";top:50%;left:calc(8px * var(--pagefind-ui-scale));width:0px;height:0px;border:solid 1px #fff;opacity:0;transform:translate(calc(4.5px * var(--pagefind-ui-scale) * -1),calc(.8px * var(--pagefind-ui-scale))) skew(-5deg) rotate(-45deg);transform-origin:top left;border-top:0;border-right:0;pointer-events:none}.pagefind-ui__filter-value.pagefind-ui__filter-value--checked.svelte-1v2r7ls.svelte-1v2r7ls:before{opacity:1;width:calc(9px * var(--pagefind-ui-scale));height:calc(4px * var(--pagefind-ui-scale));transition:width .1s ease-out .1s,height .1s ease-in}.pagefind-ui__filter-checkbox.svelte-1v2r7ls.svelte-1v2r7ls{margin:0;width:calc(16px * var(--pagefind-ui-scale));height:calc(16px * var(--pagefind-ui-scale));border:solid 1px var(--pagefind-ui-border);appearance:none;-webkit-appearance:none;border-radius:calc(var(--pagefind-ui-border-radius) / 2);background-color:var(--pagefind-ui-background);cursor:pointer}.pagefind-ui__filter-checkbox.svelte-1v2r7ls.svelte-1v2r7ls:checked{background-color:var(--pagefind-ui-primary);border:solid 1px var(--pagefind-ui-primary)}.pagefind-ui__filter-label.svelte-1v2r7ls.svelte-1v2r7ls{cursor:pointer;font-size:calc(16px * var(--pagefind-ui-scale));font-weight:400}.pagefind-ui--reset *:where(:not(html,iframe,canvas,img,svg,video):not(svg *,symbol *)){all:unset;display:revert;outline:revert}.pagefind-ui--reset *,.pagefind-ui--reset *:before,.pagefind-ui--reset *:after{box-sizing:border-box}.pagefind-ui--reset a,.pagefind-ui--reset button{cursor:revert}.pagefind-ui--reset ol,.pagefind-ui--reset ul,.pagefind-ui--reset menu{list-style:none}.pagefind-ui--reset img{max-width:100%}.pagefind-ui--reset table{border-collapse:collapse}.pagefind-ui--reset input,.pagefind-ui--reset textarea{-webkit-user-select:auto}.pagefind-ui--reset textarea{white-space:revert}.pagefind-ui--reset meter{-webkit-appearance:revert;appearance:revert}.pagefind-ui--reset ::placeholder{color:unset}.pagefind-ui--reset :where([hidden]){display:none}.pagefind-ui--reset :where([contenteditable]:not([contenteditable=false])){-moz-user-modify:read-write;-webkit-user-modify:read-write;overflow-wrap:break-word;-webkit-line-break:after-white-space;-webkit-user-select:auto}.pagefind-ui--reset :where([draggable=true]){-webkit-user-drag:element}.pagefind-ui--reset mark{all:revert}:root{--pagefind-ui-scale:.8;--pagefind-ui-primary:#393939;--pagefind-ui-text:#393939;--pagefind-ui-background:#ffffff;--pagefind-ui-border:#eeeeee;--pagefind-ui-tag:#eeeeee;--pagefind-ui-border-width:2px;--pagefind-ui-border-radius:8px;--pagefind-ui-image-border-radius:8px;--pagefind-ui-image-box-ratio:3 / 2;--pagefind-ui-font:system, -apple-system, "BlinkMacSystemFont", ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", "Ubuntu", "arial", sans-serif}.pagefind-ui.svelte-e9gkc3{width:100%;color:var(--pagefind-ui-text);font-family:var(--pagefind-ui-font)}.pagefind-ui__hidden.svelte-e9gkc3{display:none!important}.pagefind-ui__suppressed.svelte-e9gkc3{opacity:0;pointer-events:none}.pagefind-ui__form.svelte-e9gkc3{position:relative}.pagefind-ui__form.svelte-e9gkc3:before{background-color:var(--pagefind-ui-text);width:calc(18px * var(--pagefind-ui-scale));height:calc(18px * var(--pagefind-ui-scale));top:calc(23px * var(--pagefind-ui-scale));left:calc(20px * var(--pagefind-ui-scale));content:"";position:absolute;display:block;opacity:.7;-webkit-mask-image:url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");mask-image:url("data:image/svg+xml,%3Csvg width='18' height='18' viewBox='0 0 18 18' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.7549 11.255H11.9649L11.6849 10.985C12.6649 9.845 13.2549 8.365 13.2549 6.755C13.2549 3.165 10.3449 0.255005 6.75488 0.255005C3.16488 0.255005 0.254883 3.165 0.254883 6.755C0.254883 10.345 3.16488 13.255 6.75488 13.255C8.36488 13.255 9.84488 12.665 10.9849 11.685L11.2549 11.965V12.755L16.2549 17.745L17.7449 16.255L12.7549 11.255ZM6.75488 11.255C4.26488 11.255 2.25488 9.245 2.25488 6.755C2.25488 4.26501 4.26488 2.255 6.75488 2.255C9.24488 2.255 11.2549 4.26501 11.2549 6.755C11.2549 9.245 9.24488 11.255 6.75488 11.255Z' fill='%23000000'/%3E%3C/svg%3E%0A");-webkit-mask-size:100%;mask-size:100%;z-index:9;pointer-events:none}.pagefind-ui__search-input.svelte-e9gkc3{height:calc(64px * var(--pagefind-ui-scale));padding:0 calc(70px * var(--pagefind-ui-scale)) 0 calc(54px * var(--pagefind-ui-scale));background-color:var(--pagefind-ui-background);border:var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);border-radius:var(--pagefind-ui-border-radius);font-size:calc(21px * var(--pagefind-ui-scale));position:relative;appearance:none;-webkit-appearance:none;display:flex;width:100%;box-sizing:border-box;font-weight:700}.pagefind-ui__search-input.svelte-e9gkc3::placeholder{opacity:.2}.pagefind-ui__search-clear.svelte-e9gkc3{position:absolute;top:calc(3px * var(--pagefind-ui-scale));right:calc(3px * var(--pagefind-ui-scale));height:calc(58px * var(--pagefind-ui-scale));padding:0 calc(15px * var(--pagefind-ui-scale)) 0 calc(2px * var(--pagefind-ui-scale));color:var(--pagefind-ui-text);font-size:calc(14px * var(--pagefind-ui-scale));cursor:pointer;background-color:var(--pagefind-ui-background);border-radius:var(--pagefind-ui-border-radius)}.pagefind-ui__drawer.svelte-e9gkc3{gap:calc(60px * var(--pagefind-ui-scale));display:flex;flex-direction:row;flex-wrap:wrap}.pagefind-ui__results-area.svelte-e9gkc3{min-width:min(calc(400px * var(--pagefind-ui-scale)),100%);flex:1000;margin-top:calc(20px * var(--pagefind-ui-scale))}.pagefind-ui__results.svelte-e9gkc3{padding:0}.pagefind-ui__message.svelte-e9gkc3{box-sizing:content-box;font-size:calc(16px * var(--pagefind-ui-scale));height:calc(24px * var(--pagefind-ui-scale));padding:calc(20px * var(--pagefind-ui-scale)) 0;display:flex;align-items:center;font-weight:700;margin-top:0}.pagefind-ui__button.svelte-e9gkc3{margin-top:calc(40px * var(--pagefind-ui-scale));border:var(--pagefind-ui-border-width) solid var(--pagefind-ui-border);border-radius:var(--pagefind-ui-border-radius);height:calc(48px * var(--pagefind-ui-scale));padding:0 calc(12px * var(--pagefind-ui-scale));font-size:calc(16px * var(--pagefind-ui-scale));color:var(--pagefind-ui-primary);background:var(--pagefind-ui-background);width:100%;text-align:center;font-weight:700;cursor:pointer}.pagefind-ui__button.svelte-e9gkc3:hover{border-color:var(--pagefind-ui-primary);color:var(--pagefind-ui-primary);background:var(--pagefind-ui-background)} diff --git a/docs/pagefind/pagefind-ui.js b/docs/pagefind/pagefind-ui.js new file mode 100644 index 00000000..6a8a4d70 --- /dev/null +++ b/docs/pagefind/pagefind-ui.js @@ -0,0 +1,2 @@ +(()=>{var Os=Object.defineProperty;var x=(l,e)=>{for(var t in e)Os(l,t,{get:e[t],enumerable:!0})};function R(){}function Bt(l){return l()}function ct(){return Object.create(null)}function H(l){l.forEach(Bt)}function ut(l){return typeof l=="function"}function O(l,e){return l!=l?e==e:l!==e||l&&typeof l=="object"||typeof l=="function"}var ot;function ge(l,e){return l===e?!0:(ot||(ot=document.createElement("a")),ot.href=e,l===ot.href)}function Wl(l){return Object.keys(l).length===0}var ht=typeof window<"u"?window:typeof globalThis<"u"?globalThis:global;var Ct=class l{_listeners="WeakMap"in ht?new WeakMap:void 0;_observer=void 0;options;constructor(e){this.options=e}observe(e,t){return this._listeners.set(e,t),this._getObserver().observe(e,this.options),()=>{this._listeners.delete(e),this._observer.unobserve(e)}}_getObserver(){return this._observer??(this._observer=new ResizeObserver(e=>{for(let t of e)l.entries.set(t.target,t),this._listeners.get(t.target)?.(t)}))}};Ct.entries="WeakMap"in ht?new WeakMap:void 0;var Sl=!1;function Vl(){Sl=!0}function Dl(){Sl=!1}function b(l,e){l.appendChild(e)}function h(l,e,t){l.insertBefore(e,t||null)}function B(l){l.parentNode&&l.parentNode.removeChild(l)}function $(l,e){for(let t=0;tl.removeEventListener(e,t,s)}function _(l,e,t){t==null?l.removeAttribute(e):l.getAttribute(e)!==t&&l.setAttribute(e,t)}function Tl(l){return Array.from(l.childNodes)}function W(l,e){e=""+e,l.data!==e&&(l.data=e)}function oe(l,e){l.value=e??""}function z(l,e,t){l.classList.toggle(e,!!t)}var It=class{is_svg=!1;e=void 0;n=void 0;t=void 0;a=void 0;constructor(e=!1){this.is_svg=e,this.e=this.n=null}c(e){this.h(e)}m(e,t,s=null){this.e||(this.is_svg?this.e=Ps(t.nodeName):this.e=F(t.nodeType===11?"TEMPLATE":t.nodeName),this.t=t.tagName!=="TEMPLATE"?t:t.content,this.c(e)),this.i(s)}h(e){this.e.innerHTML=e,this.n=Array.from(this.e.nodeName==="TEMPLATE"?this.e.content.childNodes:this.e.childNodes)}i(e){for(let t=0;t{e[t.slot||"default"]=!0}),e}var te;function j(l){te=l}function Ut(){if(!te)throw new Error("Function called outside component initialization");return te}function xt(l){Ut().$$.on_mount.push(l)}function yt(l){Ut().$$.on_destroy.push(l)}var se=[];var Ce=[],ue=[],Gt=[],sa=Promise.resolve(),Nt=!1;function Ml(){Nt||(Nt=!0,sa.then(dt))}function Ae(l){ue.push(l)}function Yl(l){Gt.push(l)}var Zt=new Set,ce=0;function dt(){if(ce!==0)return;let l=te;do{try{for(;cel.indexOf(s)===-1?e.push(s):t.push(s)),t.forEach(s=>s()),ue=e}var At=new Set,ae;function re(){ae={r:0,c:[],p:ae}}function ie(){ae.r||H(ae.c),ae=ae.p}function k(l,e){l&&l.i&&(At.delete(l),l.i(e))}function D(l,e,t,s){if(l&&l.o){if(At.has(l))return;At.add(l),ae.c.push(()=>{At.delete(l),s&&(t&&l.d(1),s())}),l.o(e)}else s&&s()}function V(l){return l?.length!==void 0?l:Array.from(l)}function zl(l,e){D(l,1,1,()=>{e.delete(l.key)})}function Jl(l,e,t,s,a,r,i,n,g,C,u,A){let c=l.length,d=r.length,o=c,I={};for(;o--;)I[l[o].key]=o;let m=[],f=new Map,p=new Map,U=[];for(o=d;o--;){let N=A(a,r,o),S=t(N),E=i.get(S);E?s&&U.push(()=>E.p(N,e)):(E=C(S,N),E.c()),f.set(S,m[o]=E),S in I&&p.set(S,Math.abs(o-I[S]))}let y=new Set,q=new Set;function J(N){k(N,1),N.m(n,u),i.set(N.key,N),u=N.first,d--}for(;c&&d;){let N=m[d-1],S=l[c-1],E=N.key,v=S.key;N===S?(u=N.first,c--,d--):f.has(v)?!i.has(E)||y.has(E)?J(N):q.has(v)?c--:p.get(E)>p.get(v)?(q.add(E),J(N)):(y.add(v),c--):(g(S,i),c--)}for(;c--;){let N=l[c];f.has(N.key)||g(N,i)}for(;d;)J(m[d-1]);return H(U),m}var ra=["allowfullscreen","allowpaymentrequest","async","autofocus","autoplay","checked","controls","default","defer","disabled","formnovalidate","hidden","inert","ismap","loop","multiple","muted","nomodule","novalidate","open","playsinline","readonly","required","reversed","selected"],ia=new Set([...ra]);function Ol(l,e,t){let s=l.$$.props[e];s!==void 0&&(l.$$.bound[s]=t,t(l.$$.ctx[s]))}function _t(l){l&&l.c()}function _e(l,e,t){let{fragment:s,after_update:a}=l.$$;s&&s.m(e,t),Ae(()=>{let r=l.$$.on_mount.map(Bt).filter(ut);l.$$.on_destroy?l.$$.on_destroy.push(...r):H(r),l.$$.on_mount=[]}),a.forEach(Ae)}function Qe(l,e){let t=l.$$;t.fragment!==null&&(wl(t.after_update),H(t.on_destroy),t.fragment&&t.fragment.d(e),t.on_destroy=t.fragment=null,t.ctx=[])}function ga(l,e){l.$$.dirty[0]===-1&&(se.push(l),Ml(),l.$$.dirty.fill(0)),l.$$.dirty[e/31|0]|=1<{let o=d.length?d[0]:c;return C.ctx&&a(C.ctx[A],C.ctx[A]=o)&&(!C.skip_bound&&C.bound[A]&&C.bound[A](o),u&&ga(l,A)),c}):[],C.update(),u=!0,H(C.before_update),C.fragment=s?s(C.ctx):!1,e.target){if(e.hydrate){Vl();let A=Tl(e.target);C.fragment&&C.fragment.l(A),A.forEach(B)}else C.fragment&&C.fragment.c();e.intro&&k(l.$$.fragment),_e(l,e.target,e.anchor),Dl(),dt()}j(g)}var oa;typeof HTMLElement=="function"&&(oa=class extends HTMLElement{$$ctor;$$s;$$c;$$cn=!1;$$d={};$$r=!1;$$p_d={};$$l={};$$l_u=new Map;constructor(l,e,t){super(),this.$$ctor=l,this.$$s=e,t&&this.attachShadow({mode:"open"})}addEventListener(l,e,t){if(this.$$l[l]=this.$$l[l]||[],this.$$l[l].push(e),this.$$c){let s=this.$$c.$on(l,e);this.$$l_u.set(e,s)}super.addEventListener(l,e,t)}removeEventListener(l,e,t){if(super.removeEventListener(l,e,t),this.$$c){let s=this.$$l_u.get(e);s&&(s(),this.$$l_u.delete(e))}if(this.$$l[l]){let s=this.$$l[l].indexOf(e);s>=0&&this.$$l[l].splice(s,1)}}async connectedCallback(){if(this.$$cn=!0,!this.$$c){let l=function(a){return()=>{let r;return{c:function(){r=F("slot"),a!=="default"&&_(r,"name",a)},m:function(g,C){h(g,r,C)},d:function(g){g&&B(r)}}}};if(await Promise.resolve(),!this.$$cn||this.$$c)return;let e={},t=vl(this);for(let a of this.$$s)a in t&&(e[a]=[l(a)]);for(let a of this.attributes){let r=this.$$g_p(a.name);r in this.$$d||(this.$$d[r]=Xt(r,a.value,this.$$p_d,"toProp"))}for(let a in this.$$p_d)!(a in this.$$d)&&this[a]!==void 0&&(this.$$d[a]=this[a],delete this[a]);this.$$c=new this.$$ctor({target:this.shadowRoot||this,props:{...this.$$d,$$slots:e,$$scope:{ctx:[]}}});let s=()=>{this.$$r=!0;for(let a in this.$$p_d)if(this.$$d[a]=this.$$c.$$.ctx[this.$$c.$$.props[a]],this.$$p_d[a].reflect){let r=Xt(a,this.$$d[a],this.$$p_d,"toAttribute");r==null?this.removeAttribute(this.$$p_d[a].attribute||a):this.setAttribute(this.$$p_d[a].attribute||a,r)}this.$$r=!1};this.$$c.$$.after_update.push(s),s();for(let a in this.$$l)for(let r of this.$$l[a]){let i=this.$$c.$on(a,r);this.$$l_u.set(r,i)}this.$$l={}}}attributeChangedCallback(l,e,t){this.$$r||(l=this.$$g_p(l),this.$$d[l]=Xt(l,t,this.$$p_d,"toProp"),this.$$c?.$set({[l]:this.$$d[l]}))}disconnectedCallback(){this.$$cn=!1,Promise.resolve().then(()=>{!this.$$cn&&this.$$c&&(this.$$c.$destroy(),this.$$c=void 0)})}$$g_p(l){return Object.keys(this.$$p_d).find(e=>this.$$p_d[e].attribute===l||!this.$$p_d[e].attribute&&e.toLowerCase()===l)||l}});function Xt(l,e,t,s){let a=t[l]?.type;if(e=a==="Boolean"&&typeof e!="boolean"?e!=null:e,!s||!t[l])return e;if(s==="toAttribute")switch(a){case"Object":case"Array":return e==null?null:JSON.stringify(e);case"Boolean":return e?"":null;case"Number":return e??null;default:return e}else switch(a){case"Object":case"Array":return e&&JSON.parse(e);case"Boolean":return e;case"Number":return e!=null?+e:e;default:return e}}var M=class{$$=void 0;$$set=void 0;$destroy(){Qe(this,1),this.$destroy=R}$on(e,t){if(!ut(t))return R;let s=this.$$.callbacks[e]||(this.$$.callbacks[e]=[]);return s.push(t),()=>{let a=s.indexOf(t);a!==-1&&s.splice(a,1)}}$set(e){this.$$set&&!Wl(e)&&(this.$$.skip_bound=!0,this.$$set(e),this.$$.skip_bound=!1)}};typeof window<"u"&&(window.__svelte||(window.__svelte={v:new Set})).v.add("4");function T(l){let e=typeof l=="string"?l.charCodeAt(0):l;return e>=97&&e<=122||e>=65&&e<=90}function le(l){let e=typeof l=="string"?l.charCodeAt(0):l;return e>=48&&e<=57}function P(l){return T(l)||le(l)}var jl=["art-lojban","cel-gaulish","no-bok","no-nyn","zh-guoyu","zh-hakka","zh-min","zh-min-nan","zh-xiang"];var Et={"en-gb-oed":"en-GB-oxendict","i-ami":"ami","i-bnn":"bnn","i-default":null,"i-enochian":null,"i-hak":"hak","i-klingon":"tlh","i-lux":"lb","i-mingo":null,"i-navajo":"nv","i-pwn":"pwn","i-tao":"tao","i-tay":"tay","i-tsu":"tsu","sgn-be-fr":"sfb","sgn-be-nl":"vgt","sgn-ch-de":"sgg","art-lojban":"jbo","cel-gaulish":null,"no-bok":"nb","no-nyn":"nn","zh-guoyu":"cmn","zh-hakka":"hak","zh-min":null,"zh-min-nan":"nan","zh-xiang":"hsn"};var ua={}.hasOwnProperty;function Qt(l,e={}){let t=Kl(),s=String(l),a=s.toLowerCase(),r=0;if(l==null)throw new Error("Expected string, got `"+l+"`");if(ua.call(Et,a)){let n=Et[a];return(e.normalize===void 0||e.normalize===null||e.normalize)&&typeof n=="string"?Qt(n):(t[jl.includes(a)?"regular":"irregular"]=s,t)}for(;T(a.charCodeAt(r))&&r<9;)r++;if(r>1&&r<9){if(t.language=s.slice(0,r),r<4){let n=0;for(;a.charCodeAt(r)===45&&T(a.charCodeAt(r+1))&&T(a.charCodeAt(r+2))&&T(a.charCodeAt(r+3))&&!T(a.charCodeAt(r+4));){if(n>2)return i(r,3,"Too many extended language subtags, expected at most 3 subtags");t.extendedLanguageSubtags.push(s.slice(r+1,r+4)),r+=4,n++}}for(a.charCodeAt(r)===45&&T(a.charCodeAt(r+1))&&T(a.charCodeAt(r+2))&&T(a.charCodeAt(r+3))&&T(a.charCodeAt(r+4))&&!T(a.charCodeAt(r+5))&&(t.script=s.slice(r+1,r+5),r+=5),a.charCodeAt(r)===45&&(T(a.charCodeAt(r+1))&&T(a.charCodeAt(r+2))&&!T(a.charCodeAt(r+3))?(t.region=s.slice(r+1,r+3),r+=3):le(a.charCodeAt(r+1))&&le(a.charCodeAt(r+2))&&le(a.charCodeAt(r+3))&&!le(a.charCodeAt(r+4))&&(t.region=s.slice(r+1,r+4),r+=4));a.charCodeAt(r)===45;){let n=r+1,g=n;for(;P(a.charCodeAt(g));){if(g-n>7)return i(g,1,"Too long variant, expected at most 8 characters");g++}if(g-n>4||g-n>3&&le(a.charCodeAt(n)))t.variants.push(s.slice(n,g)),r=g;else break}for(;a.charCodeAt(r)===45&&!(a.charCodeAt(r+1)===120||!P(a.charCodeAt(r+1))||a.charCodeAt(r+2)!==45||!P(a.charCodeAt(r+3)));){let n=r+2,g=0;for(;a.charCodeAt(n)===45&&P(a.charCodeAt(n+1))&&P(a.charCodeAt(n+2));){let C=n+1;for(n=C+2,g++;P(a.charCodeAt(n));){if(n-C>7)return i(n,2,"Too long extension, expected at most 8 characters");n++}}if(!g)return i(n,4,"Empty extension, extensions must have at least 2 characters of content");t.extensions.push({singleton:s.charAt(r+1),extensions:s.slice(r+3,n).split("-")}),r=n}}else r=0;if(r===0&&a.charCodeAt(r)===120||a.charCodeAt(r)===45&&a.charCodeAt(r+1)===120){r=r?r+2:1;let n=r;for(;a.charCodeAt(n)===45&&P(a.charCodeAt(n+1));){let g=r+1;for(n=g;P(a.charCodeAt(n));){if(n-g>7)return i(n,5,"Too long private-use area, expected at most 8 characters");n++}t.privateuse.push(s.slice(r+1,n)),r=n}}if(r!==s.length)return i(r,6,"Found superfluous content after tag");return t;function i(n,g,C){return e.warning&&e.warning(C,g,n),e.forgiving?t:Kl()}}function Kl(){return{language:null,extendedLanguageSubtags:[],script:null,region:null,variants:[],extensions:[],privateuse:[],irregular:null,regular:null}}function Pl(l,e,t){let s=l.slice();return s[9]=e[t][0],s[10]=e[t][1],s}function Ca(l){let e,t,s,a,r,i=l[0]&&ql(l);return{c(){i&&i.c(),e=Z(),t=F("div"),s=F("p"),s.textContent=`${l[3](30)}`,a=Z(),r=F("p"),r.textContent=`${l[3](40)}`,_(s,"class","pagefind-ui__result-title pagefind-ui__loading svelte-j9e30"),_(r,"class","pagefind-ui__result-excerpt pagefind-ui__loading svelte-j9e30"),_(t,"class","pagefind-ui__result-inner svelte-j9e30")},m(n,g){i&&i.m(n,g),h(n,e,g),h(n,t,g),b(t,s),b(t,a),b(t,r)},p(n,g){n[0]?i||(i=ql(n),i.c(),i.m(e.parentNode,e)):i&&(i.d(1),i=null)},d(n){n&&(B(e),B(t)),i&&i.d(n)}}}function Ia(l){let e,t,s,a,r=l[1].meta?.title+"",i,n,g,C,u=l[1].excerpt+"",A,c=l[0]&&$l(l),d=l[2].length&&ts(l);return{c(){c&&c.c(),e=Z(),t=F("div"),s=F("p"),a=F("a"),i=G(r),g=Z(),C=F("p"),A=Z(),d&&d.c(),_(a,"class","pagefind-ui__result-link svelte-j9e30"),_(a,"href",n=l[1].meta?.url||l[1].url),_(s,"class","pagefind-ui__result-title svelte-j9e30"),_(C,"class","pagefind-ui__result-excerpt svelte-j9e30"),_(t,"class","pagefind-ui__result-inner svelte-j9e30")},m(o,I){c&&c.m(o,I),h(o,e,I),h(o,t,I),b(t,s),b(s,a),b(a,i),b(t,g),b(t,C),C.innerHTML=u,b(t,A),d&&d.m(t,null)},p(o,I){o[0]?c?c.p(o,I):(c=$l(o),c.c(),c.m(e.parentNode,e)):c&&(c.d(1),c=null),I&2&&r!==(r=o[1].meta?.title+"")&&W(i,r),I&2&&n!==(n=o[1].meta?.url||o[1].url)&&_(a,"href",n),I&2&&u!==(u=o[1].excerpt+"")&&(C.innerHTML=u),o[2].length?d?d.p(o,I):(d=ts(o),d.c(),d.m(t,null)):d&&(d.d(1),d=null)},d(o){o&&(B(e),B(t)),c&&c.d(o),d&&d.d()}}}function ql(l){let e;return{c(){e=F("div"),_(e,"class","pagefind-ui__result-thumb pagefind-ui__loading svelte-j9e30")},m(t,s){h(t,e,s)},d(t){t&&B(e)}}}function $l(l){let e,t=l[1].meta.image&&es(l);return{c(){e=F("div"),t&&t.c(),_(e,"class","pagefind-ui__result-thumb svelte-j9e30")},m(s,a){h(s,e,a),t&&t.m(e,null)},p(s,a){s[1].meta.image?t?t.p(s,a):(t=es(s),t.c(),t.m(e,null)):t&&(t.d(1),t=null)},d(s){s&&B(e),t&&t.d()}}}function es(l){let e,t,s;return{c(){e=F("img"),_(e,"class","pagefind-ui__result-image svelte-j9e30"),ge(e.src,t=l[1].meta?.image)||_(e,"src",t),_(e,"alt",s=l[1].meta?.image_alt||l[1].meta?.title)},m(a,r){h(a,e,r)},p(a,r){r&2&&!ge(e.src,t=a[1].meta?.image)&&_(e,"src",t),r&2&&s!==(s=a[1].meta?.image_alt||a[1].meta?.title)&&_(e,"alt",s)},d(a){a&&B(e)}}}function ts(l){let e,t=V(l[2]),s=[];for(let a=0;al.toLocaleUpperCase();function Aa(l,e,t){let{show_images:s=!0}=e,{process_result:a=null}=e,{result:r={data:async()=>{}}}=e,i=["title","image","image_alt","url"],n,g=[],C=(c,d)=>{if(!c||/^[a-z][a-z0-9+.-]*:/i.test(c)||/^\/\//.test(c)||c.startsWith("/"))return c;try{return new URL(c,new URL(d||"/","https://p")).pathname}catch{return c}},u=async c=>{t(1,n=await c.data()),t(1,n=a?.(n)??n),n.meta?.image&&t(1,n={...n,meta:{...n.meta,image:C(n.meta.image,n.meta.url||n.url)}}),t(2,g=Object.entries(n.meta).filter(([d])=>!i.includes(d)))},A=(c=30)=>". ".repeat(Math.floor(10+Math.random()*c));return l.$$set=c=>{"show_images"in c&&t(0,s=c.show_images),"process_result"in c&&t(4,a=c.process_result),"result"in c&&t(5,r=c.result)},l.$$.update=()=>{l.$$.dirty&32&&u(r)},[s,n,g,A,a,r]}var kt=class extends M{constructor(e){super(),K(this,e,Aa,da,O,{show_images:0,process_result:4,result:5})}},as=kt;function rs(l,e,t){let s=l.slice();return s[11]=e[t][0],s[12]=e[t][1],s}function is(l,e,t){let s=l.slice();return s[15]=e[t],s}function _a(l){let e,t,s,a,r,i=l[0]&&ns(l);return{c(){i&&i.c(),e=Z(),t=F("div"),s=F("p"),s.textContent=`${l[5](30)}`,a=Z(),r=F("p"),r.textContent=`${l[5](40)}`,_(s,"class","pagefind-ui__result-title pagefind-ui__loading svelte-4xnkmf"),_(r,"class","pagefind-ui__result-excerpt pagefind-ui__loading svelte-4xnkmf"),_(t,"class","pagefind-ui__result-inner svelte-4xnkmf")},m(n,g){i&&i.m(n,g),h(n,e,g),h(n,t,g),b(t,s),b(t,a),b(t,r)},p(n,g){n[0]?i||(i=ns(n),i.c(),i.m(e.parentNode,e)):i&&(i.d(1),i=null)},d(n){n&&(B(e),B(t)),i&&i.d(n)}}}function Qa(l){let e,t,s,a,r=l[1].meta?.title+"",i,n,g,C,u,A=l[0]&&gs(l),c=l[4]&&cs(l),d=V(l[3]),o=[];for(let m=0;ml.toLocaleUpperCase();function ba(l,e,t){let{show_images:s=!0}=e,{process_result:a=null}=e,{result:r={data:async()=>{}}}=e,i=["title","image","image_alt","url"],n,g=[],C=[],u=!1,A=(o,I)=>{if(o.length<=I)return o;let m=[...o].sort((f,p)=>p.locations.length-f.locations.length).slice(0,3).map(f=>f.url);return o.filter(f=>m.includes(f.url))},c=async o=>{t(1,n=await o.data()),t(1,n=a?.(n)??n),t(2,g=Object.entries(n.meta).filter(([I])=>!i.includes(I))),Array.isArray(n.sub_results)&&(t(4,u=n.sub_results?.[0]?.url===(n.meta?.url||n.url)),u?t(3,C=A(n.sub_results.slice(1),3)):t(3,C=A([...n.sub_results],3)))},d=(o=30)=>". ".repeat(Math.floor(10+Math.random()*o));return l.$$set=o=>{"show_images"in o&&t(0,s=o.show_images),"process_result"in o&&t(6,a=o.process_result),"result"in o&&t(7,r=o.result)},l.$$.update=()=>{l.$$.dirty&128&&c(r)},[s,n,g,C,u,d,a,r]}var Lt=class extends M{constructor(e){super(),K(this,e,ba,ma,O,{show_images:0,process_result:6,result:7})}},As=Lt;function _s(l,e,t){let s=l.slice();return s[10]=e[t][0],s[11]=e[t][1],s[12]=e,s[13]=t,s}function Qs(l,e,t){let s=l.slice();return s[14]=e[t][0],s[15]=e[t][1],s[16]=e,s[17]=t,s}function ms(l){let e,t,s=l[4]("filters_label",l[5],l[6])+"",a,r,i=V(Object.entries(l[1])),n=[];for(let g=0;gl.toLocaleUpperCase(),Bs=l=>l.toLowerCase();function Fa(l,e,t){let{available_filters:s=null}=e,{show_empty_filters:a=!0}=e,{open_filters:r=[]}=e,{translate:i=()=>""}=e,{automatic_translations:n={}}=e,{translations:g={}}=e,{selected_filters:C={}}=e,u=!1,A=!1;function c(d,o){C[`${d}:${o}`]=this.checked,t(0,C)}return l.$$set=d=>{"available_filters"in d&&t(1,s=d.available_filters),"show_empty_filters"in d&&t(2,a=d.show_empty_filters),"open_filters"in d&&t(3,r=d.open_filters),"translate"in d&&t(4,i=d.translate),"automatic_translations"in d&&t(5,n=d.automatic_translations),"translations"in d&&t(6,g=d.translations),"selected_filters"in d&&t(0,C=d.selected_filters)},l.$$.update=()=>{if(l.$$.dirty&258&&s&&!u){t(8,u=!0);let d=Object.entries(s||{});d.length===1&&Object.entries(d[0][1])?.length<=6&&t(7,A=!0)}},[C,s,a,r,i,n,g,A,u,c]}var Rt=class extends M{constructor(e){super(),K(this,e,Fa,fa,O,{available_filters:1,show_empty_filters:2,open_filters:3,translate:4,automatic_translations:5,translations:6,selected_filters:0})}},hs=Rt;var Wt={};x(Wt,{comments:()=>Ba,default:()=>xa,direction:()=>ha,strings:()=>Ua,thanks_to:()=>pa});var pa="Jan Claasen ",Ba="",ha="ltr",Ua={placeholder:"Soek",clear_search:"Opruim",load_more:"Laai nog resultate",search_label:"Soek hierdie webwerf",filters_label:"Filters",zero_results:"Geen resultate vir [SEARCH_TERM]",many_results:"[COUNT] resultate vir [SEARCH_TERM]",one_result:"[COUNT] resultate vir [SEARCH_TERM]",total_zero_results:"Geen resultate",total_one_result:"[COUNT] resultaat",total_many_results:"[COUNT] resultate",alt_search:"Geen resultate vir [SEARCH_TERM]. Toon resultate vir [DIFFERENT_TERM] in plaas daarvan",search_suggestion:"Geen resultate vir [SEARCH_TERM]. Probeer eerder een van die volgende terme:",searching:"Soek vir [SEARCH_TERM]",results_label:"Soekresultate",keyboard_navigate:"navigeer",keyboard_select:"kies",keyboard_clear:"wis",keyboard_close:"sluit",keyboard_search:"soek",error_search:"Soek het misluk",filter_selected_one:"[COUNT] gekies",filter_selected_many:"[COUNT] gekies",input_hint:"Resultate sal verskyn terwyl jy tik",loading:"Laai"},xa={thanks_to:pa,comments:Ba,direction:ha,strings:Ua};var St={};x(St,{comments:()=>Za,default:()=>Xa,direction:()=>Ga,strings:()=>Na,thanks_to:()=>ya});var ya="Jermanuts",Za="",Ga="rtl",Na={placeholder:"\u0628\u062D\u062B",clear_search:"\u0627\u0645\u0633\u062D",load_more:"\u062D\u0645\u0651\u0650\u0644 \u0627\u0644\u0645\u0632\u064A\u062F \u0645\u0646 \u0627\u0644\u0646\u062A\u0627\u0626\u062C",search_label:"\u0627\u0628\u062D\u062B \u0641\u064A \u0647\u0630\u0627 \u0627\u0644\u0645\u0648\u0642\u0639",filters_label:"\u062A\u0635\u0641\u064A\u0627\u062A",zero_results:"\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C \u0644 [SEARCH_TERM]",many_results:"[COUNT] \u0646\u062A\u0627\u0626\u062C \u0644 [SEARCH_TERM]",one_result:"[COUNT] \u0646\u062A\u064A\u062C\u0629 \u0644 [SEARCH_TERM]",total_zero_results:"\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C",total_one_result:"[COUNT] \u0646\u062A\u064A\u062C\u0629",total_many_results:"[COUNT] \u0646\u062A\u0627\u0626\u062C",alt_search:"\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C \u0644 [SEARCH_TERM]. \u064A\u0639\u0631\u0636 \u0627\u0644\u0646\u062A\u0627\u0626\u062C \u0644 [DIFFERENT_TERM] \u0628\u062F\u0644\u0627\u064B \u0645\u0646 \u0630\u0644\u0643",search_suggestion:"\u0644\u0627 \u062A\u0648\u062C\u062F \u0646\u062A\u0627\u0626\u062C \u0644 [SEARCH_TERM]. \u062C\u0631\u0628 \u0623\u062D\u062F \u0639\u0645\u0644\u064A\u0627\u062A \u0627\u0644\u0628\u062D\u062B \u0627\u0644\u062A\u0627\u0644\u064A\u0629:",searching:"\u064A\u0628\u062D\u062B \u0639\u0646 [SEARCH_TERM]...",results_label:"\u0646\u062A\u0627\u0626\u062C \u0627\u0644\u0628\u062D\u062B",keyboard_navigate:"\u062A\u0646\u0642\u0644",keyboard_select:"\u0627\u062E\u062A\u064A\u0627\u0631",keyboard_clear:"\u0627\u0645\u0633\u062D",keyboard_close:"\u0625\u063A\u0644\u0627\u0642",keyboard_search:"\u0628\u062D\u062B",error_search:"\u0641\u0634\u0644 \u0627\u0644\u0628\u062D\u062B",filter_selected_one:"[COUNT] \u0645\u062D\u062F\u062F",filter_selected_many:"[COUNT] \u0645\u062D\u062F\u062F",input_hint:"\u0633\u062A\u0638\u0647\u0631 \u0627\u0644\u0646\u062A\u0627\u0626\u062C \u0623\u062B\u0646\u0627\u0621 \u0627\u0644\u0643\u062A\u0627\u0628\u0629",loading:"\u062C\u0627\u0631\u064D \u0627\u0644\u062A\u062D\u0645\u064A\u0644"},Xa={thanks_to:ya,comments:Za,direction:Ga,strings:Na};var Vt={};x(Vt,{comments:()=>ka,default:()=>Wa,direction:()=>La,strings:()=>Ra,thanks_to:()=>Ea});var Ea="Maruf Alom ",ka="",La="ltr",Ra={placeholder:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",clear_search:"\u09AE\u09C1\u099B\u09C7 \u09AB\u09C7\u09B2\u09C1\u09A8",load_more:"\u0986\u09B0\u09CB \u09AB\u09B2\u09BE\u09AB\u09B2 \u09A6\u09C7\u0996\u09C1\u09A8",search_label:"\u098F\u0987 \u0993\u09AF\u09BC\u09C7\u09AC\u09B8\u09BE\u0987\u099F\u09C7 \u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u0995\u09B0\u09C1\u09A8",filters_label:"\u09AB\u09BF\u09B2\u09CD\u099F\u09BE\u09B0",zero_results:"[SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF",many_results:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u0997\u09BF\u09AF\u09BC\u09C7\u099B\u09C7 [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF",one_result:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u0997\u09BF\u09AF\u09BC\u09C7\u099B\u09C7 [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF",total_zero_results:"\u0995\u09CB\u09A8 \u09AB\u09B2\u09BE\u09AB\u09B2 \u09A8\u09C7\u0987",total_one_result:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2",total_many_results:"[COUNT]-\u099F\u09BF \u09AB\u09B2\u09BE\u09AB\u09B2",alt_search:"\u0995\u09CB\u09A8 \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF [SEARCH_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF. \u09AA\u09B0\u09BF\u09AC\u09B0\u09CD\u09A4\u09C7 [DIFFERENT_TERM] \u098F\u09B0 \u099C\u09A8\u09CD\u09AF \u09A6\u09C7\u0996\u09BE\u09A8\u09CB \u09B9\u099A\u09CD\u099B\u09C7",search_suggestion:"\u0995\u09CB\u09A8 \u0995\u09BF\u099B\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09AA\u09BE\u0993\u09AF\u09BC\u09BE \u09AF\u09BE\u09AF\u09BC\u09A8\u09BF [SEARCH_TERM] \u098F\u09B0 \u09AC\u09BF\u09B7\u09AF\u09BC\u09C7. \u09A8\u09BF\u09A8\u09CD\u09AE\u09C7\u09B0 \u09AC\u09BF\u09B7\u09AF\u09BC\u09AC\u09B8\u09CD\u09A4\u09C1 \u0996\u09C1\u0981\u099C\u09C7 \u09A6\u09C7\u0996\u09C1\u09A8:",searching:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u099A\u09B2\u099B\u09C7 [SEARCH_TERM]...",results_label:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8\u09C7\u09B0 \u09AB\u09B2\u09BE\u09AB\u09B2",keyboard_navigate:"\u09A8\u09C7\u09AD\u09BF\u0997\u09C7\u099F",keyboard_select:"\u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09A8",keyboard_clear:"\u09AE\u09C1\u099B\u09C1\u09A8",keyboard_close:"\u09AC\u09A8\u09CD\u09A7",keyboard_search:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8",error_search:"\u0985\u09A8\u09C1\u09B8\u09A8\u09CD\u09A7\u09BE\u09A8 \u09AC\u09CD\u09AF\u09B0\u09CD\u09A5",filter_selected_one:"[COUNT]-\u099F\u09BF \u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09BF\u09A4",filter_selected_many:"[COUNT]-\u099F\u09BF \u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09BF\u09A4",input_hint:"\u099F\u09BE\u0987\u09AA \u0995\u09B0\u09BE\u09B0 \u09B8\u09BE\u09A5\u09C7 \u09B8\u09BE\u09A5\u09C7 \u09AB\u09B2\u09BE\u09AB\u09B2 \u09A6\u09C7\u0996\u09BE \u09AF\u09BE\u09AC\u09C7",loading:"\u09B2\u09CB\u09A1 \u09B9\u099A\u09CD\u099B\u09C7"},Wa={thanks_to:Ea,comments:ka,direction:La,strings:Ra};var Dt={};x(Dt,{comments:()=>Va,default:()=>va,direction:()=>Da,strings:()=>Ta,thanks_to:()=>Sa});var Sa="Pablo Villaverde ",Va="",Da="ltr",Ta={placeholder:"Cerca",clear_search:"Netejar",load_more:"Veure m\xE9s resultats",search_label:"Cerca en aquest lloc",filters_label:"Filtres",zero_results:"No es van trobar resultats per [SEARCH_TERM]",many_results:"[COUNT] resultats trobats per [SEARCH_TERM]",one_result:"[COUNT] resultat trobat per [SEARCH_TERM]",total_zero_results:"Sense resultats",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultats",alt_search:"No es van trobar resultats per [SEARCH_TERM]. Mostrant al seu lloc resultats per [DIFFERENT_TERM]",search_suggestion:"No es van trobar resultats per [SEARCH_TERM]. Proveu una de les cerques seg\xFCents:",searching:"Cercant [SEARCH_TERM]...",results_label:"Resultats de la cerca",keyboard_navigate:"navegar",keyboard_select:"triar",keyboard_clear:"netejar",keyboard_close:"tancar",keyboard_search:"cercar",error_search:"Error en la cerca",filter_selected_one:"[COUNT] seleccionat",filter_selected_many:"[COUNT] seleccionats",input_hint:"Els resultats apareixeran mentre escriviu",loading:"Carregant"},va={thanks_to:Sa,comments:Va,direction:Da,strings:Ta};var Tt={};x(Tt,{comments:()=>Ma,default:()=>za,direction:()=>Ya,strings:()=>wa,thanks_to:()=>Ha});var Ha="Dalibor Hon ",Ma="",Ya="ltr",wa={placeholder:"Hledat",clear_search:"Smazat",load_more:"Na\u010D\xEDst dal\u0161\xED v\xFDsledky",search_label:"Prohledat tuto str\xE1nku",filters_label:"Filtry",zero_results:"\u017D\xE1dn\xE9 v\xFDsledky pro [SEARCH_TERM]",many_results:"[COUNT] v\xFDsledk\u016F pro [SEARCH_TERM]",one_result:"[COUNT] v\xFDsledek pro [SEARCH_TERM]",total_zero_results:"\u017D\xE1dn\xE9 v\xFDsledky",total_one_result:"[COUNT] v\xFDsledek",total_many_results:"[COUNT] v\xFDsledk\u016F",alt_search:"\u017D\xE1dn\xE9 v\xFDsledky pro [SEARCH_TERM]. Zobrazuj\xED se v\xFDsledky pro [DIFFERENT_TERM]",search_suggestion:"\u017D\xE1dn\xE9 v\xFDsledky pro [SEARCH_TERM]. Souvisej\xEDc\xED v\xFDsledky hled\xE1n\xED:",searching:"Hled\xE1m [SEARCH_TERM]...",results_label:"V\xFDsledky hled\xE1n\xED",keyboard_navigate:"navigovat",keyboard_select:"vybrat",keyboard_clear:"smazat",keyboard_close:"zav\u0159\xEDt",keyboard_search:"hledat",error_search:"Hled\xE1n\xED selhalo",filter_selected_one:"[COUNT] vybran\xFD",filter_selected_many:"[COUNT] vybran\xFDch",input_hint:"V\xFDsledky se zobraz\xED b\u011Bhem psan\xED",loading:"Na\u010D\xEDt\xE1n\xED"},za={thanks_to:Ha,comments:Ma,direction:Ya,strings:wa};var vt={};x(vt,{comments:()=>Oa,default:()=>Pa,direction:()=>ja,strings:()=>Ka,thanks_to:()=>Ja});var Ja="Jonas Smedegaard ",Oa="",ja="ltr",Ka={placeholder:"S\xF8g",clear_search:"Nulstil",load_more:"Indl\xE6s flere resultater",search_label:"S\xF8g p\xE5 dette website",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",total_zero_results:"Ingen resultater",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultater",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v et af disse s\xF8geord i stedet:",searching:"S\xF8ger efter [SEARCH_TERM]...",results_label:"S\xF8geresultater",keyboard_navigate:"naviger",keyboard_select:"v\xE6lg",keyboard_clear:"ryd",keyboard_close:"luk",keyboard_search:"s\xF8g",error_search:"S\xF8gning mislykkedes",filter_selected_one:"[COUNT] valgt",filter_selected_many:"[COUNT] valgte",input_hint:"Resultater vises mens du skriver",loading:"Indl\xE6ser"},Pa={thanks_to:Ja,comments:Oa,direction:ja,strings:Ka};var Ht={};x(Ht,{comments:()=>$a,default:()=>lr,direction:()=>er,strings:()=>tr,thanks_to:()=>qa});var qa="Jan Claasen ",$a="",er="ltr",tr={placeholder:"Suche",clear_search:"L\xF6schen",load_more:"Mehr Ergebnisse laden",search_label:"Suche diese Seite",filters_label:"Filter",zero_results:"Keine Ergebnisse f\xFCr [SEARCH_TERM]",many_results:"[COUNT] Ergebnisse f\xFCr [SEARCH_TERM]",one_result:"[COUNT] Ergebnis f\xFCr [SEARCH_TERM]",total_zero_results:"Keine Ergebnisse",total_one_result:"[COUNT] Ergebnis",total_many_results:"[COUNT] Ergebnisse",alt_search:"Keine Ergebnisse f\xFCr [SEARCH_TERM]. Stattdessen werden Ergebnisse f\xFCr [DIFFERENT_TERM] angezeigt",search_suggestion:"Keine Ergebnisse f\xFCr [SEARCH_TERM]. Versuchen Sie eine der folgenden Suchen:",searching:"Suche nach [SEARCH_TERM]\u202F\u2026",results_label:"Suchergebnisse",keyboard_navigate:"navigieren",keyboard_select:"ausw\xE4hlen",keyboard_clear:"l\xF6schen",keyboard_close:"schlie\xDFen",keyboard_search:"suchen",error_search:"Suche fehlgeschlagen",filter_selected_one:"[COUNT] ausgew\xE4hlt",filter_selected_many:"[COUNT] ausgew\xE4hlt",input_hint:"Ergebnisse werden w\xE4hrend der Eingabe angezeigt",loading:"Wird geladen"},lr={thanks_to:qa,comments:$a,direction:er,strings:tr};var Mt={};x(Mt,{comments:()=>ar,default:()=>nr,direction:()=>rr,strings:()=>ir,thanks_to:()=>sr});var sr="George Papadopoulos",ar="",rr="ltr",ir={placeholder:"\u0391\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7",clear_search:"\u039A\u03B1\u03B8\u03B1\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2",load_more:"\u03A6\u03CC\u03C1\u03C4\u03C9\u03C3\u03B7 \u03C0\u03B5\u03C1\u03B9\u03C3\u03C3\u03CC\u03C4\u03B5\u03C1\u03C9\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03B5\u03C3\u03BC\u03AC\u03C4\u03C9\u03BD",search_label:"\u0391\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7 \u03C3\u03B5 \u03B1\u03C5\u03C4\u03CC\u03BD \u03C4\u03BF\u03BD \u03B9\u03C3\u03C4\u03CC\u03C4\u03BF\u03C0\u03BF",filters_label:"\u03A6\u03AF\u03BB\u03C4\u03C1\u03B1",zero_results:"\u0394\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]",many_results:"[COUNT] \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]",one_result:"[COUNT] \u03B1\u03C0\u03BF\u03C4\u03AD\u03BB\u03B5\u03C3\u03BC\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]",total_zero_results:"\u0394\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1",total_one_result:"[COUNT] \u03B1\u03C0\u03BF\u03C4\u03AD\u03BB\u03B5\u03C3\u03BC\u03B1",total_many_results:"[COUNT] \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1",alt_search:"\u0394\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]. \u0395\u03BC\u03C6\u03B1\u03BD\u03AF\u03B6\u03BF\u03BD\u03C4\u03B1\u03B9 \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [DIFFERENT_TERM]",search_suggestion:"\u0394\u03B5\u03BD \u03B2\u03C1\u03AD\u03B8\u03B7\u03BA\u03B1\u03BD \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B3\u03B9\u03B1 [SEARCH_TERM]. \u0394\u03BF\u03BA\u03B9\u03BC\u03AC\u03C3\u03C4\u03B5 \u03BC\u03AF\u03B1 \u03B1\u03C0\u03CC \u03C4\u03B9\u03C2 \u03C0\u03B1\u03C1\u03B1\u03BA\u03AC\u03C4\u03C9 \u03B1\u03BD\u03B1\u03B6\u03B7\u03C4\u03AE\u03C3\u03B5\u03B9\u03C2:",searching:"\u0391\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7 \u03B3\u03B9\u03B1 [SEARCH_TERM]...",results_label:"\u0391\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B1\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7\u03C2",keyboard_navigate:"\u03C0\u03BB\u03BF\u03AE\u03B3\u03B7\u03C3\u03B7",keyboard_select:"\u03B5\u03C0\u03B9\u03BB\u03BF\u03B3\u03AE",keyboard_clear:"\u03BA\u03B1\u03B8\u03B1\u03C1\u03B9\u03C3\u03BC\u03CC\u03C2",keyboard_close:"\u03BA\u03BB\u03B5\u03AF\u03C3\u03B9\u03BC\u03BF",keyboard_search:"\u03B1\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7",error_search:"\u0397 \u03B1\u03BD\u03B1\u03B6\u03AE\u03C4\u03B7\u03C3\u03B7 \u03B1\u03C0\u03AD\u03C4\u03C5\u03C7\u03B5",filter_selected_one:"[COUNT] \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03BF",filter_selected_many:"[COUNT] \u03B5\u03C0\u03B9\u03BB\u03B5\u03B3\u03BC\u03AD\u03BD\u03B1",input_hint:"\u03A4\u03B1 \u03B1\u03C0\u03BF\u03C4\u03B5\u03BB\u03AD\u03C3\u03BC\u03B1\u03C4\u03B1 \u03B8\u03B1 \u03B5\u03BC\u03C6\u03B1\u03BD\u03AF\u03B6\u03BF\u03BD\u03C4\u03B1\u03B9 \u03BA\u03B1\u03B8\u03CE\u03C2 \u03C0\u03BB\u03B7\u03BA\u03C4\u03C1\u03BF\u03BB\u03BF\u03B3\u03B5\u03AF\u03C4\u03B5",loading:"\u03A6\u03CC\u03C1\u03C4\u03C9\u03C3\u03B7"},nr={thanks_to:sr,comments:ar,direction:rr,strings:ir};var Yt={};x(Yt,{comments:()=>or,default:()=>Cr,direction:()=>cr,strings:()=>ur,thanks_to:()=>gr});var gr="Liam Bigelow ",or="",cr="ltr",ur={placeholder:"Search",clear_search:"Clear",load_more:"Load more results",search_label:"Search this site",filters_label:"Filters",zero_results:"No results for [SEARCH_TERM]",many_results:"[COUNT] results for [SEARCH_TERM]",one_result:"[COUNT] result for [SEARCH_TERM]",total_zero_results:"No results",total_one_result:"[COUNT] result",total_many_results:"[COUNT] results",alt_search:"No results for [SEARCH_TERM]. Showing results for [DIFFERENT_TERM] instead",search_suggestion:"No results for [SEARCH_TERM]. Try one of the following searches:",searching:"Searching for [SEARCH_TERM]...",results_label:"Search results",keyboard_navigate:"navigate",keyboard_select:"select",keyboard_clear:"clear",keyboard_close:"close",keyboard_search:"search",error_search:"Search failed",filter_selected_one:"[COUNT] selected",filter_selected_many:"[COUNT] selected",input_hint:"Results will appear as you type",loading:"Loading"},Cr={thanks_to:gr,comments:or,direction:cr,strings:ur};var wt={};x(wt,{comments:()=>dr,default:()=>Qr,direction:()=>Ar,strings:()=>_r,thanks_to:()=>Ir});var Ir="Pablo Villaverde ",dr="",Ar="ltr",_r={placeholder:"Buscar",clear_search:"Limpiar",load_more:"Ver m\xE1s resultados",search_label:"Buscar en este sitio",filters_label:"Filtros",zero_results:"No se encontraron resultados para [SEARCH_TERM]",many_results:"[COUNT] resultados encontrados para [SEARCH_TERM]",one_result:"[COUNT] resultado encontrado para [SEARCH_TERM]",total_zero_results:"Sin resultados",total_one_result:"[COUNT] resultado",total_many_results:"[COUNT] resultados",alt_search:"No se encontraron resultados para [SEARCH_TERM]. Mostrando en su lugar resultados para [DIFFERENT_TERM]",search_suggestion:"No se encontraron resultados para [SEARCH_TERM]. Prueba una de las siguientes b\xFAsquedas:",searching:"Buscando [SEARCH_TERM]...",results_label:"Resultados de b\xFAsqueda",keyboard_navigate:"navegar",keyboard_select:"elegir",keyboard_clear:"limpiar",keyboard_close:"cerrar",keyboard_search:"buscar",error_search:"Error en la b\xFAsqueda",filter_selected_one:"[COUNT] seleccionado",filter_selected_many:"[COUNT] seleccionados",input_hint:"Los resultados aparecer\xE1n mientras escribe",loading:"Cargando"},Qr={thanks_to:Ir,comments:dr,direction:Ar,strings:_r};var zt={};x(zt,{comments:()=>br,default:()=>pr,direction:()=>fr,strings:()=>Fr,thanks_to:()=>mr});var mr="Mikel Larreategi ",br="",fr="ltr",Fr={placeholder:"Bilatu",clear_search:"Garbitu",load_more:"Kargatu emaitza gehiagi",search_label:"Bilatu",filters_label:"Iragazkiak",zero_results:"Ez dago emaitzarik [SEARCH_TERM] bilaketarentzat",many_results:"[COUNT] emaitza [SEARCH_TERM] bilaketarentzat",one_result:"Emaitza bat [COUNT] [SEARCH_TERM] bilaketarentzat",total_zero_results:"Emaitzarik ez",total_one_result:"[COUNT] emaitza",total_many_results:"[COUNT] emaitza",alt_search:"Ez dago emaitzarik [SEARCH_TERM] bilaketarentzat. [DIFFERENT_TERM] bilaketaren emaitzak erakusten",search_suggestion:"Ez dago emaitzarik [SEARCH_TERM] bilaketarentzat. Saiatu hauetako beste bateikin:",searching:"[SEARCH_TERM] bilatzen...",results_label:"Bilaketaren emaitzak",keyboard_navigate:"nabigatu",keyboard_select:"hautatu",keyboard_clear:"garbitu",keyboard_close:"itxi",keyboard_search:"bilatu",error_search:"Bilaketak huts egin du",filter_selected_one:"[COUNT] hautatuta",filter_selected_many:"[COUNT] hautatuta",input_hint:"Emaitzak idatzi ahala agertuko dira",loading:"Kargatzen"},pr={thanks_to:mr,comments:br,direction:fr,strings:Fr};var Jt={};x(Jt,{comments:()=>hr,default:()=>yr,direction:()=>Ur,strings:()=>xr,thanks_to:()=>Br});var Br="Ali Khaleqi Yekta ",hr="",Ur="rtl",xr={placeholder:"\u062C\u0633\u062A\u062C\u0648",clear_search:"\u067E\u0627\u06A9\u0633\u0627\u0632\u06CC",load_more:"\u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC \u0646\u062A\u0627\u06CC\u062C \u0628\u06CC\u0634\u062A\u0631",search_label:"\u062C\u0633\u062A\u062C\u0648 \u062F\u0631 \u0633\u0627\u06CC\u062A",filters_label:"\u0641\u06CC\u0644\u062A\u0631\u0647\u0627",zero_results:"\u0646\u062A\u06CC\u062C\u0647\u200C\u0627\u06CC \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0646\u0634\u062F",many_results:"[COUNT] \u0646\u062A\u06CC\u062C\u0647 \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0634\u062F",one_result:"[COUNT] \u0646\u062A\u06CC\u062C\u0647 \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0634\u062F",total_zero_results:"\u0646\u062A\u06CC\u062C\u0647\u200C\u0627\u06CC \u06CC\u0627\u0641\u062A \u0646\u0634\u062F",total_one_result:"[COUNT] \u0646\u062A\u06CC\u062C\u0647",total_many_results:"[COUNT] \u0646\u062A\u06CC\u062C\u0647",alt_search:"\u0646\u062A\u06CC\u062C\u0647\u200C\u0627\u06CC \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0646\u0634\u062F. \u062F\u0631 \u0639\u0648\u0636 \u0646\u062A\u0627\u06CC\u062C \u0628\u0631\u0627\u06CC [DIFFERENT_TERM] \u0646\u0645\u0627\u06CC\u0634 \u062F\u0627\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u062F",search_suggestion:"\u0646\u062A\u06CC\u062C\u0647\u200C\u0627\u06CC \u0628\u0631\u0627\u06CC [SEARCH_TERM] \u06CC\u0627\u0641\u062A \u0646\u0634\u062F. \u06CC\u06A9\u06CC \u0627\u0632 \u062C\u0633\u062A\u062C\u0648\u0647\u0627\u06CC \u0632\u06CC\u0631 \u0631\u0627 \u0627\u0645\u062A\u062D\u0627\u0646 \u06A9\u0646\u06CC\u062F:",searching:"\u062F\u0631 \u062D\u0627\u0644 \u062C\u0633\u062A\u062C\u0648\u06CC [SEARCH_TERM]...",results_label:"\u0646\u062A\u0627\u06CC\u062C \u062C\u0633\u062A\u062C\u0648",keyboard_navigate:"\u067E\u06CC\u0645\u0627\u06CC\u0634",keyboard_select:"\u0627\u0646\u062A\u062E\u0627\u0628",keyboard_clear:"\u067E\u0627\u06A9\u0633\u0627\u0632\u06CC",keyboard_close:"\u0628\u0633\u062A\u0646",keyboard_search:"\u062C\u0633\u062A\u062C\u0648",error_search:"\u062C\u0633\u062A\u062C\u0648 \u0646\u0627\u0645\u0648\u0641\u0642 \u0628\u0648\u062F",filter_selected_one:"[COUNT] \u0627\u0646\u062A\u062E\u0627\u0628 \u0634\u062F\u0647",filter_selected_many:"[COUNT] \u0627\u0646\u062A\u062E\u0627\u0628 \u0634\u062F\u0647",input_hint:"\u0646\u062A\u0627\u06CC\u062C \u0647\u0646\u06AF\u0627\u0645 \u062A\u0627\u06CC\u067E \u0646\u0645\u0627\u06CC\u0634 \u062F\u0627\u062F\u0647 \u0645\u06CC\u200C\u0634\u0648\u0646\u062F",loading:"\u062F\u0631 \u062D\u0627\u0644 \u0628\u0627\u0631\u06AF\u0630\u0627\u0631\u06CC"},yr={thanks_to:Br,comments:hr,direction:Ur,strings:xr};var Ot={};x(Ot,{comments:()=>Gr,default:()=>Er,direction:()=>Nr,strings:()=>Xr,thanks_to:()=>Zr});var Zr="Valtteri Laitinen ",Gr="",Nr="ltr",Xr={placeholder:"Haku",clear_search:"Tyhjenn\xE4",load_more:"Lataa lis\xE4\xE4 tuloksia",search_label:"Hae t\xE4lt\xE4 sivustolta",filters_label:"Suodattimet",zero_results:"Ei tuloksia haulle [SEARCH_TERM]",many_results:"[COUNT] tulosta haulle [SEARCH_TERM]",one_result:"[COUNT] tulos haulle [SEARCH_TERM]",total_zero_results:"Ei tuloksia",total_one_result:"[COUNT] tulos",total_many_results:"[COUNT] tulosta",alt_search:"Ei tuloksia haulle [SEARCH_TERM]. N\xE4ytet\xE4\xE4n tulokset sen sijaan haulle [DIFFERENT_TERM]",search_suggestion:"Ei tuloksia haulle [SEARCH_TERM]. Kokeile jotain seuraavista:",searching:"Haetaan [SEARCH_TERM]...",results_label:"Hakutulokset",keyboard_navigate:"siirry",keyboard_select:"valitse",keyboard_clear:"tyhjenn\xE4",keyboard_close:"sulje",keyboard_search:"hae",error_search:"Haku ep\xE4onnistui",filter_selected_one:"[COUNT] valittu",filter_selected_many:"[COUNT] valittu",input_hint:"Tulokset n\xE4kyv\xE4t kirjoittaessasi",loading:"Ladataan"},Er={thanks_to:Zr,comments:Gr,direction:Nr,strings:Xr};var jt={};x(jt,{comments:()=>Lr,default:()=>Sr,direction:()=>Rr,strings:()=>Wr,thanks_to:()=>kr});var kr="Nicolas Friedli ",Lr="",Rr="ltr",Wr={placeholder:"Rechercher",clear_search:"Nettoyer",load_more:"Charger plus de r\xE9sultats",search_label:"Recherche sur ce site",filters_label:"Filtres",zero_results:"Pas de r\xE9sultat pour [SEARCH_TERM]",many_results:"[COUNT] r\xE9sultats pour [SEARCH_TERM]",one_result:"[COUNT] r\xE9sultat pour [SEARCH_TERM]",total_zero_results:"Pas de r\xE9sultat",total_one_result:"[COUNT] r\xE9sultat",total_many_results:"[COUNT] r\xE9sultats",alt_search:"Pas de r\xE9sultat pour [SEARCH_TERM]. Montre les r\xE9sultats pour [DIFFERENT_TERM] \xE0 la place",search_suggestion:"Pas de r\xE9sultat pour [SEARCH_TERM]. Essayer une des recherches suivantes:",searching:"Recherche [SEARCH_TERM]...",results_label:"R\xE9sultats de recherche",keyboard_navigate:"naviguer",keyboard_select:"choisir",keyboard_clear:"effacer",keyboard_close:"fermer",keyboard_search:"rechercher",error_search:"\xC9chec de la recherche",filter_selected_one:"[COUNT] s\xE9lectionn\xE9",filter_selected_many:"[COUNT] s\xE9lectionn\xE9s",input_hint:"Les r\xE9sultats appara\xEEtront au fur et \xE0 mesure de la saisie",loading:"Chargement"},Sr={thanks_to:kr,comments:Lr,direction:Rr,strings:Wr};var Kt={};x(Kt,{comments:()=>Dr,default:()=>Hr,direction:()=>Tr,strings:()=>vr,thanks_to:()=>Vr});var Vr="Pablo Villaverde ",Dr="",Tr="ltr",vr={placeholder:"Buscar",clear_search:"Limpar",load_more:"Ver m\xE1is resultados",search_label:"Buscar neste sitio",filters_label:"Filtros",zero_results:"Non se atoparon resultados para [SEARCH_TERM]",many_results:"[COUNT] resultados atopados para [SEARCH_TERM]",one_result:"[COUNT] resultado atopado para [SEARCH_TERM]",total_zero_results:"Sen resultados",total_one_result:"[COUNT] resultado",total_many_results:"[COUNT] resultados",alt_search:"Non se atoparon resultados para [SEARCH_TERM]. Amosando no seu lugar resultados para [DIFFERENT_TERM]",search_suggestion:"Non se atoparon resultados para [SEARCH_TERM]. Probe unha das seguintes pesquisas:",searching:"Buscando [SEARCH_TERM]...",results_label:"Resultados da busca",keyboard_navigate:"navegar",keyboard_select:"escoller",keyboard_clear:"limpar",keyboard_close:"pechar",keyboard_search:"buscar",error_search:"Erro na busca",filter_selected_one:"[COUNT] seleccionado",filter_selected_many:"[COUNT] seleccionados",input_hint:"Os resultados aparecer\xE1n mentres escribe",loading:"Cargando"},Hr={thanks_to:Vr,comments:Dr,direction:Tr,strings:vr};var Pt={};x(Pt,{comments:()=>Yr,default:()=>Jr,direction:()=>wr,strings:()=>zr,thanks_to:()=>Mr});var Mr="Nir Tamir ",Yr="",wr="rtl",zr={placeholder:"\u05D7\u05D9\u05E4\u05D5\u05E9",clear_search:"\u05E0\u05D9\u05E7\u05D5\u05D9",load_more:"\u05E2\u05D5\u05D3 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA",search_label:"\u05D7\u05D9\u05E4\u05D5\u05E9 \u05D1\u05D0\u05EA\u05E8 \u05D6\u05D4",filters_label:"\u05DE\u05E1\u05E0\u05E0\u05D9\u05DD",zero_results:"\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]",many_results:"\u05E0\u05DE\u05E6\u05D0\u05D5 [COUNT] \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]",one_result:"\u05E0\u05DE\u05E6\u05D0\u05D4 \u05EA\u05D5\u05E6\u05D0\u05D4 \u05D0\u05D7\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]",total_zero_results:"\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA",total_one_result:"\u05EA\u05D5\u05E6\u05D0\u05D4 [COUNT]",total_many_results:"[COUNT] \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA",alt_search:"\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]. \u05DE\u05D5\u05E6\u05D2\u05D5\u05EA \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [DIFFERENT_TERM]",search_suggestion:"\u05DC\u05D0 \u05E0\u05DE\u05E6\u05D0\u05D5 \u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05E2\u05D1\u05D5\u05E8 [SEARCH_TERM]. \u05E0\u05E1\u05D5 \u05D0\u05D7\u05D3 \u05DE\u05D4\u05D7\u05D9\u05E4\u05D5\u05E9\u05D9\u05DD \u05D4\u05D1\u05D0\u05D9\u05DD:",searching:"\u05DE\u05D7\u05E4\u05E9 \u05D0\u05EA [SEARCH_TERM]...",results_label:"\u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05D7\u05D9\u05E4\u05D5\u05E9",keyboard_navigate:"\u05E0\u05D9\u05D5\u05D5\u05D8",keyboard_select:"\u05D1\u05D7\u05D9\u05E8\u05D4",keyboard_clear:"\u05E0\u05D9\u05E7\u05D5\u05D9",keyboard_close:"\u05E1\u05D2\u05D9\u05E8\u05D4",keyboard_search:"\u05D7\u05D9\u05E4\u05D5\u05E9",error_search:"\u05D4\u05D7\u05D9\u05E4\u05D5\u05E9 \u05E0\u05DB\u05E9\u05DC",filter_selected_one:"[COUNT] \u05E0\u05D1\u05D7\u05E8",filter_selected_many:"[COUNT] \u05E0\u05D1\u05D7\u05E8\u05D5",input_hint:"\u05D4\u05EA\u05D5\u05E6\u05D0\u05D5\u05EA \u05D9\u05D5\u05E4\u05D9\u05E2\u05D5 \u05EA\u05D5\u05DA \u05DB\u05D3\u05D9 \u05D4\u05E7\u05DC\u05D3\u05D4",loading:"\u05D8\u05D5\u05E2\u05DF"},Jr={thanks_to:Mr,comments:Yr,direction:wr,strings:zr};var qt={};x(qt,{comments:()=>jr,default:()=>qr,direction:()=>Kr,strings:()=>Pr,thanks_to:()=>Or});var Or="Amit Yadav ",jr="",Kr="ltr",Pr={placeholder:"\u0916\u094B\u091C\u0947\u0902",clear_search:"\u0938\u093E\u092B \u0915\u0930\u0947\u0902",load_more:"\u0914\u0930 \u0905\u0927\u093F\u0915 \u092A\u0930\u093F\u0923\u093E\u092E \u0932\u094B\u0921 \u0915\u0930\u0947\u0902",search_label:"\u0907\u0938 \u0938\u093E\u0907\u091F \u092E\u0947\u0902 \u0916\u094B\u091C\u0947\u0902",filters_label:"\u092B\u093C\u093F\u0932\u094D\u091F\u0930",zero_results:"\u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E",many_results:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u092E\u093F\u0932\u0947",one_result:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E [SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u092E\u093F\u0932\u093E",total_zero_results:"\u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902",total_one_result:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E",total_many_results:"[COUNT] \u092A\u0930\u093F\u0923\u093E\u092E",alt_search:"[SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964 \u0907\u0938\u0915\u0947 \u092C\u091C\u093E\u092F [DIFFERENT_TERM] \u0915\u0947 \u0932\u093F\u090F \u092A\u0930\u093F\u0923\u093E\u092E \u0926\u093F\u0916\u093E \u0930\u0939\u093E \u0939\u0948",search_suggestion:"[SEARCH_TERM] \u0915\u0947 \u0932\u093F\u090F \u0915\u094B\u0908 \u092A\u0930\u093F\u0923\u093E\u092E \u0928\u0939\u0940\u0902 \u092E\u093F\u0932\u093E\u0964 \u0928\u093F\u092E\u094D\u0928\u0932\u093F\u0916\u093F\u0924 \u0916\u094B\u091C\u094B\u0902 \u092E\u0947\u0902 \u0938\u0947 \u0915\u094B\u0908 \u090F\u0915 \u0906\u091C\u093C\u092E\u093E\u090F\u0902:",searching:"[SEARCH_TERM] \u0915\u0940 \u0916\u094B\u091C \u0915\u0940 \u091C\u093E \u0930\u0939\u0940 \u0939\u0948...",results_label:"\u0916\u094B\u091C \u092A\u0930\u093F\u0923\u093E\u092E",keyboard_navigate:"\u0928\u0947\u0935\u093F\u0917\u0947\u091F",keyboard_select:"\u091A\u0941\u0928\u0947\u0902",keyboard_clear:"\u0938\u093E\u092B\u093C \u0915\u0930\u0947\u0902",keyboard_close:"\u092C\u0902\u0926 \u0915\u0930\u0947\u0902",keyboard_search:"\u0916\u094B\u091C\u0947\u0902",error_search:"\u0916\u094B\u091C \u0935\u093F\u092B\u0932",filter_selected_one:"[COUNT] \u091A\u092F\u0928\u093F\u0924",filter_selected_many:"[COUNT] \u091A\u092F\u0928\u093F\u0924",input_hint:"\u091F\u093E\u0907\u092A \u0915\u0930\u0924\u0947 \u0938\u092E\u092F \u092A\u0930\u093F\u0923\u093E\u092E \u0926\u093F\u0916\u093E\u0908 \u0926\u0947\u0902\u0917\u0947",loading:"\u0932\u094B\u0921 \u0939\u094B \u0930\u0939\u093E \u0939\u0948"},qr={thanks_to:Or,comments:jr,direction:Kr,strings:Pr};var $t={};x($t,{comments:()=>ei,default:()=>si,direction:()=>ti,strings:()=>li,thanks_to:()=>$r});var $r="Diomed ",ei="",ti="ltr",li={placeholder:"Tra\u017Ei",clear_search:"O\u010Disti",load_more:"U\u010Ditaj vi\u0161e rezultata",search_label:"Pretra\u017Ei ovu stranicu",filters_label:"Filteri",zero_results:"Nema rezultata za [SEARCH_TERM]",many_results:"[COUNT] rezultata za [SEARCH_TERM]",one_result:"[COUNT] rezultat za [SEARCH_TERM]",total_zero_results:"Nema rezultata",total_one_result:"[COUNT] rezultat",total_many_results:"[COUNT] rezultata",alt_search:"Nema rezultata za [SEARCH_TERM]. Prikazujem rezultate za [DIFFERENT_TERM]",search_suggestion:"Nema rezultata za [SEARCH_TERM]. Poku\u0161aj s jednom od ovih pretraga:",searching:"Pretra\u017Eujem [SEARCH_TERM]...",results_label:"Rezultati pretrage",keyboard_navigate:"navigiraj",keyboard_select:"odaberi",keyboard_clear:"o\u010Disti",keyboard_close:"zatvori",keyboard_search:"tra\u017Ei",error_search:"Pretraga nije uspjela",filter_selected_one:"[COUNT] odabran",filter_selected_many:"[COUNT] odabranih",input_hint:"Rezultati \u0107e se pojaviti dok tipkate",loading:"U\u010Ditavanje"},si={thanks_to:$r,comments:ei,direction:ti,strings:li};var el={};x(el,{comments:()=>ri,default:()=>gi,direction:()=>ii,strings:()=>ni,thanks_to:()=>ai});var ai="Adam Laki ",ri="",ii="ltr",ni={placeholder:"Keres\xE9s",clear_search:"T\xF6rl\xE9s",load_more:"Tov\xE1bbi tal\xE1latok bet\xF6lt\xE9se",search_label:"Keres\xE9s az oldalon",filters_label:"Sz\u0171r\xE9s",zero_results:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",many_results:"[COUNT] db tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",one_result:"[COUNT] db tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre",total_zero_results:"Nincs tal\xE1lat",total_one_result:"[COUNT] tal\xE1lat",total_many_results:"[COUNT] tal\xE1lat",alt_search:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre. Tal\xE1latok mutat\xE1sa ink\xE1bb a(z) [DIFFERENT_TERM] kifejez\xE9sre",search_suggestion:"Nincs tal\xE1lat a(z) [SEARCH_TERM] kifejez\xE9sre. Pr\xF3b\xE1ld meg a k\xF6vetkez\u0151 keres\xE9sek egyik\xE9t:",searching:"Keres\xE9s a(z) [SEARCH_TERM] kifejez\xE9sre...",results_label:"Keres\xE9si tal\xE1latok",keyboard_navigate:"navig\xE1l\xE1s",keyboard_select:"kiv\xE1laszt\xE1s",keyboard_clear:"t\xF6rl\xE9s",keyboard_close:"bez\xE1r\xE1s",keyboard_search:"keres\xE9s",error_search:"A keres\xE9s sikertelen",filter_selected_one:"[COUNT] kiv\xE1lasztva",filter_selected_many:"[COUNT] kiv\xE1lasztva",input_hint:"A tal\xE1latok g\xE9pel\xE9s k\xF6zben jelennek meg",loading:"Bet\xF6lt\xE9s"},gi={thanks_to:ai,comments:ri,direction:ii,strings:ni};var tl={};x(tl,{comments:()=>ci,default:()=>Ii,direction:()=>ui,strings:()=>Ci,thanks_to:()=>oi});var oi="Nixentric",ci="",ui="ltr",Ci={placeholder:"Cari",clear_search:"Bersihkan",load_more:"Muat lebih banyak hasil",search_label:"Telusuri situs ini",filters_label:"Filter",zero_results:"[SEARCH_TERM] tidak ditemukan",many_results:"Ditemukan [COUNT] hasil untuk [SEARCH_TERM]",one_result:"Ditemukan [COUNT] hasil untuk [SEARCH_TERM]",total_zero_results:"Tidak ada hasil",total_one_result:"[COUNT] hasil",total_many_results:"[COUNT] hasil",alt_search:"[SEARCH_TERM] tidak ditemukan. Menampilkan hasil [DIFFERENT_TERM] sebagai gantinya",search_suggestion:"[SEARCH_TERM] tidak ditemukan. Coba salah satu pencarian berikut ini:",searching:"Mencari [SEARCH_TERM]...",results_label:"Hasil pencarian",keyboard_navigate:"navigasi",keyboard_select:"pilih",keyboard_clear:"bersihkan",keyboard_close:"tutup",keyboard_search:"cari",error_search:"Pencarian gagal",filter_selected_one:"[COUNT] dipilih",filter_selected_many:"[COUNT] dipilih",input_hint:"Hasil akan muncul saat Anda mengetik",loading:"Memuat"},Ii={thanks_to:oi,comments:ci,direction:ui,strings:Ci};var ll={};x(ll,{comments:()=>Ai,default:()=>mi,direction:()=>_i,strings:()=>Qi,thanks_to:()=>di});var di="Cosette Bruhns Alonso, Andrew Janco ",Ai="",_i="ltr",Qi={placeholder:"Cerca",clear_search:"Cancella la cronologia",load_more:"Mostra pi\xF9 risultati",search_label:"Cerca nel sito",filters_label:"Filtri di ricerca",zero_results:"Nessun risultato per [SEARCH_TERM]",many_results:"[COUNT] risultati per [SEARCH_TERM]",one_result:"[COUNT] risultato per [SEARCH_TERM]",total_zero_results:"Nessun risultato",total_one_result:"[COUNT] risultato",total_many_results:"[COUNT] risultati",alt_search:"Nessun risultato per [SEARCH_TERM]. Mostrando risultati per [DIFFERENT_TERM] come alternativa.",search_suggestion:"Nessun risultato per [SEARCH_TERM]. Prova una delle seguenti ricerche:",searching:"Cercando [SEARCH_TERM]...",results_label:"Risultati della ricerca",keyboard_navigate:"naviga",keyboard_select:"seleziona",keyboard_clear:"cancella",keyboard_close:"chiudi",keyboard_search:"cerca",error_search:"Ricerca fallita",filter_selected_one:"[COUNT] selezionato",filter_selected_many:"[COUNT] selezionati",input_hint:"I risultati appariranno durante la digitazione",loading:"Caricamento"},mi={thanks_to:di,comments:Ai,direction:_i,strings:Qi};var sl={};x(sl,{comments:()=>fi,default:()=>Bi,direction:()=>Fi,strings:()=>pi,thanks_to:()=>bi});var bi="Tate",fi="",Fi="ltr",pi={placeholder:"\u691C\u7D22",clear_search:"\u30AF\u30EA\u30A2",load_more:"\u6B21\u3092\u8AAD\u307F\u8FBC\u3080",search_label:"\u3053\u306E\u30B5\u30A4\u30C8\u3092\u691C\u7D22",filters_label:"\u30D5\u30A3\u30EB\u30BF",zero_results:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u60C5\u5831\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F",many_results:"[SEARCH_TERM]\u306E[COUNT]\u4EF6\u306E\u691C\u7D22\u7D50\u679C",one_result:"[SEARCH_TERM]\u306E[COUNT]\u4EF6\u306E\u691C\u7D22\u7D50\u679C",total_zero_results:"\u7D50\u679C\u306A\u3057",total_one_result:"[COUNT]\u4EF6\u306E\u7D50\u679C",total_many_results:"[COUNT]\u4EF6\u306E\u7D50\u679C",alt_search:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u60C5\u5831\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002[DIFFERENT_TERM]\u306E\u691C\u7D22\u7D50\u679C\u3092\u8868\u793A\u3057\u3066\u3044\u307E\u3059",search_suggestion:"[SEARCH_TERM]\u306E\u691C\u7D22\u306B\u4E00\u81F4\u3059\u308B\u60C5\u5831\u306F\u3042\u308A\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u6B21\u306E\u3044\u305A\u308C\u304B\u306E\u691C\u7D22\u3092\u8A66\u3057\u3066\u304F\u3060\u3055\u3044",searching:"[SEARCH_TERM]\u3092\u691C\u7D22\u3057\u3066\u3044\u307E\u3059",results_label:"\u691C\u7D22\u7D50\u679C",keyboard_navigate:"\u79FB\u52D5",keyboard_select:"\u9078\u629E",keyboard_clear:"\u30AF\u30EA\u30A2",keyboard_close:"\u9589\u3058\u308B",keyboard_search:"\u691C\u7D22",error_search:"\u691C\u7D22\u306B\u5931\u6557\u3057\u307E\u3057\u305F",filter_selected_one:"[COUNT]\u4EF6\u9078\u629E\u4E2D",filter_selected_many:"[COUNT]\u4EF6\u9078\u629E\u4E2D",input_hint:"\u5165\u529B\u4E2D\u306B\u691C\u7D22\u7D50\u679C\u304C\u8868\u793A\u3055\u308C\u307E\u3059",loading:"\u8AAD\u307F\u8FBC\u307F\u4E2D"},Bi={thanks_to:bi,comments:fi,direction:Fi,strings:pi};var al={};x(al,{comments:()=>Ui,default:()=>Zi,direction:()=>xi,strings:()=>yi,thanks_to:()=>hi});var hi="Seokho Son ",Ui="",xi="ltr",yi={placeholder:"\uAC80\uC0C9\uC5B4",clear_search:"\uBE44\uC6B0\uAE30",load_more:"\uAC80\uC0C9 \uACB0\uACFC \uB354 \uBCF4\uAE30",search_label:"\uC0AC\uC774\uD2B8 \uAC80\uC0C9",filters_label:"\uD544\uD130",zero_results:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC \uC5C6\uC74C",many_results:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC [COUNT]\uAC74",one_result:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC [COUNT]\uAC74",total_zero_results:"\uACB0\uACFC \uC5C6\uC74C",total_one_result:"\uACB0\uACFC [COUNT]\uAC74",total_many_results:"\uACB0\uACFC [COUNT]\uAC74",alt_search:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC \uC5C6\uC74C. [DIFFERENT_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC",search_suggestion:"[SEARCH_TERM]\uC5D0 \uB300\uD55C \uACB0\uACFC \uC5C6\uC74C. \uCD94\uCC9C \uAC80\uC0C9\uC5B4: ",searching:"[SEARCH_TERM] \uAC80\uC0C9 \uC911...",results_label:"\uAC80\uC0C9 \uACB0\uACFC",keyboard_navigate:"\uC774\uB3D9",keyboard_select:"\uC120\uD0DD",keyboard_clear:"\uBE44\uC6B0\uAE30",keyboard_close:"\uB2EB\uAE30",keyboard_search:"\uAC80\uC0C9",error_search:"\uAC80\uC0C9 \uC2E4\uD328",filter_selected_one:"[COUNT]\uAC1C \uC120\uD0DD\uB428",filter_selected_many:"[COUNT]\uAC1C \uC120\uD0DD\uB428",input_hint:"\uC785\uB825\uD558\uB294 \uB3D9\uC548 \uACB0\uACFC\uAC00 \uD45C\uC2DC\uB429\uB2C8\uB2E4",loading:"\uB85C\uB529 \uC911"},Zi={thanks_to:hi,comments:Ui,direction:xi,strings:yi};var rl={};x(rl,{comments:()=>Ni,default:()=>ki,direction:()=>Xi,strings:()=>Ei,thanks_to:()=>Gi});var Gi="",Ni="",Xi="ltr",Ei={placeholder:"Rapu",clear_search:"Whakakore",load_more:"Whakauta \u0113tahi otinga k\u0113",search_label:"Rapu",filters_label:"T\u0101tari",zero_results:"Otinga kore ki [SEARCH_TERM]",many_results:"[COUNT] otinga ki [SEARCH_TERM]",one_result:"[COUNT] otinga ki [SEARCH_TERM]",total_zero_results:"K\u0101ore he otinga",total_one_result:"[COUNT] otinga",total_many_results:"[COUNT] ng\u0101 otinga",alt_search:"Otinga kore ki [SEARCH_TERM]. Otinga k\u0113 ki [DIFFERENT_TERM]",search_suggestion:"Otinga kore ki [SEARCH_TERM]. whakam\u0101tau ki ng\u0101 mea atu:",searching:"Rapu ki [SEARCH_TERM]...",results_label:"Ng\u0101 otinga rapu",keyboard_navigate:"whakatere",keyboard_select:"t\u012Bpako",keyboard_clear:"whakakore",keyboard_close:"kati",keyboard_search:"rapu",error_search:"K\u0101ore i eke te rapu",filter_selected_one:"[COUNT] kua t\u012Bpakohia",filter_selected_many:"[COUNT] kua t\u012Bpakohia",input_hint:"Ka puta ng\u0101 otinga i a koe e patopato ana",loading:"E uta ana"},ki={thanks_to:Gi,comments:Ni,direction:Xi,strings:Ei};var il={};x(il,{comments:()=>Ri,default:()=>Vi,direction:()=>Wi,strings:()=>Si,thanks_to:()=>Li});var Li="Harry Min Khant ",Ri="",Wi="ltr",Si={placeholder:"\u101B\u103E\u102C\u101B\u1014\u103A",clear_search:"\u101B\u103E\u102C\u1016\u103D\u1031\u1019\u103E\u102F\u1000\u102D\u102F \u101B\u103E\u1004\u103A\u1038\u101C\u1004\u103A\u1038\u1015\u102B\u104B",load_more:"\u1014\u1031\u102C\u1000\u103A\u1011\u1015\u103A\u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038\u1000\u102D\u102F \u1010\u1004\u103A\u1015\u102B\u104B",search_label:"\u1024\u1006\u102D\u102F\u1000\u103A\u1010\u103D\u1004\u103A\u101B\u103E\u102C\u1016\u103D\u1031\u1015\u102B\u104B",filters_label:"\u1005\u1005\u103A\u1011\u102F\u1010\u103A\u1019\u103E\u102F\u1019\u103B\u102C\u1038",zero_results:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038 \u1019\u101B\u103E\u102D\u1015\u102B",many_results:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A [COUNT] \u1001\u102F",one_result:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A [COUNT]",total_zero_results:"\u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038 \u1019\u101B\u103E\u102D\u1015\u102B",total_one_result:"\u101B\u101C\u1012\u103A [COUNT] \u1001\u102F",total_many_results:"\u101B\u101C\u1012\u103A [COUNT] \u1001\u102F",alt_search:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A\u1019\u101B\u103E\u102D\u1015\u102B\u104B \u104E\u1004\u103A\u1038\u1021\u1005\u102C\u1038 [DIFFERENT_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038\u1000\u102D\u102F \u1015\u103C\u101E\u101E\u100A\u103A\u104B",search_suggestion:"[SEARCH_TERM] \u1021\u1010\u103D\u1000\u103A \u101B\u101C\u1012\u103A\u1019\u101B\u103E\u102D\u1015\u102B\u104B \u1021\u1031\u102C\u1000\u103A\u1015\u102B\u101B\u103E\u102C\u1016\u103D\u1031\u1019\u103E\u102F\u1019\u103B\u102C\u1038\u1011\u1032\u1019\u103E \u1010\u1005\u103A\u1001\u102F\u1000\u102D\u102F \u1005\u1019\u103A\u1038\u1000\u103C\u100A\u1037\u103A\u1015\u102B:",searching:"[SEARCH_TERM] \u1000\u102D\u102F \u101B\u103E\u102C\u1016\u103D\u1031\u1014\u1031\u101E\u100A\u103A...",results_label:"\u101B\u103E\u102C\u1016\u103D\u1031\u1019\u103E\u102F \u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038",keyboard_navigate:"\u101C\u1019\u103A\u1038\u100A\u103D\u103E\u1014\u103A",keyboard_select:"\u101B\u103D\u1031\u1038\u1001\u103B\u101A\u103A",keyboard_clear:"\u101B\u103E\u1004\u103A\u1038\u101C\u1004\u103A\u1038",keyboard_close:"\u1015\u102D\u1010\u103A",keyboard_search:"\u101B\u103E\u102C\u101B\u1014\u103A",error_search:"\u101B\u103E\u102C\u1016\u103D\u1031\u1019\u103E\u102F \u1019\u1021\u1031\u102C\u1004\u103A\u1019\u103C\u1004\u103A\u1015\u102B",filter_selected_one:"[COUNT] \u1001\u102F \u101B\u103D\u1031\u1038\u1001\u103B\u101A\u103A\u1011\u102C\u1038\u101E\u100A\u103A",filter_selected_many:"[COUNT] \u1001\u102F \u101B\u103D\u1031\u1038\u1001\u103B\u101A\u103A\u1011\u102C\u1038\u101E\u100A\u103A",input_hint:"\u101B\u102D\u102F\u1000\u103A\u1014\u1031\u1005\u1009\u103A \u101B\u101C\u1012\u103A\u1019\u103B\u102C\u1038 \u1015\u1031\u102B\u103A\u101C\u102C\u1015\u102B\u1019\u100A\u103A",loading:"\u1010\u1004\u103A\u1014\u1031\u101E\u100A\u103A"},Vi={thanks_to:Li,comments:Ri,direction:Wi,strings:Si};var nl={};x(nl,{comments:()=>Ti,default:()=>Mi,direction:()=>vi,strings:()=>Hi,thanks_to:()=>Di});var Di="Eirik Mikkelsen",Ti="",vi="ltr",Hi={placeholder:"S\xF8k",clear_search:"Fjern",load_more:"Last flere resultater",search_label:"S\xF8k p\xE5 denne siden",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",total_zero_results:"Ingen resultater",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultater",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v en av disse s\xF8keordene i stedet:",searching:"S\xF8ker etter [SEARCH_TERM]",results_label:"S\xF8keresultater",keyboard_navigate:"naviger",keyboard_select:"velg",keyboard_clear:"fjern",keyboard_close:"lukk",keyboard_search:"s\xF8k",error_search:"S\xF8k feilet",filter_selected_one:"[COUNT] valgt",filter_selected_many:"[COUNT] valgte",input_hint:"Resultater vises mens du skriver",loading:"Laster"},Mi={thanks_to:Di,comments:Ti,direction:vi,strings:Hi};var gl={};x(gl,{comments:()=>wi,default:()=>Oi,direction:()=>zi,strings:()=>Ji,thanks_to:()=>Yi});var Yi="Paul van Brouwershaven",wi="",zi="ltr",Ji={placeholder:"Zoeken",clear_search:"Reset",load_more:"Meer resultaten laden",search_label:"Doorzoek deze site",filters_label:"Filters",zero_results:"Geen resultaten voor [SEARCH_TERM]",many_results:"[COUNT] resultaten voor [SEARCH_TERM]",one_result:"[COUNT] resultaat voor [SEARCH_TERM]",total_zero_results:"Geen resultaten",total_one_result:"[COUNT] resultaat",total_many_results:"[COUNT] resultaten",alt_search:"Geen resultaten voor [SEARCH_TERM]. In plaats daarvan worden resultaten voor [DIFFERENT_TERM] weergegeven",search_suggestion:"Geen resultaten voor [SEARCH_TERM]. Probeer een van de volgende zoekopdrachten:",searching:"Zoeken naar [SEARCH_TERM]...",results_label:"Zoekresultaten",keyboard_navigate:"navigeren",keyboard_select:"selecteren",keyboard_clear:"wissen",keyboard_close:"sluiten",keyboard_search:"zoeken",error_search:"Zoeken mislukt",filter_selected_one:"[COUNT] geselecteerd",filter_selected_many:"[COUNT] geselecteerd",input_hint:"Resultaten verschijnen terwijl u typt",loading:"Laden"},Oi={thanks_to:Yi,comments:wi,direction:zi,strings:Ji};var ol={};x(ol,{comments:()=>Ki,default:()=>$i,direction:()=>Pi,strings:()=>qi,thanks_to:()=>ji});var ji="Eirik Mikkelsen",Ki="",Pi="ltr",qi={placeholder:"S\xF8k",clear_search:"Fjern",load_more:"Last fleire resultat",search_label:"S\xF8k p\xE5 denne sida",filters_label:"Filter",zero_results:"Ingen resultat for [SEARCH_TERM]",many_results:"[COUNT] resultat for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",total_zero_results:"Ingen resultat",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultat",alt_search:"Ingen resultat for [SEARCH_TERM]. Viser resultat for [DIFFERENT_TERM] i staden",search_suggestion:"Ingen resultat for [SEARCH_TERM]. Pr\xF8v eitt av desse s\xF8keorda i staden:",searching:"S\xF8ker etter [SEARCH_TERM]",results_label:"S\xF8keresultat",keyboard_navigate:"naviger",keyboard_select:"vel",keyboard_clear:"fjern",keyboard_close:"lukk",keyboard_search:"s\xF8k",error_search:"S\xF8k feila",filter_selected_one:"[COUNT] vald",filter_selected_many:"[COUNT] valde",input_hint:"Resultat visast medan du skriv",loading:"Lastar"},$i={thanks_to:ji,comments:Ki,direction:Pi,strings:qi};var cl={};x(cl,{comments:()=>tn,default:()=>an,direction:()=>ln,strings:()=>sn,thanks_to:()=>en});var en="Christopher Wingate",tn="",ln="ltr",sn={placeholder:"S\xF8k",clear_search:"Fjern",load_more:"Last flere resultater",search_label:"S\xF8k p\xE5 denne siden",filters_label:"Filtre",zero_results:"Ingen resultater for [SEARCH_TERM]",many_results:"[COUNT] resultater for [SEARCH_TERM]",one_result:"[COUNT] resultat for [SEARCH_TERM]",total_zero_results:"Ingen resultater",total_one_result:"[COUNT] resultat",total_many_results:"[COUNT] resultater",alt_search:"Ingen resultater for [SEARCH_TERM]. Viser resultater for [DIFFERENT_TERM] i stedet",search_suggestion:"Ingen resultater for [SEARCH_TERM]. Pr\xF8v en av disse s\xF8keordene i stedet:",searching:"S\xF8ker etter [SEARCH_TERM]",results_label:"S\xF8keresultater",keyboard_navigate:"naviger",keyboard_select:"velg",keyboard_clear:"fjern",keyboard_close:"lukk",keyboard_search:"s\xF8k",error_search:"S\xF8k feilet",filter_selected_one:"[COUNT] valgt",filter_selected_many:"[COUNT] valgte",input_hint:"Resultater vises mens du skriver",loading:"Laster"},an={thanks_to:en,comments:tn,direction:ln,strings:sn};var ul={};x(ul,{comments:()=>nn,default:()=>cn,direction:()=>gn,strings:()=>on,thanks_to:()=>rn});var rn="",nn="",gn="ltr",on={placeholder:"Szukaj",clear_search:"Wyczy\u015B\u0107",load_more:"Za\u0142aduj wi\u0119cej",search_label:"Przeszukaj t\u0119 stron\u0119",filters_label:"Filtry",zero_results:"Brak wynik\xF3w dla [SEARCH_TERM]",many_results:"[COUNT] wynik\xF3w dla [SEARCH_TERM]",one_result:"[COUNT] wynik dla [SEARCH_TERM]",total_zero_results:"Brak wynik\xF3w",total_one_result:"[COUNT] wynik",total_many_results:"[COUNT] wynik\xF3w",alt_search:"Brak wynik\xF3w dla [SEARCH_TERM]. Wy\u015Bwietlam wyniki dla [DIFFERENT_TERM]",search_suggestion:"Brak wynik\xF3w dla [SEARCH_TERM]. Pokrewne wyniki wyszukiwania:",searching:"Szukam [SEARCH_TERM]...",results_label:"Wyniki wyszukiwania",keyboard_navigate:"nawiguj",keyboard_select:"wybierz",keyboard_clear:"wyczy\u015B\u0107",keyboard_close:"zamknij",keyboard_search:"szukaj",error_search:"Wyszukiwanie nie powiod\u0142o si\u0119",filter_selected_one:"[COUNT] wybrany",filter_selected_many:"[COUNT] wybranych",input_hint:"Wyniki pojawi\u0105 si\u0119 podczas pisania",loading:"\u0141adowanie"},cn={thanks_to:rn,comments:nn,direction:gn,strings:on};var Cl={};x(Cl,{comments:()=>Cn,default:()=>An,direction:()=>In,strings:()=>dn,thanks_to:()=>un});var un="Jonatah",Cn="",In="ltr",dn={placeholder:"Pesquisar",clear_search:"Limpar",load_more:"Ver mais resultados",search_label:"Pesquisar",filters_label:"Filtros",zero_results:"Nenhum resultado encontrado para [SEARCH_TERM]",many_results:"[COUNT] resultados encontrados para [SEARCH_TERM]",one_result:"[COUNT] resultado encontrado para [SEARCH_TERM]",total_zero_results:"Nenhum resultado",total_one_result:"[COUNT] resultado",total_many_results:"[COUNT] resultados",alt_search:"Nenhum resultado encontrado para [SEARCH_TERM]. Exibindo resultados para [DIFFERENT_TERM]",search_suggestion:"Nenhum resultado encontrado para [SEARCH_TERM]. Tente uma das seguintes pesquisas:",searching:"Pesquisando por [SEARCH_TERM]...",results_label:"Resultados da pesquisa",keyboard_navigate:"navegar",keyboard_select:"selecionar",keyboard_clear:"limpar",keyboard_close:"fechar",keyboard_search:"pesquisar",error_search:"Falha na pesquisa",filter_selected_one:"[COUNT] selecionado",filter_selected_many:"[COUNT] selecionados",input_hint:"Os resultados aparecer\xE3o enquanto voc\xEA digita",loading:"Carregando"},An={thanks_to:un,comments:Cn,direction:In,strings:dn};var Il={};x(Il,{comments:()=>Qn,default:()=>fn,direction:()=>mn,strings:()=>bn,thanks_to:()=>_n});var _n="Bogdan Mateescu ",Qn="",mn="ltr",bn={placeholder:"C\u0103utare",clear_search:"\u015Eterge\u0163i",load_more:"\xCEnc\u0103rca\u021Bi mai multe rezultate",search_label:"C\u0103uta\u021Bi \xEEn acest site",filters_label:"Filtre",zero_results:"Niciun rezultat pentru [SEARCH_TERM]",many_results:"[COUNT] rezultate pentru [SEARCH_TERM]",one_result:"[COUNT] rezultat pentru [SEARCH_TERM]",total_zero_results:"Niciun rezultat",total_one_result:"[COUNT] rezultat",total_many_results:"[COUNT] rezultate",alt_search:"Niciun rezultat pentru [SEARCH_TERM]. Se afi\u0219eaz\u0103 \xEEn schimb rezultatele pentru [DIFFERENT_TERM]",search_suggestion:"Niciun rezultat pentru [SEARCH_TERM]. \xCEncerca\u021Bi una dintre urm\u0103toarele c\u0103ut\u0103ri:",searching:"Se caut\u0103 dup\u0103: [SEARCH_TERM]...",results_label:"Rezultatele c\u0103ut\u0103rii",keyboard_navigate:"navigare",keyboard_select:"selectare",keyboard_clear:"\u0219tergere",keyboard_close:"\xEEnchidere",keyboard_search:"c\u0103utare",error_search:"C\u0103utarea a e\u0219uat",filter_selected_one:"[COUNT] selectat",filter_selected_many:"[COUNT] selectate",input_hint:"Rezultatele vor ap\u0103rea pe m\u0103sur\u0103 ce tasta\u021Bi",loading:"Se \xEEncarc\u0103"},fn={thanks_to:_n,comments:Qn,direction:mn,strings:bn};var dl={};x(dl,{comments:()=>pn,default:()=>Un,direction:()=>Bn,strings:()=>hn,thanks_to:()=>Fn});var Fn="Aleksandr Gordeev",pn="",Bn="ltr",hn={placeholder:"\u041F\u043E\u0438\u0441\u043A",clear_search:"\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u044C \u043F\u043E\u043B\u0435",load_more:"\u0417\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044C \u0435\u0449\u0435",search_label:"\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0441\u0430\u0439\u0442\u0443",filters_label:"\u0424\u0438\u043B\u044C\u0442\u0440\u044B",zero_results:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u0432 \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",total_zero_results:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E",total_one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442",total_many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u043E\u0432",alt_search:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]. \u041F\u043E\u043A\u0430\u0437\u0430\u043D\u044B \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [DIFFERENT_TERM]",search_suggestion:"\u041D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]. \u041F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043E\u0434\u0438\u043D \u0438\u0437 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043D\u0442\u043E\u0432",searching:"\u041F\u043E\u0438\u0441\u043A \u043F\u043E \u0437\u0430\u043F\u0440\u043E\u0441\u0443: [SEARCH_TERM]",results_label:"\u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u043F\u043E\u0438\u0441\u043A\u0430",keyboard_navigate:"\u043D\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u044F",keyboard_select:"\u0432\u044B\u0431\u0440\u0430\u0442\u044C",keyboard_clear:"\u043E\u0447\u0438\u0441\u0442\u0438\u0442\u044C",keyboard_close:"\u0437\u0430\u043A\u0440\u044B\u0442\u044C",keyboard_search:"\u043F\u043E\u0438\u0441\u043A",error_search:"\u041E\u0448\u0438\u0431\u043A\u0430 \u043F\u043E\u0438\u0441\u043A\u0430",filter_selected_one:"[COUNT] \u0432\u044B\u0431\u0440\u0430\u043D",filter_selected_many:"[COUNT] \u0432\u044B\u0431\u0440\u0430\u043D\u043E",input_hint:"\u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u044B \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u044F\u0432\u043B\u044F\u0442\u044C\u0441\u044F \u043F\u043E \u043C\u0435\u0440\u0435 \u0432\u0432\u043E\u0434\u0430",loading:"\u0417\u0430\u0433\u0440\u0443\u0437\u043A\u0430"},Un={thanks_to:Fn,comments:pn,direction:Bn,strings:hn};var Al={};x(Al,{comments:()=>yn,default:()=>Nn,direction:()=>Zn,strings:()=>Gn,thanks_to:()=>xn});var xn="Andrija Sagicc",yn="",Zn="ltr",Gn={placeholder:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430",clear_search:"\u0411\u0440\u0438\u0441\u0430\u045A\u0435",load_more:"\u041F\u0440\u0438\u043A\u0430\u0437 \u0432\u0438\u0448\u0435 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430",search_label:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u0441\u0430\u0458\u0442\u0430",filters_label:"\u0424\u0438\u043B\u0442\u0435\u0440\u0438",zero_results:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]",total_zero_results:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430",total_one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442",total_many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430",alt_search:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]. \u041F\u0440\u0438\u043A\u0430\u0437 \u0434\u043E\u0434\u0430\u0442\u043D\u0438\u043A \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [DIFFERENT_TERM]",search_suggestion:"\u041D\u0435\u043C\u0430 \u0440\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0430 \u0437\u0430 [SEARCH_TERM]. \u041F\u043E\u043A\u0443\u0448\u0430\u0458\u0442\u0435 \u0441\u0430 \u043D\u0435\u043A\u043E\u043C \u043E\u0434 \u0441\u043B\u0435\u0434\u0435\u045B\u0438\u0445 \u043F\u0440\u0435\u0442\u0440\u0430\u0433\u0430:",searching:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u0442\u0435\u0440\u043C\u0438\u043D\u0430 [SEARCH_TERM]...",results_label:"\u0420\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0438 \u043F\u0440\u0435\u0442\u0440\u0430\u0433\u0435",keyboard_navigate:"\u043D\u0430\u0432\u0438\u0433\u0430\u0446\u0438\u0458\u0430",keyboard_select:"\u0438\u0437\u0430\u0431\u0435\u0440\u0438",keyboard_clear:"\u043E\u0431\u0440\u0438\u0448\u0438",keyboard_close:"\u0437\u0430\u0442\u0432\u043E\u0440\u0438",keyboard_search:"\u043F\u0440\u0435\u0442\u0440\u0430\u0433\u0430",error_search:"\u041F\u0440\u0435\u0442\u0440\u0430\u0433\u0430 \u043D\u0438\u0458\u0435 \u0443\u0441\u043F\u0435\u043B\u0430",filter_selected_one:"[COUNT] \u0438\u0437\u0430\u0431\u0440\u0430\u043D",filter_selected_many:"[COUNT] \u0438\u0437\u0430\u0431\u0440\u0430\u043D\u0438\u0445",input_hint:"\u0420\u0435\u0437\u0443\u043B\u0442\u0430\u0442\u0438 \u045B\u0435 \u0441\u0435 \u043F\u043E\u0458\u0430\u0432\u0459\u0438\u0432\u0430\u0442\u0438 \u0434\u043E\u043A \u043A\u0443\u0446\u0430\u0442\u0435",loading:"\u0423\u0447\u0438\u0442\u0430\u0432\u0430\u045A\u0435"},Nn={thanks_to:xn,comments:yn,direction:Zn,strings:Gn};var _l={};x(_l,{comments:()=>En,default:()=>Rn,direction:()=>kn,strings:()=>Ln,thanks_to:()=>Xn});var Xn="Montazar Al-Jaber ",En="",kn="ltr",Ln={placeholder:"S\xF6k",clear_search:"Rensa",load_more:"Visa fler tr\xE4ffar",search_label:"S\xF6k p\xE5 denna sida",filters_label:"Filter",zero_results:"[SEARCH_TERM] gav inga tr\xE4ffar",many_results:"[SEARCH_TERM] gav [COUNT] tr\xE4ffar",one_result:"[SEARCH_TERM] gav [COUNT] tr\xE4ff",total_zero_results:"Inga tr\xE4ffar",total_one_result:"[COUNT] tr\xE4ff",total_many_results:"[COUNT] tr\xE4ffar",alt_search:"[SEARCH_TERM] gav inga tr\xE4ffar. Visar resultat f\xF6r [DIFFERENT_TERM] ist\xE4llet",search_suggestion:"[SEARCH_TERM] gav inga tr\xE4ffar. F\xF6rs\xF6k igen med en av f\xF6ljande s\xF6kord:",searching:"S\xF6ker efter [SEARCH_TERM]...",results_label:"S\xF6kresultat",keyboard_navigate:"navigera",keyboard_select:"v\xE4lj",keyboard_clear:"rensa",keyboard_close:"st\xE4ng",keyboard_search:"s\xF6k",error_search:"S\xF6kningen misslyckades",filter_selected_one:"[COUNT] vald",filter_selected_many:"[COUNT] valda",input_hint:"Resultat visas medan du skriver",loading:"L\xE4ser in"},Rn={thanks_to:Xn,comments:En,direction:kn,strings:Ln};var Ql={};x(Ql,{comments:()=>Sn,default:()=>Tn,direction:()=>Vn,strings:()=>Dn,thanks_to:()=>Wn});var Wn="Anonymous",Sn="",Vn="ltr",Dn={placeholder:"Tafuta",clear_search:"Futa",load_more:"Pakia matokeo zaidi",search_label:"Tafuta tovuti hii",filters_label:"Vichujio",zero_results:"Hakuna matokeo ya [SEARCH_TERM]",many_results:"Matokeo [COUNT] ya [SEARCH_TERM]",one_result:"Tokeo [COUNT] la [SEARCH_TERM]",total_zero_results:"Hakuna matokeo",total_one_result:"Tokeo [COUNT]",total_many_results:"Matokeo [COUNT]",alt_search:"Hakuna mayokeo ya [SEARCH_TERM]. Badala yake, inaonyesha matokeo ya [DIFFERENT_TERM]",search_suggestion:"Hakuna matokeo ya [SEARCH_TERM]. Jaribu mojawapo ya utafutaji ufuatao:",searching:"Kutafuta [SEARCH_TERM]...",results_label:"Matokeo ya utafutaji",keyboard_navigate:"sogeza",keyboard_select:"chagua",keyboard_clear:"futa",keyboard_close:"funga",keyboard_search:"tafuta",error_search:"Utafutaji umeshindwa",filter_selected_one:"[COUNT] imechaguliwa",filter_selected_many:"[COUNT] zimechaguliwa",input_hint:"Matokeo yataonekana unapoandika",loading:"Inapakia"},Tn={thanks_to:Wn,comments:Sn,direction:Vn,strings:Dn};var ml={};x(ml,{comments:()=>Hn,default:()=>wn,direction:()=>Mn,strings:()=>Yn,thanks_to:()=>vn});var vn="",Hn="",Mn="ltr",Yn={placeholder:"\u0BA4\u0BC7\u0B9F\u0BC1\u0B95",clear_search:"\u0B85\u0BB4\u0BBF\u0B95\u0BCD\u0B95\u0BC1\u0B95",load_more:"\u0BAE\u0BC7\u0BB2\u0BC1\u0BAE\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BC8\u0B95\u0BCD \u0B95\u0BBE\u0B9F\u0BCD\u0B9F\u0BC1\u0B95",search_label:"\u0B87\u0BA8\u0BCD\u0BA4 \u0BA4\u0BB3\u0BA4\u0BCD\u0BA4\u0BBF\u0BB2\u0BCD \u0BA4\u0BC7\u0B9F\u0BC1\u0B95",filters_label:"\u0BB5\u0B9F\u0BBF\u0B95\u0B9F\u0BCD\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BCD",zero_results:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8",many_results:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 [COUNT] \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",one_result:"[SEARCH_TERM] \u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1",total_zero_results:"\u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8",total_one_result:"[COUNT] \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1",total_many_results:"[COUNT] \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",alt_search:"[SEARCH_TERM] \u0B87\u0BA4\u0BCD\u0BA4\u0BC7\u0B9F\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8, \u0B87\u0BA8\u0BCD\u0BA4 \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0B92\u0BA4\u0BCD\u0BA4 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD [DIFFERENT_TERM]",search_suggestion:"[SEARCH_TERM] \u0B87\u0BA4\u0BCD \u0BA4\u0BC7\u0B9F\u0BB2\u0BC1\u0B95\u0BCD\u0B95\u0BBE\u0BA9 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0B87\u0BB2\u0BCD\u0BB2\u0BC8.\u0B87\u0BA4\u0BB1\u0BCD\u0B95\u0BC1 \u0BAA\u0BA4\u0BBF\u0BB2\u0BC0\u0B9F\u0BBE\u0BA9 \u0BA4\u0BC7\u0B9F\u0BB2\u0BCD\u0B95\u0BB3\u0BC8 \u0BA4\u0BC7\u0B9F\u0BC1\u0B95:",searching:"[SEARCH_TERM] \u0BA4\u0BC7\u0B9F\u0BAA\u0BCD\u0BAA\u0B9F\u0BC1\u0B95\u0BBF\u0BA9\u0BCD\u0BB1\u0BA4\u0BC1",results_label:"\u0BA4\u0BC7\u0B9F\u0BB2\u0BCD \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD",keyboard_navigate:"\u0BB5\u0BB4\u0BBF\u0BA8\u0B9F\u0BA4\u0BCD\u0BA4\u0BC1",keyboard_select:"\u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1",keyboard_clear:"\u0B85\u0BB4\u0BBF",keyboard_close:"\u0BAE\u0BC2\u0B9F\u0BC1",keyboard_search:"\u0BA4\u0BC7\u0B9F\u0BC1",error_search:"\u0BA4\u0BC7\u0B9F\u0BB2\u0BCD \u0BA4\u0BCB\u0BB2\u0BCD\u0BB5\u0BBF",filter_selected_one:"[COUNT] \u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA4\u0BC1",filter_selected_many:"[COUNT] \u0BA4\u0BC7\u0BB0\u0BCD\u0BA8\u0BCD\u0BA4\u0BC6\u0B9F\u0BC1\u0B95\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0B9F\u0BCD\u0B9F\u0BA9",input_hint:"\u0BA8\u0BC0\u0B99\u0BCD\u0B95\u0BB3\u0BCD \u0BA4\u0B9F\u0BCD\u0B9F\u0B9A\u0BCD\u0B9A\u0BC1 \u0B9A\u0BC6\u0BAF\u0BCD\u0BAF\u0BC1\u0BAE\u0BCD\u0BAA\u0BCB\u0BA4\u0BC1 \u0BAE\u0BC1\u0B9F\u0BBF\u0BB5\u0BC1\u0B95\u0BB3\u0BCD \u0BA4\u0BCB\u0BA9\u0BCD\u0BB1\u0BC1\u0BAE\u0BCD",loading:"\u0B8F\u0BB1\u0BCD\u0BB1\u0BC1\u0B95\u0BBF\u0BB1\u0BA4\u0BC1"},wn={thanks_to:vn,comments:Hn,direction:Mn,strings:Yn};var bl={};x(bl,{comments:()=>Jn,default:()=>Kn,direction:()=>On,strings:()=>jn,thanks_to:()=>zn});var zn="Patiphon Loetsuthakun ",Jn="",On="ltr",jn={placeholder:"\u0E04\u0E49\u0E19\u0E2B\u0E32",clear_search:"\u0E25\u0E49\u0E32\u0E07",load_more:"\u0E42\u0E2B\u0E25\u0E14\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E40\u0E15\u0E34\u0E21",search_label:"\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E1A\u0E19\u0E40\u0E27\u0E47\u0E1A\u0E44\u0E0B\u0E15\u0E4C",filters_label:"\u0E15\u0E31\u0E27\u0E01\u0E23\u0E2D\u0E07",zero_results:"\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM]",many_results:"\u0E1E\u0E1A [COUNT] \u0E1C\u0E25\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM]",one_result:"\u0E1E\u0E1A [COUNT] \u0E1C\u0E25\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM]",total_zero_results:"\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C",total_one_result:"[COUNT] \u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C",total_many_results:"[COUNT] \u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C",alt_search:"\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM] \u0E41\u0E2A\u0E14\u0E07\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E08\u0E32\u0E01\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32 [DIFFERENT_TERM] \u0E41\u0E17\u0E19",search_suggestion:"\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E2A\u0E33\u0E2B\u0E23\u0E31\u0E1A [SEARCH_TERM] \u0E25\u0E2D\u0E07\u0E04\u0E33\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E40\u0E2B\u0E25\u0E48\u0E32\u0E19\u0E35\u0E49\u0E41\u0E17\u0E19:",searching:"\u0E01\u0E33\u0E25\u0E31\u0E07\u0E04\u0E49\u0E19\u0E2B\u0E32 [SEARCH_TERM]...",results_label:"\u0E1C\u0E25\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32",keyboard_navigate:"\u0E19\u0E33\u0E17\u0E32\u0E07",keyboard_select:"\u0E40\u0E25\u0E37\u0E2D\u0E01",keyboard_clear:"\u0E25\u0E49\u0E32\u0E07",keyboard_close:"\u0E1B\u0E34\u0E14",keyboard_search:"\u0E04\u0E49\u0E19\u0E2B\u0E32",error_search:"\u0E01\u0E32\u0E23\u0E04\u0E49\u0E19\u0E2B\u0E32\u0E25\u0E49\u0E21\u0E40\u0E2B\u0E25\u0E27",filter_selected_one:"\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E41\u0E25\u0E49\u0E27 [COUNT] \u0E23\u0E32\u0E22\u0E01\u0E32\u0E23",filter_selected_many:"\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E41\u0E25\u0E49\u0E27 [COUNT] \u0E23\u0E32\u0E22\u0E01\u0E32\u0E23",input_hint:"\u0E1C\u0E25\u0E25\u0E31\u0E1E\u0E18\u0E4C\u0E08\u0E30\u0E1B\u0E23\u0E32\u0E01\u0E0F\u0E02\u0E13\u0E30\u0E17\u0E35\u0E48\u0E04\u0E38\u0E13\u0E1E\u0E34\u0E21\u0E1E\u0E4C",loading:"\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14"},Kn={thanks_to:zn,comments:Jn,direction:On,strings:jn};var fl={};x(fl,{comments:()=>qn,default:()=>tg,direction:()=>$n,strings:()=>eg,thanks_to:()=>Pn});var Pn="Taylan \xD6zg\xFCr Bildik",qn="",$n="ltr",eg={placeholder:"Ara\u015Ft\u0131r",clear_search:"Temizle",load_more:"Daha fazla sonu\xE7",search_label:"Site genelinde arama",filters_label:"Filtreler",zero_results:"[SEARCH_TERM] i\xE7in sonu\xE7 yok",many_results:"[SEARCH_TERM] i\xE7in [COUNT] sonu\xE7 bulundu",one_result:"[SEARCH_TERM] i\xE7in [COUNT] sonu\xE7 bulundu",total_zero_results:"Sonu\xE7 yok",total_one_result:"[COUNT] sonu\xE7",total_many_results:"[COUNT] sonu\xE7",alt_search:"[SEARCH_TERM] i\xE7in sonu\xE7 yok. Bunun yerine [DIFFERENT_TERM] i\xE7in sonu\xE7lar g\xF6steriliyor",search_suggestion:"[SEARCH_TERM] i\xE7in sonu\xE7 yok. Alternatif olarak a\u015Fa\u011F\u0131daki kelimelerden birini deneyebilirsiniz:",searching:"[SEARCH_TERM] ara\u015Ft\u0131r\u0131l\u0131yor...",results_label:"Arama sonu\xE7lar\u0131",keyboard_navigate:"gezin",keyboard_select:"se\xE7",keyboard_clear:"temizle",keyboard_close:"kapat",keyboard_search:"ara",error_search:"Arama ba\u015Far\u0131s\u0131z",filter_selected_one:"[COUNT] se\xE7ili",filter_selected_many:"[COUNT] se\xE7ili",input_hint:"Sonu\xE7lar siz yazarken g\xF6r\xFCnecektir",loading:"Y\xFCkleniyor"},tg={thanks_to:Pn,comments:qn,direction:$n,strings:eg};var Fl={};x(Fl,{comments:()=>sg,default:()=>ig,direction:()=>ag,strings:()=>rg,thanks_to:()=>lg});var lg="Vladyslav Lyshenko ",sg="",ag="ltr",rg={placeholder:"\u041F\u043E\u0448\u0443\u043A",clear_search:"\u041E\u0447\u0438\u0441\u0442\u0438\u0442\u0438 \u043F\u043E\u043B\u0435",load_more:"\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0438\u0442\u0438 \u0449\u0435",search_label:"\u041F\u043E\u0448\u0443\u043A \u043F\u043E \u0441\u0430\u0439\u0442\u0443",filters_label:"\u0424\u0456\u043B\u044C\u0442\u0440\u0438",zero_results:"\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E \u0437\u0430 \u0437\u0430\u043F\u0438\u0442\u043E\u043C: [SEARCH_TERM]",many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0456\u0432 \u043D\u0430 \u0437\u0430\u043F\u0438\u0442: [SEARCH_TERM]",one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442 \u0437\u0430 \u0437\u0430\u043F\u0438\u0442\u043E\u043C: [SEARCH_TERM]",total_zero_results:"\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E",total_one_result:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442",total_many_results:"[COUNT] \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0456\u0432",alt_search:"\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043D\u0430 \u0437\u0430\u043F\u0438\u0442: [SEARCH_TERM]. \u041F\u043E\u043A\u0430\u0437\u0430\u043D\u043E \u0440\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0438 \u043D\u0430 \u0437\u0430\u043F\u0438\u0442: [DIFFERENT_TERM]",search_suggestion:"\u041D\u0456\u0447\u043E\u0433\u043E \u043D\u0435 \u0437\u043D\u0430\u0439\u0434\u0435\u043D\u043E \u043D\u0430 \u0437\u0430\u043F\u0438\u0442: [SEARCH_TERM]. \u0421\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u043E\u0434\u0438\u043D \u0456\u0437 \u0442\u0430\u043A\u0438\u0445 \u0432\u0430\u0440\u0456\u0430\u043D\u0442\u0456\u0432",searching:"\u041F\u043E\u0448\u0443\u043A \u0437\u0430 \u0437\u0430\u043F\u0438\u0442\u043E\u043C: [SEARCH_TERM]",results_label:"\u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0438 \u043F\u043E\u0448\u0443\u043A\u0443",keyboard_navigate:"\u043D\u0430\u0432\u0456\u0433\u0430\u0446\u0456\u044F",keyboard_select:"\u0432\u0438\u0431\u0440\u0430\u0442\u0438",keyboard_clear:"\u043E\u0447\u0438\u0441\u0442\u0438\u0442\u0438",keyboard_close:"\u0437\u0430\u043A\u0440\u0438\u0442\u0438",keyboard_search:"\u043F\u043E\u0448\u0443\u043A",error_search:"\u041F\u043E\u043C\u0438\u043B\u043A\u0430 \u043F\u043E\u0448\u0443\u043A\u0443",filter_selected_one:"[COUNT] \u0432\u0438\u0431\u0440\u0430\u043D\u043E",filter_selected_many:"[COUNT] \u0432\u0438\u0431\u0440\u0430\u043D\u043E",input_hint:"\u0420\u0435\u0437\u0443\u043B\u044C\u0442\u0430\u0442\u0438 \u0437'\u044F\u0432\u043B\u044F\u0442\u0438\u043C\u0443\u0442\u044C\u0441\u044F \u043F\u0456\u0434 \u0447\u0430\u0441 \u0432\u0432\u0435\u0434\u0435\u043D\u043D\u044F",loading:"\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F"},ig={thanks_to:lg,comments:sg,direction:ag,strings:rg};var pl={};x(pl,{comments:()=>gg,default:()=>ug,direction:()=>og,strings:()=>cg,thanks_to:()=>ng});var ng="Long Nhat Nguyen",gg="",og="ltr",cg={placeholder:"T\xECm ki\u1EBFm",clear_search:"X\xF3a",load_more:"Nhi\u1EC1u k\u1EBFt qu\u1EA3 h\u01A1n",search_label:"T\xECm ki\u1EBFm trong trang n\xE0y",filters_label:"B\u1ED9 l\u1ECDc",zero_results:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",many_results:"[COUNT] k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",one_result:"[COUNT] k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]",total_zero_results:"Kh\xF4ng c\xF3 k\u1EBFt qu\u1EA3",total_one_result:"[COUNT] k\u1EBFt qu\u1EA3",total_many_results:"[COUNT] k\u1EBFt qu\u1EA3",alt_search:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]. Ki\u1EC3m th\u1ECB k\u1EBFt qu\u1EA3 thay th\u1EBF v\u1EDBi [DIFFERENT_TERM]",search_suggestion:"Kh\xF4ng t\xECm th\u1EA5y k\u1EBFt qu\u1EA3 cho [SEARCH_TERM]. Th\u1EED m\u1ED9t trong c\xE1c t\xECm ki\u1EBFm:",searching:"\u0110ang t\xECm ki\u1EBFm cho [SEARCH_TERM]...",results_label:"K\u1EBFt qu\u1EA3 t\xECm ki\u1EBFm",keyboard_navigate:"chuy\u1EC3n",keyboard_select:"ch\u1ECDn",keyboard_clear:"x\xF3a",keyboard_close:"\u0111\xF3ng",keyboard_search:"t\xECm ki\u1EBFm",error_search:"T\xECm ki\u1EBFm th\u1EA5t b\u1EA1i",filter_selected_one:"\u0110\xE3 ch\u1ECDn [COUNT]",filter_selected_many:"\u0110\xE3 ch\u1ECDn [COUNT]",input_hint:"K\u1EBFt qu\u1EA3 s\u1EBD xu\u1EA5t hi\u1EC7n khi b\u1EA1n nh\u1EADp",loading:"\u0110ang t\u1EA3i"},ug={thanks_to:ng,comments:gg,direction:og,strings:cg};var Bl={};x(Bl,{comments:()=>Ig,default:()=>_g,direction:()=>dg,strings:()=>Ag,thanks_to:()=>Cg});var Cg="Amber Song",Ig="",dg="ltr",Ag={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F7D\u66F4\u591A\u7ED3\u679C",search_label:"\u7AD9\u5185\u641C\u7D22",filters_label:"\u7B5B\u9009",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",many_results:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",one_result:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",total_zero_results:"\u65E0\u7ED3\u679C",total_one_result:"[COUNT] \u4E2A\u7ED3\u679C",total_many_results:"[COUNT] \u4E2A\u7ED3\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u6539\u4E3A\u663E\u793A [DIFFERENT_TERM] \u7684\u76F8\u5173\u7ED3\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u8BF7\u5C1D\u8BD5\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]...",results_label:"\u641C\u7D22\u7ED3\u679C",keyboard_navigate:"\u5BFC\u822A",keyboard_select:"\u9009\u62E9",keyboard_clear:"\u6E05\u9664",keyboard_close:"\u5173\u95ED",keyboard_search:"\u641C\u7D22",error_search:"\u641C\u7D22\u5931\u8D25",filter_selected_one:"\u5DF2\u9009\u62E9 [COUNT] \u4E2A",filter_selected_many:"\u5DF2\u9009\u62E9 [COUNT] \u4E2A",input_hint:"\u8F93\u5165\u65F6\u5C06\u663E\u793A\u7ED3\u679C",loading:"\u52A0\u8F7D\u4E2D"},_g={thanks_to:Cg,comments:Ig,direction:dg,strings:Ag};var hl={};x(hl,{comments:()=>mg,default:()=>Fg,direction:()=>bg,strings:()=>fg,thanks_to:()=>Qg});var Qg="Amber Song",mg="",bg="ltr",fg={placeholder:"\u641C\u5C0B",clear_search:"\u6E05\u9664",load_more:"\u8F09\u5165\u66F4\u591A\u7D50\u679C",search_label:"\u7AD9\u5167\u641C\u5C0B",filters_label:"\u7BE9\u9078",zero_results:"\u627E\u4E0D\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",many_results:"\u627E\u5230 [COUNT] \u500B [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",one_result:"\u627E\u5230 [COUNT] \u500B [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C",total_zero_results:"\u7121\u7D50\u679C",total_one_result:"[COUNT] \u500B\u7D50\u679C",total_many_results:"[COUNT] \u500B\u7D50\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C\u3002\u6539\u70BA\u986F\u793A [DIFFERENT_TERM] \u7684\u76F8\u95DC\u7D50\u679C",search_suggestion:"\u627E\u4E0D\u5230 [SEARCH_TERM] \u7684\u76F8\u95DC\u7D50\u679C\u3002\u8ACB\u5617\u8A66\u4EE5\u4E0B\u7684\u5EFA\u8B70\u4E4B\u4E00\u3002",searching:"\u6B63\u5728\u641C\u5C0B[SEARCH_TERM]...",results_label:"\u641C\u5C0B\u7D50\u679C",keyboard_navigate:"\u5C0E\u89BD",keyboard_select:"\u9078\u64C7",keyboard_clear:"\u6E05\u9664",keyboard_close:"\u95DC\u9589",keyboard_search:"\u641C\u5C0B",error_search:"\u641C\u5C0B\u5931\u6557",filter_selected_one:"\u5DF2\u9078\u64C7 [COUNT] \u500B",filter_selected_many:"\u5DF2\u9078\u64C7 [COUNT] \u500B",input_hint:"\u8F38\u5165\u6642\u5C07\u986F\u793A\u7D50\u679C",loading:"\u8F09\u5165\u4E2D"},Fg={thanks_to:Qg,comments:mg,direction:bg,strings:fg};var Ul={};x(Ul,{comments:()=>Bg,default:()=>xg,direction:()=>hg,strings:()=>Ug,thanks_to:()=>pg});var pg="Amber Song",Bg="",hg="ltr",Ug={placeholder:"\u641C\u7D22",clear_search:"\u6E05\u9664",load_more:"\u52A0\u8F7D\u66F4\u591A\u7ED3\u679C",search_label:"\u7AD9\u5185\u641C\u7D22",filters_label:"\u7B5B\u9009",zero_results:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",many_results:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",one_result:"\u627E\u5230 [COUNT] \u4E2A [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C",total_zero_results:"\u65E0\u7ED3\u679C",total_one_result:"[COUNT] \u4E2A\u7ED3\u679C",total_many_results:"[COUNT] \u4E2A\u7ED3\u679C",alt_search:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u6539\u4E3A\u663E\u793A [DIFFERENT_TERM] \u7684\u76F8\u5173\u7ED3\u679C",search_suggestion:"\u672A\u627E\u5230 [SEARCH_TERM] \u7684\u76F8\u5173\u7ED3\u679C\u3002\u8BF7\u5C1D\u8BD5\u4EE5\u4E0B\u641C\u7D22\u3002",searching:"\u6B63\u5728\u641C\u7D22 [SEARCH_TERM]...",results_label:"\u641C\u7D22\u7ED3\u679C",keyboard_navigate:"\u5BFC\u822A",keyboard_select:"\u9009\u62E9",keyboard_clear:"\u6E05\u9664",keyboard_close:"\u5173\u95ED",keyboard_search:"\u641C\u7D22",error_search:"\u641C\u7D22\u5931\u8D25",filter_selected_one:"\u5DF2\u9009\u62E9 [COUNT] \u4E2A",filter_selected_many:"\u5DF2\u9009\u62E9 [COUNT] \u4E2A",input_hint:"\u8F93\u5165\u65F6\u5C06\u663E\u793A\u7ED3\u679C",loading:"\u52A0\u8F7D\u4E2D"},xg={thanks_to:pg,comments:Bg,direction:hg,strings:Ug};var yg=[Wt,St,Vt,Dt,Tt,vt,Ht,Mt,Yt,wt,zt,Jt,Ot,jt,Kt,Pt,qt,$t,el,tl,ll,sl,al,rl,il,nl,gl,ol,cl,ul,Cl,Il,dl,Al,_l,Ql,ml,bl,fl,Fl,pl,Bl,hl,Ul],Us=yg,xs=["../../translations/af.json","../../translations/ar.json","../../translations/bn.json","../../translations/ca.json","../../translations/cs.json","../../translations/da.json","../../translations/de.json","../../translations/el.json","../../translations/en.json","../../translations/es.json","../../translations/eu.json","../../translations/fa.json","../../translations/fi.json","../../translations/fr.json","../../translations/gl.json","../../translations/he.json","../../translations/hi.json","../../translations/hr.json","../../translations/hu.json","../../translations/id.json","../../translations/it.json","../../translations/ja.json","../../translations/ko.json","../../translations/mi.json","../../translations/my.json","../../translations/nb.json","../../translations/nl.json","../../translations/nn.json","../../translations/no.json","../../translations/pl.json","../../translations/pt.json","../../translations/ro.json","../../translations/ru.json","../../translations/sr.json","../../translations/sv.json","../../translations/sw.json","../../translations/ta.json","../../translations/th.json","../../translations/tr.json","../../translations/uk.json","../../translations/vi.json","../../translations/zh-cn.json","../../translations/zh-tw.json","../../translations/zh.json"];function ys(l,e,t){let s=l.slice();return s[53]=e[t],s}function Zs(l){let e,t,s;function a(i){l[38](i)}let r={show_empty_filters:l[5],open_filters:l[6],available_filters:l[18],translate:l[20],automatic_translations:l[19],translations:l[7]};return l[0]!==void 0&&(r.selected_filters=l[0]),e=new hs({props:r}),Ce.push(()=>Ol(e,"selected_filters",a)),{c(){_t(e.$$.fragment)},m(i,n){_e(e,i,n),s=!0},p(i,n){let g={};n[0]&32&&(g.show_empty_filters=i[5]),n[0]&64&&(g.open_filters=i[6]),n[0]&262144&&(g.available_filters=i[18]),n[0]&524288&&(g.automatic_translations=i[19]),n[0]&128&&(g.translations=i[7]),!t&&n[0]&1&&(t=!0,g.selected_filters=i[0],Yl(()=>t=!1)),e.$set(g)},i(i){s||(k(e.$$.fragment,i),s=!0)},o(i){D(e.$$.fragment,i),s=!1},d(i){Qe(e,i)}}}function Gs(l){let e,t,s,a,r=[Ng,Gg],i=[];function n(g,C){return g[14]?0:1}return t=n(l,[-1,-1]),s=i[t]=r[t](l),{c(){e=F("div"),s.c(),_(e,"class","pagefind-ui__results-area svelte-e9gkc3")},m(g,C){h(g,e,C),i[t].m(e,null),a=!0},p(g,C){let u=t;t=n(g,C),t===u?i[t].p(g,C):(re(),D(i[u],1,1,()=>{i[u]=null}),ie(),s=i[t],s?s.p(g,C):(s=i[t]=r[t](g),s.c()),k(s,1),s.m(e,null))},i(g){a||(k(s),a=!0)},o(g){D(s),a=!1},d(g){g&&B(e),i[t].d()}}}function Gg(l){let e,t,s,a=[],r=new Map,i,n,g;function C(I,m){return I[13].results.length===0?kg:I[13].results.length===1?Eg:Xg}let u=C(l,[-1,-1]),A=u(l),c=V(l[13].results.slice(0,l[17])),d=I=>I[53].id;for(let I=0;Il[17]&&Xs(l);return{c(){e=F("p"),A.c(),t=Z(),s=F("ol");for(let I=0;II[17]?o?o.p(I,m):(o=Xs(I),o.c(),o.m(n.parentNode,n)):o&&(o.d(1),o=null)},i(I){if(!g){for(let m=0;m{g[c]=null}),ie(),a=g[s],a?a.p(e,A):(a=g[s]=n[s](e),a.c()),k(a,1),a.m(r.parentNode,r))},i(u){i||(k(a),i=!0)},o(u){D(a),i=!1},d(u){u&&(B(t),B(r)),g[s].d(u)}}}function Xs(l){let e,t=l[20]("load_more",l[19],l[7])+"",s,a,r;return{c(){e=F("button"),s=G(t),_(e,"type","button"),_(e,"class","pagefind-ui__button svelte-e9gkc3")},m(i,n){h(i,e,n),b(e,s),a||(r=w(e,"click",l[22]),a=!0)},p(i,n){n[0]&524416&&t!==(t=i[20]("load_more",i[19],i[7])+"")&&W(s,t)},d(i){i&&B(e),a=!1,r()}}}function Es(l){let e,t=l[20]("searching",l[19],l[7]).replace(/\[SEARCH_TERM\]/,l[16])+"",s;return{c(){e=F("p"),s=G(t),_(e,"class","pagefind-ui__message svelte-e9gkc3")},m(a,r){h(a,e,r),b(e,s)},p(a,r){r[0]&589952&&t!==(t=a[20]("searching",a[19],a[7]).replace(/\[SEARCH_TERM\]/,a[16])+"")&&W(s,t)},d(a){a&&B(e)}}}function Wg(l){let e,t,s,a,r,i,n,g=l[20]("clear_search",l[19],l[7])+"",C,u,A,c,d,o,I,m,f=l[12]&&Zs(l),p=l[15]&&Gs(l);return{c(){e=F("div"),t=F("form"),s=F("input"),i=Z(),n=F("button"),C=G(g),u=Z(),A=F("div"),f&&f.c(),c=Z(),p&&p.c(),_(s,"class","pagefind-ui__search-input svelte-e9gkc3"),_(s,"type","text"),_(s,"placeholder",a=l[20]("placeholder",l[19],l[7])),_(s,"title",r=l[20]("placeholder",l[19],l[7])),_(s,"autocapitalize","none"),_(s,"enterkeyhint","search"),s.autofocus=l[8],_(n,"class","pagefind-ui__search-clear svelte-e9gkc3"),z(n,"pagefind-ui__suppressed",!l[9]),_(A,"class","pagefind-ui__drawer svelte-e9gkc3"),z(A,"pagefind-ui__hidden",!l[15]),_(t,"class","pagefind-ui__form svelte-e9gkc3"),_(t,"role","search"),_(t,"aria-label",d=l[20]("search_label",l[19],l[7])),_(t,"action","javascript:void(0);"),_(e,"class","pagefind-ui svelte-e9gkc3"),z(e,"pagefind-ui--reset",l[1])},m(U,y){h(U,e,y),b(e,t),b(t,s),oe(s,l[9]),l[35](s),b(t,i),b(t,n),b(n,C),l[36](n),b(t,u),b(t,A),f&&f.m(A,null),b(A,c),p&&p.m(A,null),o=!0,l[8]&&s.focus(),I||(m=[w(s,"focus",l[21]),w(s,"keydown",l[33]),w(s,"input",l[34]),w(n,"click",l[37]),w(t,"submit",Sg)],I=!0)},p(U,y){(!o||y[0]&524416&&a!==(a=U[20]("placeholder",U[19],U[7])))&&_(s,"placeholder",a),(!o||y[0]&524416&&r!==(r=U[20]("placeholder",U[19],U[7])))&&_(s,"title",r),(!o||y[0]&256)&&(s.autofocus=U[8]),y[0]&512&&s.value!==U[9]&&oe(s,U[9]),(!o||y[0]&524416)&&g!==(g=U[20]("clear_search",U[19],U[7])+"")&&W(C,g),(!o||y[0]&512)&&z(n,"pagefind-ui__suppressed",!U[9]),U[12]?f?(f.p(U,y),y[0]&4096&&k(f,1)):(f=Zs(U),f.c(),k(f,1),f.m(A,c)):f&&(re(),D(f,1,1,()=>{f=null}),ie()),U[15]?p?(p.p(U,y),y[0]&32768&&k(p,1)):(p=Gs(U),p.c(),k(p,1),p.m(A,null)):p&&(re(),D(p,1,1,()=>{p=null}),ie()),(!o||y[0]&32768)&&z(A,"pagefind-ui__hidden",!U[15]),(!o||y[0]&524416&&d!==(d=U[20]("search_label",U[19],U[7])))&&_(t,"aria-label",d),(!o||y[0]&2)&&z(e,"pagefind-ui--reset",U[1])},i(U){o||(k(f),k(p),o=!0)},o(U){D(f),D(p),o=!1},d(U){U&&B(e),l[35](null),l[36](null),f&&f.d(),p&&p.d(),I=!1,H(m)}}}var Sg=l=>l.preventDefault();function Vg(l,e,t){let s={},a=xs.map(Q=>Q.match(/([^\/]+)\.json$/)[1]);for(let Q=0;QL[Q]??X[Q]??"",kl=Q=>{if(!q)return;let X=document.activeElement,L=X&&(X.tagName==="INPUT"||X.tagName==="TEXTAREA"||X.isContentEditable);Q.key==="/"&&!L&&(Q.preventDefault(),v?.focus())};xt(()=>{let Q=document?.querySelector?.("html")?.getAttribute?.("lang")||"en",X=Qt(Q.toLocaleLowerCase());t(19,El=s[`${X.language}-${X.script}-${X.region}`]||s[`${X.language}-${X.region}`]||s[`${X.language}`]||s.en),q&&document.addEventListener("keydown",kl)}),yt(()=>{E?.destroy?.(),E=null,q&&document.removeEventListener("keydown",kl)});let Ll=async()=>{if(!bt&&(t(12,bt=!0),!E)){let Q;try{Q=await import(`${r}pagefind.js`)}catch(L){console.error(L),console.error([`Pagefind couldn't be loaded from ${this.options.bundlePath}pagefind.js`,"You can configure this by passing a bundlePath option to PagefindUI"].join(` +`)),document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?console.error(`[DEBUG: Loaded from ${document.currentScript.src??"bad script location"}]`):console.error("no known script location")}u||t(24,u=C?12:30);let X={...m||{},excerptLength:u};await Q.options(X);for(let L of f){if(!L.bundlePath)throw new Error("mergeIndex requires a bundlePath parameter");let Y=L.bundlePath;delete L.bundlePath,await Q.mergeIndex(Y,L)}E=Q,Ws()}},Ws=async()=>{E&&(Xl=await E.filters(),(!Ie||!Object.keys(Ie).length)&&t(18,Ie=Xl))},Ss=Q=>{let X={};return Object.entries(Q).filter(([,L])=>L).forEach(([L])=>{let[Y,ne]=L.split(/:(.*)$/);X[Y]=X[Y]||[],X[Y].push(ne)}),X},de,Vs=async(Q,X)=>{if(!Q){t(15,Ft=!1),de&&clearTimeout(de);return}let L=Ss(X),Y=()=>Ds(Q,L);I>0&&Q?(de&&clearTimeout(de),de=setTimeout(Y,I),await Rl(),E.preload(Q,{filters:L})):Y(),Ts()},Rl=async()=>{for(;!E;)Ll(),await new Promise(Q=>setTimeout(Q,50))},Ds=async(Q,X)=>{t(16,Nl=Q||""),typeof c=="function"&&(Q=c(Q)),t(14,ft=!0),t(15,Ft=!0),await Rl();let L=++Gl,Y={filters:X};J&&typeof J=="object"&&(Y.sort=J);let ne=await E.search(Q,Y);Gl===L&&(ne.filters&&Object.keys(ne.filters)?.length&&t(18,Ie=ne.filters),t(13,Zl=ne),t(14,ft=!1),t(17,pt=i))},Ts=()=>{let Q=gt.offsetWidth;Q!=Ls&&t(10,v.style.paddingRight=`${Q+2}px`,v)},vs=Q=>{Q?.preventDefault(),t(17,pt+=i)},Hs=Q=>{Q.key==="Escape"&&(t(9,S=""),v.blur()),Q.key==="Enter"&&Q.preventDefault()};function Ms(){S=this.value,t(9,S),t(23,p)}function Ys(Q){Ce[Q?"unshift":"push"](()=>{v=Q,t(10,v)})}function ws(Q){Ce[Q?"unshift":"push"](()=>{gt=Q,t(11,gt)})}let zs=()=>{t(9,S=""),v.blur()};function Js(Q){N=Q,t(0,N)}return l.$$set=Q=>{"base_path"in Q&&t(25,r=Q.base_path),"page_size"in Q&&t(26,i=Q.page_size),"reset_styles"in Q&&t(1,n=Q.reset_styles),"show_images"in Q&&t(2,g=Q.show_images),"show_sub_results"in Q&&t(3,C=Q.show_sub_results),"excerpt_length"in Q&&t(24,u=Q.excerpt_length),"process_result"in Q&&t(4,A=Q.process_result),"process_term"in Q&&t(27,c=Q.process_term),"show_empty_filters"in Q&&t(5,d=Q.show_empty_filters),"open_filters"in Q&&t(6,o=Q.open_filters),"debounce_timeout_ms"in Q&&t(28,I=Q.debounce_timeout_ms),"pagefind_options"in Q&&t(29,m=Q.pagefind_options),"merge_index"in Q&&t(30,f=Q.merge_index),"trigger_search_term"in Q&&t(23,p=Q.trigger_search_term),"translations"in Q&&t(7,U=Q.translations),"autofocus"in Q&&t(8,y=Q.autofocus),"focus_on_slash"in Q&&t(31,q=Q.focus_on_slash),"sort"in Q&&t(32,J=Q.sort),"selected_filters"in Q&&t(0,N=Q.selected_filters)},l.$$.update=()=>{l.$$.dirty[0]&8388608&&p&&(t(9,S=p),t(23,p="")),l.$$.dirty[0]&513&&Vs(S,N)},[N,n,g,C,A,d,o,U,y,S,v,gt,bt,Zl,ft,Ft,Nl,pt,Ie,El,Rs,Ll,vs,p,u,r,i,c,I,m,f,q,J,Hs,Ms,Ys,ws,zs,Js]}var xl=class extends M{constructor(e){super(),K(this,e,Vg,Wg,O,{base_path:25,page_size:26,reset_styles:1,show_images:2,show_sub_results:3,excerpt_length:24,process_result:4,process_term:27,show_empty_filters:5,open_filters:6,debounce_timeout_ms:28,pagefind_options:29,merge_index:30,trigger_search_term:23,translations:7,autofocus:8,focus_on_slash:31,sort:32,selected_filters:0},null,[-1,-1])}},ks=xl;var yl;try{document?.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"&&(yl=new URL(document.currentScript.src).pathname.match(/^(.*\/)(?:pagefind-)?ui.js.*$/)[1])}catch{yl="/pagefind/"}var mt=class{constructor(e){this._pfs=null;let t=e.element??"[data-pagefind-ui]",s=e.bundlePath??yl,a=e.pageSize??5,r=e.resetStyles??!0,i=e.showImages??!0,n=e.showSubResults??!1,g=e.excerptLength??0,C=e.processResult??null,u=e.processTerm??null,A=e.showEmptyFilters??!0,c=e.openFilters??[],d=e.debounceTimeoutMs??300,o=e.mergeIndex??[],I=e.translations??[],m=e.autofocus??!1,f=e.focusOnSlash??!1,p=e.sort??null;delete e.element,delete e.bundlePath,delete e.pageSize,delete e.resetStyles,delete e.showImages,delete e.showSubResults,delete e.excerptLength,delete e.processResult,delete e.processTerm,delete e.showEmptyFilters,delete e.openFilters,delete e.debounceTimeoutMs,delete e.mergeIndex,delete e.translations,delete e.autofocus,delete e.focusOnSlash,delete e.sort;let U=t instanceof HTMLElement?t:document.querySelector(t);U?this._pfs=new ks({target:U,props:{base_path:s,page_size:a,reset_styles:r,show_images:i,show_sub_results:n,excerpt_length:g,process_result:C,process_term:u,show_empty_filters:A,open_filters:c,debounce_timeout_ms:d,merge_index:o,translations:I,autofocus:m,focus_on_slash:f,sort:p,pagefind_options:e}}):console.error(`Pagefind UI couldn't find the selector ${t}`)}triggerSearch(e){this._pfs.$$set({trigger_search_term:e})}triggerFilters(e){let t={};for(let[s,a]of Object.entries(e))if(Array.isArray(a))for(let r of a)t[`${s}:${r}`]=!0;else t[`${s}:${a}`]=!0;this._pfs.$$set({selected_filters:t})}destroy(){this._pfs.$destroy()}};window.PagefindUI=mt;})(); diff --git a/docs/pagefind/pagefind-worker.js b/docs/pagefind/pagefind-worker.js new file mode 100644 index 00000000..e2b1dd2f --- /dev/null +++ b/docs/pagefind/pagefind-worker.js @@ -0,0 +1,6 @@ +const pagefind_version="1.5.2";let wasm_bindgen=(function(exports){let script_src;if(typeof document!=='undefined'&&document.currentScript!==null){script_src=new URL("UNHANDLED",location.href).toString();}function add_synthetic_filter(ptr,filter){const ptr0=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.add_synthetic_filter(ptr,ptr0,len0);return ret>>>0;}exports.add_synthetic_filter=add_synthetic_filter;function enter_playground_mode(ptr){const ret=wasm.enter_playground_mode(ptr);return ret>>>0;}exports.enter_playground_mode=enter_playground_mode;function filters(ptr){let deferred1_0;let deferred1_1;try{const ret=wasm.filters(ptr);deferred1_0=ret[0];deferred1_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred1_0,deferred1_1,1);}}exports.filters=filters;function init_pagefind(metadata_bytes){const ptr0=passArray8ToWasm0(metadata_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.init_pagefind(ptr0,len0);return ret>>>0;}exports.init_pagefind=init_pagefind;function load_filter_chunk(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_filter_chunk(ptr,ptr0,len0);return ret>>>0;}exports.load_filter_chunk=load_filter_chunk;function load_index_chunk(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_index_chunk(ptr,ptr0,len0);return ret>>>0;}exports.load_index_chunk=load_index_chunk;function request_all_filter_indexes(ptr){let deferred1_0;let deferred1_1;try{const ret=wasm.request_all_filter_indexes(ptr);deferred1_0=ret[0];deferred1_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred1_0,deferred1_1,1);}}exports.request_all_filter_indexes=request_all_filter_indexes;function request_filter_indexes(ptr,filters){let deferred2_0;let deferred2_1;try{const ptr0=passStringToWasm0(filters,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.request_filter_indexes(ptr,ptr0,len0);deferred2_0=ret[0];deferred2_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred2_0,deferred2_1,1);}}exports.request_filter_indexes=request_filter_indexes;function request_indexes(ptr,query){let deferred2_0;let deferred2_1;try{const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.request_indexes(ptr,ptr0,len0);deferred2_0=ret[0];deferred2_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred2_0,deferred2_1,1);}}exports.request_indexes=request_indexes;function search(ptr,query,original_query,filter,sort,exact,exact_diacritics){let deferred5_0;let deferred5_1;try{const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ptr1=passStringToWasm0(original_query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len1=WASM_VECTOR_LEN;const ptr2=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len2=WASM_VECTOR_LEN;const ptr3=passStringToWasm0(sort,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len3=WASM_VECTOR_LEN;const ret=wasm.search(ptr,ptr0,len0,ptr1,len1,ptr2,len2,ptr3,len3,exact,exact_diacritics);deferred5_0=ret[0];deferred5_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred5_0,deferred5_1,1);}}exports.search=search;function set_ranking_weights(ptr,weights){const ptr0=passStringToWasm0(weights,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.set_ranking_weights(ptr,ptr0,len0);return ret>>>0;}exports.set_ranking_weights=set_ranking_weights;function __wbg_get_imports(){const import0={__proto__:null,__wbindgen_init_externref_table:function(){const table=wasm.__wbindgen_externrefs;const offset=table.grow(4);table.set(0,undefined);table.set(offset+0,undefined);table.set(offset+1,null);table.set(offset+2,true);table.set(offset+3,false);},};return{__proto__:null,"./pagefind_web_bg.js":import0,};}function getStringFromWasm0(ptr,len){ptr=ptr>>>0;return decodeText(ptr,len);}let cachedUint8ArrayMemory0=null;function getUint8ArrayMemory0(){if(cachedUint8ArrayMemory0===null||cachedUint8ArrayMemory0.byteLength===0){cachedUint8ArrayMemory0=new Uint8Array(wasm.memory.buffer);}return cachedUint8ArrayMemory0;}function passArray8ToWasm0(arg,malloc){const ptr=malloc(arg.length*1,1)>>>0;getUint8ArrayMemory0().set(arg,ptr/1);WASM_VECTOR_LEN=arg.length;return ptr;}function passStringToWasm0(arg,malloc,realloc){if(realloc===undefined){const buf=cachedTextEncoder.encode(arg);const ptr=malloc(buf.length,1)>>>0;getUint8ArrayMemory0().subarray(ptr,ptr+buf.length).set(buf);WASM_VECTOR_LEN=buf.length;return ptr;}let len=arg.length;let ptr=malloc(len,1)>>>0;const mem=getUint8ArrayMemory0();let offset=0;for(;offset0x7F)break;mem[ptr+offset]=code;}if(offset!==len){if(offset!==0){arg=arg.slice(offset);}ptr=realloc(ptr,len,len=offset+arg.length*3,1)>>>0;const view=getUint8ArrayMemory0().subarray(ptr+offset,ptr+len);const ret=cachedTextEncoder.encodeInto(arg,view);offset+=ret.written;ptr=realloc(ptr,len,offset,1)>>>0;}WASM_VECTOR_LEN=offset;return ptr;}let cachedTextDecoder=new TextDecoder('utf-8',{ignoreBOM:true,fatal:true});cachedTextDecoder.decode();function decodeText(ptr,len){return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr,ptr+len));}const cachedTextEncoder=new TextEncoder();if(!('encodeInto'in cachedTextEncoder)){cachedTextEncoder.encodeInto=function(arg,view){const buf=cachedTextEncoder.encode(arg);view.set(buf);return{read:arg.length,written:buf.length};};}let WASM_VECTOR_LEN=0;let wasmModule,wasm;function __wbg_finalize_init(instance,module){wasm=instance.exports;wasmModule=module;cachedUint8ArrayMemory0=null;wasm.__wbindgen_start();return wasm;}async function __wbg_load(module,imports){if(typeof Response==='function'&&module instanceof Response){if(typeof WebAssembly.instantiateStreaming==='function'){try{return await WebAssembly.instantiateStreaming(module,imports);}catch(e){const validResponse=module.ok&&expectedResponseType(module.type);if(validResponse&&module.headers.get('Content-Type')!=='application/wasm'){console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",e);}else{throw e;}}}const bytes=await module.arrayBuffer();return await WebAssembly.instantiate(bytes,imports);}else{const instance=await WebAssembly.instantiate(module,imports);if(instance instanceof WebAssembly.Instance){return{instance,module};}else{return instance;}}function expectedResponseType(type){switch(type){case'basic':case'cors':case'default':return true;}return false;}}function initSync(module){if(wasm!==undefined)return wasm;if(module!==undefined){if(Object.getPrototypeOf(module)===Object.prototype){({module}=module)}else{console.warn('using deprecated parameters for `initSync()`; pass a single object instead')}}const imports=__wbg_get_imports();if(!(module instanceof WebAssembly.Module)){module=new WebAssembly.Module(module);}const instance=new WebAssembly.Instance(module,imports);return __wbg_finalize_init(instance,module);}async function __wbg_init(module_or_path){if(wasm!==undefined)return wasm;if(module_or_path!==undefined){if(Object.getPrototypeOf(module_or_path)===Object.prototype){({module_or_path}=module_or_path)}else{console.warn('using deprecated parameters for the initialization function; pass a single object instead')}}if(module_or_path===undefined&&script_src!==undefined){module_or_path=script_src.replace(/\.js$/,"_bg.wasm");}const imports=__wbg_get_imports();if(typeof module_or_path==='string'||(typeof Request==='function'&&module_or_path instanceof Request)||(typeof URL==='function'&&module_or_path instanceof URL)){module_or_path=fetch(module_or_path);}const{instance,module}=await __wbg_load(await module_or_path,imports);return __wbg_finalize_init(instance,module);}return Object.assign(__wbg_init,{initSync},exports);})({__proto__:null});"use strict";(()=>{var __defProp=Object.defineProperty;var __defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:true,configurable:true,writable:true,value}):obj[key]=value;var __publicField=(obj,key,value)=>__defNormalProp(obj,typeof key!=="symbol"?key+"":key,value);var u8=Uint8Array;var u16=Uint16Array;var u32=Uint32Array;var fleb=new u8([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]);var fdeb=new u8([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]);var clim=new u8([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);var freb=function(eb,start){var b=new u16(31);for(var i2=0;i2<31;++i2){b[i2]=start+=1<>>1|(i&21845)<<1;x=(x&52428)>>>2|(x&13107)<<2;x=(x&61680)>>>4|(x&3855)<<4;rev[i]=((x&65280)>>>8|(x&255)<<8)>>>1;}var x;var i;var hMap=function(cd,mb,r){var s=cd.length;var i2=0;var l=new u16(mb);for(;i2>>rvb]=sv;}}}}else{co=new u16(s);for(i2=0;i2>>15-cd[i2];}}}return co;};var flt=new u8(288);for(i=0;i<144;++i)flt[i]=8;var i;for(i=144;i<256;++i)flt[i]=9;var i;for(i=256;i<280;++i)flt[i]=7;var i;for(i=280;i<288;++i)flt[i]=8;var i;var fdt=new u8(32);for(i=0;i<32;++i)fdt[i]=5;var i;var flrm=hMap(flt,9,1);var fdrm=hMap(fdt,5,1);var max=function(a){var m=a[0];for(var i2=1;i2m)m=a[i2];}return m;};var bits=function(d,p,m){var o=p/8|0;return(d[o]|d[o+1]<<8)>>(p&7)&m;};var bits16=function(d,p){var o=p/8|0;return(d[o]|d[o+1]<<8|d[o+2]<<16)>>(p&7);};var shft=function(p){return(p+7)/8|0;};var slc=function(v,s,e){if(s==null||s<0)s=0;if(e==null||e>v.length)e=v.length;var n=new(v.BYTES_PER_ELEMENT==2?u16:v.BYTES_PER_ELEMENT==4?u32:u8)(e-s);n.set(v.subarray(s,e));return n;};var ec=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"];var err=function(ind,msg,nt){var e=new Error(msg||ec[ind]);e.code=ind;if(Error.captureStackTrace)Error.captureStackTrace(e,err);if(!nt)throw e;return e;};var inflt=function(dat,buf,st){var sl=dat.length;if(!sl||st&&st.f&&!st.l)return buf||new u8(0);var noBuf=!buf||st;var noSt=!st||st.i;if(!st)st={};if(!buf)buf=new u8(sl*3);var cbuf=function(l2){var bl=buf.length;if(l2>bl){var nbuf=new u8(Math.max(bl*2,l2));nbuf.set(buf);buf=nbuf;}};var final=st.f||0,pos=st.p||0,bt=st.b||0,lm=st.l,dm=st.d,lbt=st.m,dbt=st.n;var tbts=sl*8;do{if(!lm){final=bits(dat,pos,1);var type=bits(dat,pos+1,3);pos+=3;if(!type){var s=shft(pos)+4,l=dat[s-4]|dat[s-3]<<8,t=s+l;if(t>sl){if(noSt)err(0);break;}if(noBuf)cbuf(bt+l);buf.set(dat.subarray(s,t),bt);st.b=bt+=l,st.p=pos=t*8,st.f=final;continue;}else if(type==1)lm=flrm,dm=fdrm,lbt=9,dbt=5;else if(type==2){var hLit=bits(dat,pos,31)+257,hcLen=bits(dat,pos+10,15)+4;var tl=hLit+bits(dat,pos+5,31)+1;pos+=14;var ldt=new u8(tl);var clt=new u8(19);for(var i2=0;i2>>4;if(s<16){ldt[i2++]=s;}else{var c=0,n=0;if(s==16)n=3+bits(dat,pos,3),pos+=2,c=ldt[i2-1];else if(s==17)n=3+bits(dat,pos,7),pos+=3;else if(s==18)n=11+bits(dat,pos,127),pos+=7;while(n--)ldt[i2++]=c;}}var lt=ldt.subarray(0,hLit),dt=ldt.subarray(hLit);lbt=max(lt);dbt=max(dt);lm=hMap(lt,lbt,1);dm=hMap(dt,dbt,1);}else err(1);if(pos>tbts){if(noSt)err(0);break;}}if(noBuf)cbuf(bt+131072);var lms=(1<>>4;pos+=c&15;if(pos>tbts){if(noSt)err(0);break;}if(!c)err(2);if(sym<256)buf[bt++]=sym;else if(sym==256){lpos=pos,lm=null;break;}else{var add=sym-254;if(sym>264){var i2=sym-257,b=fleb[i2];add=bits(dat,pos,(1<>>4;if(!d)err(3);pos+=d&15;var dt=fd[dsym];if(dsym>3){var b=fdeb[dsym];dt+=bits16(dat,pos)&(1<tbts){if(noSt)err(0);break;}if(noBuf)cbuf(bt+131072);var end=bt+add;for(;bt>3&1)+(flg>>4&1);zs>0;zs-=!d[st++]);return st+(flg&2);};var gzl=function(d){var l=d.length;return(d[l-4]|d[l-3]<<8|d[l-2]<<16|d[l-1]<<24)>>>0;};function gunzipSync(data,out){return inflt(data.subarray(gzs(data),-8),out||new u8(gzl(data)));}var td=typeof TextDecoder!="undefined"&&new TextDecoder();var tds=0;try{td.decode(et,{stream:true});tds=1;}catch(e){}var gz_default=gunzipSync;var calculate_excerpt_region=(word_positions,excerpt_length)=>{if(word_positions.length===0){return 0;}let words=[];for(const word of word_positions){words[word.location]=words[word.location]||0;words[word.location]+=word.balanced_score;}if(words.length<=excerpt_length){return 0;}let densest=words.slice(0,excerpt_length).reduce((partialSum,a)=>partialSum+a,0);let working_sum=densest;let densest_at=[0];for(let i2=0;i2densest){densest=working_sum;densest_at=[i2];}else if(working_sum===densest&&densest_at[densest_at.length-1]===i2-1){densest_at.push(i2);}}let midpoint=densest_at[Math.floor(densest_at.length/2)];return midpoint;};var build_excerpt=(content,start,length,locations,not_before,not_from)=>{let is_zws_delimited=content.includes("\u200B");let fragment_words=[];if(is_zws_delimited){fragment_words=content.split("\u200B");}else{fragment_words=content.split(/[\r\n\s]+/g);}let endcap=not_from??fragment_words.length;let startcap=not_before??0;if(endcap-startcapendcap){start=endcap-length;}if(start`)){continue;}fragment_words[word]=`${fragment_words[word]}`;}const excerpt=fragment_words.slice(start,start+length).join(joiner).trim();return{excerpt,plain_excerpt};};var calculate_sub_results=(fragment,desired_excerpt_length)=>{const effective_url=fragment.meta?.url||fragment.url;const anchors=fragment.anchors.filter((a)=>/h\d/i.test(a.element)&&a.text?.length&&/\S/.test(a.text)).sort((a,b)=>a.location-b.location);const results=[];let current_anchor_position=0;let current_anchor={title:fragment.meta["title"],url:effective_url,weighted_locations:[],locations:[],excerpt:"",plain_excerpt:""};const add_result=(end_range)=>{if(current_anchor.locations.length){const relative_weighted_locations=current_anchor.weighted_locations.map((l)=>{return{weight:l.weight,balanced_score:l.balanced_score,location:l.location-current_anchor_position};});const excerpt_start=calculate_excerpt_region(relative_weighted_locations,desired_excerpt_length)+current_anchor_position;const excerpt_length=end_range?Math.min(end_range-excerpt_start,desired_excerpt_length):desired_excerpt_length;const excerpts=build_excerpt(fragment.raw_content??"",excerpt_start,excerpt_length,current_anchor.locations,current_anchor_position,end_range);current_anchor.excerpt=excerpts.excerpt;current_anchor.plain_excerpt=excerpts.plain_excerpt;results.push(current_anchor);}};for(let word of fragment.weighted_locations){if(!anchors.length||word.location=anchors[0].location){next_anchor=anchors.shift();}let anchored_url=effective_url;try{const url_is_fq=/^((https?:)?\/\/)/.test(anchored_url);if(url_is_fq){let fq_url=new URL(anchored_url);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString();}else{if(!/^\//.test(anchored_url)){anchored_url=`/${anchored_url}`;}let fq_url=new URL(`https://example.com${anchored_url}`);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString().replace(/^https:\/\/example.com/,"");}}catch(e){console.error(`Pagefind: Couldn't process ${anchored_url} for a search result`);}current_anchor_position=next_anchor.location;current_anchor={title:next_anchor.text,url:anchored_url,anchor:next_anchor,weighted_locations:[word],locations:[word.location],excerpt:"",plain_excerpt:""};}}add_result(anchors[0]?.location);return results;};var import_meta={};var asyncSleep=async(ms=100)=>{return new Promise((r)=>setTimeout(r,ms));};var normalizeDiacritics=(str)=>{return str.normalize("NFD").replace(/\p{M}/gu,"");};var isBrowser=()=>typeof window!=="undefined"&&typeof document!=="undefined";var needsWordSegmentation=(lang)=>{if(!lang)return false;const primaryLang=lang.split("-")[0].toLowerCase();return["zh","ja","th"].includes(primaryLang);};var PagefindInstance=class{constructor(opts={}){__publicField(this,"backend");__publicField(this,"decoder");__publicField(this,"wasm");__publicField(this,"basePath");__publicField(this,"baseUrl");__publicField(this,"primary");__publicField(this,"indexWeight");__publicField(this,"excerptLength");__publicField(this,"mergeFilter");__publicField(this,"ranking");__publicField(this,"highlightParam");__publicField(this,"exactDiacritics");__publicField(this,"metaCacheTag");__publicField(this,"loaded_chunks");__publicField(this,"loaded_filters");__publicField(this,"loaded_fragments");__publicField(this,"fetchQueue",[]);__publicField(this,"activeFetches",0);__publicField(this,"maxConcurrentFetches",100);__publicField(this,"raw_ptr");__publicField(this,"initError");__publicField(this,"searchMeta");__publicField(this,"languages");__publicField(this,"loadedLanguage");__publicField(this,"includeCharacters");__publicField(this,"version");__publicField(this,"loadedVersion");this.version=pagefind_version;this.backend=wasm_bindgen;this.decoder=new TextDecoder("utf-8");this.wasm=null;let basePath=opts.basePath||"/pagefind/";let primary=opts.primary||false;if(primary&&!opts.basePath&&isBrowser()){basePath=this.initPrimaryBasePath(basePath);}if(/[^\/]$/.test(basePath)){basePath=`${basePath}/`;}if(isBrowser()&&window?.location?.origin&&basePath.startsWith(window.location.origin)){basePath=basePath.replace(window.location.origin,"");}this.basePath=basePath;this.baseUrl=opts.baseUrl||this.getDefaultBaseUrl(basePath);if(!/^(\/|https?:\/\/)/.test(this.baseUrl)){this.baseUrl=`/${this.baseUrl}`;}this.primary=primary;this.indexWeight=opts.indexWeight??1;this.excerptLength=opts.excerptLength??30;this.mergeFilter=opts.mergeFilter??{};this.ranking=opts.ranking;this.highlightParam=opts.highlightParam??null;this.exactDiacritics=opts.exactDiacritics??false;this.metaCacheTag=opts.metaCacheTag??null;this.loaded_chunks={};this.loaded_filters={};this.loaded_fragments={};this.raw_ptr=null;this.initError=null;this.searchMeta=null;this.languages=null;}throttledFetch(input){return new Promise((resolve,reject)=>{this.fetchQueue.push({resolve,reject,input});this.dequeueNextFetch();});}dequeueNextFetch(){while(this.fetchQueue.length>0&&this.activeFetchesb.page_count-a.page_count);if(topLang[0])return topLang[0];}throw new Error("Pagefind Error: No language indexes found.");}async loadMeta(index){try{let compressed_resp=await this.throttledFetch(`${this.basePath}pagefind.${index}.pf_meta`);let compressed_meta=await compressed_resp.arrayBuffer();this.searchMeta=this.decompress(new Uint8Array(compressed_meta),"Pagefind metadata");}catch(e){console.error(`Failed to load the meta index: +${e?.toString()}`);}}async loadWasm(language){try{const wasm_url=`${this.basePath}wasm.${language}.pagefind`;let compressed_resp=await this.throttledFetch(wasm_url);let compressed_wasm=await compressed_resp.arrayBuffer();const final_wasm=this.decompress(new Uint8Array(compressed_wasm),"Pagefind WebAssembly");if(!final_wasm){throw new Error("No WASM after decompression");}this.wasm=await this.backend({module_or_path:final_wasm});}catch(e){console.error(`Failed to load the Pagefind WASM: +${e?.toString()}`);throw new Error(`Failed to load the Pagefind WASM: +${e?.toString()}`);}}async _loadGenericChunk(url,method){try{let compressed_resp=await this.throttledFetch(url);let compressed_chunk=await compressed_resp.arrayBuffer();let chunk=this.decompress(new Uint8Array(compressed_chunk),url);let ptr=await this.getPtr();this.raw_ptr=this.backend[method](ptr,chunk);}catch(e){console.error(`Failed to load the index chunk ${url}: +${e?.toString()}`);}}async loadChunk(hash){if(!this.loaded_chunks[hash]){const url=`${this.basePath}index/${hash}.pf_index`;this.loaded_chunks[hash]=this._loadGenericChunk(url,"load_index_chunk");}return await this.loaded_chunks[hash];}async loadFilterChunk(hash){if(!this.loaded_filters[hash]){const url=`${this.basePath}filter/${hash}.pf_filter`;this.loaded_filters[hash]=this._loadGenericChunk(url,"load_filter_chunk");}return await this.loaded_filters[hash];}async _loadFragment(hash){let compressed_resp=await this.throttledFetch(`${this.basePath}fragment/${hash}.pf_fragment`);let compressed_fragment=await compressed_resp.arrayBuffer();let fragment=this.decompress(new Uint8Array(compressed_fragment),`Fragment ${hash}`);return JSON.parse(new TextDecoder().decode(fragment));}async loadFragment(hash,weighted_locations=[],search_term){if(!this.loaded_fragments[hash]){this.loaded_fragments[hash]=this._loadFragment(hash);}let fragment=await this.loaded_fragments[hash];fragment.weighted_locations=weighted_locations;fragment.locations=weighted_locations.map((l)=>l.location);if(!fragment.raw_content){fragment.raw_content=fragment.content.replace(//g,">");fragment.content=fragment.content.replace(/\u200B/g,"");}if(!fragment.raw_url){fragment.raw_url=fragment.url;}fragment.url=this.processedUrl(fragment.raw_url,search_term);const excerpt_start=calculate_excerpt_region(weighted_locations,this.excerptLength);const excerpts=build_excerpt(fragment.raw_content,excerpt_start,this.excerptLength,fragment.locations);fragment.excerpt=excerpts.excerpt;fragment.plain_excerpt=excerpts.plain_excerpt;fragment.sub_results=calculate_sub_results(fragment,this.excerptLength);return fragment;}fullUrl(raw){if(/^(https?:)?\/\//.test(raw)){return raw;}return`${this.baseUrl}/${raw}`.replace(/\/+/g,"/").replace(/^(https?:\/)/,"$1/");}processedUrl(url,search_term){const normalized=this.fullUrl(url);if(this.highlightParam===null){return normalized;}let individual_terms=search_term.split(/\s+/);try{let processed=new URL(normalized);for(const term of individual_terms){processed.searchParams.append(this.highlightParam,term);}return processed.toString();}catch(e){try{let processed=new URL(`https://example.com${normalized}`);for(const term of individual_terms){processed.searchParams.append(this.highlightParam,term);}return processed.toString().replace(/^https:\/\/example\.com/,"");}catch(e2){return normalized;}}}async getPtr(){while(this.raw_ptr===null){if(this.initError){throw this.initError;}await asyncSleep(50);}if(!this.raw_ptr){console.error("Pagefind: WASM Error (No pointer)");throw new Error("Pagefind: WASM Error (No pointer)");}return this.raw_ptr;}stringifyFilters(obj={}){return JSON.stringify(obj);}stringifySorts(obj={}){let sorts=Object.entries(obj);for(let[sort,direction]of sorts){if(sorts.length>1){console.warn(`Pagefind was provided multiple sort options in this search, but can only operate on one. Using the ${sort} sort.`);}if(direction!=="asc"&&direction!=="desc"){console.warn(`Pagefind was provided a sort with unknown direction ${direction}. Supported: [asc, desc]`);}return`${sort}:${direction}`;}return``;}async filters(){let ptr=await this.getPtr();let filters=this.backend.request_all_filter_indexes(ptr);let filter_array=JSON.parse(filters);if(Array.isArray(filter_array)){let filter_chunks=filter_array.filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...filter_chunks]);}ptr=await this.getPtr();let results=this.backend.filters(ptr);return JSON.parse(results);}async preload(term,options={}){await this.search(term,{...options,preload:true});}async search(term,options={}){options={verbose:false,filters:{},sort:{},...options};const log=(str)=>{if(options.verbose)console.log(str);};log(`Starting search on ${this.basePath}`);let start=Date.now();let ptr=await this.getPtr();let filter_only=term===null;term=term??"";let exact_search=/^\s*".+"\s*$/.test(term);if(exact_search){log(`Running an exact search`);}let trueLanguage=null;try{trueLanguage=Intl.getCanonicalLocales(this.loadedLanguage)[0];}catch(err2){}const term_chunks=[];if(trueLanguage&&typeof Intl.Segmenter!=="undefined"){const graphemeSegmenter=new Intl.Segmenter(trueLanguage,{granularity:"grapheme"});if(needsWordSegmentation(trueLanguage)){const wordSegmenter=new Intl.Segmenter(trueLanguage,{granularity:"word"});for(const{segment:word}of wordSegmenter.segment(term)){const wordChunks=[];for(const{segment:grapheme}of graphemeSegmenter.segment(word)){if(this.includeCharacters?.includes(grapheme)){wordChunks.push(grapheme);}else if(!/^\p{Pd}|\p{Pe}|\p{Pf}|\p{Pi}|\p{Po}|\p{Ps}$/u.test(grapheme)){wordChunks.push(grapheme.toLocaleLowerCase());}}if(wordChunks.length>0){term_chunks.push(wordChunks.join(""));}}term=term_chunks.join(" ").replace(/\s{2,}/g," ").trim();}else{for(const{segment:grapheme}of graphemeSegmenter.segment(term)){if(this.includeCharacters?.includes(grapheme)){term_chunks.push(grapheme);}else if(!/^\p{Pd}|\p{Pe}|\p{Pf}|\p{Pi}|\p{Po}|\p{Ps}$/u.test(grapheme)){term_chunks.push(grapheme.toLocaleLowerCase());}}term=term_chunks.join("").replace(/\s{2,}/g," ").trim();}}else{for(const char of term){if(this.includeCharacters?.includes(char)){term_chunks.push(char);}else if(!/^\p{Pd}|\p{Pe}|\p{Pf}|\p{Pi}|\p{Po}|\p{Ps}$/u.test(char)){term_chunks.push(char.toLocaleLowerCase());}}term=term_chunks.join("").replace(/\s{2,}/g," ").trim();}const originalTerm=term;term=normalizeDiacritics(term);log(`Normalized search term to ${term}`);if(!term?.length&&!filter_only){return{results:[],unfilteredResultCount:0,filters:{},totalFilters:{},timings:{preload:Date.now()-start,search:Date.now()-start,total:Date.now()-start}};}let sort_list=this.stringifySorts(options.sort);log(`Stringified sort to ${sort_list}`);const filter_list=this.stringifyFilters(options.filters);log(`Stringified filters to ${filter_list}`);let index_resp=this.backend.request_indexes(ptr,term);let index_array=JSON.parse(index_resp);let filter_resp=this.backend.request_filter_indexes(ptr,filter_list);let filter_array=JSON.parse(filter_resp);let chunks=index_array.filter((v)=>v).map((chunk)=>this.loadChunk(chunk));let filter_chunks=filter_array.filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...chunks,...filter_chunks]);log(`Loaded necessary chunks to run search`);if(options.preload){log(`Preload \u2014 bailing out of search operation now.`);return null;}ptr=await this.getPtr();let searchStart=Date.now();let result=this.backend.search(ptr,term,originalTerm,filter_list,sort_list,exact_search,this.exactDiacritics);log(`Got the raw search result: ${result}`);let{filtered_counts,total_counts,results,unfiltered_total,search_keywords,query_term_idfs}=JSON.parse(result);let resultsInterface=results.map((result2)=>{let weighted_locations=result2.l.map((l)=>{let loc={weight:l.w/24,balanced_score:l.s,location:l.l};if(l.v){loc.verbose={word_string:l.v.ws,length_bonus:l.v.lb};}return loc;});let locations=weighted_locations.map((l)=>l.location);let res={id:result2.p,score:result2.s*this.indexWeight,words:locations,data:async()=>await this.loadFragment(result2.p,weighted_locations,term)};if(result2.params){res.params={document_length:result2.params.dl,average_page_length:result2.params.apl,total_pages:result2.params.tp};}if(result2.scores){res.scores=result2.scores.map((r)=>{return{search_term:r.w,idf:r.idf,saturating_tf:r.b_tf,raw_tf:r.r_tf,pagefind_tf:r.p_tf,score:r.s,params:{weighted_term_frequency:r.params.w_tf,pages_containing_term:r.params.pct,length_bonus:r.params.lb}};});}if(result2.mf&&result2.mf.length>0){res.matchedMetaFields=result2.mf;}if(result2.vms&&result2.vms.length>0){res.verbose_meta_scores=result2.vms.map((s)=>({field_name:s.fn,field_weight:s.fw,matched_terms:s.mt,matched_idf:s.mi,query_total_idf:s.ti,coverage:s.cv,coverage_boost:s.cb}));}return res;});const searchTime=Date.now()-searchStart;const realTime=Date.now()-start;log(`Found ${results.length} result${results.length == 1 ? "" : "s"} for "${term}" in ${Date.now() - searchStart}ms (${Date.now() - start}ms realtime)`);let response={results:resultsInterface,unfilteredResultCount:unfiltered_total,filters:filtered_counts,totalFilters:total_counts,timings:{preload:realTime-searchTime,search:searchTime,total:realTime}};if(search_keywords){response.search_keywords=search_keywords;}if(query_term_idfs){response.query_term_idfs=query_term_idfs.map((q)=>({term:q.t,idf:q.i}));}return response;}};var Pagefind=class{constructor(options={}){__publicField(this,"primaryLanguage");__publicField(this,"searchID");__publicField(this,"primary");__publicField(this,"instances");this.primaryLanguage="unknown";this.searchID=0;this.primary=new PagefindInstance({...options,primary:true});this.instances=[this.primary];this.init(options?.language);}async options(options){await this.primary.options(options);}async enterPlaygroundMode(){await this.primary.enterPlaygroundMode();}async init(overrideLanguage){if(isBrowser()&&document?.querySelector){const langCode=document.querySelector("html")?.getAttribute("lang")||"unknown";this.primaryLanguage=langCode.toLocaleLowerCase();}if(overrideLanguage){this.primaryLanguage=overrideLanguage;}await this.primary.init(overrideLanguage?overrideLanguage:this.primaryLanguage,{load_wasm:true});}async mergeIndex(indexPath,options={}){if(this.primary.basePath.startsWith(indexPath)){console.warn(`Skipping mergeIndex ${indexPath} that appears to be the same as the primary index (${this.primary.basePath})`);return;}let newInstance=new PagefindInstance({primary:false,basePath:indexPath,...options});this.instances.push(newInstance);while(this.primary.wasm===null){await asyncSleep(50);}await newInstance.init(options.language||this.primaryLanguage,{load_wasm:false});const{language,...remainingOptions}=options;await newInstance.options(remainingOptions);}mergeFilters(filters){const merged={};for(const searchFilter of filters){for(const[filterKey,values]of Object.entries(searchFilter)){if(!merged[filterKey]){merged[filterKey]=values;continue;}else{const filter=merged[filterKey];for(const[valueKey,count]of Object.entries(values)){filter[valueKey]=(filter[valueKey]||0)+count;}}}}return merged;}async filters(){let filters=await Promise.all(this.instances.map((i2)=>i2.filters()));return this.mergeFilters(filters);}async preload(term,options={}){await Promise.all(this.instances.map((i2)=>i2.preload(term,options)));}async debouncedSearch(term,options,debounceTimeoutMs){const thisSearchID=++this.searchID;this.preload(term,options);await asyncSleep(debounceTimeoutMs);if(thisSearchID!==this.searchID){return null;}const searchResult=await this.search(term,options);if(thisSearchID!==this.searchID){return null;}return searchResult;}async search(term,options={}){let search=await Promise.all(this.instances.map((i2)=>i2.search(term,options)));const filters=this.mergeFilters(search.map((s)=>s.filters));const totalFilters=this.mergeFilters(search.map((s)=>s.totalFilters));const results=search.map((s)=>s.results).flat().sort((a,b)=>b.score-a.score);const timings=search.map((s)=>s.timings);const unfilteredResultCount=search.reduce((sum,s)=>sum+s.unfilteredResultCount,0);let response={results,unfilteredResultCount,filters,totalFilters,timings};if(search[0].search_keywords){response.search_keywords=search[0].search_keywords;}if(search[0].query_term_idfs){response.query_term_idfs=search[0].query_term_idfs;}return response;}};var dataCallbacks=new Map();var instanceDataIds=new Map();var instances=new Map();var DEFAULT_INSTANCE="default";var getInstance=(instanceId)=>{const instance=instances.get(instanceId);if(!instance){throw new Error(`Pagefind instance "${instanceId}" not initialized`);}return instance;};var registerDataCallback=(instanceId,dataId,dataFn)=>{dataCallbacks.set(dataId,{getData:dataFn});if(!instanceDataIds.has(instanceId)){instanceDataIds.set(instanceId,new Set());}instanceDataIds.get(instanceId).add(dataId);};var handleMessage=async(message)=>{const{id,method,args}=message;const instanceId=message.instanceId??DEFAULT_INSTANCE;try{switch(method){case"init":{const[options]=args;instances.set(instanceId,new Pagefind(options));return{id,result:true};}case"options":{const pagefindInstance=getInstance(instanceId);const[options]=args;await pagefindInstance.options(options);return{id,result:true};}case"enterPlaygroundMode":{const pagefindInstance=getInstance(instanceId);await pagefindInstance.enterPlaygroundMode();return{id,result:true};}case"mergeIndex":{const pagefindInstance=getInstance(instanceId);const[indexPath,options]=args;await pagefindInstance.mergeIndex(indexPath,options);return{id,result:true};}case"search":{const pagefindInstance=getInstance(instanceId);const[term,options]=args;const results=await pagefindInstance.search(term,options);if(results&&results.results){for(let i2=0;i2{const message=event.data;const response=await handleMessage(message);self.postMessage(response);});})(); \ No newline at end of file diff --git a/docs/pagefind/pagefind.en-us_27757699fa6c6.pf_meta b/docs/pagefind/pagefind.en-us_27757699fa6c6.pf_meta new file mode 100644 index 00000000..428d5c44 Binary files /dev/null and b/docs/pagefind/pagefind.en-us_27757699fa6c6.pf_meta differ diff --git a/docs/pagefind/pagefind.js b/docs/pagefind/pagefind.js new file mode 100644 index 00000000..1e38c36c --- /dev/null +++ b/docs/pagefind/pagefind.js @@ -0,0 +1,6 @@ +const pagefind_version="1.5.2";let wasm_bindgen=(function(exports){let script_src;if(typeof document!=='undefined'&&document.currentScript!==null){script_src=new URL("UNHANDLED",location.href).toString();}function add_synthetic_filter(ptr,filter){const ptr0=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.add_synthetic_filter(ptr,ptr0,len0);return ret>>>0;}exports.add_synthetic_filter=add_synthetic_filter;function enter_playground_mode(ptr){const ret=wasm.enter_playground_mode(ptr);return ret>>>0;}exports.enter_playground_mode=enter_playground_mode;function filters(ptr){let deferred1_0;let deferred1_1;try{const ret=wasm.filters(ptr);deferred1_0=ret[0];deferred1_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred1_0,deferred1_1,1);}}exports.filters=filters;function init_pagefind(metadata_bytes){const ptr0=passArray8ToWasm0(metadata_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.init_pagefind(ptr0,len0);return ret>>>0;}exports.init_pagefind=init_pagefind;function load_filter_chunk(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_filter_chunk(ptr,ptr0,len0);return ret>>>0;}exports.load_filter_chunk=load_filter_chunk;function load_index_chunk(ptr,chunk_bytes){const ptr0=passArray8ToWasm0(chunk_bytes,wasm.__wbindgen_malloc);const len0=WASM_VECTOR_LEN;const ret=wasm.load_index_chunk(ptr,ptr0,len0);return ret>>>0;}exports.load_index_chunk=load_index_chunk;function request_all_filter_indexes(ptr){let deferred1_0;let deferred1_1;try{const ret=wasm.request_all_filter_indexes(ptr);deferred1_0=ret[0];deferred1_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred1_0,deferred1_1,1);}}exports.request_all_filter_indexes=request_all_filter_indexes;function request_filter_indexes(ptr,filters){let deferred2_0;let deferred2_1;try{const ptr0=passStringToWasm0(filters,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.request_filter_indexes(ptr,ptr0,len0);deferred2_0=ret[0];deferred2_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred2_0,deferred2_1,1);}}exports.request_filter_indexes=request_filter_indexes;function request_indexes(ptr,query){let deferred2_0;let deferred2_1;try{const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.request_indexes(ptr,ptr0,len0);deferred2_0=ret[0];deferred2_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred2_0,deferred2_1,1);}}exports.request_indexes=request_indexes;function search(ptr,query,original_query,filter,sort,exact,exact_diacritics){let deferred5_0;let deferred5_1;try{const ptr0=passStringToWasm0(query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ptr1=passStringToWasm0(original_query,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len1=WASM_VECTOR_LEN;const ptr2=passStringToWasm0(filter,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len2=WASM_VECTOR_LEN;const ptr3=passStringToWasm0(sort,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len3=WASM_VECTOR_LEN;const ret=wasm.search(ptr,ptr0,len0,ptr1,len1,ptr2,len2,ptr3,len3,exact,exact_diacritics);deferred5_0=ret[0];deferred5_1=ret[1];return getStringFromWasm0(ret[0],ret[1]);}finally{wasm.__wbindgen_free(deferred5_0,deferred5_1,1);}}exports.search=search;function set_ranking_weights(ptr,weights){const ptr0=passStringToWasm0(weights,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;const ret=wasm.set_ranking_weights(ptr,ptr0,len0);return ret>>>0;}exports.set_ranking_weights=set_ranking_weights;function __wbg_get_imports(){const import0={__proto__:null,__wbindgen_init_externref_table:function(){const table=wasm.__wbindgen_externrefs;const offset=table.grow(4);table.set(0,undefined);table.set(offset+0,undefined);table.set(offset+1,null);table.set(offset+2,true);table.set(offset+3,false);},};return{__proto__:null,"./pagefind_web_bg.js":import0,};}function getStringFromWasm0(ptr,len){ptr=ptr>>>0;return decodeText(ptr,len);}let cachedUint8ArrayMemory0=null;function getUint8ArrayMemory0(){if(cachedUint8ArrayMemory0===null||cachedUint8ArrayMemory0.byteLength===0){cachedUint8ArrayMemory0=new Uint8Array(wasm.memory.buffer);}return cachedUint8ArrayMemory0;}function passArray8ToWasm0(arg,malloc){const ptr=malloc(arg.length*1,1)>>>0;getUint8ArrayMemory0().set(arg,ptr/1);WASM_VECTOR_LEN=arg.length;return ptr;}function passStringToWasm0(arg,malloc,realloc){if(realloc===undefined){const buf=cachedTextEncoder.encode(arg);const ptr=malloc(buf.length,1)>>>0;getUint8ArrayMemory0().subarray(ptr,ptr+buf.length).set(buf);WASM_VECTOR_LEN=buf.length;return ptr;}let len=arg.length;let ptr=malloc(len,1)>>>0;const mem=getUint8ArrayMemory0();let offset=0;for(;offset0x7F)break;mem[ptr+offset]=code;}if(offset!==len){if(offset!==0){arg=arg.slice(offset);}ptr=realloc(ptr,len,len=offset+arg.length*3,1)>>>0;const view=getUint8ArrayMemory0().subarray(ptr+offset,ptr+len);const ret=cachedTextEncoder.encodeInto(arg,view);offset+=ret.written;ptr=realloc(ptr,len,offset,1)>>>0;}WASM_VECTOR_LEN=offset;return ptr;}let cachedTextDecoder=new TextDecoder('utf-8',{ignoreBOM:true,fatal:true});cachedTextDecoder.decode();function decodeText(ptr,len){return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr,ptr+len));}const cachedTextEncoder=new TextEncoder();if(!('encodeInto'in cachedTextEncoder)){cachedTextEncoder.encodeInto=function(arg,view){const buf=cachedTextEncoder.encode(arg);view.set(buf);return{read:arg.length,written:buf.length};};}let WASM_VECTOR_LEN=0;let wasmModule,wasm;function __wbg_finalize_init(instance,module){wasm=instance.exports;wasmModule=module;cachedUint8ArrayMemory0=null;wasm.__wbindgen_start();return wasm;}async function __wbg_load(module,imports){if(typeof Response==='function'&&module instanceof Response){if(typeof WebAssembly.instantiateStreaming==='function'){try{return await WebAssembly.instantiateStreaming(module,imports);}catch(e){const validResponse=module.ok&&expectedResponseType(module.type);if(validResponse&&module.headers.get('Content-Type')!=='application/wasm'){console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",e);}else{throw e;}}}const bytes=await module.arrayBuffer();return await WebAssembly.instantiate(bytes,imports);}else{const instance=await WebAssembly.instantiate(module,imports);if(instance instanceof WebAssembly.Instance){return{instance,module};}else{return instance;}}function expectedResponseType(type){switch(type){case'basic':case'cors':case'default':return true;}return false;}}function initSync(module){if(wasm!==undefined)return wasm;if(module!==undefined){if(Object.getPrototypeOf(module)===Object.prototype){({module}=module)}else{console.warn('using deprecated parameters for `initSync()`; pass a single object instead')}}const imports=__wbg_get_imports();if(!(module instanceof WebAssembly.Module)){module=new WebAssembly.Module(module);}const instance=new WebAssembly.Instance(module,imports);return __wbg_finalize_init(instance,module);}async function __wbg_init(module_or_path){if(wasm!==undefined)return wasm;if(module_or_path!==undefined){if(Object.getPrototypeOf(module_or_path)===Object.prototype){({module_or_path}=module_or_path)}else{console.warn('using deprecated parameters for the initialization function; pass a single object instead')}}if(module_or_path===undefined&&script_src!==undefined){module_or_path=script_src.replace(/\.js$/,"_bg.wasm");}const imports=__wbg_get_imports();if(typeof module_or_path==='string'||(typeof Request==='function'&&module_or_path instanceof Request)||(typeof URL==='function'&&module_or_path instanceof URL)){module_or_path=fetch(module_or_path);}const{instance,module}=await __wbg_load(await module_or_path,imports);return __wbg_finalize_init(instance,module);}return Object.assign(__wbg_init,{initSync},exports);})({__proto__:null});var __defProp=Object.defineProperty;var __defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:true,configurable:true,writable:true,value}):obj[key]=value;var __publicField=(obj,key,value)=>__defNormalProp(obj,typeof key!=="symbol"?key+"":key,value);var u8=Uint8Array;var u16=Uint16Array;var u32=Uint32Array;var fleb=new u8([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]);var fdeb=new u8([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]);var clim=new u8([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]);var freb=function(eb,start){var b=new u16(31);for(var i2=0;i2<31;++i2){b[i2]=start+=1<>>1|(i&21845)<<1;x=(x&52428)>>>2|(x&13107)<<2;x=(x&61680)>>>4|(x&3855)<<4;rev[i]=((x&65280)>>>8|(x&255)<<8)>>>1;}var x;var i;var hMap=function(cd,mb,r){var s=cd.length;var i2=0;var l=new u16(mb);for(;i2>>rvb]=sv;}}}}else{co=new u16(s);for(i2=0;i2>>15-cd[i2];}}}return co;};var flt=new u8(288);for(i=0;i<144;++i)flt[i]=8;var i;for(i=144;i<256;++i)flt[i]=9;var i;for(i=256;i<280;++i)flt[i]=7;var i;for(i=280;i<288;++i)flt[i]=8;var i;var fdt=new u8(32);for(i=0;i<32;++i)fdt[i]=5;var i;var flrm=hMap(flt,9,1);var fdrm=hMap(fdt,5,1);var max=function(a){var m=a[0];for(var i2=1;i2m)m=a[i2];}return m;};var bits=function(d,p,m){var o=p/8|0;return(d[o]|d[o+1]<<8)>>(p&7)&m;};var bits16=function(d,p){var o=p/8|0;return(d[o]|d[o+1]<<8|d[o+2]<<16)>>(p&7);};var shft=function(p){return(p+7)/8|0;};var slc=function(v,s,e){if(s==null||s<0)s=0;if(e==null||e>v.length)e=v.length;var n=new(v.BYTES_PER_ELEMENT==2?u16:v.BYTES_PER_ELEMENT==4?u32:u8)(e-s);n.set(v.subarray(s,e));return n;};var ec=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"];var err=function(ind,msg,nt){var e=new Error(msg||ec[ind]);e.code=ind;if(Error.captureStackTrace)Error.captureStackTrace(e,err);if(!nt)throw e;return e;};var inflt=function(dat,buf,st){var sl=dat.length;if(!sl||st&&st.f&&!st.l)return buf||new u8(0);var noBuf=!buf||st;var noSt=!st||st.i;if(!st)st={};if(!buf)buf=new u8(sl*3);var cbuf=function(l2){var bl=buf.length;if(l2>bl){var nbuf=new u8(Math.max(bl*2,l2));nbuf.set(buf);buf=nbuf;}};var final=st.f||0,pos=st.p||0,bt=st.b||0,lm=st.l,dm=st.d,lbt=st.m,dbt=st.n;var tbts=sl*8;do{if(!lm){final=bits(dat,pos,1);var type=bits(dat,pos+1,3);pos+=3;if(!type){var s=shft(pos)+4,l=dat[s-4]|dat[s-3]<<8,t=s+l;if(t>sl){if(noSt)err(0);break;}if(noBuf)cbuf(bt+l);buf.set(dat.subarray(s,t),bt);st.b=bt+=l,st.p=pos=t*8,st.f=final;continue;}else if(type==1)lm=flrm,dm=fdrm,lbt=9,dbt=5;else if(type==2){var hLit=bits(dat,pos,31)+257,hcLen=bits(dat,pos+10,15)+4;var tl=hLit+bits(dat,pos+5,31)+1;pos+=14;var ldt=new u8(tl);var clt=new u8(19);for(var i2=0;i2>>4;if(s<16){ldt[i2++]=s;}else{var c=0,n=0;if(s==16)n=3+bits(dat,pos,3),pos+=2,c=ldt[i2-1];else if(s==17)n=3+bits(dat,pos,7),pos+=3;else if(s==18)n=11+bits(dat,pos,127),pos+=7;while(n--)ldt[i2++]=c;}}var lt=ldt.subarray(0,hLit),dt=ldt.subarray(hLit);lbt=max(lt);dbt=max(dt);lm=hMap(lt,lbt,1);dm=hMap(dt,dbt,1);}else err(1);if(pos>tbts){if(noSt)err(0);break;}}if(noBuf)cbuf(bt+131072);var lms=(1<>>4;pos+=c&15;if(pos>tbts){if(noSt)err(0);break;}if(!c)err(2);if(sym<256)buf[bt++]=sym;else if(sym==256){lpos=pos,lm=null;break;}else{var add=sym-254;if(sym>264){var i2=sym-257,b=fleb[i2];add=bits(dat,pos,(1<>>4;if(!d)err(3);pos+=d&15;var dt=fd[dsym];if(dsym>3){var b=fdeb[dsym];dt+=bits16(dat,pos)&(1<tbts){if(noSt)err(0);break;}if(noBuf)cbuf(bt+131072);var end=bt+add;for(;bt>3&1)+(flg>>4&1);zs>0;zs-=!d[st++]);return st+(flg&2);};var gzl=function(d){var l=d.length;return(d[l-4]|d[l-3]<<8|d[l-2]<<16|d[l-1]<<24)>>>0;};function gunzipSync(data,out){return inflt(data.subarray(gzs(data),-8),out||new u8(gzl(data)));}var td=typeof TextDecoder!="undefined"&&new TextDecoder();var tds=0;try{td.decode(et,{stream:true});tds=1;}catch(e){}var gz_default=gunzipSync;var calculate_excerpt_region=(word_positions,excerpt_length)=>{if(word_positions.length===0){return 0;}let words=[];for(const word of word_positions){words[word.location]=words[word.location]||0;words[word.location]+=word.balanced_score;}if(words.length<=excerpt_length){return 0;}let densest=words.slice(0,excerpt_length).reduce((partialSum,a)=>partialSum+a,0);let working_sum=densest;let densest_at=[0];for(let i2=0;i2densest){densest=working_sum;densest_at=[i2];}else if(working_sum===densest&&densest_at[densest_at.length-1]===i2-1){densest_at.push(i2);}}let midpoint=densest_at[Math.floor(densest_at.length/2)];return midpoint;};var build_excerpt=(content,start,length,locations,not_before,not_from)=>{let is_zws_delimited=content.includes("\u200B");let fragment_words=[];if(is_zws_delimited){fragment_words=content.split("\u200B");}else{fragment_words=content.split(/[\r\n\s]+/g);}let endcap=not_from??fragment_words.length;let startcap=not_before??0;if(endcap-startcapendcap){start=endcap-length;}if(start`)){continue;}fragment_words[word]=`${fragment_words[word]}`;}const excerpt=fragment_words.slice(start,start+length).join(joiner).trim();return{excerpt,plain_excerpt};};var calculate_sub_results=(fragment,desired_excerpt_length)=>{const effective_url=fragment.meta?.url||fragment.url;const anchors=fragment.anchors.filter((a)=>/h\d/i.test(a.element)&&a.text?.length&&/\S/.test(a.text)).sort((a,b)=>a.location-b.location);const results=[];let current_anchor_position=0;let current_anchor={title:fragment.meta["title"],url:effective_url,weighted_locations:[],locations:[],excerpt:"",plain_excerpt:""};const add_result=(end_range)=>{if(current_anchor.locations.length){const relative_weighted_locations=current_anchor.weighted_locations.map((l)=>{return{weight:l.weight,balanced_score:l.balanced_score,location:l.location-current_anchor_position};});const excerpt_start=calculate_excerpt_region(relative_weighted_locations,desired_excerpt_length)+current_anchor_position;const excerpt_length=end_range?Math.min(end_range-excerpt_start,desired_excerpt_length):desired_excerpt_length;const excerpts=build_excerpt(fragment.raw_content??"",excerpt_start,excerpt_length,current_anchor.locations,current_anchor_position,end_range);current_anchor.excerpt=excerpts.excerpt;current_anchor.plain_excerpt=excerpts.plain_excerpt;results.push(current_anchor);}};for(let word of fragment.weighted_locations){if(!anchors.length||word.location=anchors[0].location){next_anchor=anchors.shift();}let anchored_url=effective_url;try{const url_is_fq=/^((https?:)?\/\/)/.test(anchored_url);if(url_is_fq){let fq_url=new URL(anchored_url);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString();}else{if(!/^\//.test(anchored_url)){anchored_url=`/${anchored_url}`;}let fq_url=new URL(`https://example.com${anchored_url}`);fq_url.hash=next_anchor.id;anchored_url=fq_url.toString().replace(/^https:\/\/example.com/,"");}}catch(e){console.error(`Pagefind: Couldn't process ${anchored_url} for a search result`);}current_anchor_position=next_anchor.location;current_anchor={title:next_anchor.text,url:anchored_url,anchor:next_anchor,weighted_locations:[word],locations:[word.location],excerpt:"",plain_excerpt:""};}}add_result(anchors[0]?.location);return results;};var asyncSleep=async(ms=100)=>{return new Promise((r)=>setTimeout(r,ms));};var normalizeDiacritics=(str)=>{return str.normalize("NFD").replace(/\p{M}/gu,"");};var isBrowser=()=>typeof window!=="undefined"&&typeof document!=="undefined";var needsWordSegmentation=(lang)=>{if(!lang)return false;const primaryLang=lang.split("-")[0].toLowerCase();return["zh","ja","th"].includes(primaryLang);};var PagefindInstance=class{constructor(opts={}){__publicField(this,"backend");__publicField(this,"decoder");__publicField(this,"wasm");__publicField(this,"basePath");__publicField(this,"baseUrl");__publicField(this,"primary");__publicField(this,"indexWeight");__publicField(this,"excerptLength");__publicField(this,"mergeFilter");__publicField(this,"ranking");__publicField(this,"highlightParam");__publicField(this,"exactDiacritics");__publicField(this,"metaCacheTag");__publicField(this,"loaded_chunks");__publicField(this,"loaded_filters");__publicField(this,"loaded_fragments");__publicField(this,"fetchQueue",[]);__publicField(this,"activeFetches",0);__publicField(this,"maxConcurrentFetches",100);__publicField(this,"raw_ptr");__publicField(this,"initError");__publicField(this,"searchMeta");__publicField(this,"languages");__publicField(this,"loadedLanguage");__publicField(this,"includeCharacters");__publicField(this,"version");__publicField(this,"loadedVersion");this.version=pagefind_version;this.backend=wasm_bindgen;this.decoder=new TextDecoder("utf-8");this.wasm=null;let basePath=opts.basePath||"/pagefind/";let primary=opts.primary||false;if(primary&&!opts.basePath&&isBrowser()){basePath=this.initPrimaryBasePath(basePath);}if(/[^\/]$/.test(basePath)){basePath=`${basePath}/`;}if(isBrowser()&&window?.location?.origin&&basePath.startsWith(window.location.origin)){basePath=basePath.replace(window.location.origin,"");}this.basePath=basePath;this.baseUrl=opts.baseUrl||this.getDefaultBaseUrl(basePath);if(!/^(\/|https?:\/\/)/.test(this.baseUrl)){this.baseUrl=`/${this.baseUrl}`;}this.primary=primary;this.indexWeight=opts.indexWeight??1;this.excerptLength=opts.excerptLength??30;this.mergeFilter=opts.mergeFilter??{};this.ranking=opts.ranking;this.highlightParam=opts.highlightParam??null;this.exactDiacritics=opts.exactDiacritics??false;this.metaCacheTag=opts.metaCacheTag??null;this.loaded_chunks={};this.loaded_filters={};this.loaded_fragments={};this.raw_ptr=null;this.initError=null;this.searchMeta=null;this.languages=null;}throttledFetch(input){return new Promise((resolve,reject)=>{this.fetchQueue.push({resolve,reject,input});this.dequeueNextFetch();});}dequeueNextFetch(){while(this.fetchQueue.length>0&&this.activeFetchesb.page_count-a.page_count);if(topLang[0])return topLang[0];}throw new Error("Pagefind Error: No language indexes found.");}async loadMeta(index){try{let compressed_resp=await this.throttledFetch(`${this.basePath}pagefind.${index}.pf_meta`);let compressed_meta=await compressed_resp.arrayBuffer();this.searchMeta=this.decompress(new Uint8Array(compressed_meta),"Pagefind metadata");}catch(e){console.error(`Failed to load the meta index: +${e?.toString()}`);}}async loadWasm(language){try{const wasm_url=`${this.basePath}wasm.${language}.pagefind`;let compressed_resp=await this.throttledFetch(wasm_url);let compressed_wasm=await compressed_resp.arrayBuffer();const final_wasm=this.decompress(new Uint8Array(compressed_wasm),"Pagefind WebAssembly");if(!final_wasm){throw new Error("No WASM after decompression");}this.wasm=await this.backend({module_or_path:final_wasm});}catch(e){console.error(`Failed to load the Pagefind WASM: +${e?.toString()}`);throw new Error(`Failed to load the Pagefind WASM: +${e?.toString()}`);}}async _loadGenericChunk(url,method){try{let compressed_resp=await this.throttledFetch(url);let compressed_chunk=await compressed_resp.arrayBuffer();let chunk=this.decompress(new Uint8Array(compressed_chunk),url);let ptr=await this.getPtr();this.raw_ptr=this.backend[method](ptr,chunk);}catch(e){console.error(`Failed to load the index chunk ${url}: +${e?.toString()}`);}}async loadChunk(hash){if(!this.loaded_chunks[hash]){const url=`${this.basePath}index/${hash}.pf_index`;this.loaded_chunks[hash]=this._loadGenericChunk(url,"load_index_chunk");}return await this.loaded_chunks[hash];}async loadFilterChunk(hash){if(!this.loaded_filters[hash]){const url=`${this.basePath}filter/${hash}.pf_filter`;this.loaded_filters[hash]=this._loadGenericChunk(url,"load_filter_chunk");}return await this.loaded_filters[hash];}async _loadFragment(hash){let compressed_resp=await this.throttledFetch(`${this.basePath}fragment/${hash}.pf_fragment`);let compressed_fragment=await compressed_resp.arrayBuffer();let fragment=this.decompress(new Uint8Array(compressed_fragment),`Fragment ${hash}`);return JSON.parse(new TextDecoder().decode(fragment));}async loadFragment(hash,weighted_locations=[],search_term){if(!this.loaded_fragments[hash]){this.loaded_fragments[hash]=this._loadFragment(hash);}let fragment=await this.loaded_fragments[hash];fragment.weighted_locations=weighted_locations;fragment.locations=weighted_locations.map((l)=>l.location);if(!fragment.raw_content){fragment.raw_content=fragment.content.replace(//g,">");fragment.content=fragment.content.replace(/\u200B/g,"");}if(!fragment.raw_url){fragment.raw_url=fragment.url;}fragment.url=this.processedUrl(fragment.raw_url,search_term);const excerpt_start=calculate_excerpt_region(weighted_locations,this.excerptLength);const excerpts=build_excerpt(fragment.raw_content,excerpt_start,this.excerptLength,fragment.locations);fragment.excerpt=excerpts.excerpt;fragment.plain_excerpt=excerpts.plain_excerpt;fragment.sub_results=calculate_sub_results(fragment,this.excerptLength);return fragment;}fullUrl(raw){if(/^(https?:)?\/\//.test(raw)){return raw;}return`${this.baseUrl}/${raw}`.replace(/\/+/g,"/").replace(/^(https?:\/)/,"$1/");}processedUrl(url,search_term){const normalized=this.fullUrl(url);if(this.highlightParam===null){return normalized;}let individual_terms=search_term.split(/\s+/);try{let processed=new URL(normalized);for(const term of individual_terms){processed.searchParams.append(this.highlightParam,term);}return processed.toString();}catch(e){try{let processed=new URL(`https://example.com${normalized}`);for(const term of individual_terms){processed.searchParams.append(this.highlightParam,term);}return processed.toString().replace(/^https:\/\/example\.com/,"");}catch(e2){return normalized;}}}async getPtr(){while(this.raw_ptr===null){if(this.initError){throw this.initError;}await asyncSleep(50);}if(!this.raw_ptr){console.error("Pagefind: WASM Error (No pointer)");throw new Error("Pagefind: WASM Error (No pointer)");}return this.raw_ptr;}stringifyFilters(obj={}){return JSON.stringify(obj);}stringifySorts(obj={}){let sorts=Object.entries(obj);for(let[sort,direction]of sorts){if(sorts.length>1){console.warn(`Pagefind was provided multiple sort options in this search, but can only operate on one. Using the ${sort} sort.`);}if(direction!=="asc"&&direction!=="desc"){console.warn(`Pagefind was provided a sort with unknown direction ${direction}. Supported: [asc, desc]`);}return`${sort}:${direction}`;}return``;}async filters(){let ptr=await this.getPtr();let filters2=this.backend.request_all_filter_indexes(ptr);let filter_array=JSON.parse(filters2);if(Array.isArray(filter_array)){let filter_chunks=filter_array.filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...filter_chunks]);}ptr=await this.getPtr();let results=this.backend.filters(ptr);return JSON.parse(results);}async preload(term,options2={}){await this.search(term,{...options2,preload:true});}async search(term,options2={}){options2={verbose:false,filters:{},sort:{},...options2};const log=(str)=>{if(options2.verbose)console.log(str);};log(`Starting search on ${this.basePath}`);let start=Date.now();let ptr=await this.getPtr();let filter_only=term===null;term=term??"";let exact_search=/^\s*".+"\s*$/.test(term);if(exact_search){log(`Running an exact search`);}let trueLanguage=null;try{trueLanguage=Intl.getCanonicalLocales(this.loadedLanguage)[0];}catch(err2){}const term_chunks=[];if(trueLanguage&&typeof Intl.Segmenter!=="undefined"){const graphemeSegmenter=new Intl.Segmenter(trueLanguage,{granularity:"grapheme"});if(needsWordSegmentation(trueLanguage)){const wordSegmenter=new Intl.Segmenter(trueLanguage,{granularity:"word"});for(const{segment:word}of wordSegmenter.segment(term)){const wordChunks=[];for(const{segment:grapheme}of graphemeSegmenter.segment(word)){if(this.includeCharacters?.includes(grapheme)){wordChunks.push(grapheme);}else if(!/^\p{Pd}|\p{Pe}|\p{Pf}|\p{Pi}|\p{Po}|\p{Ps}$/u.test(grapheme)){wordChunks.push(grapheme.toLocaleLowerCase());}}if(wordChunks.length>0){term_chunks.push(wordChunks.join(""));}}term=term_chunks.join(" ").replace(/\s{2,}/g," ").trim();}else{for(const{segment:grapheme}of graphemeSegmenter.segment(term)){if(this.includeCharacters?.includes(grapheme)){term_chunks.push(grapheme);}else if(!/^\p{Pd}|\p{Pe}|\p{Pf}|\p{Pi}|\p{Po}|\p{Ps}$/u.test(grapheme)){term_chunks.push(grapheme.toLocaleLowerCase());}}term=term_chunks.join("").replace(/\s{2,}/g," ").trim();}}else{for(const char of term){if(this.includeCharacters?.includes(char)){term_chunks.push(char);}else if(!/^\p{Pd}|\p{Pe}|\p{Pf}|\p{Pi}|\p{Po}|\p{Ps}$/u.test(char)){term_chunks.push(char.toLocaleLowerCase());}}term=term_chunks.join("").replace(/\s{2,}/g," ").trim();}const originalTerm=term;term=normalizeDiacritics(term);log(`Normalized search term to ${term}`);if(!term?.length&&!filter_only){return{results:[],unfilteredResultCount:0,filters:{},totalFilters:{},timings:{preload:Date.now()-start,search:Date.now()-start,total:Date.now()-start}};}let sort_list=this.stringifySorts(options2.sort);log(`Stringified sort to ${sort_list}`);const filter_list=this.stringifyFilters(options2.filters);log(`Stringified filters to ${filter_list}`);let index_resp=this.backend.request_indexes(ptr,term);let index_array=JSON.parse(index_resp);let filter_resp=this.backend.request_filter_indexes(ptr,filter_list);let filter_array=JSON.parse(filter_resp);let chunks=index_array.filter((v)=>v).map((chunk)=>this.loadChunk(chunk));let filter_chunks=filter_array.filter((v)=>v).map((chunk)=>this.loadFilterChunk(chunk));await Promise.all([...chunks,...filter_chunks]);log(`Loaded necessary chunks to run search`);if(options2.preload){log(`Preload \u2014 bailing out of search operation now.`);return null;}ptr=await this.getPtr();let searchStart=Date.now();let result=this.backend.search(ptr,term,originalTerm,filter_list,sort_list,exact_search,this.exactDiacritics);log(`Got the raw search result: ${result}`);let{filtered_counts,total_counts,results,unfiltered_total,search_keywords,query_term_idfs}=JSON.parse(result);let resultsInterface=results.map((result2)=>{let weighted_locations=result2.l.map((l)=>{let loc={weight:l.w/24,balanced_score:l.s,location:l.l};if(l.v){loc.verbose={word_string:l.v.ws,length_bonus:l.v.lb};}return loc;});let locations=weighted_locations.map((l)=>l.location);let res={id:result2.p,score:result2.s*this.indexWeight,words:locations,data:async()=>await this.loadFragment(result2.p,weighted_locations,term)};if(result2.params){res.params={document_length:result2.params.dl,average_page_length:result2.params.apl,total_pages:result2.params.tp};}if(result2.scores){res.scores=result2.scores.map((r)=>{return{search_term:r.w,idf:r.idf,saturating_tf:r.b_tf,raw_tf:r.r_tf,pagefind_tf:r.p_tf,score:r.s,params:{weighted_term_frequency:r.params.w_tf,pages_containing_term:r.params.pct,length_bonus:r.params.lb}};});}if(result2.mf&&result2.mf.length>0){res.matchedMetaFields=result2.mf;}if(result2.vms&&result2.vms.length>0){res.verbose_meta_scores=result2.vms.map((s)=>({field_name:s.fn,field_weight:s.fw,matched_terms:s.mt,matched_idf:s.mi,query_total_idf:s.ti,coverage:s.cv,coverage_boost:s.cb}));}return res;});const searchTime=Date.now()-searchStart;const realTime=Date.now()-start;log(`Found ${results.length} result${results.length == 1 ? "" : "s"} for "${term}" in ${Date.now() - searchStart}ms (${Date.now() - start}ms realtime)`);let response={results:resultsInterface,unfilteredResultCount:unfiltered_total,filters:filtered_counts,totalFilters:total_counts,timings:{preload:realTime-searchTime,search:searchTime,total:realTime}};if(search_keywords){response.search_keywords=search_keywords;}if(query_term_idfs){response.query_term_idfs=query_term_idfs.map((q)=>({term:q.t,idf:q.i}));}return response;}};var Pagefind=class{constructor(options2={}){__publicField(this,"primaryLanguage");__publicField(this,"searchID");__publicField(this,"primary");__publicField(this,"instances");this.primaryLanguage="unknown";this.searchID=0;this.primary=new PagefindInstance({...options2,primary:true});this.instances=[this.primary];this.init(options2?.language);}async options(options2){await this.primary.options(options2);}async enterPlaygroundMode(){await this.primary.enterPlaygroundMode();}async init(overrideLanguage){if(isBrowser()&&document?.querySelector){const langCode=document.querySelector("html")?.getAttribute("lang")||"unknown";this.primaryLanguage=langCode.toLocaleLowerCase();}if(overrideLanguage){this.primaryLanguage=overrideLanguage;}await this.primary.init(overrideLanguage?overrideLanguage:this.primaryLanguage,{load_wasm:true});}async mergeIndex(indexPath,options2={}){if(this.primary.basePath.startsWith(indexPath)){console.warn(`Skipping mergeIndex ${indexPath} that appears to be the same as the primary index (${this.primary.basePath})`);return;}let newInstance=new PagefindInstance({primary:false,basePath:indexPath,...options2});this.instances.push(newInstance);while(this.primary.wasm===null){await asyncSleep(50);}await newInstance.init(options2.language||this.primaryLanguage,{load_wasm:false});const{language,...remainingOptions}=options2;await newInstance.options(remainingOptions);}mergeFilters(filters2){const merged={};for(const searchFilter of filters2){for(const[filterKey,values]of Object.entries(searchFilter)){if(!merged[filterKey]){merged[filterKey]=values;continue;}else{const filter=merged[filterKey];for(const[valueKey,count]of Object.entries(values)){filter[valueKey]=(filter[valueKey]||0)+count;}}}}return merged;}async filters(){let filters2=await Promise.all(this.instances.map((i2)=>i2.filters()));return this.mergeFilters(filters2);}async preload(term,options2={}){await Promise.all(this.instances.map((i2)=>i2.preload(term,options2)));}async debouncedSearch(term,options2,debounceTimeoutMs){const thisSearchID=++this.searchID;this.preload(term,options2);await asyncSleep(debounceTimeoutMs);if(thisSearchID!==this.searchID){return null;}const searchResult=await this.search(term,options2);if(thisSearchID!==this.searchID){return null;}return searchResult;}async search(term,options2={}){let search2=await Promise.all(this.instances.map((i2)=>i2.search(term,options2)));const filters2=this.mergeFilters(search2.map((s)=>s.filters));const totalFilters=this.mergeFilters(search2.map((s)=>s.totalFilters));const results=search2.map((s)=>s.results).flat().sort((a,b)=>b.score-a.score);const timings=search2.map((s)=>s.timings);const unfilteredResultCount=search2.reduce((sum,s)=>sum+s.unfilteredResultCount,0);let response={results,unfilteredResultCount,filters:filters2,totalFilters,timings};if(search2[0].search_keywords){response.search_keywords=search2[0].search_keywords;}if(search2[0].query_term_idfs){response.query_term_idfs=search2[0].query_term_idfs;}return response;}};var hasWorkerSupport=typeof window!=="undefined"&&typeof document!=="undefined"&&typeof Worker!=="undefined";var sharedWorker=null;var sharedWorkerRefCount=0;var sharedMessageHandlers=new Map();var nextInstanceId=0;var generateInstanceId=()=>`pf_${nextInstanceId++}`;function initSharedWorker(basePath){if(sharedWorker)return true;try{const workerUrl=`${basePath}pagefind-worker.js`;sharedWorker=new Worker(workerUrl);sharedWorker.addEventListener("error",(error)=>{console.warn("The Pagefind web worker encountered an error, falling back to main thread:",error);sharedWorker=null;const pending=Array.from(sharedMessageHandlers.values());sharedMessageHandlers.clear();for(const{reject}of pending){reject(new Error("Worker failed, falling back to main thread"));}});sharedWorker.addEventListener("message",(event)=>{const{id,result,error}=event.data;const pending=sharedMessageHandlers.get(id);if(pending){sharedMessageHandlers.delete(id);if(error){pending.reject(new Error(error));}else{pending.resolve(result);}}});return true;}catch(e){return false;}}function releaseSharedWorker(){sharedWorkerRefCount--;if(sharedWorkerRefCount<=0&&sharedWorker){sharedWorker.terminate();sharedWorker=null;sharedWorkerRefCount=0;const pending=Array.from(sharedMessageHandlers.values());sharedMessageHandlers.clear();for(const{reject}of pending){reject(new Error("Pagefind worker terminated"));}}}var globalMessageId=0;function sendWorkerMessage(instanceId,method,args){if(!sharedWorker){return Promise.reject(new Error("Worker not available"));}return new Promise((resolve,reject)=>{const id=`msg_${globalMessageId++}`;sharedMessageHandlers.set(id,{resolve,reject});sharedWorker.postMessage({id,instanceId,method,args});});}var PagefindWrapper=class{constructor(options2={}){__publicField(this,"instanceId");__publicField(this,"fallback",null);__publicField(this,"basePath");__publicField(this,"initOptions");__publicField(this,"cleanup");__publicField(this,"initPromise",null);__publicField(this,"initialized",false);__publicField(this,"useWorker",false);this.instanceId=generateInstanceId();this.basePath=options2.basePath||"/pagefind/";this.initOptions=options2;if(/[^\/]$/.test(this.basePath)){this.basePath=`${this.basePath}/`;}if(hasWorkerSupport&&window?.location?.origin&&this.basePath.startsWith(window.location.origin)){this.basePath=this.basePath.replace(window.location.origin,"");}this.initOptions={...this.initOptions,basePath:this.basePath};this.initCleanup();this.initPromise=this.init();}initCleanup(){if(typeof FinalizationRegistry!=="undefined"){this.cleanup=new FinalizationRegistry((dataId)=>{if(this.useWorker&&sharedWorker){try{sendWorkerMessage(this.instanceId,"releaseData",[dataId]).catch(()=>{});}catch(e){}}});}}async init(){if(hasWorkerSupport&&!this.initOptions.noWorker){const workerAvailable=initSharedWorker(this.basePath);if(workerAvailable){try{sharedWorkerRefCount++;this.useWorker=true;await Promise.race([sendWorkerMessage(this.instanceId,"init",[this.initOptions]),new Promise((_,reject)=>setTimeout(()=>reject(new Error("Worker initialization timeout")),5e3))]);this.initialized=true;}catch(error){console.warn("Failed to initialize Pagefind in the web worker, falling back to main thread:",error);sendWorkerMessage(this.instanceId,"destroy",[]).catch(()=>{});this.useWorker=false;sharedWorkerRefCount--;this.initFallback();this.initialized=true;}}else{this.initFallback();this.initialized=true;}}else{this.initFallback();this.initialized=true;}}waitForInit(){return this.initPromise??Promise.resolve();}initFallback(){if(!this.fallback){this.fallback=new Pagefind(this.initOptions);}}async sendMessage(method,args){if(!this.initialized&&method!=="init"){if(this.initPromise){await this.initPromise;}}if(this.fallback){const fn=this.fallback[method];if(typeof fn==="function"){const result=await fn.apply(this.fallback,args);if((method==="search"||method==="debouncedSearch")&&result&&args[1]&&args[1].verbose){result.search_environment="mainthread";}return result;}throw new Error(`Method ${method} not found on fallback`);}if(!this.useWorker||!sharedWorker){throw new Error("Worker not initialized");}return sendWorkerMessage(this.instanceId,method,args);}async options(options2){return this.sendMessage("options",[options2]);}async enterPlaygroundMode(){return this.sendMessage("enterPlaygroundMode",[]);}async mergeIndex(indexPath,options2={}){return this.sendMessage("mergeIndex",[indexPath,options2]);}async search(term,options2={}){const results=await this.sendMessage("search",[term,options2]);if(results&&results.results){for(const result of results.results){if(typeof result.data==="string"){const dataId=result.data;if(this.cleanup){this.cleanup.register(result,dataId);}result.data=async()=>{return this.sendMessage("getData",[dataId]);};}}}return results;}async debouncedSearch(term,options2,debounceTimeoutMs){const results=await this.sendMessage("debouncedSearch",[term,options2,debounceTimeoutMs]);if(results&&results.results){for(const result of results.results){if(typeof result.data==="string"){const dataId=result.data;if(this.cleanup){this.cleanup.register(result,dataId);}result.data=async()=>{return this.sendMessage("getData",[dataId]);};}}}return results;}async preload(term,options2={}){return this.sendMessage("preload",[term,options2]);}async filters(){return this.sendMessage("filters",[]);}async destroy(){if(this.useWorker){try{await sendWorkerMessage(this.instanceId,"destroy",[]);}catch(e){}this.useWorker=false;releaseSharedWorker();}if(this.fallback){this.fallback=null;}}};var pagefind=void 0;var initial_options=void 0;var deriveBasePath=(explicit)=>{if(explicit)return explicit;if(typeof import.meta.url!=="undefined"){return import.meta.url.match(/^(.*\/)pagefind.js.*$/)?.[1];}};var detectLanguage=()=>{if(typeof document!=="undefined"&&document?.querySelector){return(document.querySelector("html")?.getAttribute("lang")||"unknown").toLowerCase();}return"unknown";};var init_pagefind=()=>{if(!pagefind){pagefind=new PagefindWrapper({...initial_options,basePath:deriveBasePath(initial_options?.basePath),language:detectLanguage(),primary:true});}};var options=async(new_options)=>{if(pagefind){await pagefind.options(new_options);}else{initial_options=new_options;}};var init=async()=>{init_pagefind();};var destroy=async()=>{if(pagefind){await pagefind.destroy();}pagefind=void 0;initial_options=void 0;};var mergeIndex=async(indexPath,options2)=>{init_pagefind();return await pagefind.mergeIndex(indexPath,options2);};var search=async(term,options2)=>{init_pagefind();return await pagefind.search(term,options2);};var debouncedSearch=async(term,options2,debounceTimeoutMs=300)=>{init_pagefind();return await pagefind.debouncedSearch(term,options2,debounceTimeoutMs);};var preload=async(term,options2)=>{init_pagefind();return await pagefind.preload(term,options2);};var filters=async()=>{init_pagefind();return await pagefind.filters();};var createInstance=(instanceOptions)=>{const wrapper=new PagefindWrapper({...instanceOptions,basePath:deriveBasePath(instanceOptions?.basePath),language:detectLanguage(),primary:true});return{options:(opts)=>wrapper.options(opts),init:()=>wrapper.waitForInit(),destroy:()=>wrapper.destroy(),mergeIndex:(indexPath,options2)=>wrapper.mergeIndex(indexPath,options2),search:(term,options2={})=>wrapper.search(term,options2),debouncedSearch:(term,options2,debounceTimeoutMs=300)=>wrapper.debouncedSearch(term,options2,debounceTimeoutMs),preload:(term,options2={})=>wrapper.preload(term,options2),filters:()=>wrapper.filters()};};export{createInstance,debouncedSearch,destroy,filters,init,mergeIndex,options,preload,search}; \ No newline at end of file diff --git a/docs/pagefind/wasm.en-us.pagefind b/docs/pagefind/wasm.en-us.pagefind new file mode 100644 index 00000000..0bc5eb4c Binary files /dev/null and b/docs/pagefind/wasm.en-us.pagefind differ diff --git a/docs/pagefind/wasm.unknown.pagefind b/docs/pagefind/wasm.unknown.pagefind new file mode 100644 index 00000000..13511dc2 Binary files /dev/null and b/docs/pagefind/wasm.unknown.pagefind differ diff --git a/docs/robots.txt b/docs/robots.txt new file mode 100644 index 00000000..d204ee34 --- /dev/null +++ b/docs/robots.txt @@ -0,0 +1,42 @@ +User-agent: Googlebot +User-agent: YandexBot +User-agent: baiduspider +User-agent: Applebot +Allow: / + +User-agent: * +Disallow: /docs/borrowing/ +Disallow: /docs/cargo-crates-and-basic-project-structure/ +Disallow: /docs/combinators/ +Disallow: /docs/comments-and-documenting-the-code/ +Disallow: /docs/control-flows/ +Disallow: /docs/crates/ +Disallow: /docs/custom-error-types/ +Disallow: /docs/ +Disallow: /docs/enums/ +Disallow: /docs/error-and-none-propagation/ +Disallow: /docs/functions/ +Disallow: /docs/generics/ +Disallow: /docs/hello-world/ +Disallow: /home/ +Disallow: /docs/impls/ +Disallow: /docs/installation/ +Disallow: / +Disallow: /docs/lifetimes/ +Disallow: /docs/modules/ +Disallow: /docs/operators/ +Disallow: /docs/option-and-result/ +Disallow: /docs/overview/ +Disallow: /docs/ownership/ +Disallow: /docs/panicking/ +Disallow: /docs/primitive-data-types/ +Disallow: /docs/smart-compiler/ +Disallow: /docs/std-primitives-and-preludes/ +Disallow: /docs/structs/ +Disallow: /docs/traits/ +Disallow: /docs/unwrap-and-expect/ +Disallow: /docs/use/ +Disallow: /docs/variable-bindings-constants-and-statics/ +Disallow: /docs/vectors/ +Disallow: /docs/why-rust/ +Disallow: /docs/workspaces/ \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml new file mode 100644 index 00000000..4c1c3b9f --- /dev/null +++ b/docs/sitemap.xml @@ -0,0 +1 @@ +https://learning-rust.github.io/docs/borrowing/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/cargo-crates-and-basic-project-structure/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/combinators/2026-05-25T20:25:54+08:00https://learning-rust.github.io/docs/comments-and-documenting-the-code/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/control-flows/2025-10-30T20:23:09+08:00https://learning-rust.github.io/docs/crates/2026-06-07T10:03:06+08:00https://learning-rust.github.io/docs/custom-error-types/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/2026-06-30T11:31:15+08:00https://learning-rust.github.io/docs/enums/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/error-and-none-propagation/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/functions/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/generics/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/hello-world/2026-05-26T05:10:50+08:00https://learning-rust.github.io/home/2026-06-15T01:56:20+08:00https://learning-rust.github.io/docs/impls/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/installation/2026-05-26T05:10:50+08:00https://learning-rust.github.io/2026-06-30T11:31:15+08:00https://learning-rust.github.io/docs/lifetimes/2026-06-18T08:55:35+08:00https://learning-rust.github.io/docs/modules/2026-06-29T15:31:56+08:00https://learning-rust.github.io/docs/operators/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/option-and-result/2026-05-25T20:25:54+08:00https://learning-rust.github.io/docs/overview/2024-03-10T20:15:12+08:00https://learning-rust.github.io/docs/ownership/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/panicking/2023-11-11T20:38:50+08:00https://learning-rust.github.io/docs/primitive-data-types/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/smart-compiler/2026-06-07T10:03:06+08:00https://learning-rust.github.io/docs/std-primitives-and-preludes/2026-06-07T10:03:06+08:00https://learning-rust.github.io/docs/structs/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/traits/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/unwrap-and-expect/2026-05-25T20:25:54+08:00https://learning-rust.github.io/docs/use/2026-06-30T11:31:15+08:00https://learning-rust.github.io/docs/variable-bindings-constants-and-statics/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/vectors/2026-05-26T05:10:50+08:00https://learning-rust.github.io/docs/why-rust/2026-06-18T08:55:35+08:00https://learning-rust.github.io/docs/workspaces/2026-06-25T21:08:05+08:00 \ No newline at end of file diff --git a/docs/sw.js b/docs/sw.js new file mode 100644 index 00000000..9c27f9f3 --- /dev/null +++ b/docs/sw.js @@ -0,0 +1,56 @@ +const cacheName = 'learning-rust-{{ now.Format "2006-01-02" }}'; +const staticAssets = [ + './', + './index.html', + './manifest.json', + './docs/**/*', + './favicon/android-chrome-192x192.png', + './favicon/android-chrome-512x512.png', + './favicon/apple-touch-icon.png', + './favicon/favicon.ico', + './favicon/favicon-16x16.png', + './favicon/favicon-32x32.png', + './css/home.min.*.css', + './css/docs.min.*.css', + './js/home.min.*.js', + './js/docs.min.*.js', +]; + +self.addEventListener('install', async e => { + const cache = await caches.open(cacheName); + await cache.addAll(staticAssets); + return self.skipWaiting(); +}); + +self.addEventListener('activate', e => { + self.clients.claim(); +}); + +self.addEventListener('fetch', async e => { + const req = e.request; + const url = new URL(req.url); + + if (url.origin === location.origin) { + e.respondWith(cacheFirst(req)); + } else { + e.respondWith(networkFirst(req)); + } +}); + +async function cacheFirst(req) { + const cache = await caches.open(cacheName); + const cached = await cache.match(req); + return cached || fetch(req); +} + +async function networkFirst(req) { + const cache = await caches.open(cacheName); + try { + const fresh = await fetch(req); + cache.put(req, fresh.clone()); + return fresh; + } catch (e) { + const cached = await cache.match(req); + return cached; + } +} \ No newline at end of file diff --git a/hugo.yaml b/hugo.yaml new file mode 100644 index 00000000..878708b9 --- /dev/null +++ b/hugo.yaml @@ -0,0 +1,72 @@ +theme: E25DX + +enableGitInfo: true +disableKinds: [taxonomy, term] +disableHugoGeneratorInject: true + +publishDir: docs +baseURL: https://learning-rust.github.io + +defaultContentLanguage: en + +languages: + en: + languageName: English + languageCode: en-US + contentDir: content/en + title: Learning Rust + params: + description: Rust Programming Language Tutorials for Everyone! + weight: 1 + +services: + googleAnalytics: + ID: G-FZHQCXSZ89 + +enableRobotsTXT: true + +markup: + highlight: + noClasses: false + goldmark: + renderer: + unsafe: true + parser: + attribute: + block: true + title: true + tableOfContents: + pre: + +params: + project: + startYear: 2016 + presentYear: 2026 + repo: + main: https://github.com/learning-rust/learning-rust.github.io + themeColor: + light: "#ffffff" + dark: "#101010" + + author: + name: Dumindu Madunuwan + url: https://github.com/dumindu + + enableOGXSEO: true # og images need to add manually + enablePageFind: true + +menus: + main: + - identifier: home + name: Home + pageRef: / + weight: 10 + params: + icon: home + - identifier: docs + name: Documentation + pageRef: /docs + weight: 20 + params: + icon: menu_book diff --git a/image_generator/.gitignore b/image_generator/.gitignore new file mode 100644 index 00000000..ea8c4bf7 --- /dev/null +++ b/image_generator/.gitignore @@ -0,0 +1 @@ +/target diff --git a/image_generator/Cargo.lock b/image_generator/Cargo.lock new file mode 100644 index 00000000..6dc51113 --- /dev/null +++ b/image_generator/Cargo.lock @@ -0,0 +1,917 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "conv" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" +dependencies = [ + "custom_derive", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "custom_derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "exr" +version = "1.74.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "gif" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "image_generator" +version = "0.1.0" +dependencies = [ + "anyhow", + "image", + "imageproc", + "mozjpeg", + "rusttype", + "serde", + "serde_yaml", + "walkdir", +] + +[[package]] +name = "imageproc" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aee993351d466301a29655d628bfc6f5a35a0d062b6160ca0808f425805fd7" +dependencies = [ + "approx", + "conv", + "image", + "itertools", + "nalgebra", + "num", + "rand", + "rand_distr", + "rayon", + "rusttype", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" +dependencies = [ + "rayon", +] + +[[package]] +name = "lebe" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mozjpeg" +version = "0.10.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7891b80aaa86097d38d276eb98b3805d6280708c4e0a1e6f6aed9380c51fec9" +dependencies = [ + "arrayvec", + "bytemuck", + "libc", + "mozjpeg-sys", + "rgb", +] + +[[package]] +name = "mozjpeg-sys" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f0dc668bf9bf888c88e2fb1ab16a406d2c380f1d082b20d51dd540ab2aa70c1" +dependencies = [ + "cc", + "dunce", + "libc", + "nasm-rs", +] + +[[package]] +name = "nalgebra" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb2d0de08694bed883320212c18ee3008576bfe8c306f4c3c4a58b4876998be" +dependencies = [ + "approx", + "matrixmultiply", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nasm-rs" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706bf8a5e8c8ddb99128c3291d31bd21f4bcde17f0f4c20ec678d85c74faa149" +dependencies = [ + "jobserver", + "log", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e6affeb1632d6ff6a23d2cd40ffed138e82f1532571a26f527c8a284bb2fbb" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_distr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2" +dependencies = [ + "rand", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "rusttype" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff8374aa04134254b7995b63ad3dc41c7f7236f69528b28553da7d72efaa967" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simba" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3fd720c48c53cace224ae62bef1bbff363a70c68c4802a78b5cc6159618176" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "zerocopy" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/image_generator/Cargo.toml b/image_generator/Cargo.toml new file mode 100644 index 00000000..4c0ad243 --- /dev/null +++ b/image_generator/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "image_generator" +version = "0.1.0" +edition = "2024" + +[dependencies] +image = "0.24" +mozjpeg = "0.10" +imageproc = "0.23" +rusttype = "0.9" +anyhow = "1.0" +serde = { version = "1.0", features = ["derive"] } +serde_yaml = "0.9" +walkdir = "2" diff --git a/image_generator/fonts/Lato-Regular.ttf b/image_generator/fonts/Lato-Regular.ttf new file mode 100644 index 00000000..bb2e8875 Binary files /dev/null and b/image_generator/fonts/Lato-Regular.ttf differ diff --git a/image_generator/logo.png b/image_generator/logo.png new file mode 100644 index 00000000..84816ca1 Binary files /dev/null and b/image_generator/logo.png differ diff --git a/image_generator/secondary.png b/image_generator/secondary.png new file mode 100644 index 00000000..c2d2037b Binary files /dev/null and b/image_generator/secondary.png differ diff --git a/image_generator/src/main.rs b/image_generator/src/main.rs new file mode 100644 index 00000000..14dce9b9 --- /dev/null +++ b/image_generator/src/main.rs @@ -0,0 +1,349 @@ +use anyhow::{Context, Result, anyhow}; +use image::{DynamicImage, ImageBuffer, Rgba}; +use imageproc::drawing::draw_text_mut; +use rusttype::{Font, Scale, point}; +use serde::Deserialize; +use std::fs; +use std::path::{Path, PathBuf}; +use walkdir::WalkDir; + +#[derive(Debug, Deserialize)] +struct Frontmatter { + title: String, + slug: String, +} + +const HOST_TEXT: &str = "learning-rust.github.io"; +const DOCS_DIR: &str = "../content/en/docs"; +const OUTPUT_DOCS_DIR: &str = "../docs/docs"; + +const WIDTH: u32 = 1200; +const HEIGHT: u32 = 630; +const PADDING: u32 = 32; + +const OUTPUT_JPEG_QUALITY: f32 = 85.0; + +const FONT_PATH: &str = "fonts/Lato-Regular.ttf"; +const LOGO_IMAGE_PATH: &str = "logo.png"; +const SECONDARY_IMAGE_PATH: &str = "secondary.png"; + +const TEXT_COLOR: Rgba = Rgba([255, 255, 255, 255]); // White +const GRADIENT_COLORS: [Rgba; 5] = [ + Rgba([0x86, 0x6e, 0xe7, 255]), // #866ee7 + Rgba([0xea, 0x60, 0xda, 255]), // #ea60da + Rgba([0xed, 0x8f, 0x57, 255]), // #ed8f57 + Rgba([0xfb, 0xd4, 0x1d, 255]), // #fbd41d + Rgba([0x2c, 0xca, 0x91, 255]), // #2cca91 +]; + +const LEFT_SECTION_WIDTH_RATIO: f32 = 0.40; +const LOGO_TARGET_HEIGHT: u32 = 48; + +const HOST_TEXT_SCALE: f32 = 28.0; +const PAGE_TITLE_SCALE: f32 = 85.0; + +fn main() -> Result<()> { + let font = load_font(FONT_PATH)?; + let mut processed_count = 0; + for entry in markdown_files(DOCS_DIR) { + match process_markdown_file(entry.path(), &font) { + Ok(()) => { + processed_count += 1; + println!("✓ Generated OG image for: {}", entry.path().display()); + } + Err(e) => { + eprintln!("✗ Failed to process {}: {}", entry.path().display(), e); + } + } + } + + println!("\nProcessed {} markdown files", processed_count); + Ok(()) +} + +fn load_font(font_path: &str) -> Result> { + let font_data = + fs::read(font_path).with_context(|| format!("Could not read font file: {}", font_path))?; + + Font::try_from_vec(font_data) + .ok_or_else(|| anyhow!("Could not parse font data from {}", font_path)) +} + +fn markdown_files(root: &str) -> impl Iterator { + WalkDir::new(root) + .into_iter() + .filter_map(|entry| entry.ok()) + .filter(|entry| entry.path().extension().is_some_and(|ext| ext == "md")) +} + +fn process_markdown_file(file_path: &Path, font: &Font) -> Result<()> { + // Read the markdown file + let content = fs::read_to_string(file_path) + .with_context(|| format!("Failed to read file: {}", file_path.display()))?; + + // Parse frontmatter + let frontmatter = parse_frontmatter(&content) + .with_context(|| format!("Failed to parse frontmatter from: {}", file_path.display()))?; + + // Generate the image + let img = generate_image(&frontmatter.title, font)?; + let output_path = output_image_path(&frontmatter.slug); + + let rgb = DynamicImage::ImageRgba8(img).to_rgb8(); + + let buf = std::panic::catch_unwind(|| -> std::io::Result> { + let mut comp = mozjpeg::Compress::new(mozjpeg::ColorSpace::JCS_RGB); + + comp.set_size(rgb.width() as usize, rgb.height() as usize); + comp.set_quality(OUTPUT_JPEG_QUALITY); + comp.set_progressive_mode(); + + let mut comp = comp.start_compress(Vec::new())?; + + // mozjpeg expects rows (scanlines) + let row_stride = (rgb.width() as usize) * 3; + for row in rgb.as_raw().chunks_exact(row_stride) { + comp.write_scanlines(row)?; + } + + comp.finish() + }) + .map_err(|_| anyhow::anyhow!("mozjpeg panicked while encoding {}", output_path.display()))??; + + std::fs::write(&output_path, &buf) + .with_context(|| format!("Failed to save image: {}", output_path.display()))?; + + Ok(()) +} + +fn output_image_path(slug: &str) -> PathBuf { + Path::new(OUTPUT_DOCS_DIR) + .join(Path::new(slug)) + .join("og.jpg") +} + +fn parse_frontmatter(content: &str) -> Result { + // Find the frontmatter section (between --- lines) + if !content.starts_with("---") { + anyhow::bail!("File does not start with frontmatter delimiter"); + } + + let lines: Vec<&str> = content.lines().collect(); + let mut end_index = None; + + for (i, line) in lines.iter().enumerate().skip(1) { + if line.trim() == "---" { + end_index = Some(i); + break; + } + } + + let end_index = + end_index.ok_or_else(|| anyhow::anyhow!("Could not find end of frontmatter"))?; + + // Extract frontmatter content (skip the first --- line) + let frontmatter_content = lines[1..end_index].join("\n"); + + // Parse YAML + serde_yaml::from_str(&frontmatter_content).context("Failed to parse frontmatter as YAML") +} + +fn generate_image(page_title: &str, font: &Font) -> Result, Vec>> { + let mut img = generate_gradient_background(WIDTH, HEIGHT); + + // --- Layout Calculations --- + let left_section_width = (WIDTH as f32 * LEFT_SECTION_WIDTH_RATIO) as u32; + let right_section_width = WIDTH - left_section_width; + + let mut current_y_offset = PADDING as i32; + + // --- Left Section: Top Row (Logo & Host Text) --- + let mut logo_final_width = 0; + let logo_render_x_offset = PADDING as i64; + + if !LOGO_IMAGE_PATH.is_empty() { + if let Ok(logo_img_dyn) = image::open(LOGO_IMAGE_PATH) { + let logo_img_orig = logo_img_dyn.to_rgba8(); + let (orig_w, orig_h) = logo_img_orig.dimensions(); + + let logo_final_height = LOGO_TARGET_HEIGHT; + logo_final_width = (orig_w as f32 * (logo_final_height as f32 / orig_h as f32)) as u32; + + let resized_logo = image::imageops::resize( + &logo_img_orig, + logo_final_width, + logo_final_height, + image::imageops::FilterType::Lanczos3, + ); + + image::imageops::overlay( + &mut img, + &resized_logo, + logo_render_x_offset, + current_y_offset as i64, + ); + } else { + eprintln!( + "Warning: Logo image not found at {}. Skipping.", + LOGO_IMAGE_PATH + ); + } + } + + // Host Text + let host_text_scale = Scale::uniform(HOST_TEXT_SCALE); + let (_, host_text_height) = measure_text_size(HOST_TEXT, host_text_scale, font); + + let host_text_render_x = + logo_render_x_offset + logo_final_width as i64 + (PADDING / 2) as i64 / 2; + let host_text_render_y = + current_y_offset + (LOGO_TARGET_HEIGHT as i32 / 2) - (host_text_height as i32 / 2); + + draw_text_mut( + &mut img, + TEXT_COLOR, + host_text_render_x as i32, + host_text_render_y as i32, + host_text_scale, + font, + HOST_TEXT, + ); + + // Update y_offset after logo and host text row + let top_row_height = LOGO_TARGET_HEIGHT.max(host_text_height) as i32; + current_y_offset += top_row_height + PADDING as i32; + + // --- Left Section: Page Title --- + let page_title_scale = Scale::uniform(PAGE_TITLE_SCALE); + let page_title_render_x = PADDING as i32; + + let max_title_width_for_wrap = left_section_width - (PADDING * 2); + + let words = page_title.split_whitespace(); + let mut lines = Vec::new(); + let mut current_line = String::new(); + + for word in words { + let test_line = if current_line.is_empty() { + word.to_string() + } else { + format!("{} {}", current_line, word) + }; + let (test_width, _) = measure_text_size(&test_line, page_title_scale, font); + + if test_width <= max_title_width_for_wrap { + current_line = test_line; + } else { + if !current_line.is_empty() { + lines.push(current_line); + } + current_line = word.to_string(); + } + } + if !current_line.is_empty() { + lines.push(current_line); + } + + let line_height_val = font.v_metrics(page_title_scale).line_gap + + font.v_metrics(page_title_scale).ascent + - font.v_metrics(page_title_scale).descent; + + for (i, line) in lines.iter().enumerate() { + draw_text_mut( + &mut img, + TEXT_COLOR, + page_title_render_x, + current_y_offset + (i as f32 * line_height_val) as i32, + page_title_scale, + font, + line, + ); + } + + // --- Right Section: Secondary Image (Oversized with hidden overflow, top-left shown) --- + let secondary_section_x_start = left_section_width as i64; + let secondary_section_y_start = (PADDING * 3) as i64; // Apply top padding to the section itself + let secondary_section_width = right_section_width; + let secondary_section_height = HEIGHT - PADDING; // No bottom padding, extends to full height minus top padding + + if let Ok(secondary_img_orig_dyn) = image::open(SECONDARY_IMAGE_PATH) { + let secondary_img_orig = secondary_img_orig_dyn.to_rgba8(); + let (orig_w, orig_h) = secondary_img_orig.dimensions(); + let resized_secondary = image::imageops::resize( + &secondary_img_orig, + orig_w, + orig_h, + image::imageops::FilterType::Lanczos3, + ); + + // Create a temporary buffer for the right section + let mut right_section_buffer = + ImageBuffer::new(secondary_section_width, secondary_section_height); + + // Place the top-left of the oversized image at the top-left of the buffer + // This will effectively show the top-left part of the secondary image, + // with overflow hidden. + let render_x_in_buffer = 0; + let render_y_in_buffer = 0; + + image::imageops::overlay( + &mut right_section_buffer, + &resized_secondary, + render_x_in_buffer, + render_y_in_buffer, + ); + + // Overlay the clipped right_section_buffer onto the main image + image::imageops::overlay( + &mut img, + &right_section_buffer, + secondary_section_x_start, + secondary_section_y_start, + ); + } else { + eprintln!( + "Warning: Secondary image not found at {}. Skipping right section.", + SECONDARY_IMAGE_PATH + ); + } + + Ok(img) +} + +fn generate_gradient_background(width: u32, height: u32) -> ImageBuffer, Vec> { + let mut img = ImageBuffer::new(width, height); + let num_stops = (GRADIENT_COLORS.len() - 1) as f32; + + for y in 0..height { + for x in 0..width { + let diagonal_pos = (x as f32 / width as f32 + y as f32 / height as f32) / 2.0; + + let stop_index = (diagonal_pos * num_stops).floor() as usize; + let local_pos = (diagonal_pos * num_stops) % 1.0; + + let color1 = GRADIENT_COLORS[stop_index]; + let color2 = GRADIENT_COLORS[(stop_index + 1).min(GRADIENT_COLORS.len() - 1)]; + + let r = (color1[0] as f32 * (1.0 - local_pos) + color2[0] as f32 * local_pos) as u8; + let g = (color1[1] as f32 * (1.0 - local_pos) + color2[1] as f32 * local_pos) as u8; + let b = (color1[2] as f32 * (1.0 - local_pos) + color2[2] as f32 * local_pos) as u8; + let a = (color1[3] as f32 * (1.0 - local_pos) + color2[3] as f32 * local_pos) as u8; + + img.put_pixel(x, y, Rgba([r, g, b, a])); + } + } + img +} + +fn measure_text_size(text: &str, scale: Scale, font: &Font) -> (u32, u32) { + let v_metrics = font.v_metrics(scale); + let height = (v_metrics.ascent - v_metrics.descent + v_metrics.line_gap).ceil() as u32; + let mut width: f32 = 0.0; + + for glyph in font.layout(text, scale, point(0.0, 0.0)) { + if let Some(bounding_box) = glyph.pixel_bounding_box() { + width = width.max(bounding_box.max.x as f32); + } + } + (width as u32, height) +} diff --git a/layouts/home.html b/layouts/home.html new file mode 100644 index 00000000..ae9edb50 --- /dev/null +++ b/layouts/home.html @@ -0,0 +1,75 @@ + + + + + + + + + + + {{ with .Params.robots }} + + {{ end }} + + {{ site.Title | .RenderString }} · {{ site.Params.description | .RenderString }} + + + {{ $cssFiles := slice + (resources.Get "css/reset.css") + (resources.Get "css/theme.css") + (resources.Get "css/components/button.css") + (resources.Get "css/components/dropdown.css") + (resources.Get "css/components/badge.css") + (resources.Get "css/site/header.css") + (resources.Get "css/site/footer.css") + (resources.Get "css/site/layout-home.css") + (resources.Get "css/sections/hero.css") + (resources.Get "css/sections/card-grid.css") + }} + + {{ if site.GetPage "/home/showcase" }} + {{ $cssFiles = $cssFiles | append + (resources.Get "css/components/chroma.css") + (resources.Get "css/components/code-window.css") + (resources.Get "css/components/accordion-vertical-tabs.css") + (resources.Get "css/sections/showcase.css") + }} + {{ end }} + + {{ $css := $cssFiles | resources.Concat "assets/css/home.css" | minify | fingerprint }} + + + {{ with site.Params.enablePageFind }} + + {{ end }} + + {{ partial "favicons.html" . }} + {{ partial "analytics.html" . }} + {{ partial "og-x-seo.html" . }} + + + +
    + {{ partial "site/header.html" . }} + +
    + {{ partial "sections/hero.html" (index site.Data site.Language.Lang "home" "hero") }} + {{ with site.GetPage "/home/showcase" }}{{ partial "sections/showcase.html" . }}{{ end }} + {{ partial "sections/card-grid.html" (index site.Data site.Language.Lang "home" "card-grid") }} +
    + + {{ partial "site/footer.html" . }} +
    + + {{- $js := slice + (resources.Get "js/components/dropdown.js") + (resources.Get "js/site/color-preference.js") + | resources.Concat "assets/js/home.js" | minify | fingerprint }} + + + {{ with site.Params.enablePageFind }} + + {{ end }} + + \ No newline at end of file diff --git a/static/logo.svg b/static/logo.svg new file mode 100644 index 00000000..cf3256de --- /dev/null +++ b/static/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/static/manifest.json b/static/manifest.json index 5710e32f..87493f4f 100644 --- a/static/manifest.json +++ b/static/manifest.json @@ -1,38 +1,21 @@ { - "short_name": "Learning Rust", "name": "Learning Rust", - "description": "Rust Programming Language Tutorials", + "short_name": "Learning Rust", + "description": "Rust Programming Language Tutorials for Everyone!", "start_url": "/?source=pwa", "display": "standalone", "icons": [ { - "src": "/img/icon/icon-192.png", - "type": "image/png", - "sizes": "192x192" - }, - { - "src": "/img/icon/icon-512.png", - "type": "image/png", - "sizes": "512x512" - }, - { - "src": "/img/icon/maskable-icon-192.png", - "type": "image/png", + "src": "/favicon/android-chrome-192x192.png", "sizes": "192x192", - "purpose": "maskable" + "type": "image/png" }, { - "src": "/img/icon/maskable-icon-512.png", - "type": "image/png", + "src": "/favicon/android-chrome-512x512.png", "sizes": "512x512", - "purpose": "maskable" - }, - { - "src": "/img/icon/icon-vector.svg", - "type": "image/svg+xml", - "sizes": "512x512" + "type": "image/png" } ], - "background_color": "#ffffff", - "theme_color": "#ffffff" + "background_color": "#866ee7", + "theme_color": "#866ee7" } \ No newline at end of file diff --git a/static/sw.js b/static/sw.js index 990c71d3..9c27f9f3 100644 --- a/static/sw.js +++ b/static/sw.js @@ -4,21 +4,16 @@ const staticAssets = [ './index.html', './manifest.json', './docs/**/*', - './font/*', - './img/icon/favicon.ico', - './img/icon/icon-16.png', - './img/icon/icon-32.png', - './img/icon/icon-180.png', - './img/icon/icon-192.png', - './img/icon/icon-512.png', - './img/icon/icon-vector.svg', - './img/icon/maskable-icon-192.png', - './img/icon/maskable-icon-512.png', - './js/base.min.js', - './js/component/docsearch.min.js', - './scss/base.css', - './scss/component/docsearch.css', - './scss/home.css', + './favicon/android-chrome-192x192.png', + './favicon/android-chrome-512x512.png', + './favicon/apple-touch-icon.png', + './favicon/favicon.ico', + './favicon/favicon-16x16.png', + './favicon/favicon-32x32.png', + './css/home.min.*.css', + './css/docs.min.*.css', + './js/home.min.*.js', + './js/docs.min.*.js', ]; self.addEventListener('install', async e => { diff --git a/themes/E25DX b/themes/E25DX new file mode 160000 index 00000000..1a80e451 --- /dev/null +++ b/themes/E25DX @@ -0,0 +1 @@ +Subproject commit 1a80e4516c85c46678cffa06d5a2af7d48e27265 diff --git a/themes/docura b/themes/docura deleted file mode 160000 index c5a41148..00000000 --- a/themes/docura +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c5a411485eac5a72f1f4ae86c930354f0d9e20f1