From 3aec55d6b1d7d7b90bd7543c3705b13a2668bb0d Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:15:20 +0200 Subject: [PATCH 01/29] Begin of refactoring for translations --- .env | 1 + .github/workflows/build.yml | 109 + .github/workflows/install-mdbook/action.yml | 28 + .github/workflows/lint.yml | 17 + .github/workflows/publish.yml | 60 + book.toml | 15 +- language-picker.css | 8 + po/de.po | 7341 ++++++++++++++++ po/messages.pot | 7400 +++++++++++++++++ src/SUMMARY.md | 55 + src/additional_resources/design-principles.md | 95 + src/additional_resources/index.md | 16 + src/anti_patterns/borrow_clone.md | 73 + src/anti_patterns/deny-warnings.md | 106 + src/anti_patterns/deref.md | 128 + src/anti_patterns/index.md | 8 + src/functional/generics-type-classes.md | 286 + src/functional/index.md | 10 + src/functional/lenses.md | 368 + src/functional/paradigms.md | 69 + src/idioms/coercion-arguments.md | 138 + src/idioms/concat-format.md | 33 + src/idioms/ctor.md | 115 + src/idioms/default.md | 69 + src/idioms/deref.md | 79 + src/idioms/dtor-finally.md | 90 + src/idioms/ffi/accepting-strings.md | 143 + src/idioms/ffi/errors.md | 139 + src/idioms/ffi/intro.md | 14 + src/idioms/ffi/passing-strings.md | 105 + src/idioms/index.md | 18 + src/idioms/mem-replace.md | 113 + src/idioms/on-stack-dyn-dispatch.md | 93 + src/idioms/option-iter.md | 59 + src/idioms/pass-var-to-closure.md | 59 + src/idioms/priv-extend.md | 122 + src/idioms/return-consumed-arg-on-error.md | 61 + src/idioms/rustdoc-init.md | 94 + src/idioms/temporary-mutability.md | 45 + src/intro.md | 40 + src/patterns/behavioural/RAII.md | 121 + src/patterns/behavioural/command.md | 222 + src/patterns/behavioural/interpreter.md | 147 + src/patterns/behavioural/intro.md | 6 + src/patterns/behavioural/newtype.md | 111 + src/patterns/behavioural/strategy.md | 179 + src/patterns/behavioural/visitor.md | 113 + src/patterns/creational/builder.md | 115 + src/patterns/creational/fold.md | 122 + src/patterns/creational/intro.md | 8 + src/patterns/ffi/export.md | 260 + src/patterns/ffi/intro.md | 13 + src/patterns/ffi/wrappers.md | 162 + src/patterns/index.md | 28 + src/patterns/structural/compose-structs.md | 99 + src/patterns/structural/intro.md | 6 + src/patterns/structural/small-crates.md | 47 + src/patterns/structural/unsafe-mods.md | 34 + src/refactoring/index.md | 22 + src/translations.md | 6 + theme/book.js | 1 + theme/index.hbs | 376 + 62 files changed, 20019 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/install-mdbook/action.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/publish.yml create mode 100644 language-picker.css create mode 100644 po/de.po create mode 100644 po/messages.pot create mode 100644 src/SUMMARY.md create mode 100644 src/additional_resources/design-principles.md create mode 100644 src/additional_resources/index.md create mode 100644 src/anti_patterns/borrow_clone.md create mode 100644 src/anti_patterns/deny-warnings.md create mode 100644 src/anti_patterns/deref.md create mode 100644 src/anti_patterns/index.md create mode 100644 src/functional/generics-type-classes.md create mode 100644 src/functional/index.md create mode 100644 src/functional/lenses.md create mode 100644 src/functional/paradigms.md create mode 100644 src/idioms/coercion-arguments.md create mode 100644 src/idioms/concat-format.md create mode 100644 src/idioms/ctor.md create mode 100644 src/idioms/default.md create mode 100644 src/idioms/deref.md create mode 100644 src/idioms/dtor-finally.md create mode 100644 src/idioms/ffi/accepting-strings.md create mode 100644 src/idioms/ffi/errors.md create mode 100644 src/idioms/ffi/intro.md create mode 100644 src/idioms/ffi/passing-strings.md create mode 100644 src/idioms/index.md create mode 100644 src/idioms/mem-replace.md create mode 100644 src/idioms/on-stack-dyn-dispatch.md create mode 100644 src/idioms/option-iter.md create mode 100644 src/idioms/pass-var-to-closure.md create mode 100644 src/idioms/priv-extend.md create mode 100644 src/idioms/return-consumed-arg-on-error.md create mode 100644 src/idioms/rustdoc-init.md create mode 100644 src/idioms/temporary-mutability.md create mode 100644 src/intro.md create mode 100644 src/patterns/behavioural/RAII.md create mode 100644 src/patterns/behavioural/command.md create mode 100644 src/patterns/behavioural/interpreter.md create mode 100644 src/patterns/behavioural/intro.md create mode 100644 src/patterns/behavioural/newtype.md create mode 100644 src/patterns/behavioural/strategy.md create mode 100644 src/patterns/behavioural/visitor.md create mode 100644 src/patterns/creational/builder.md create mode 100644 src/patterns/creational/fold.md create mode 100644 src/patterns/creational/intro.md create mode 100644 src/patterns/ffi/export.md create mode 100644 src/patterns/ffi/intro.md create mode 100644 src/patterns/ffi/wrappers.md create mode 100644 src/patterns/index.md create mode 100644 src/patterns/structural/compose-structs.md create mode 100644 src/patterns/structural/intro.md create mode 100644 src/patterns/structural/small-crates.md create mode 100644 src/patterns/structural/unsafe-mods.md create mode 100644 src/refactoring/index.md create mode 100644 src/translations.md create mode 100644 theme/book.js create mode 100644 theme/index.hbs diff --git a/.env b/.env index a61fb32d..4dae58c1 100644 --- a/.env +++ b/.env @@ -1 +1,2 @@ MDBOOK_VERSION=0.4.28 +MDBOOK_I8N_HELPERS_VERSION=0.1.0 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..8cdc144a --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,109 @@ +name: Test + +on: + pull_request: + push: + branches: + - main + +env: + CARGO_TERM_COLOR: always + +jobs: + mdbook: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install mdbook + uses: ./.github/workflows/install-mdbook + + - name: Test code snippets + run: mdbook test + + i18n-helpers: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Gettext + run: sudo apt install gettext + + - name: Install mdbook + uses: ./.github/workflows/install-mdbook + + - name: Generate po/messages.pot + run: mdbook build -d po + env: + MDBOOK_OUTPUT: '{"xgettext": {"pot-file": "messages.pot"}}' + + - name: Test messages.pot + run: msgfmt --statistics -o /dev/null po/messages.pot + + - name: Expand includes without translation + run: mdbook build -d expanded + env: + MDBOOK_OUTPUT: '{"markdown": {}}' + + - name: Expand includes with no-op translation + run: mdbook build -d no-op + env: + MDBOOK_OUTPUT: '{"markdown": {}}' + MDBOOK_PREPROCESSOR__GETTEXT__PO_FILE: po/messages.pot + + - name: Compare no translation to no-op translation + run: diff --color=always --unified --recursive expanded no-op + + find-translations: + runs-on: ubuntu-latest + outputs: + languages: ${{ steps.find-translations.outputs.languages }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Find translations + id: find-translations + shell: python + run: | + import os, json, pathlib + languages = [p.stem for p in pathlib.Path("po").iterdir() if p.suffix == ".po"] + github_output = open(os.environ["GITHUB_OUTPUT"], "a") + github_output.write("languages=") + json.dump(sorted(languages), github_output) + + translations: + runs-on: ubuntu-latest + needs: + - find-translations + strategy: + matrix: + language: ${{ fromJSON(needs.find-translations.outputs.languages) }} + env: + MDBOOK_BOOK__LANGUAGE: ${{ matrix.language }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install Gettext + run: sudo apt install gettext + + - name: Install mdbook + uses: ./.github/workflows/install-mdbook + + - name: Test ${{ matrix.language }} translation + run: msgfmt --statistics -o /dev/null po/${{ matrix.language }}.po + + - name: Build book with ${{ matrix.language }} translation + run: mdbook build + + - name: Upload ${{ matrix.language }} translation + uses: actions/upload-artifact@v3 + with: + name: comprehensive-rust-${{ matrix.language }} + path: book/ + + - name: Test code snippets with ${{ matrix.language }} translation + run: mdbook test diff --git a/.github/workflows/install-mdbook/action.yml b/.github/workflows/install-mdbook/action.yml new file mode 100644 index 00000000..711cf084 --- /dev/null +++ b/.github/workflows/install-mdbook/action.yml @@ -0,0 +1,28 @@ +name: Install mdbook and dependencies + +description: Install the mdbook with the dependencies we need. + +runs: + using: composite + steps: + - name: Read mdbook version from .env + id: mdbook-version + run: | + . ./.env + echo "::set-output name=MDBOOK_VERSION::${MDBOOK_VERSION}" + + - name: Read mdbook-i8n-helpers version from .env + id: mdbook-i8n-helpers-version + run: | + . ./.env + echo "::set-output name=MDBOOK_I8N_HELPERS_VERSION::${MDBOOK_I8N_HELPERS_VERSION}" + + # The --locked flag is important for reproducible builds. It also + # avoids breakage due to skews between mdbook and mdbook-svgbob. + - name: Install mdbook + run: cargo install mdbook --locked --version '${{ steps.mdbook-version.outputs.MDBOOK_VERSION }}' + shell: bash + + - name: Install i18n-helpers + run: cargo install mdbook-i18n-helpers --locked '${{ steps.mdbook-i8n-helpers-version.outputs.MDBOOK_I8N_HELPERS_VERSION }}' + shell: bash diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..42e6a2d6 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,17 @@ +name: Continuous Integration + +on: + push: + branches: [main] + pull_request: + +jobs: + markdown-lint: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + - name: Lint all original files recursively + uses: avto-dev/markdown-lint@v1 + with: + config: ".markdownlint.yaml" + args: "**/*.md" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..96177f40 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,60 @@ +name: Publish + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment +concurrency: + group: pages + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + # Update the language picker in index.hbs to link new languages. + LANGUAGES: da pt-BR ko + +jobs: + publish: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install mdbook + uses: ./.github/workflows/install-mdbook + + - name: Build course in English + run: mdbook build -d book + + - name: Build all translations + run: | + for po_lang in ${{ env.LANGUAGES }}; do + echo "::group::Building $po_lang translation" + MDBOOK_BOOK__LANGUAGE=$po_lang \ + MDBOOK_OUTPUT__HTML__SITE_URL=/patterns/$po_lang/ \ + mdbook build -d book/$po_lang + echo "::endgroup::" + done + + - name: Setup Pages + uses: actions/configure-pages@v2 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: book + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 diff --git a/book.toml b/book.toml index f87401c6..51a6580b 100644 --- a/book.toml +++ b/book.toml @@ -4,10 +4,14 @@ authors = ["the rust-unofficial authors"] description = "A catalogue of Rust design patterns, anti-patterns and idioms" language = "en" multilingual = false -src = "." +src = "src" [build] create-missing = false +extra-watch-dirs = ["po"] + +[preprocessor.gettext] +after = ["links"] [rust] edition = "2018" @@ -18,5 +22,14 @@ site-url = "/patterns/" git-repository-url = "https://github.com/rust-unofficial/patterns" git-repository-icon = "fa-github" edit-url-template = "https://github.com/rust-unofficial/patterns/edit/main/{path}" +additional-css = ["language-picker.css"] +curly-quotes = true + +[output.html.fold] +enable = true +level = 0 + +[output.html.playground] +editable = true # [output.linkcheck] # enable the "mdbook-linkcheck" renderer, disabled due to gh-actions diff --git a/language-picker.css b/language-picker.css new file mode 100644 index 00000000..1f7d5377 --- /dev/null +++ b/language-picker.css @@ -0,0 +1,8 @@ +#language-list { + left: auto; + right: 10px; +} + +#language-list a { + color: inherit; +} diff --git a/po/de.po b/po/de.po new file mode 100644 index 00000000..b0b63f55 --- /dev/null +++ b/po/de.po @@ -0,0 +1,7341 @@ +msgid "" +msgstr "" +"Project-Id-Version: Rust Design Patterns\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2023-04-05 14:27+0200\n" +"Last-Translator: \n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: .\SUMMARY.md:3 +msgid "Introduction" +msgstr "" + +#: .\SUMMARY.md:4 +msgid "Translations" +msgstr "" + +#: .\SUMMARY.md:5 +msgid "Idioms" +msgstr "" + +#: .\SUMMARY.md:6 +msgid "Use borrowed types for arguments" +msgstr "" + +#: .\SUMMARY.md:7 +msgid "Concatenating Strings with format!" +msgstr "" + +#: .\SUMMARY.md:8 +msgid "Constructor" +msgstr "" + +#: .\SUMMARY.md:9 +msgid "The Default Trait" +msgstr "" + +#: .\SUMMARY.md:10 +msgid "Collections Are Smart Pointers" +msgstr "" + +#: .\SUMMARY.md:11 +msgid "Finalisation in Destructors" +msgstr "" + +#: .\SUMMARY.md:12 +msgid "mem::{take(_), replace(_)}" +msgstr "" + +#: .\SUMMARY.md:13 +msgid "On-Stack Dynamic Dispatch" +msgstr "" + +#: .\SUMMARY.md:14 .\SUMMARY.md:40 +msgid "Foreign function interface (FFI)" +msgstr "" + +#: .\SUMMARY.md:15 +msgid "Idiomatic Errors" +msgstr "" + +#: .\SUMMARY.md:16 +msgid "Accepting Strings" +msgstr "" + +#: .\SUMMARY.md:17 +msgid "Passing Strings" +msgstr "" + +#: .\SUMMARY.md:18 +msgid "Iterating over an Option" +msgstr "" + +#: .\SUMMARY.md:19 +msgid "Pass Variables to Closure" +msgstr "" + +#: .\SUMMARY.md:20 +msgid "Privacy For Extensibility" +msgstr "" + +#: .\SUMMARY.md:21 +msgid "Easy doc initialization" +msgstr "" + +#: .\SUMMARY.md:22 +msgid "Temporary mutability" +msgstr "" + +#: .\SUMMARY.md:23 +msgid "Return consumed arg on error" +msgstr "" + +#: .\SUMMARY.md:25 +msgid "Design Patterns" +msgstr "" + +#: .\SUMMARY.md:26 +msgid "Behavioural" +msgstr "" + +#: .\SUMMARY.md:27 +msgid "Command" +msgstr "" + +#: .\SUMMARY.md:28 +msgid "Interpreter" +msgstr "" + +#: .\SUMMARY.md:29 +msgid "Newtype" +msgstr "" + +#: .\SUMMARY.md:30 +msgid "RAII Guards" +msgstr "" + +#: .\SUMMARY.md:31 +msgid "Strategy" +msgstr "" + +#: .\SUMMARY.md:32 +msgid "Visitor" +msgstr "" + +#: .\SUMMARY.md:33 +msgid "Creational" +msgstr "" + +#: .\SUMMARY.md:34 +msgid "Builder" +msgstr "" + +#: .\SUMMARY.md:35 +msgid "Fold" +msgstr "" + +#: .\SUMMARY.md:36 +msgid "Structural" +msgstr "" + +#: .\SUMMARY.md:37 +msgid "Compose Structs" +msgstr "" + +#: .\SUMMARY.md:38 +msgid "Prefer Small Crates" +msgstr "" + +#: .\SUMMARY.md:39 +msgid "Contain unsafety in small modules" +msgstr "" + +#: .\SUMMARY.md:41 +msgid "Object-Based APIs" +msgstr "" + +#: .\SUMMARY.md:42 +msgid "Type Consolidation into Wrappers" +msgstr "" + +#: .\SUMMARY.md:44 +msgid "Anti-patterns" +msgstr "" + +#: .\SUMMARY.md:45 +msgid "Clone to satisfy the borrow checker" +msgstr "" + +#: .\SUMMARY.md:46 +msgid "#[deny(warnings)]" +msgstr "" + +#: .\SUMMARY.md:47 +msgid "Deref Polymorphism" +msgstr "" + +#: .\SUMMARY.md:49 +msgid "Functional Programming" +msgstr "" + +#: .\SUMMARY.md:50 +msgid "Programming paradigms" +msgstr "" + +#: .\SUMMARY.md:51 +msgid "Generics as Type Classes" +msgstr "" + +#: .\SUMMARY.md:52 +msgid "Lenses and Prisms" +msgstr "" + +#: .\SUMMARY.md:54 +msgid "Additional Resources" +msgstr "" + +#: .\SUMMARY.md:55 +msgid "Design principles" +msgstr "" + +#: .\intro.md:1 +msgid "# Introduction\r" +msgstr "" + +#: .\intro.md:3 +msgid "## Participation\r" +msgstr "" + +#: .\intro.md:5 +msgid "" +"If you are interested in contributing to this book, check out the\r\n" +"[contribution guidelines](https://github.com/rust-unofficial/patterns/blob/" +"master/CONTRIBUTING.md)." +msgstr "" + +#: .\intro.md:8 +msgid "## Design patterns\r" +msgstr "" + +#: .\intro.md:10 +msgid "" +"In software development, we often come across problems that share\r\n" +"similarities regardless of the environment they appear in. Although the\r\n" +"implementation details are crucial to solve the task at hand, we may\r\n" +"abstract from these particularities to find the common practices that\r\n" +"are generically applicable." +msgstr "" + +#: .\intro.md:16 +msgid "" +"Design patterns are a collection of reusable and tested solutions to\r\n" +"recurring problems in engineering. They make our software more modular,\r\n" +"maintainable, and extensible. Moreover, these patterns provide a common\r\n" +"language for developers, making them an excellent tool for effective\r\n" +"communication when problem-solving in teams." +msgstr "" + +#: .\intro.md:22 .\patterns/index.md:14 +msgid "## Design patterns in Rust\r" +msgstr "" + +#: .\intro.md:24 +msgid "" +"Rust is not object-oriented, and the combination of all its characteristics," +"\r\n" +"such as functional elements, a strong type system, and the borrow checker," +"\r\n" +"makes it unique.\r\n" +"Because of this, Rust design patterns vary with respect to other\r\n" +"traditional object-oriented programming languages.\r\n" +"That's why we decided to write this book. We hope you enjoy reading it!\r\n" +"The book is divided in three main chapters:" +msgstr "" + +#: .\intro.md:32 +msgid "" +"- [Idioms](./idioms/index.md): guidelines to follow when coding.\r\n" +" They are the social norms of the community.\r\n" +" You should break them only if you have a good reason for it.\r\n" +"- [Design patterns](./patterns/index.md): methods to solve common problems" +"\r\n" +" when coding.\r\n" +"- [Anti-patterns](./anti_patterns/index.md): methods to solve common problems" +"\r\n" +" when coding.\r\n" +" However, while design patterns give us benefits,\r\n" +" anti-patterns create more problems.\r" +msgstr "" + +#: .\translations.md:1 +msgid "# Translations\r" +msgstr "" + +#: .\translations.md:3 +msgid "" +"- [简体中文](https://fomalhauthmj.github.io/patterns/)\r\n" +"\r" +msgstr "" + +#: .\translations.md:5 +msgid "" +"If you want to add a translation, please open an issue in the\r\n" +"[main repository](https://github.com/rust-unofficial/patterns)." +msgstr "" + +#: .\idioms/index.md:1 +msgid "# Idioms\r" +msgstr "" + +#: .\idioms/index.md:3 +msgid "" +"[Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly used" +"\r\n" +"styles, guidelines and patterns largely agreed upon by a community.\r\n" +"Writing idiomatic code allows other developers to understand better what is" +"\r\n" +"happening." +msgstr "" + +#: .\idioms/index.md:8 +msgid "" +"After all, the computer only cares about the machine code that is generated" +"\r\n" +"by the compiler.\r\n" +"Instead, the source code is mainly beneficial to the developer.\r\n" +"So, since we have this abstraction layer, why not make it more readable?" +msgstr "" + +#: .\idioms/index.md:13 +msgid "" +"Remember the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle):" +"\r\n" +"\"Keep It Simple, Stupid\". It claims that \"most systems work best if they " +"are\r\n" +"kept simple rather than made complicated; therefore, simplicity should be a " +"key\r\n" +"goal in design, and unnecessary complexity should be avoided\"." +msgstr "" + +#: .\idioms/index.md:18 +msgid "> Code is there for humans, not computers, to understand.\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:1 +msgid "# Use borrowed types for arguments\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:3 .\idioms/concat-format.md:3 +#: .\idioms/ctor.md:3 .\idioms/default.md:3 .\idioms/deref.md:3 +#: .\idioms/dtor-finally.md:3 .\idioms/mem-replace.md:3 +#: .\idioms/on-stack-dyn-dispatch.md:3 .\idioms/ffi/errors.md:3 +#: .\idioms/ffi/accepting-strings.md:3 .\idioms/ffi/passing-strings.md:3 +#: .\idioms/option-iter.md:3 .\idioms/pass-var-to-closure.md:3 +#: .\idioms/priv-extend.md:3 .\idioms/rustdoc-init.md:3 +#: .\idioms/temporary-mutability.md:3 +#: .\idioms/return-consumed-arg-on-error.md:3 +#: .\patterns/behavioural/command.md:3 .\patterns/behavioural/interpreter.md:3 +#: .\patterns/behavioural/newtype.md:13 .\patterns/behavioural/RAII.md:3 +#: .\patterns/behavioural/strategy.md:3 .\patterns/behavioural/visitor.md:3 +#: .\patterns/creational/builder.md:3 .\patterns/creational/fold.md:3 +#: .\patterns/structural/compose-structs.md:5 +#: .\patterns/structural/small-crates.md:3 +#: .\patterns/structural/unsafe-mods.md:3 .\patterns/ffi/export.md:3 +#: .\patterns/ffi/wrappers.md:3 .\anti_patterns/borrow_clone.md:3 +#: .\anti_patterns/deny-warnings.md:3 .\anti_patterns/deref.md:3 +#: .\functional/generics-type-classes.md:3 +msgid "## Description\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:5 +msgid "" +"Using a target of a deref coercion can increase the flexibility of your code" +"\r\n" +"when you are deciding which argument type to use for a function argument.\r\n" +"In this way, the function will accept more input types." +msgstr "" + +#: .\idioms/coercion-arguments.md:9 +msgid "" +"This is not limited to slice-able or fat pointer types.\r\n" +"In fact, you should always prefer using the __borrowed type__ over\r\n" +"__borrowing the owned type__.\r\n" +"Such as `&str` over `&String`, `&[T]` over `&Vec`, or `&T` over `&Box`." +msgstr "" + +#: .\idioms/coercion-arguments.md:14 +msgid "" +"Using borrowed types you can avoid layers of indirection for those instances" +"\r\n" +"where the owned type already provides a layer of indirection. For instance, a" +"\r\n" +"`String` has a layer of indirection, so a `&String` will have two layers of" +"\r\n" +"indirection. We can avoid this by using `&str` instead, and letting `&String`" +"\r\n" +"coerce to a `&str` whenever the function is invoked." +msgstr "" + +#: .\idioms/coercion-arguments.md:20 .\idioms/concat-format.md:10 +#: .\idioms/default.md:20 .\idioms/deref.md:9 .\idioms/dtor-finally.md:9 +#: .\idioms/mem-replace.md:11 .\idioms/on-stack-dyn-dispatch.md:10 +#: .\idioms/pass-var-to-closure.md:12 .\idioms/priv-extend.md:18 +#: .\idioms/rustdoc-init.md:45 .\idioms/temporary-mutability.md:12 +#: .\idioms/return-consumed-arg-on-error.md:8 +#: .\patterns/behavioural/command.md:18 .\patterns/behavioural/newtype.md:18 +#: .\patterns/behavioural/RAII.md:11 .\patterns/behavioural/strategy.md:28 +#: .\patterns/behavioural/visitor.md:13 .\patterns/creational/builder.md:7 +#: .\patterns/creational/fold.md:12 .\patterns/structural/compose-structs.md:17 +#: .\anti_patterns/borrow_clone.md:11 .\anti_patterns/deny-warnings.md:8 +#: .\anti_patterns/deref.md:8 .\functional/generics-type-classes.md:38 +msgid "## Example\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:22 +msgid "" +"For this example, we will illustrate some differences for using `&String` as " +"a\r\n" +"function argument versus using a `&str`, but the ideas apply as well to using" +"\r\n" +"`&Vec` versus using a `&[T]` or using a `&Box` versus a `&T`." +msgstr "" + +#: .\idioms/coercion-arguments.md:26 +msgid "" +"Consider an example where we wish to determine if a word contains three\r\n" +"consecutive vowels. We don't need to own the string to determine this, so we" +"\r\n" +"will take a reference." +msgstr "" + +#: .\idioms/coercion-arguments.md:30 +msgid "The code might look something like this:" +msgstr "" + +#: .\idioms/coercion-arguments.md:32 +msgid "" +"```rust\r\n" +"fn three_vowels(word: &String) -> bool {\r\n" +" let mut vowel_count = 0;\r\n" +" for c in word.chars() {\r\n" +" match c {\r\n" +" 'a' | 'e' | 'i' | 'o' | 'u' => {\r\n" +" vowel_count += 1;\r\n" +" if vowel_count >= 3 {\r\n" +" return true\r\n" +" }\r\n" +" }\r\n" +" _ => vowel_count = 0\r\n" +" }\r\n" +" }\r\n" +" false\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let ferris = \"Ferris\".to_string();\r\n" +" let curious = \"Curious\".to_string();\r\n" +" println!(\"{}: {}\", ferris, three_vowels(&ferris));\r\n" +" println!(\"{}: {}\", curious, three_vowels(&curious));\r\n" +"\r\n" +" // This works fine, but the following two lines would fail:\r\n" +" // println!(\"Ferris: {}\", three_vowels(\"Ferris\"));\r\n" +" // println!(\"Curious: {}\", three_vowels(\"Curious\"));\r\n" +"\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:62 +msgid "" +"This works fine because we are passing a `&String` type as a parameter.\r\n" +"If we remove the comments on the last two lines, the example will fail. This" +"\r\n" +"is because a `&str` type will not coerce to a `&String` type. We can fix this" +"\r\n" +"by simply modifying the type for our argument." +msgstr "" + +#: .\idioms/coercion-arguments.md:67 +msgid "For instance, if we change our function declaration to:" +msgstr "" + +#: .\idioms/coercion-arguments.md:69 +msgid "" +"```rust, ignore\r\n" +"fn three_vowels(word: &str) -> bool {\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:73 +msgid "then both versions will compile and print the same output." +msgstr "" + +#: .\idioms/coercion-arguments.md:75 +msgid "" +"```bash\r\n" +"Ferris: false\r\n" +"Curious: true\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:80 +msgid "" +"But wait, that's not all! There is more to this story.\r\n" +"It's likely that you may say to yourself: that doesn't matter, I will never " +"be\r\n" +"using a `&'static str` as an input anyways (as we did when we used `\"Ferris" +"\"`).\r\n" +"Even ignoring this special example, you may still find that using `&str` will" +"\r\n" +"give you more flexibility than using a `&String`." +msgstr "" + +#: .\idioms/coercion-arguments.md:86 +msgid "" +"Let's now take an example where someone gives us a sentence, and we want to" +"\r\n" +"determine if any of the words in the sentence contain three consecutive " +"vowels.\r\n" +"We probably should make use of the function we have already defined and " +"simply\r\n" +"feed in each word from the sentence." +msgstr "" + +#: .\idioms/coercion-arguments.md:91 +msgid "An example of this could look like this:" +msgstr "" + +#: .\idioms/coercion-arguments.md:93 +msgid "" +"```rust\r\n" +"fn three_vowels(word: &str) -> bool {\r\n" +" let mut vowel_count = 0;\r\n" +" for c in word.chars() {\r\n" +" match c {\r\n" +" 'a' | 'e' | 'i' | 'o' | 'u' => {\r\n" +" vowel_count += 1;\r\n" +" if vowel_count >= 3 {\r\n" +" return true\r\n" +" }\r\n" +" }\r\n" +" _ => vowel_count = 0\r\n" +" }\r\n" +" }\r\n" +" false\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let sentence_string =\r\n" +" \"Once upon a time, there was a friendly curious crab named Ferris\"." +"to_string();\r\n" +" for word in sentence_string.split(' ') {\r\n" +" if three_vowels(word) {\r\n" +" println!(\"{} has three consecutive vowels!\", word);\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:121 +msgid "" +"Running this example using our function declared with an argument type `&str`" +"\r\n" +"will yield" +msgstr "" + +#: .\idioms/coercion-arguments.md:124 +msgid "" +"```bash\r\n" +"curious has three consecutive vowels!\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:128 +msgid "" +"However, this example will not run when our function is declared with an\r\n" +"argument type `&String`. This is because string slices are a `&str` and not a" +"\r\n" +"`&String` which would require an allocation to be converted to `&String` " +"which\r\n" +"is not implicit, whereas converting from `String` to `&str` is cheap and " +"implicit." +msgstr "" + +#: .\idioms/coercion-arguments.md:133 .\idioms/ctor.md:101 +#: .\idioms/default.md:58 .\idioms/deref.md:76 .\idioms/dtor-finally.md:88 +#: .\idioms/mem-replace.md:110 .\idioms/on-stack-dyn-dispatch.md:84 +#: .\idioms/option-iter.md:46 .\idioms/priv-extend.md:120 +#: .\patterns/behavioural/command.md:218 +#: .\patterns/behavioural/interpreter.md:143 +#: .\patterns/behavioural/newtype.md:104 .\patterns/behavioural/RAII.md:111 +#: .\patterns/behavioural/strategy.md:175 .\patterns/behavioural/visitor.md:106 +#: .\patterns/creational/builder.md:108 .\patterns/creational/fold.md:109 +#: .\patterns/structural/small-crates.md:45 +#: .\patterns/structural/unsafe-mods.md:32 .\anti_patterns/borrow_clone.md:68 +#: .\anti_patterns/deny-warnings.md:96 .\anti_patterns/deref.md:123 +#: .\functional/generics-type-classes.md:238 +msgid "## See also\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:135 +msgid "" +"- [Rust Language Reference on Type Coercions](https://doc.rust-lang.org/" +"reference/type-coercions.html)\r\n" +"- For more discussion on how to handle `String` and `&str` see\r\n" +" [this blog series (2015)](https://web.archive.org/web/20201112023149/" +"https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html)\r\n" +" by Herman J. Radtke III\r" +msgstr "" + +#: .\idioms/concat-format.md:1 +msgid "# Concatenating strings with `format!`\r" +msgstr "" + +#: .\idioms/concat-format.md:5 +msgid "" +"It is possible to build up strings using the `push` and `push_str` methods " +"on a\r\n" +"mutable `String`, or using its `+` operator. However, it is often more\r\n" +"convenient to use `format!`, especially where there is a mix of literal and" +"\r\n" +"non-literal strings." +msgstr "" + +#: .\idioms/concat-format.md:12 +msgid "" +"```rust\r\n" +"fn say_hello(name: &str) -> String {\r\n" +" // We could construct the result string manually.\r\n" +" // let mut result = \"Hello \".to_owned();\r\n" +" // result.push_str(name);\r\n" +" // result.push('!');\r\n" +" // result\r\n" +"\r\n" +" // But using format! is better.\r\n" +" format!(\"Hello {}!\", name)\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/concat-format.md:25 .\idioms/deref.md:43 +#: .\idioms/dtor-finally.md:42 .\idioms/mem-replace.md:84 +#: .\idioms/on-stack-dyn-dispatch.md:48 .\idioms/ffi/errors.md:131 +#: .\idioms/ffi/accepting-strings.md:68 .\idioms/ffi/passing-strings.md:68 +#: .\idioms/pass-var-to-closure.md:48 .\idioms/rustdoc-init.md:77 +#: .\idioms/temporary-mutability.md:38 +#: .\idioms/return-consumed-arg-on-error.md:55 +#: .\patterns/behavioural/newtype.md:66 .\patterns/behavioural/RAII.md:78 +#: .\patterns/behavioural/strategy.md:96 .\patterns/creational/builder.md:68 +#: .\patterns/structural/compose-structs.md:75 +#: .\patterns/structural/small-crates.md:12 +#: .\patterns/structural/unsafe-mods.md:11 .\patterns/ffi/export.md:111 +#: .\patterns/ffi/wrappers.md:63 .\anti_patterns/deny-warnings.md:16 +#: .\anti_patterns/deref.md:69 .\functional/generics-type-classes.md:211 +msgid "## Advantages\r" +msgstr "" + +#: .\idioms/concat-format.md:27 +msgid "" +"Using `format!` is usually the most succinct and readable way to combine " +"strings." +msgstr "" + +#: .\idioms/concat-format.md:29 .\idioms/deref.md:50 +#: .\idioms/dtor-finally.md:47 .\idioms/mem-replace.md:88 +#: .\idioms/on-stack-dyn-dispatch.md:54 .\idioms/ffi/errors.md:136 +#: .\idioms/ffi/accepting-strings.md:141 .\idioms/ffi/passing-strings.md:103 +#: .\idioms/pass-var-to-closure.md:57 .\idioms/rustdoc-init.md:81 +#: .\idioms/temporary-mutability.md:42 +#: .\idioms/return-consumed-arg-on-error.md:59 +#: .\patterns/behavioural/newtype.md:77 .\patterns/behavioural/strategy.md:104 +#: .\patterns/creational/builder.md:76 +#: .\patterns/structural/compose-structs.md:81 +#: .\patterns/structural/small-crates.md:22 +#: .\patterns/structural/unsafe-mods.md:17 .\patterns/ffi/export.md:234 +#: .\patterns/ffi/wrappers.md:69 .\anti_patterns/deref.md:81 +#: .\functional/generics-type-classes.md:222 +msgid "## Disadvantages\r" +msgstr "" + +#: .\idioms/concat-format.md:31 +msgid "" +"It is usually not the most efficient way to combine strings - a series of " +"`push`\r\n" +"operations on a mutable string is usually the most efficient (especially if " +"the\r\n" +"string has been pre-allocated to the expected size)." +msgstr "" + +#: .\idioms/ctor.md:1 +msgid "# Constructors\r" +msgstr "" + +#: .\idioms/ctor.md:5 +msgid "" +"Rust does not have constructors as a language construct. Instead, the\r\n" +"convention is to use an [associated function][] `new` to create an object:" +msgstr "" + +#: .\idioms/ctor.md:8 +msgid "" +"```rust\r\n" +"/// Time in seconds.\r\n" +"///\r\n" +"/// # Example\r\n" +"///\r\n" +"/// ```\r\n" +"/// let s = Second::new(42);\r\n" +"/// assert_eq!(42, s.value());\r\n" +"/// ```\r\n" +"pub struct Second {\r\n" +" value: u64\r\n" +"}\r\n" +"\r\n" +"impl Second {\r\n" +" // Constructs a new instance of [`Second`].\r\n" +" // Note this is an associated function - no self.\r\n" +" pub fn new(value: u64) -> Self {\r\n" +" Self { value }\r\n" +" }\r\n" +"\r\n" +" /// Returns the value in seconds.\r\n" +" pub fn value(&self) -> u64 {\r\n" +" self.value\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ctor.md:35 +msgid "## Default Constructors\r" +msgstr "" + +#: .\idioms/ctor.md:37 +msgid "" +"Rust supports default constructors with the [`Default`][std-default] trait:" +msgstr "" + +#: .\idioms/ctor.md:39 +msgid "" +"```rust\r\n" +"/// Time in seconds.\r\n" +"///\r\n" +"/// # Example\r\n" +"///\r\n" +"/// ```\r\n" +"/// let s = Second::default();\r\n" +"/// assert_eq!(0, s.value());\r\n" +"/// ```\r\n" +"pub struct Second {\r\n" +" value: u64\r\n" +"}\r\n" +"\r\n" +"impl Second {\r\n" +" /// Returns the value in seconds.\r\n" +" pub fn value(&self) -> u64 {\r\n" +" self.value\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"impl Default for Second {\r\n" +" fn default() -> Self {\r\n" +" Self { value: 0 }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ctor.md:66 +msgid "" +"`Default` can also be derived if all types of all fields implement `Default`," +"\r\n" +"like they do with `Second`:" +msgstr "" + +#: .\idioms/ctor.md:69 +msgid "" +"```rust\r\n" +"/// Time in seconds.\r\n" +"///\r\n" +"/// # Example\r\n" +"///\r\n" +"/// ```\r\n" +"/// let s = Second::default();\r\n" +"/// assert_eq!(0, s.value());\r\n" +"/// ```\r\n" +"#[derive(Default)]\r\n" +"pub struct Second {\r\n" +" value: u64\r\n" +"}\r\n" +"\r\n" +"impl Second {\r\n" +" /// Returns the value in seconds.\r\n" +" pub fn value(&self) -> u64 {\r\n" +" self.value\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ctor.md:91 +msgid "" +"**Note:** It is common and expected for types to implement both\r\n" +"`Default` and an empty `new` constructor. `new` is the constructor\r\n" +"convention in Rust, and users expect it to exist, so if it is\r\n" +"reasonable for the basic constructor to take no arguments, then it\r\n" +"should, even if it is functionally identical to default." +msgstr "" + +#: .\idioms/ctor.md:97 +msgid "" +"**Hint:** The advantage of implementing or deriving `Default` is that your " +"type\r\n" +"can now be used where a `Default` implementation is required, most " +"prominently,\r\n" +"any of the [`*or_default` functions in the standard library][std-or-default]." +msgstr "" + +#: .\idioms/ctor.md:103 +msgid "" +"- The [default idiom](default.md) for a more in-depth description of the\r\n" +" `Default` trait.\r\n" +"\r\n" +"- The [builder pattern](../patterns/creational/builder.md) for constructing" +"\r\n" +" objects where there are multiple configurations.\r\n" +"\r\n" +"- [API Guidelines/C-COMMON-TRAITS][API Guidelines/C-COMMON-TRAITS] for\r\n" +" implementing both, `Default` and `new`.\r\n" +"\r" +msgstr "" + +#: .\idioms/default.md:1 +msgid "# The `Default` Trait\r" +msgstr "" + +#: .\idioms/default.md:5 +msgid "" +"Many types in Rust have a [constructor]. However, this is *specific* to the" +"\r\n" +"type; Rust cannot abstract over \"everything that has a `new()` method\". To" +"\r\n" +"allow this, the [`Default`] trait was conceived, which can be used with\r\n" +"containers and other generic types (e.g. see [`Option::" +"unwrap_or_default()`]).\r\n" +"Notably, some containers already implement it where applicable." +msgstr "" + +#: .\idioms/default.md:11 +msgid "" +"Not only do one-element containers like `Cow`, `Box` or `Arc` implement\r\n" +"`Default` for contained `Default` types, one can automatically\r\n" +"`#[derive(Default)]` for structs whose fields all implement it, so the more" +"\r\n" +"types implement `Default`, the more useful it becomes." +msgstr "" + +#: .\idioms/default.md:16 +msgid "" +"On the other hand, constructors can take multiple arguments, while the\r\n" +"`default()` method does not. There can even be multiple constructors with\r\n" +"different names, but there can only be one `Default` implementation per type." +msgstr "" + +#: .\idioms/default.md:22 +msgid "" +"```rust\r\n" +"use std::{path::PathBuf, time::Duration};\r\n" +"\r\n" +"// note that we can simply auto-derive Default here.\r\n" +"#[derive(Default, Debug, PartialEq)]\r\n" +"struct MyConfiguration {\r\n" +" // Option defaults to None\r\n" +" output: Option,\r\n" +" // Vecs default to empty vector\r\n" +" search_path: Vec,\r\n" +" // Duration defaults to zero time\r\n" +" timeout: Duration,\r\n" +" // bool defaults to false\r\n" +" check: bool,\r\n" +"}\r\n" +"\r\n" +"impl MyConfiguration {\r\n" +" // add setters here\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" // construct a new instance with default values\r\n" +" let mut conf = MyConfiguration::default();\r\n" +" // do something with conf here\r\n" +" conf.check = true;\r\n" +" println!(\"conf = {:#?}\", conf);\r\n" +" \r\n" +" // partial initialization with default values, creates the same instance" +"\r\n" +" let conf1 = MyConfiguration {\r\n" +" check: true,\r\n" +" ..Default::default()\r\n" +" };\r\n" +" assert_eq!(conf, conf1);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/default.md:60 +msgid "" +"- The [constructor] idiom is another way to generate instances that may or " +"may\r\n" +"not be \"default\"\r\n" +"- The [`Default`] documentation (scroll down for the list of " +"implementors)\r\n" +"- [`Option::unwrap_or_default()`]\r\n" +"- [`derive(new)`]\r\n" +"\r" +msgstr "" + +#: .\idioms/deref.md:1 +msgid "# Collections are smart pointers\r" +msgstr "" + +#: .\idioms/deref.md:5 +msgid "" +"Use the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html)\r\n" +"trait to treat collections like smart pointers, offering owning\r\n" +"and borrowed views of data." +msgstr "" + +#: .\idioms/deref.md:11 +msgid "" +"```rust,ignore\r\n" +"use std::ops::Deref;\r\n" +"\r\n" +"struct Vec {\r\n" +" data: RawVec,\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"impl Deref for Vec {\r\n" +" type Target = [T];\r\n" +"\r\n" +" fn deref(&self) -> &[T] {\r\n" +" //..\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/deref.md:28 +msgid "" +"A `Vec` is an owning collection of `T`s, while a slice (`&[T]`) is a " +"borrowed\r\n" +"collection of `T`s. Implementing `Deref` for `Vec` allows implicit " +"dereferencing\r\n" +"from `&Vec` to `&[T]` and includes the relationship in auto-derefencing" +"\r\n" +"searches. Most methods you might expect to be implemented for `Vec`s are " +"instead\r\n" +"implemented for slices." +msgstr "" + +#: .\idioms/deref.md:34 +msgid "Also `String` and `&str` have a similar relation." +msgstr "" + +#: .\idioms/deref.md:36 .\idioms/dtor-finally.md:32 .\idioms/mem-replace.md:57 +#: .\idioms/on-stack-dyn-dispatch.md:37 .\idioms/ffi/accepting-strings.md:12 +#: .\idioms/ffi/passing-strings.md:14 .\idioms/rustdoc-init.md:9 +#: .\idioms/return-consumed-arg-on-error.md:43 +#: .\patterns/behavioural/command.md:8 .\patterns/behavioural/interpreter.md:16 +#: .\patterns/behavioural/newtype.md:56 .\patterns/behavioural/RAII.md:72 +#: .\patterns/behavioural/strategy.md:19 .\patterns/behavioural/visitor.md:72 +#: .\patterns/creational/builder.md:63 .\patterns/creational/fold.md:73 +#: .\patterns/structural/compose-structs.md:71 .\patterns/ffi/export.md:15 +#: .\anti_patterns/borrow_clone.md:30 +msgid "## Motivation\r" +msgstr "" + +#: .\idioms/deref.md:38 +msgid "" +"Ownership and borrowing are key aspects of the Rust language. Data structures" +"\r\n" +"must account for these semantics properly to give a good user\r\n" +"experience. When implementing a data structure that owns its data, offering a" +"\r\n" +"borrowed view of that data allows for more flexible APIs." +msgstr "" + +#: .\idioms/deref.md:45 +msgid "" +"Most methods can be implemented only for the borrowed view, they are then\r\n" +"implicitly available for the owning view." +msgstr "" + +#: .\idioms/deref.md:48 +msgid "Gives clients a choice between borrowing or taking ownership of data." +msgstr "" + +#: .\idioms/deref.md:52 +msgid "" +"Methods and traits only available via dereferencing are not taken into " +"account\r\n" +"when bounds checking, so generic programming with data structures using this" +"\r\n" +"pattern can get complex (see the `Borrow` and `AsRef` traits, etc.)." +msgstr "" + +#: .\idioms/deref.md:56 .\idioms/dtor-finally.md:61 .\idioms/mem-replace.md:99 +#: .\idioms/on-stack-dyn-dispatch.md:68 .\idioms/priv-extend.md:85 +#: .\idioms/rustdoc-init.md:87 .\patterns/behavioural/command.md:203 +#: .\patterns/behavioural/interpreter.md:104 +#: .\patterns/behavioural/newtype.md:85 .\patterns/behavioural/RAII.md:83 +#: .\patterns/behavioural/strategy.md:110 .\patterns/behavioural/visitor.md:79 +#: .\patterns/creational/builder.md:81 .\patterns/creational/fold.md:85 +#: .\patterns/structural/compose-structs.md:89 .\anti_patterns/deref.md:102 +msgid "## Discussion\r" +msgstr "" + +#: .\idioms/deref.md:58 +msgid "" +"Smart pointers and collections are analogous: a smart pointer points to a " +"single\r\n" +"object, whereas a collection points to many objects. From the point of view " +"of\r\n" +"the type system, there is little difference between the two. A collection " +"owns\r\n" +"its data if the only way to access each datum is via the collection and the" +"\r\n" +"collection is responsible for deleting the data (even in cases of shared\r\n" +"ownership, some kind of borrowed view may be appropriate). If a collection " +"owns\r\n" +"its data, it is usually useful to provide a view of the data as borrowed so " +"that\r\n" +"it can be referenced multiple times." +msgstr "" + +#: .\idioms/deref.md:67 +msgid "" +"Most smart pointers (e.g., `Foo`) implement `Deref`. However," +"\r\n" +"collections will usually dereference to a custom type. `[T]` and `str` have " +"some\r\n" +"language support, but in the general case, this is not necessary. `Foo` " +"can\r\n" +"implement `Deref>` where `Bar` is a dynamically sized type and" +"\r\n" +"`&Bar` is a borrowed view of the data in `Foo`." +msgstr "" + +#: .\idioms/deref.md:73 +msgid "" +"Commonly, ordered collections will implement `Index` for `Range`s to provide" +"\r\n" +"slicing syntax. The target will be the borrowed view." +msgstr "" + +#: .\idioms/deref.md:78 +msgid "" +"- [Deref polymorphism anti-pattern](../anti_patterns/deref.md).\r\n" +"- [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait." +"Deref.html).\r" +msgstr "" + +#: .\idioms/dtor-finally.md:1 +msgid "# Finalisation in destructors\r" +msgstr "" + +#: .\idioms/dtor-finally.md:5 +msgid "" +"Rust does not provide the equivalent to `finally` blocks - code that will be" +"\r\n" +"executed no matter how a function is exited. Instead, an object's destructor " +"can\r\n" +"be used to run code that must be run before exit." +msgstr "" + +#: .\idioms/dtor-finally.md:11 +msgid "" +"```rust,ignore\r\n" +"fn bar() -> Result<(), ()> {\r\n" +" // These don't need to be defined inside the function.\r\n" +" struct Foo;\r\n" +"\r\n" +" // Implement a destructor for Foo.\r\n" +" impl Drop for Foo {\r\n" +" fn drop(&mut self) {\r\n" +" println!(\"exit\");\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" // The dtor of _exit will run however the function `bar` is exited.\r\n" +" let _exit = Foo;\r\n" +" // Implicit return with `?` operator.\r\n" +" baz()?;\r\n" +" // Normal return.\r\n" +" Ok(())\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/dtor-finally.md:34 +msgid "" +"If a function has multiple return points, then executing code on exit becomes" +"\r\n" +"difficult and repetitive (and thus bug-prone). This is especially the case " +"where\r\n" +"return is implicit due to a macro. A common case is the `?` operator which" +"\r\n" +"returns if the result is an `Err`, but continues if it is `Ok`. `?` is used " +"as\r\n" +"an exception handling mechanism, but unlike Java (which has `finally`), " +"there is\r\n" +"no way to schedule code to run in both the normal and exceptional cases.\r\n" +"Panicking will also exit a function early." +msgstr "" + +#: .\idioms/dtor-finally.md:44 +msgid "" +"Code in destructors will (nearly) always be run - copes with panics, early" +"\r\n" +"returns, etc." +msgstr "" + +#: .\idioms/dtor-finally.md:49 +msgid "" +"It is not guaranteed that destructors will run. For example, if there is an" +"\r\n" +"infinite loop in a function or if running a function crashes before exit.\r\n" +"Destructors are also not run in the case of a panic in an already panicking" +"\r\n" +"thread. Therefore, destructors cannot be relied on as finalizers where it is" +"\r\n" +"absolutely essential that finalisation happens." +msgstr "" + +#: .\idioms/dtor-finally.md:55 +msgid "" +"This pattern introduces some hard to notice, implicit code. Reading a " +"function\r\n" +"gives no clear indication of destructors to be run on exit. This can make\r\n" +"debugging tricky." +msgstr "" + +#: .\idioms/dtor-finally.md:59 +msgid "" +"Requiring an object and `Drop` impl just for finalisation is heavy on " +"boilerplate." +msgstr "" + +#: .\idioms/dtor-finally.md:63 +msgid "" +"There is some subtlety about how exactly to store the object used as a\r\n" +"finalizer. It must be kept alive until the end of the function and must then " +"be\r\n" +"destroyed. The object must always be a value or uniquely owned pointer (e.g.," +"\r\n" +"`Box`). If a shared pointer (such as `Rc`) is used, then the finalizer " +"can\r\n" +"be kept alive beyond the lifetime of the function. For similar reasons, the" +"\r\n" +"finalizer should not be moved or returned." +msgstr "" + +#: .\idioms/dtor-finally.md:70 +msgid "" +"The finalizer must be assigned into a variable, otherwise it will be " +"destroyed\r\n" +"immediately, rather than when it goes out of scope. The variable name must " +"start\r\n" +"with `_` if the variable is only used as a finalizer, otherwise the compiler" +"\r\n" +"will warn that the finalizer is never used. However, do not call the variable" +"\r\n" +"`_` with no suffix - in that case it will be destroyed immediately." +msgstr "" + +#: .\idioms/dtor-finally.md:76 +msgid "" +"In Rust, destructors are run when an object goes out of scope. This happens" +"\r\n" +"whether we reach the end of block, there is an early return, or the program" +"\r\n" +"panics. When panicking, Rust unwinds the stack running destructors for each" +"\r\n" +"object in each stack frame. So, destructors get called even if the panic " +"happens\r\n" +"in a function being called." +msgstr "" + +#: .\idioms/dtor-finally.md:82 +msgid "" +"If a destructor panics while unwinding, there is no good action to take, so " +"Rust\r\n" +"aborts the thread immediately, without running further destructors. This " +"means\r\n" +"that destructors are not absolutely guaranteed to run. It also means that you" +"\r\n" +"must take extra care in your destructors not to panic, since it could leave" +"\r\n" +"resources in an unexpected state." +msgstr "" + +#: .\idioms/dtor-finally.md:90 +msgid "[RAII guards](../patterns/behavioural/RAII.md)." +msgstr "" + +#: .\idioms/mem-replace.md:1 +msgid "# `mem::{take(_), replace(_)}` to keep owned values in changed enums\r" +msgstr "" + +#: .\idioms/mem-replace.md:5 +msgid "" +"Say we have a `&mut MyEnum` which has (at least) two variants,\r\n" +"`A { name: String, x: u8 }` and `B { name: String }`. Now we want to change" +"\r\n" +"`MyEnum::A` to a `B` if `x` is zero, while keeping `MyEnum::B` intact." +msgstr "" + +#: .\idioms/mem-replace.md:9 +msgid "We can do this without cloning the `name`." +msgstr "" + +#: .\idioms/mem-replace.md:13 +msgid "" +"```rust\r\n" +"use std::mem;\r\n" +"\r\n" +"enum MyEnum {\r\n" +" A { name: String, x: u8 },\r\n" +" B { name: String }\r\n" +"}\r\n" +"\r\n" +"fn a_to_b(e: &mut MyEnum) {\r\n" +" if let MyEnum::A { name, x: 0 } = e {\r\n" +" // this takes out our `name` and put in an empty String instead\r\n" +" // (note that empty strings don't allocate).\r\n" +" // Then, construct the new enum variant (which will\r\n" +" // be assigned to `*e`).\r\n" +" *e = MyEnum::B { name: mem::take(name) }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/mem-replace.md:32 +msgid "This also works with more variants:" +msgstr "" + +#: .\idioms/mem-replace.md:34 +msgid "" +"```rust\r\n" +"use std::mem;\r\n" +"\r\n" +"enum MultiVariateEnum {\r\n" +" A { name: String },\r\n" +" B { name: String },\r\n" +" C,\r\n" +" D\r\n" +"}\r\n" +"\r\n" +"fn swizzle(e: &mut MultiVariateEnum) {\r\n" +" use MultiVariateEnum::*;\r\n" +" *e = match e {\r\n" +" // Ownership rules do not allow taking `name` by value, but we cannot" +"\r\n" +" // take the value out of a mutable reference, unless we replace it:" +"\r\n" +" A { name } => B { name: mem::take(name) },\r\n" +" B { name } => A { name: mem::take(name) },\r\n" +" C => D,\r\n" +" D => C\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/mem-replace.md:59 +msgid "" +"When working with enums, we may want to change an enum value in place, " +"perhaps\r\n" +"to another variant. This is usually done in two phases to keep the borrow\r\n" +"checker happy. In the first phase, we observe the existing value and look at" +"\r\n" +"its parts to decide what to do next. In the second phase we may conditionally" +"\r\n" +"change the value (as in the example above)." +msgstr "" + +#: .\idioms/mem-replace.md:65 +msgid "" +"The borrow checker won't allow us to take out `name` of the enum (because\r\n" +"*something* must be there.) We could of course `.clone()` name and put the " +"clone\r\n" +"into our `MyEnum::B`, but that would be an instance of the [Clone to satisfy" +"\r\n" +"the borrow checker](../anti_patterns/borrow_clone.md) anti-pattern. Anyway, " +"we\r\n" +"can avoid the extra allocation by changing `e` with only a mutable borrow." +msgstr "" + +#: .\idioms/mem-replace.md:71 +msgid "" +"`mem::take` lets us swap out the value, replacing it with it's default value," +"\r\n" +"and returning the previous value. For `String`, the default value is an empty" +"\r\n" +"`String`, which does not need to allocate. As a result, we get the original" +"\r\n" +"`name` *as an owned value*. We can then wrap this in another enum." +msgstr "" + +#: .\idioms/mem-replace.md:76 +msgid "" +"__NOTE:__ `mem::replace` is very similar, but allows us to specify what to" +"\r\n" +"replace the value with. An equivalent to our `mem::take` line would be\r\n" +"`mem::replace(name, String::new())`." +msgstr "" + +#: .\idioms/mem-replace.md:80 +msgid "" +"Note, however, that if we are using an `Option` and want to replace its\r\n" +"value with a `None`, `Option`’s `take()` method provides a shorter and\r\n" +"more idiomatic alternative." +msgstr "" + +#: .\idioms/mem-replace.md:86 +msgid "" +"Look ma, no allocation! Also you may feel like Indiana Jones while doing it." +msgstr "" + +#: .\idioms/mem-replace.md:90 +msgid "" +"This gets a bit wordy. Getting it wrong repeatedly will make you hate the\r\n" +"borrow checker. The compiler may fail to optimize away the double store,\r\n" +"resulting in reduced performance as opposed to what you'd do in unsafe\r\n" +"languages." +msgstr "" + +#: .\idioms/mem-replace.md:95 +msgid "" +"Furthermore, the type you are taking needs to implement the [`Default`\r\n" +"trait](./default.md). However, if the type you're working with doesn't\r\n" +"implement this, you can instead use `mem::replace`." +msgstr "" + +#: .\idioms/mem-replace.md:101 +msgid "" +"This pattern is only of interest in Rust. In GC'd languages, you'd take the" +"\r\n" +"reference to the value by default (and the GC would keep track of refs), and " +"in\r\n" +"other low-level languages like C you'd simply alias the pointer and fix " +"things\r\n" +"later." +msgstr "" + +#: .\idioms/mem-replace.md:106 +msgid "" +"However, in Rust, we have to do a little more work to do this. An owned value" +"\r\n" +"may only have one owner, so to take it out, we need to put something back in " +"–\r\n" +"like Indiana Jones, replacing the artifact with a bag of sand." +msgstr "" + +#: .\idioms/mem-replace.md:112 +msgid "" +"This gets rid of the [Clone to satisfy the borrow checker](../anti_patterns/" +"borrow_clone.md)\r\n" +"anti-pattern in a specific case." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:1 +msgid "# On-Stack Dynamic Dispatch\r" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:5 +msgid "" +"We can dynamically dispatch over multiple values, however, to do so, we need" +"\r\n" +"to declare multiple variables to bind differently-typed objects. To extend " +"the\r\n" +"lifetime as necessary, we can use deferred conditional initialization, as " +"seen\r\n" +"below:" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:12 +msgid "" +"```rust\r\n" +"use std::io;\r\n" +"use std::fs;\r\n" +"\r\n" +"# fn main() -> Result<(), Box> {\r\n" +"# let arg = \"-\";\r\n" +"\r\n" +"// These must live longer than `readable`, and thus are declared first:\r\n" +"let (mut stdin_read, mut file_read);\r\n" +"\r\n" +"// We need to ascribe the type to get dynamic dispatch.\r\n" +"let readable: &mut dyn io::Read = if arg == \"-\" {\r\n" +" stdin_read = io::stdin();\r\n" +" &mut stdin_read\r\n" +"} else {\r\n" +" file_read = fs::File::open(arg)?;\r\n" +" &mut file_read\r\n" +"};\r\n" +"\r\n" +"// Read from `readable` here.\r\n" +"\r\n" +"# Ok(())\r\n" +"# }\r\n" +"```" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:39 +msgid "" +"Rust monomorphises code by default. This means a copy of the code will be\r\n" +"generated for each type it is used with and optimized independently. While " +"this\r\n" +"allows for very fast code on the hot path, it also bloats the code in places" +"\r\n" +"where performance is not of the essence, thus costing compile time and cache" +"\r\n" +"usage." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:45 +msgid "" +"Luckily, Rust allows us to use dynamic dispatch, but we have to explicitly " +"ask\r\n" +"for it." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:50 +msgid "" +"We do not need to allocate anything on the heap. Neither do we need to\r\n" +"initialize something we won't use later, nor do we need to monomorphize the" +"\r\n" +"whole code that follows to work with both `File` or `Stdin`." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:56 +msgid "The code needs more moving parts than the `Box`-based version:" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:58 +msgid "" +"```rust,ignore\r\n" +"// We still need to ascribe the type for dynamic dispatch.\r\n" +"let readable: Box = if arg == \"-\" {\r\n" +" Box::new(io::stdin())\r\n" +"} else {\r\n" +" Box::new(fs::File::open(arg)?)\r\n" +"};\r\n" +"// Read from `readable` here.\r\n" +"```" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:70 +msgid "" +"Rust newcomers will usually learn that Rust requires all variables to be\r\n" +"initialized *before use*, so it's easy to overlook the fact that *unused*\r\n" +"variables may well be uninitialized. Rust works quite hard to ensure that " +"this\r\n" +"works out fine and only the initialized values are dropped at the end of " +"their\r\n" +"scope." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:76 +msgid "The example meets all the constraints Rust places on us:" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:78 +msgid "" +"* All variables are initialized before using (in this case borrowing) them" +"\r\n" +"* Each variable only holds values of a single type. In our example, `stdin` " +"is\r\n" +"of type `Stdin`, `file` is of type `File` and `readable` is of type `&mut dyn" +"\r\n" +"Read`\r\n" +"* Each borrowed value outlives all the references borrowed from it\r\n" +"\r" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:86 +msgid "" +"* [Finalisation in destructors](dtor-finally.md) and\r\n" +"[RAII guards](../patterns/behavioural/RAII.md) can benefit from tight " +"control over\r\n" +"lifetimes.\r\n" +"* For conditionally filled `Option<&T>`s of (mutable) references, one can\r\n" +"initialize an `Option` directly and use its [`.as_ref()`] method to get an" +"\r\n" +"optional reference.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/intro.md:1 +msgid "# FFI Idioms\r" +msgstr "" + +#: .\idioms/ffi/intro.md:3 +msgid "" +"Writing FFI code is an entire course in itself.\r\n" +"However, there are several idioms here that can act as pointers, and avoid" +"\r\n" +"traps for inexperienced users of `unsafe` Rust." +msgstr "" + +#: .\idioms/ffi/intro.md:7 +msgid "This section contains idioms that may be useful when doing FFI." +msgstr "" + +#: .\idioms/ffi/intro.md:9 +msgid "" +"1. [Idiomatic Errors](./errors.md) - Error handling with integer codes and" +"\r\n" +" sentinel return values (such as `NULL` pointers)\r\n" +"\r\n" +"2. [Accepting Strings](./accepting-strings.md) with minimal unsafe code\r\n" +"\r\n" +"3. [Passing Strings](./passing-strings.md) to FFI functions\r" +msgstr "" + +#: .\idioms/ffi/errors.md:1 +msgid "# Error Handling in FFI\r" +msgstr "" + +#: .\idioms/ffi/errors.md:5 +msgid "" +"In foreign languages like C, errors are represented by return codes.\r\n" +"However, Rust's type system allows much more rich error information to be\r\n" +"captured and propogated through a full type." +msgstr "" + +#: .\idioms/ffi/errors.md:9 +msgid "" +"This best practice shows different kinds of error codes, and how to expose " +"them\r\n" +"in a usable way:" +msgstr "" + +#: .\idioms/ffi/errors.md:12 +msgid "" +"1. Flat Enums should be converted to integers and returned as codes.\r\n" +"2. Structured Enums should be converted to an integer code with a string " +"error\r\n" +" message for detail.\r\n" +"3. Custom Error Types should become \"transparent\", with a C representation." +"\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/errors.md:17 .\idioms/ffi/accepting-strings.md:29 +#: .\idioms/ffi/passing-strings.md:26 .\patterns/ffi/export.md:40 +#: .\patterns/ffi/wrappers.md:23 +msgid "## Code Example\r" +msgstr "" + +#: .\idioms/ffi/errors.md:19 +msgid "### Flat Enums\r" +msgstr "" + +#: .\idioms/ffi/errors.md:21 +msgid "" +"```rust,ignore\r\n" +"enum DatabaseError {\r\n" +" IsReadOnly = 1, // user attempted a write operation\r\n" +" IOError = 2, // user should read the C errno() for what it was\r\n" +" FileCorrupted = 3, // user should run a repair tool to recover it\r\n" +"}\r\n" +"\r\n" +"impl From for libc::c_int {\r\n" +" fn from(e: DatabaseError) -> libc::c_int {\r\n" +" (e as i8).into()\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/errors.md:35 +msgid "### Structured Enums\r" +msgstr "" + +#: .\idioms/ffi/errors.md:37 +msgid "" +"```rust,ignore\r\n" +"pub mod errors {\r\n" +" enum DatabaseError {\r\n" +" IsReadOnly,\r\n" +" IOError(std::io::Error),\r\n" +" FileCorrupted(String), // message describing the issue\r\n" +" }\r\n" +"\r\n" +" impl From for libc::c_int {\r\n" +" fn from(e: DatabaseError) -> libc::c_int {\r\n" +" match e {\r\n" +" DatabaseError::IsReadOnly => 1,\r\n" +" DatabaseError::IOError(_) => 2,\r\n" +" DatabaseError::FileCorrupted(_) => 3,\r\n" +" }\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub mod c_api {\r\n" +" use super::errors::DatabaseError;\r\n" +"\r\n" +" #[no_mangle]\r\n" +" pub extern \"C\" fn db_error_description(\r\n" +" e: *const DatabaseError\r\n" +" ) -> *mut libc::c_char {\r\n" +"\r\n" +" let error: &DatabaseError = unsafe {\r\n" +" // SAFETY: pointer lifetime is greater than the current stack " +"frame\r\n" +" &*e\r\n" +" };\r\n" +"\r\n" +" let error_str: String = match error {\r\n" +" DatabaseError::IsReadOnly => {\r\n" +" format!(\"cannot write to read-only database\");\r\n" +" }\r\n" +" DatabaseError::IOError(e) => {\r\n" +" format!(\"I/O Error: {}\", e);\r\n" +" }\r\n" +" DatabaseError::FileCorrupted(s) => {\r\n" +" format!(\"File corrupted, run repair: {}\", &s);\r\n" +" }\r\n" +" };\r\n" +"\r\n" +" let c_error = unsafe {\r\n" +" // SAFETY: copying error_str to an allocated buffer with a NUL" +"\r\n" +" // character at the end\r\n" +" let mut malloc: *mut u8 = libc::malloc(error_str.len() + 1) as " +"*mut _;\r\n" +"\r\n" +" if malloc.is_null() {\r\n" +" return std::ptr::null_mut();\r\n" +" }\r\n" +"\r\n" +" let src = error_str.as_bytes().as_ptr();\r\n" +"\r\n" +" std::ptr::copy_nonoverlapping(src, malloc, error_str.len());\r\n" +"\r\n" +" std::ptr::write(malloc.add(error_str.len()), 0);\r\n" +"\r\n" +" malloc as *mut libc::c_char\r\n" +" };\r\n" +"\r\n" +" c_error\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/errors.md:104 +msgid "### Custom Error Types\r" +msgstr "" + +#: .\idioms/ffi/errors.md:106 +msgid "" +"```rust,ignore\r\n" +"struct ParseError {\r\n" +" expected: char,\r\n" +" line: u32,\r\n" +" ch: u16\r\n" +"}\r\n" +"\r\n" +"impl ParseError { /* ... */ }\r\n" +"\r\n" +"/* Create a second version which is exposed as a C structure */\r\n" +"#[repr(C)]\r\n" +"pub struct parse_error {\r\n" +" pub expected: libc::c_char,\r\n" +" pub line: u32,\r\n" +" pub ch: u16\r\n" +"}\r\n" +"\r\n" +"impl From for parse_error {\r\n" +" fn from(e: ParseError) -> parse_error {\r\n" +" let ParseError { expected, line, ch } = e;\r\n" +" parse_error { expected, line, ch }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/errors.md:133 +msgid "" +"This ensures that the foreign language has clear access to error information" +"\r\n" +"while not compromising the Rust code's API at all." +msgstr "" + +#: .\idioms/ffi/errors.md:138 +msgid "" +"It's a lot of typing, and some types may not be able to be converted easily" +"\r\n" +"to C." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:1 +msgid "# Accepting Strings\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:5 +msgid "" +"When accepting strings via FFI through pointers, there are two principles " +"that\r\n" +"should be followed:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:8 +msgid "" +"1. Keep foreign strings \"borrowed\", rather than copying them directly.\r\n" +"2. Minimize the amount of complexity and `unsafe` code involved in converting" +"\r\n" +" from a C-style string to native Rust strings.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:14 +msgid "" +"The strings used in C have different behaviours to those used in Rust, " +"namely:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:16 +msgid "" +"- C strings are null-terminated while Rust strings store their length\r\n" +"- C strings can contain any arbitrary non-zero byte while Rust strings must " +"be\r\n" +" UTF-8\r\n" +"- C strings are accessed and manipulated using `unsafe` pointer operations" +"\r\n" +" while interactions with Rust strings go through safe methods\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:22 +msgid "" +"The Rust standard library comes with C equivalents of Rust's `String` and " +"`&str`\r\n" +"called `CString` and `&CStr`, that allow us to avoid a lot of the complexity" +"\r\n" +"and `unsafe` code involved in converting between C strings and Rust strings." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:26 +msgid "" +"The `&CStr` type also allows us to work with borrowed data, meaning passing" +"\r\n" +"strings between Rust and C is a zero-cost operation." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:31 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" /// Log a message at the specified level.\r\n" +" ///\r\n" +" /// # Safety\r\n" +" ///\r\n" +" /// It is the caller's guarantee to ensure `msg`:\r\n" +" ///\r\n" +" /// - is not a null pointer\r\n" +" /// - points to valid, initialized data\r\n" +" /// - points to memory ending in a null byte\r\n" +" /// - won't be mutated for the duration of this function call\r\n" +" #[no_mangle]\r\n" +" pub unsafe extern \"C\" fn mylib_log(\r\n" +" msg: *const libc::c_char,\r\n" +" level: libc::c_int\r\n" +" ) {\r\n" +" let level: crate::LogLevel = match level { /* ... */ };\r\n" +"\r\n" +" // SAFETY: The caller has already guaranteed this is okay (see the" +"\r\n" +" // `# Safety` section of the doc-comment).\r\n" +" let msg_str: &str = match std::ffi::CStr::from_ptr(msg).to_str() " +"{\r\n" +" Ok(s) => s,\r\n" +" Err(e) => {\r\n" +" crate::log_error(\"FFI string conversion failed\");\r\n" +" return;\r\n" +" }\r\n" +" };\r\n" +"\r\n" +" crate::log(msg_str, level);\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:70 +msgid "The example is is written to ensure that:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:72 +msgid "" +"1. The `unsafe` block is as small as possible.\r\n" +"2. The pointer with an \"untracked\" lifetime becomes a \"tracked\" shared" +"\r\n" +" reference\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:76 +msgid "Consider an alternative, where the string is actually copied:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:78 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" pub extern \"C\" fn mylib_log(msg: *const libc::c_char, level: libc::" +"c_int) {\r\n" +" // DO NOT USE THIS CODE.\r\n" +" // IT IS UGLY, VERBOSE, AND CONTAINS A SUBTLE BUG.\r\n" +"\r\n" +" let level: crate::LogLevel = match level { /* ... */ };\r\n" +"\r\n" +" let msg_len = unsafe { /* SAFETY: strlen is what it is, I guess? */" +"\r\n" +" libc::strlen(msg)\r\n" +" };\r\n" +"\r\n" +" let mut msg_data = Vec::with_capacity(msg_len + 1);\r\n" +"\r\n" +" let msg_cstr: std::ffi::CString = unsafe {\r\n" +" // SAFETY: copying from a foreign pointer expected to live\r\n" +" // for the entire stack frame into owned memory\r\n" +" std::ptr::copy_nonoverlapping(msg, msg_data.as_mut(), msg_len);" +"\r\n" +"\r\n" +" msg_data.set_len(msg_len + 1);\r\n" +"\r\n" +" std::ffi::CString::from_vec_with_nul(msg_data).unwrap()\r\n" +" }\r\n" +"\r\n" +" let msg_str: String = unsafe {\r\n" +" match msg_cstr.into_string() {\r\n" +" Ok(s) => s,\r\n" +" Err(e) => {\r\n" +" crate::log_error(\"FFI string conversion failed\");\r\n" +" return;\r\n" +" }\r\n" +" }\r\n" +" };\r\n" +"\r\n" +" crate::log(&msg_str, level);\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:120 +msgid "This code in inferior to the original in two respects:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:122 +msgid "" +"1. There is much more `unsafe` code, and more importantly, more invariants it" +"\r\n" +" must uphold.\r\n" +"2. Due to the extensive arithmetic required, there is a bug in this version" +"\r\n" +" that cases Rust `undefined behaviour`.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:127 +msgid "" +"The bug here is a simple mistake in pointer arithmetic: the string was " +"copied,\r\n" +"all `msg_len` bytes of it. However, the `NUL` terminator at the end was not." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:130 +msgid "" +"The Vector then had its size *set* to the length of the *zero padded string* " +"--\r\n" +"rather than *resized* to it, which could have added a zero at the end.\r\n" +"As a result, the last byte in the Vector is uninitialized memory.\r\n" +"When the `CString` is created at the bottom of the block, its read of the\r\n" +"Vector will cause `undefined behaviour`!" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:136 +msgid "" +"Like many such issues, this would be difficult issue to track down.\r\n" +"Sometimes it would panic because the string was not `UTF-8`, sometimes it " +"would\r\n" +"put a weird character at the end of the string, sometimes it would just\r\n" +"completely crash." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:143 .\idioms/ffi/passing-strings.md:105 +msgid "None?" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:1 +msgid "# Passing Strings\r" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:5 +msgid "" +"When passing strings to FFI functions, there are four principles that should " +"be\r\n" +"followed:" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:8 +msgid "" +"1. Make the lifetime of owned strings as long as possible.\r\n" +"2. Minimize `unsafe` code during the conversion.\r\n" +"3. If the C code can modify the string data, use `Vec` instead of `CString`." +"\r\n" +"4. Unless the Foreign Function API requires it, the ownership of the string" +"\r\n" +" should not transfer to the callee.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:16 +msgid "" +"Rust has built-in support for C-style strings with its `CString` and `CStr`" +"\r\n" +"types. However, there are different approaches one can take with strings that" +"\r\n" +"are being sent to a foreign function call from a Rust function." +msgstr "" + +#: .\idioms/ffi/passing-strings.md:20 +msgid "" +"The best practice is simple: use `CString` in such a way as to minimize\r\n" +"`unsafe` code. However, a secondary caveat is that\r\n" +"*the object must live long enough*, meaning the lifetime should be maximized." +"\r\n" +"In addition, the documentation explains that \"round-tripping\" a `CString` " +"after\r\n" +"modification is UB, so additional work is necessary in that case." +msgstr "" + +#: .\idioms/ffi/passing-strings.md:28 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" extern \"C\" {\r\n" +" fn seterr(message: *const libc::c_char);\r\n" +" fn geterr(buffer: *mut libc::c_char, size: libc::c_int) -> libc::" +"c_int;\r\n" +" }\r\n" +"\r\n" +" fn report_error_to_ffi>(\r\n" +" err: S\r\n" +" ) -> Result<(), std::ffi::NulError>{\r\n" +" let c_err = std::ffi::CString::new(err.into())?;\r\n" +"\r\n" +" unsafe {\r\n" +" // SAFETY: calling an FFI whose documentation says the pointer is" +"\r\n" +" // const, so no modification should occur\r\n" +" seterr(c_err.as_ptr());\r\n" +" }\r\n" +"\r\n" +" Ok(())\r\n" +" // The lifetime of c_err continues until here\r\n" +" }\r\n" +"\r\n" +" fn get_error_from_ffi() -> Result " +"{\r\n" +" let mut buffer = vec![0u8; 1024];\r\n" +" unsafe {\r\n" +" // SAFETY: calling an FFI whose documentation implies\r\n" +" // that the input need only live as long as the call\r\n" +" let written: usize = geterr(buffer.as_mut_ptr(), 1023).into();" +"\r\n" +"\r\n" +" buffer.truncate(written + 1);\r\n" +" }\r\n" +"\r\n" +" std::ffi::CString::new(buffer).unwrap().into_string()\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:70 +msgid "The example is written in a way to ensure that:" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:72 +msgid "" +"1. The `unsafe` block is as small as possible.\r\n" +"2. The `CString` lives long enough.\r\n" +"3. Errors with typecasts are always propagated when possible.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:76 +msgid "" +"A common mistake (so common it's in the documentation) is to not use the\r\n" +"variable in the first block:" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:79 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" fn report_error>(err: S) -> Result<(), std::ffi::" +"NulError> {\r\n" +" unsafe {\r\n" +" // SAFETY: whoops, this contains a dangling pointer!\r\n" +" seterr(std::ffi::CString::new(err.into())?.as_ptr());\r\n" +" }\r\n" +" Ok(())\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:94 +msgid "" +"This code will result in a dangling pointer, because the lifetime of the\r\n" +"`CString` is not extended by the pointer creation, unlike if a reference were" +"\r\n" +"created." +msgstr "" + +#: .\idioms/ffi/passing-strings.md:98 +msgid "" +"Another issue frequently raised is that the initialization of a 1k vector of" +"\r\n" +"zeroes is \"slow\". However, recent versions of Rust actually optimize that" +"\r\n" +"particular macro to a call to `zmalloc`, meaning it is as fast as the " +"operating\r\n" +"system's ability to return zeroed memory (which is quite fast)." +msgstr "" + +#: .\idioms/option-iter.md:1 +msgid "# Iterating over an `Option`\r" +msgstr "" + +#: .\idioms/option-iter.md:5 +msgid "" +"`Option` can be viewed as a container that contains either zero or one\r\n" +"element. In particular, it implements the `IntoIterator` trait, and as such" +"\r\n" +"can be used with generic code that needs such a type." +msgstr "" + +#: .\idioms/option-iter.md:9 .\patterns/structural/small-crates.md:34 +#: .\patterns/structural/unsafe-mods.md:22 +msgid "## Examples\r" +msgstr "" + +#: .\idioms/option-iter.md:11 +msgid "" +"Since `Option` implements `IntoIterator`, it can be used as an argument to" +"\r\n" +"[`.extend()`](https://doc.rust-lang.org/std/iter/trait.Extend.html#tymethod." +"extend):" +msgstr "" + +#: .\idioms/option-iter.md:14 +msgid "" +"```rust\r\n" +"let turing = Some(\"Turing\");\r\n" +"let mut logicians = vec![\"Curry\", \"Kleene\", \"Markov\"];\r\n" +"\r\n" +"logicians.extend(turing);\r\n" +"\r\n" +"// equivalent to\r\n" +"if let Some(turing_inner) = turing {\r\n" +" logicians.push(turing_inner);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/option-iter.md:26 +msgid "" +"If you need to tack an `Option` to the end of an existing iterator, you can" +"\r\n" +"pass it to [`.chain()`](https://doc.rust-lang.org/std/iter/trait.Iterator." +"html#method.chain):" +msgstr "" + +#: .\idioms/option-iter.md:29 +msgid "" +"```rust\r\n" +"let turing = Some(\"Turing\");\r\n" +"let logicians = vec![\"Curry\", \"Kleene\", \"Markov\"];\r\n" +"\r\n" +"for logician in logicians.iter().chain(turing.iter()) {\r\n" +" println!(\"{} is a logician\", logician);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/option-iter.md:38 +msgid "" +"Note that if the `Option` is always `Some`, then it is more idiomatic to use" +"\r\n" +"[`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) on the" +"\r\n" +"element instead." +msgstr "" + +#: .\idioms/option-iter.md:42 +msgid "" +"Also, since `Option` implements `IntoIterator`, it's possible to iterate over" +"\r\n" +"it using a `for` loop. This is equivalent to matching it with `if let " +"Some(..)`,\r\n" +"and in most cases you should prefer the latter." +msgstr "" + +#: .\idioms/option-iter.md:48 +msgid "" +"* [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is an" +"\r\n" +"iterator which yields exactly one element. It's a more readable alternative " +"to\r\n" +"`Some(foo).into_iter()`.\r\n" +"\r\n" +"* [`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator." +"html#method.filter_map)\r\n" +" is a version of [`Iterator::map`](https://doc.rust-lang.org/std/iter/trait." +"Iterator.html#method.map),\r\n" +" specialized to mapping functions which return `Option`.\r\n" +"\r\n" +"* The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides " +"functions\r\n" +" for converting an `Option` to a zero- or one-element slice.\r\n" +"\r\n" +"* [Documentation for `Option`](https://doc.rust-lang.org/std/option/enum." +"Option.html)\r" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:1 +msgid "# Pass variables to closure\r" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:5 +msgid "" +"By default, closures capture their environment by borrowing. Or you can use" +"\r\n" +"`move`-closure to move whole environment. However, often you want to move " +"just\r\n" +"some variables to closure, give it copy of some data, pass it by reference, " +"or\r\n" +"perform some other transformation." +msgstr "" + +#: .\idioms/pass-var-to-closure.md:10 +msgid "Use variable rebinding in separate scope for that." +msgstr "" + +#: .\idioms/pass-var-to-closure.md:14 +msgid "Use" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:16 +msgid "" +"```rust\r\n" +"use std::rc::Rc;\r\n" +"\r\n" +"let num1 = Rc::new(1);\r\n" +"let num2 = Rc::new(2);\r\n" +"let num3 = Rc::new(3);\r\n" +"let closure = {\r\n" +" // `num1` is moved\r\n" +" let num2 = num2.clone(); // `num2` is cloned\r\n" +" let num3 = num3.as_ref(); // `num3` is borrowed\r\n" +" move || {\r\n" +" *num1 + *num2 + *num3;\r\n" +" }\r\n" +"};\r\n" +"```" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:32 +msgid "instead of" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:34 +msgid "" +"```rust\r\n" +"use std::rc::Rc;\r\n" +"\r\n" +"let num1 = Rc::new(1);\r\n" +"let num2 = Rc::new(2);\r\n" +"let num3 = Rc::new(3);\r\n" +"\r\n" +"let num2_cloned = num2.clone();\r\n" +"let num3_borrowed = num3.as_ref();\r\n" +"let closure = move || {\r\n" +" *num1 + *num2_cloned + *num3_borrowed;\r\n" +"};\r\n" +"```" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:50 +msgid "" +"Copied data are grouped together with closure definition, so their purpose is" +"\r\n" +"more clear, and they will be dropped immediately even if they are not " +"consumed\r\n" +"by closure." +msgstr "" + +#: .\idioms/pass-var-to-closure.md:54 +msgid "" +"Closure uses same variable names as surrounding code whether data are copied " +"or\r\n" +"moved." +msgstr "" + +#: .\idioms/pass-var-to-closure.md:59 +msgid "Additional indentation of closure body." +msgstr "" + +#: .\idioms/priv-extend.md:1 +msgid "# `#[non_exhaustive]` and private fields for extensibility\r" +msgstr "" + +#: .\idioms/priv-extend.md:5 +msgid "" +"A small set of scenarios exist where a library author may want to add public" +"\r\n" +"fields to a public struct or new variants to an enum without breaking " +"backwards\r\n" +"compatibility." +msgstr "" + +#: .\idioms/priv-extend.md:9 +msgid "Rust offers two solutions to this problem:" +msgstr "" + +#: .\idioms/priv-extend.md:11 +msgid "" +"- Use `#[non_exhaustive]` on `struct`s, `enum`s, and `enum` variants.\r\n" +" For extensive documentation on all the places where `#[non_exhaustive]` " +"can be\r\n" +" used, see [the docs](https://doc.rust-lang.org/reference/attributes/" +"type_system.html#the-non_exhaustive-attribute).\r\n" +"\r\n" +"- You may add a private field to a struct to prevent it from being directly" +"\r\n" +" instantiated or matched against (see Alternative)\r\n" +"\r" +msgstr "" + +#: .\idioms/priv-extend.md:20 +msgid "" +"```rust\r\n" +"mod a {\r\n" +" // Public struct.\r\n" +" #[non_exhaustive]\r\n" +" pub struct S {\r\n" +" pub foo: i32,\r\n" +" }\r\n" +" \r\n" +" #[non_exhaustive]\r\n" +" pub enum AdmitMoreVariants {\r\n" +" VariantA,\r\n" +" VariantB,\r\n" +" #[non_exhaustive]\r\n" +" VariantC { a: String }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn print_matched_variants(s: a::S) {\r\n" +" // Because S is `#[non_exhaustive]`, it cannot be named here and\r\n" +" // we must use `..` in the pattern.\r\n" +" let a::S { foo: _, ..} = s;\r\n" +" \r\n" +" let some_enum = a::AdmitMoreVariants::VariantA;\r\n" +" match some_enum {\r\n" +" a::AdmitMoreVariants::VariantA => println!(\"it's an A\"),\r\n" +" a::AdmitMoreVariants::VariantB => println!(\"it's a b\"),\r\n" +"\r\n" +" // .. required because this variant is non-exhaustive as well\r\n" +" a::AdmitMoreVariants::VariantC { a, .. } => println!(\"it's a c\")," +"\r\n" +"\r\n" +" // The wildcard match is required because more variants may be\r\n" +" // added in the future\r\n" +" _ => println!(\"it's a new variant\")\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/priv-extend.md:57 +msgid "## Alternative: `Private fields` for structs\r" +msgstr "" + +#: .\idioms/priv-extend.md:59 +msgid "" +"`#[non_exhaustive]` only works across crate boundaries.\r\n" +"Within a crate, the private field method may be used." +msgstr "" + +#: .\idioms/priv-extend.md:62 +msgid "" +"Adding a field to a struct is a mostly backwards compatible change.\r\n" +"However, if a client uses a pattern to deconstruct a struct instance, they" +"\r\n" +"might name all the fields in the struct and adding a new one would break that" +"\r\n" +"pattern.\r\n" +"The client could name some fields and use `..` in the pattern, in which case " +"adding\r\n" +"another field is backwards compatible.\r\n" +"Making at least one of the struct's fields private forces clients to use the " +"latter\r\n" +"form of patterns, ensuring that the struct is future-proof." +msgstr "" + +#: .\idioms/priv-extend.md:71 +msgid "" +"The downside of this approach is that you might need to add an otherwise " +"unneeded\r\n" +"field to the struct.\r\n" +"You can use the `()` type so that there is no runtime overhead and prepend " +"`_` to\r\n" +"the field name to avoid the unused field warning." +msgstr "" + +#: .\idioms/priv-extend.md:76 +msgid "" +"```rust\r\n" +"pub struct S {\r\n" +" pub a: i32,\r\n" +" // Because `b` is private, you cannot match on `S` without using `..` " +"and `S`\r\n" +" // cannot be directly instantiated or matched against\r\n" +" _b: ()\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/priv-extend.md:87 +msgid "" +"On `struct`s, `#[non_exhaustive]` allows adding additional fields in a " +"backwards\r\n" +"compatible way.\r\n" +"It will also prevent clients from using the struct constructor, even if all " +"the\r\n" +"fields are public.\r\n" +"This may be helpful, but it's worth considering if you _want_ an additional " +"field\r\n" +"to be found by clients as a compiler error rather than something that may be " +"silently\r\n" +"undiscovered." +msgstr "" + +#: .\idioms/priv-extend.md:95 +msgid "" +"`#[non_exhaustive]` can be applied to enum variants as well.\r\n" +"A `#[non_exhaustive]` variant behaves in the same way as a " +"`#[non_exhaustive]` struct." +msgstr "" + +#: .\idioms/priv-extend.md:98 +msgid "" +"Use this deliberately and with caution: incrementing the major version when " +"adding\r\n" +"fields or variants is often a better option.\r\n" +"`#[non_exhaustive]` may be appropriate in scenarios where you're modeling an " +"external\r\n" +"resource that may change out-of-sync with your library, but is not a general " +"purpose\r\n" +"tool." +msgstr "" + +#: .\idioms/priv-extend.md:104 +msgid "### Disadvantages\r" +msgstr "" + +#: .\idioms/priv-extend.md:106 +msgid "" +"`#[non_exhaustive]` can make your code much less ergonomic to use, " +"especially when\r\n" +"forced to handle unknown enum variants.\r\n" +"It should only be used when these sorts of evolutions are required " +"**without**\r\n" +"incrementing the major version." +msgstr "" + +#: .\idioms/priv-extend.md:111 +msgid "" +"When `#[non_exhaustive]` is applied to `enum`s, it forces clients to handle a" +"\r\n" +"wildcard variant.\r\n" +"If there is no sensible action to take in this case, this may lead to awkward" +"\r\n" +"code and code paths that are only executed in extremely rare circumstances." +"\r\n" +"If a client decides to `panic!()` in this scenario, it may have been better " +"to\r\n" +"expose this error at compile time.\r\n" +"In fact, `#[non_exhaustive]` forces clients to handle the \"Something else\" " +"case;\r\n" +"there is rarely a sensible action to take in this scenario." +msgstr "" + +#: .\idioms/priv-extend.md:122 +msgid "" +"- [RFC introducing #[non_exhaustive] attribute for enums and structs]" +"(https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md)\r" +msgstr "" + +#: .\idioms/rustdoc-init.md:1 +msgid "# Easy doc initialization\r" +msgstr "" + +#: .\idioms/rustdoc-init.md:5 +msgid "" +"If a struct takes significant effort to initialize when writing docs, it can " +"be\r\n" +"quicker to wrap your example with a helper function which takes the struct " +"as an\r\n" +"argument." +msgstr "" + +#: .\idioms/rustdoc-init.md:11 +msgid "" +"Sometimes there is a struct with multiple or complicated parameters and " +"several\r\n" +"methods. Each of these methods should have examples." +msgstr "" + +#: .\idioms/rustdoc-init.md:14 +msgid "For example:" +msgstr "" + +#: .\idioms/rustdoc-init.md:16 +msgid "" +"```rust,ignore\r\n" +"struct Connection {\r\n" +" name: String,\r\n" +" stream: TcpStream,\r\n" +"}\r\n" +"\r\n" +"impl Connection {\r\n" +" /// Sends a request over the connection.\r\n" +" ///\r\n" +" /// # Example\r\n" +" /// ```no_run\r\n" +" /// # // Boilerplate are required to get an example working.\r\n" +" /// # let stream = TcpStream::connect(\"127.0.0.1:34254\");\r\n" +" /// # let connection = Connection { name: \"foo\".to_owned(), stream };" +"\r\n" +" /// # let request = Request::new(\"RequestId\", RequestType::Get, " +"\"payload\");\r\n" +" /// let response = connection.send_request(request);\r\n" +" /// assert!(response.is_ok());\r\n" +" /// ```\r\n" +" fn send_request(&self, request: Request) -> Result {\r\n" +" // ...\r\n" +" }\r\n" +"\r\n" +" /// Oh no, all that boilerplate needs to be repeated here!\r\n" +" fn check_status(&self) -> Status {\r\n" +" // ...\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/rustdoc-init.md:47 +msgid "" +"Instead of typing all of this boilerplate to create a `Connection` and\r\n" +"`Request`, it is easier to just create a wrapping helper function which takes" +"\r\n" +"them as arguments:" +msgstr "" + +#: .\idioms/rustdoc-init.md:51 +msgid "" +"```rust,ignore\r\n" +"struct Connection {\r\n" +" name: String,\r\n" +" stream: TcpStream,\r\n" +"}\r\n" +"\r\n" +"impl Connection {\r\n" +" /// Sends a request over the connection.\r\n" +" ///\r\n" +" /// # Example\r\n" +" /// ```\r\n" +" /// # fn call_send(connection: Connection, request: Request) {\r\n" +" /// let response = connection.send_request(request);\r\n" +" /// assert!(response.is_ok());\r\n" +" /// # }\r\n" +" /// ```\r\n" +" fn send_request(&self, request: Request) {\r\n" +" // ...\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/rustdoc-init.md:73 +msgid "" +"**Note** in the above example the line `assert!(response.is_ok());` will not" +"\r\n" +"actually run while testing because it is inside a function which is never\r\n" +"invoked." +msgstr "" + +#: .\idioms/rustdoc-init.md:79 +msgid "This is much more concise and avoids repetitive code in examples." +msgstr "" + +#: .\idioms/rustdoc-init.md:83 +msgid "" +"As example is in a function, the code will not be tested. Though it will " +"still be\r\n" +"checked to make sure it compiles when running a `cargo test`. So this " +"pattern is\r\n" +"most useful when you need `no_run`. With this, you do not need to add " +"`no_run`." +msgstr "" + +#: .\idioms/rustdoc-init.md:89 +msgid "If assertions are not required this pattern works well." +msgstr "" + +#: .\idioms/rustdoc-init.md:91 +msgid "" +"If they are, an alternative can be to create a public method to create a " +"helper\r\n" +"instance which is annotated with `#[doc(hidden)]` (so that users won't see " +"it).\r\n" +"Then this method can be called inside of rustdoc because it is part of the" +"\r\n" +"crate's public API." +msgstr "" + +#: .\idioms/temporary-mutability.md:1 +msgid "# Temporary mutability\r" +msgstr "" + +#: .\idioms/temporary-mutability.md:5 +msgid "" +"Often it is necessary to prepare and process some data, but after that data " +"are\r\n" +"only inspected and never modified. The intention can be made explicit by " +"redefining\r\n" +"the mutable variable as immutable." +msgstr "" + +#: .\idioms/temporary-mutability.md:9 +msgid "" +"It can be done either by processing data within a nested block or by " +"redefining\r\n" +"the variable." +msgstr "" + +#: .\idioms/temporary-mutability.md:14 +msgid "Say, vector must be sorted before usage." +msgstr "" + +#: .\idioms/temporary-mutability.md:16 +msgid "Using nested block:" +msgstr "" + +#: .\idioms/temporary-mutability.md:18 +msgid "" +"```rust,ignore\r\n" +"let data = {\r\n" +" let mut data = get_vec();\r\n" +" data.sort();\r\n" +" data\r\n" +"};\r\n" +"\r\n" +"// Here `data` is immutable.\r\n" +"```" +msgstr "" + +#: .\idioms/temporary-mutability.md:28 +msgid "Using variable rebinding:" +msgstr "" + +#: .\idioms/temporary-mutability.md:30 +msgid "" +"```rust,ignore\r\n" +"let mut data = get_vec();\r\n" +"data.sort();\r\n" +"let data = data;\r\n" +"\r\n" +"// Here `data` is immutable.\r\n" +"```" +msgstr "" + +#: .\idioms/temporary-mutability.md:40 +msgid "" +"Compiler ensures that you don't accidentally mutate data after some point." +msgstr "" + +#: .\idioms/temporary-mutability.md:44 +msgid "" +"Nested block requires additional indentation of block body.\r\n" +"One more line to return data from block or redefine variable." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:1 +msgid "# Return consumed argument on error\r" +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:5 +msgid "" +"If a fallible function consumes (moves) an argument, return that argument " +"back inside\r\n" +"an error." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:10 +msgid "" +"```rust\r\n" +"pub fn send(value: String) -> Result<(), SendError> {\r\n" +" println!(\"using {value} in a meaningful way\");\r\n" +" // Simulate non-deterministic fallible action.\r\n" +" use std::time::SystemTime;\r\n" +" let period = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)." +"unwrap();\r\n" +" if period.subsec_nanos() % 2 == 1 {\r\n" +" Ok(())\r\n" +" } else {\r\n" +" Err(SendError(value))\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub struct SendError(String);\r\n" +"\r\n" +"fn main() {\r\n" +" let mut value = \"imagine this is very long string\".to_string();\r\n" +"\r\n" +" let success = 's: {\r\n" +" // Try to send value two times.\r\n" +" for _ in 0..2 {\r\n" +" value = match send(value) {\r\n" +" Ok(()) => break 's true,\r\n" +" Err(SendError(value)) => value,\r\n" +" }\r\n" +" }\r\n" +" false\r\n" +" };\r\n" +"\r\n" +" println!(\"success: {}\", success);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:45 +msgid "" +"In case of error you may want to try some alternative way or to\r\n" +"retry action in case of non-deterministic function. But if the argument\r\n" +"is always consumed, you are forced to clone it on every call, which\r\n" +"is not very efficient." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:50 +msgid "" +"The standard library uses this approach in e.g. `String::from_utf8` method." +"\r\n" +"When given a vector that doesn't contain valid UTF-8, a `FromUtf8Error`\r\n" +"is returned.\r\n" +"You can get original vector back using `FromUtf8Error::into_bytes` method." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:57 +msgid "Better performance because of moving arguments whenever possible." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:61 +msgid "Slightly more complex error types." +msgstr "" + +#: .\patterns/index.md:1 +msgid "# Design Patterns\r" +msgstr "" + +#: .\patterns/index.md:3 +msgid "" +"[Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) are" +"\r\n" +"\"general reusable solutions to a commonly occurring problem within a given" +"\r\n" +"context in software design\". Design patterns are a great way to describe the" +"\r\n" +"culture of a programming language. Design patterns are very language-" +"specific -\r\n" +"what is a pattern in one language may be unnecessary in another due to a\r\n" +"language feature, or impossible to express due to a missing feature." +msgstr "" + +#: .\patterns/index.md:10 +msgid "" +"If overused, design patterns can add unnecessary complexity to programs.\r\n" +"However, they are a great way to share intermediate and advanced level " +"knowledge\r\n" +"about a programming language." +msgstr "" + +#: .\patterns/index.md:16 +msgid "" +"Rust has many unique features. These features give us great benefit by " +"removing\r\n" +"whole classes of problems. Some of them are also patterns that are _unique_ " +"to Rust." +msgstr "" + +#: .\patterns/index.md:19 +msgid "## YAGNI\r" +msgstr "" + +#: .\patterns/index.md:21 +msgid "" +"YAGNI is an acronym that stands for `You Aren't Going to Need It`.\r\n" +"It's a vital software design principle to apply as you write code." +msgstr "" + +#: .\patterns/index.md:24 +msgid "> The best code I ever wrote was code I never wrote.\r" +msgstr "" + +#: .\patterns/index.md:26 +msgid "" +"If we apply YAGNI to design patterns, we see that the features of Rust allow " +"us to\r\n" +"throw out many patterns. For instance, there is no need for the [strategy " +"pattern](https://en.wikipedia.org/wiki/Strategy_pattern)\r\n" +"in Rust because we can just use [traits](https://doc.rust-lang.org/book/" +"traits.html)." +msgstr "" + +#: .\patterns/behavioural/intro.md:1 +msgid "# Behavioural Patterns\r" +msgstr "" + +#: .\patterns/behavioural/intro.md:3 +msgid "From [Wikipedia](https://en.wikipedia.org/wiki/Behavioral_pattern):" +msgstr "" + +#: .\patterns/behavioural/intro.md:5 +msgid "" +"> Design patterns that identify common communication patterns among objects." +"\r\n" +"> By doing so, these patterns increase flexibility in carrying out " +"communication.\r" +msgstr "" + +#: .\patterns/behavioural/command.md:1 +msgid "# Command\r" +msgstr "" + +#: .\patterns/behavioural/command.md:5 +msgid "" +"The basic idea of the Command pattern is to separate out actions into its own" +"\r\n" +"objects and pass them as parameters." +msgstr "" + +#: .\patterns/behavioural/command.md:10 +msgid "" +"Suppose we have a sequence of actions or transactions encapsulated as " +"objects.\r\n" +"We want these actions or commands to be executed or invoked in some order " +"later\r\n" +"at different time. These commands may also be triggered as a result of some " +"event.\r\n" +"For example, when a user pushes a button, or on arrival of a data packet.\r\n" +"In addition, these commands might be undoable. This may come in useful for" +"\r\n" +"operations of an editor. We might want to store logs of executed commands so " +"that\r\n" +"we could reapply the changes later if the system crashes." +msgstr "" + +#: .\patterns/behavioural/command.md:20 +msgid "" +"Define two database operations `create table` and `add field`. Each of these" +"\r\n" +"operations is a command which knows how to undo the command, e.g., `drop " +"table`\r\n" +"and `remove field`. When a user invokes a database migration operation then " +"each\r\n" +"command is executed in the defined order, and when the user invokes the " +"rollback\r\n" +"operation then the whole set of commands is invoked in reverse order." +msgstr "" + +#: .\patterns/behavioural/command.md:26 +msgid "## Approach: Using trait objects\r" +msgstr "" + +#: .\patterns/behavioural/command.md:28 +msgid "" +"We define a common trait which encapsulates our command with two operations" +"\r\n" +"`execute` and `rollback`. All command `structs` must implement this trait." +msgstr "" + +#: .\patterns/behavioural/command.md:31 +msgid "" +"```rust\r\n" +"pub trait Migration {\r\n" +" fn execute(&self) -> &str;\r\n" +" fn rollback(&self) -> &str;\r\n" +"}\r\n" +"\r\n" +"pub struct CreateTable;\r\n" +"impl Migration for CreateTable {\r\n" +" fn execute(&self) -> &str {\r\n" +" \"create table\"\r\n" +" }\r\n" +" fn rollback(&self) -> &str {\r\n" +" \"drop table\"\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub struct AddField;\r\n" +"impl Migration for AddField {\r\n" +" fn execute(&self) -> &str {\r\n" +" \"add field\"\r\n" +" }\r\n" +" fn rollback(&self) -> &str {\r\n" +" \"remove field\"\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"struct Schema {\r\n" +" commands: Vec>,\r\n" +"}\r\n" +"\r\n" +"impl Schema {\r\n" +" fn new() -> Self {\r\n" +" Self { commands: vec![] }\r\n" +" }\r\n" +"\r\n" +" fn add_migration(&mut self, cmd: Box) {\r\n" +" self.commands.push(cmd);\r\n" +" }\r\n" +"\r\n" +" fn execute(&self) -> Vec<&str> {\r\n" +" self.commands.iter().map(|cmd| cmd.execute()).collect()\r\n" +" }\r\n" +" fn rollback(&self) -> Vec<&str> {\r\n" +" self.commands\r\n" +" .iter()\r\n" +" .rev() // reverse iterator's direction\r\n" +" .map(|cmd| cmd.rollback())\r\n" +" .collect()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let mut schema = Schema::new();\r\n" +"\r\n" +" let cmd = Box::new(CreateTable);\r\n" +" schema.add_migration(cmd);\r\n" +" let cmd = Box::new(AddField);\r\n" +" schema.add_migration(cmd);\r\n" +"\r\n" +" assert_eq!(vec![\"create table\", \"add field\"], schema.execute());\r\n" +" assert_eq!(vec![\"remove field\", \"drop table\"], schema.rollback());" +"\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/command.md:95 +msgid "## Approach: Using function pointers\r" +msgstr "" + +#: .\patterns/behavioural/command.md:97 +msgid "" +"We could follow another approach by creating each individual command as\r\n" +"a different function and store function pointers to invoke these functions " +"later\r\n" +"at a different time. Since function pointers implement all three traits `Fn`," +"\r\n" +"`FnMut`, and `FnOnce` we could as well pass and store closures instead of\r\n" +"function pointers." +msgstr "" + +#: .\patterns/behavioural/command.md:103 +msgid "" +"```rust\r\n" +"type FnPtr = fn() -> String;\r\n" +"struct Command {\r\n" +" execute: FnPtr,\r\n" +" rollback: FnPtr,\r\n" +"}\r\n" +"\r\n" +"struct Schema {\r\n" +" commands: Vec,\r\n" +"}\r\n" +"\r\n" +"impl Schema {\r\n" +" fn new() -> Self {\r\n" +" Self { commands: vec![] }\r\n" +" }\r\n" +" fn add_migration(&mut self, execute: FnPtr, rollback: FnPtr) {\r\n" +" self.commands.push(Command { execute, rollback });\r\n" +" }\r\n" +" fn execute(&self) -> Vec {\r\n" +" self.commands.iter().map(|cmd| (cmd.execute)()).collect()\r\n" +" }\r\n" +" fn rollback(&self) -> Vec {\r\n" +" self.commands\r\n" +" .iter()\r\n" +" .rev()\r\n" +" .map(|cmd| (cmd.rollback)())\r\n" +" .collect()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn add_field() -> String {\r\n" +" \"add field\".to_string()\r\n" +"}\r\n" +"\r\n" +"fn remove_field() -> String {\r\n" +" \"remove field\".to_string()\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let mut schema = Schema::new();\r\n" +" schema.add_migration(|| \"create table\".to_string(), || \"drop table\"." +"to_string());\r\n" +" schema.add_migration(add_field, remove_field);\r\n" +" assert_eq!(vec![\"create table\", \"add field\"], schema.execute());\r\n" +" assert_eq!(vec![\"remove field\", \"drop table\"], schema.rollback());" +"\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/command.md:150 +msgid "## Approach: Using `Fn` trait objects\r" +msgstr "" + +#: .\patterns/behavioural/command.md:152 +msgid "" +"Finally, instead of defining a common command trait we could store\r\n" +"each command implementing the `Fn` trait separately in vectors." +msgstr "" + +#: .\patterns/behavioural/command.md:155 +msgid "" +"```rust\r\n" +"type Migration<'a> = Box &'a str>;\r\n" +"\r\n" +"struct Schema<'a> {\r\n" +" executes: Vec>,\r\n" +" rollbacks: Vec>,\r\n" +"}\r\n" +"\r\n" +"impl<'a> Schema<'a> {\r\n" +" fn new() -> Self {\r\n" +" Self {\r\n" +" executes: vec![],\r\n" +" rollbacks: vec![],\r\n" +" }\r\n" +" }\r\n" +" fn add_migration(&mut self, execute: E, rollback: R)\r\n" +" where\r\n" +" E: Fn() -> &'a str + 'static,\r\n" +" R: Fn() -> &'a str + 'static,\r\n" +" {\r\n" +" self.executes.push(Box::new(execute));\r\n" +" self.rollbacks.push(Box::new(rollback));\r\n" +" }\r\n" +" fn execute(&self) -> Vec<&str> {\r\n" +" self.executes.iter().map(|cmd| cmd()).collect()\r\n" +" }\r\n" +" fn rollback(&self) -> Vec<&str> {\r\n" +" self.rollbacks.iter().rev().map(|cmd| cmd()).collect()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn add_field() -> &'static str {\r\n" +" \"add field\"\r\n" +"}\r\n" +"\r\n" +"fn remove_field() -> &'static str {\r\n" +" \"remove field\"\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let mut schema = Schema::new();\r\n" +" schema.add_migration(|| \"create table\", || \"drop table\");\r\n" +" schema.add_migration(add_field, remove_field);\r\n" +" assert_eq!(vec![\"create table\", \"add field\"], schema.execute());\r\n" +" assert_eq!(vec![\"remove field\", \"drop table\"], schema.rollback());" +"\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/command.md:205 +msgid "" +"If our commands are small and may be defined as functions or passed as a " +"closure\r\n" +"then using function pointers might be preferable since it does not exploit" +"\r\n" +"dynamic dispatch. But if our command is a whole struct with a bunch of " +"functions\r\n" +"and variables defined as seperated module then using trait objects would be" +"\r\n" +"more suitable. A case of application can be found in [`actix`](https://actix." +"rs/),\r\n" +"which uses trait objects when it registers a handler function for routes.\r\n" +"In case of using `Fn` trait objects we can create and use commands in the " +"same\r\n" +"way as we used in case of function pointers." +msgstr "" + +#: .\patterns/behavioural/command.md:214 +msgid "" +"As performance, there is always a trade-off between performance and code\r\n" +"simplicity and organisation. Static dispatch gives faster performance, while" +"\r\n" +"dynamic dispatch provides flexibility when we structure our application." +msgstr "" + +#: .\patterns/behavioural/command.md:220 +msgid "" +"- [Command pattern](https://en.wikipedia.org/wiki/Command_pattern)\r\n" +"\r\n" +"- [Another example for the `command` pattern](https://web.archive.org/" +"web/20210223131236/https://chercher.tech/rust/command-design-pattern-rust)\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:1 +msgid "# Interpreter\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:5 +msgid "" +"If a problem occurs very often and requires long and repetitive steps to " +"solve\r\n" +"it, then the problem instances might be expressed in a simple language and an" +"\r\n" +"interpreter object could solve it by interpreting the sentences written in " +"this\r\n" +"simple language." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:10 +msgid "Basically, for any kind of problems we define:" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:12 +msgid "" +"- A [domain specific language](https://en.wikipedia.org/wiki/Domain-" +"specific_language),\r\n" +"- A grammar for this language,\r\n" +"- An interpreter that solves the problem instances.\r\n" +"\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:18 +msgid "" +"Our goal is to translate simple mathematical expressions into postfix " +"expressions\r\n" +"(or [Reverse Polish notation](https://en.wikipedia.org/wiki/" +"Reverse_Polish_notation))\r\n" +"For simplicity, our expressions consist of ten digits `0`, ..., `9` and two" +"\r\n" +"operations `+`, `-`. For example, the expression `2 + 4` is translated into" +"\r\n" +"`2 4 +`." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:24 +msgid "## Context Free Grammar for our problem\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:26 +msgid "" +"Our task is translating infix expressions into postfix ones. Let's define a " +"context\r\n" +"free grammar for a set of infix expressions over `0`, ..., `9`, `+`, and `-`," +"\r\n" +"where:" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:30 +msgid "" +"- Terminal symbols: `0`, `...`, `9`, `+`, `-`\r\n" +"- Non-terminal symbols: `exp`, `term`\r\n" +"- Start symbol is `exp`\r\n" +"- And the following are production rules\r\n" +"\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:35 +msgid "" +"```ignore\r\n" +"exp -> exp + term\r\n" +"exp -> exp - term\r\n" +"exp -> term\r\n" +"term -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:42 +msgid "" +"__NOTE:__ This grammar should be further transformed depending on what we " +"are going\r\n" +"to do with it. For example, we might need to remove left recursion. For more" +"\r\n" +"details please see [Compilers: Principles,Techniques, and Tools\r\n" +"](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques," +"_and_Tools)\r\n" +"(aka Dragon Book)." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:48 +msgid "## Solution\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:50 +msgid "" +"We simply implement a recursive descent parser. For simplicity's sake, the " +"code\r\n" +"panics when an expression is syntactically wrong (for example `2-34` or `2+5-" +"`\r\n" +"are wrong according to the grammar definition)." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:54 +msgid "" +"```rust\r\n" +"pub struct Interpreter<'a> {\r\n" +" it: std::str::Chars<'a>,\r\n" +"}\r\n" +"\r\n" +"impl<'a> Interpreter<'a> {\r\n" +"\r\n" +" pub fn new(infix: &'a str) -> Self {\r\n" +" Self { it: infix.chars() }\r\n" +" }\r\n" +"\r\n" +" fn next_char(&mut self) -> Option {\r\n" +" self.it.next()\r\n" +" }\r\n" +"\r\n" +" pub fn interpret(&mut self, out: &mut String) {\r\n" +" self.term(out);\r\n" +"\r\n" +" while let Some(op) = self.next_char() {\r\n" +" if op == '+' || op == '-' {\r\n" +" self.term(out);\r\n" +" out.push(op);\r\n" +" } else {\r\n" +" panic!(\"Unexpected symbol '{}'\", op);\r\n" +" }\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" fn term(&mut self, out: &mut String) {\r\n" +" match self.next_char() {\r\n" +" Some(ch) if ch.is_digit(10) => out.push(ch),\r\n" +" Some(ch) => panic!(\"Unexpected symbol '{}'\", ch),\r\n" +" None => panic!(\"Unexpected end of string\"),\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub fn main() {\r\n" +" let mut intr = Interpreter::new(\"2+3\");\r\n" +" let mut postfix = String::new();\r\n" +" intr.interpret(&mut postfix);\r\n" +" assert_eq!(postfix, \"23+\");\r\n" +"\r\n" +" intr = Interpreter::new(\"1-2+3-4\");\r\n" +" postfix.clear();\r\n" +" intr.interpret(&mut postfix);\r\n" +" assert_eq!(postfix, \"12-3+4-\");\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:106 +msgid "" +"There may be a wrong perception that the Interpreter design pattern is about " +"design\r\n" +"grammars for formal languages and implementation of parsers for these " +"grammars.\r\n" +"In fact, this pattern is about expressing problem instances in a more " +"specific\r\n" +"way and implementing functions/classes/structs that solve these problem " +"instances.\r\n" +"Rust language has `macro_rules!` that allow us to define special syntax and " +"rules\r\n" +"on how to expand this syntax into source code." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:113 +msgid "" +"In the following example we create a simple `macro_rules!` that computes\r\n" +"[Euclidean length](https://en.wikipedia.org/wiki/Euclidean_distance) of `n`" +"\r\n" +"dimensional vectors. Writing `norm!(x,1,2)` might be easier to express and " +"more\r\n" +"efficient than packing `x,1,2` into a `Vec` and calling a function computing" +"\r\n" +"the length." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:119 +msgid "" +"```rust\r\n" +"macro_rules! norm {\r\n" +" ($($element:expr),*) => {\r\n" +" {\r\n" +" let mut n = 0.0;\r\n" +" $(\r\n" +" n += ($element as f64)*($element as f64);\r\n" +" )*\r\n" +" n.sqrt()\r\n" +" }\r\n" +" };\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let x = -3f64;\r\n" +" let y = 4f64;\r\n" +"\r\n" +" assert_eq!(3f64, norm!(x));\r\n" +" assert_eq!(5f64, norm!(x, y));\r\n" +" assert_eq!(0f64, norm!(0, 0, 0)); \r\n" +" assert_eq!(1f64, norm!(0.5, -0.5, 0.5, -0.5));\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:145 +msgid "" +"- [Interpreter pattern](https://en.wikipedia.org/wiki/" +"Interpreter_pattern)\r\n" +"- [Context free grammar](https://en.wikipedia.org/wiki/Context-" +"free_grammar)\r\n" +"- [macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html)\r" +msgstr "" + +#: .\patterns/behavioural/newtype.md:1 +msgid "# Newtype\r" +msgstr "" + +#: .\patterns/behavioural/newtype.md:3 +msgid "" +"What if in some cases we want a type to behave similar to another type or\r\n" +"enforce some behaviour at compile time when using only type aliases would\r\n" +"not be enough?" +msgstr "" + +#: .\patterns/behavioural/newtype.md:7 +msgid "" +"For example, if we want to create a custom `Display` implementation for " +"`String`\r\n" +"due to security considerations (e.g. passwords)." +msgstr "" + +#: .\patterns/behavioural/newtype.md:10 +msgid "" +"For such cases we could use the `Newtype` pattern to provide __type safety__" +"\r\n" +"and __encapsulation__." +msgstr "" + +#: .\patterns/behavioural/newtype.md:15 +msgid "" +"Use a tuple struct with a single field to make an opaque wrapper for a type." +"\r\n" +"This creates a new type, rather than an alias to a type (`type` items)." +msgstr "" + +#: .\patterns/behavioural/newtype.md:20 +msgid "" +"```rust,ignore\r\n" +"// Some type, not necessarily in the same module or even crate.\r\n" +"struct Foo {\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"impl Foo {\r\n" +" // These functions are not present on Bar.\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"// The newtype.\r\n" +"pub struct Bar(Foo);\r\n" +"\r\n" +"impl Bar {\r\n" +" // Constructor.\r\n" +" pub fn new(\r\n" +" //..\r\n" +" ) -> Self {\r\n" +"\r\n" +" //..\r\n" +"\r\n" +" }\r\n" +"\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let b = Bar::new(...);\r\n" +"\r\n" +" // Foo and Bar are type incompatible, the following do not type check." +"\r\n" +" // let f: Foo = b;\r\n" +" // let b: Bar = Foo { ... };\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/newtype.md:58 +msgid "" +"The primary motivation for newtypes is abstraction. It allows you to share" +"\r\n" +"implementation details between types while precisely controlling the " +"interface.\r\n" +"By using a newtype rather than exposing the implementation type as part of an" +"\r\n" +"API, it allows you to change implementation backwards compatibly." +msgstr "" + +#: .\patterns/behavioural/newtype.md:63 +msgid "" +"Newtypes can be used for distinguishing units, e.g., wrapping `f64` to give" +"\r\n" +"distinguishable `Miles` and `Kilometres`." +msgstr "" + +#: .\patterns/behavioural/newtype.md:68 +msgid "" +"The wrapped and wrapper types are not type compatible (as opposed to using" +"\r\n" +"`type`), so users of the newtype will never 'confuse' the wrapped and wrapper" +"\r\n" +"types." +msgstr "" + +#: .\patterns/behavioural/newtype.md:72 +msgid "Newtypes are a zero-cost abstraction - there is no runtime overhead." +msgstr "" + +#: .\patterns/behavioural/newtype.md:74 +msgid "" +"The privacy system ensures that users cannot access the wrapped type (if the" +"\r\n" +"field is private, which it is by default)." +msgstr "" + +#: .\patterns/behavioural/newtype.md:79 +msgid "" +"The downside of newtypes (especially compared with type aliases), is that " +"there\r\n" +"is no special language support. This means there can be *a lot* of " +"boilerplate.\r\n" +"You need a 'pass through' method for every method you want to expose on the" +"\r\n" +"wrapped type, and an impl for every trait you want to also be implemented for" +"\r\n" +"the wrapper type." +msgstr "" + +#: .\patterns/behavioural/newtype.md:87 +msgid "" +"Newtypes are very common in Rust code. Abstraction or representing units are " +"the\r\n" +"most common uses, but they can be used for other reasons:" +msgstr "" + +#: .\patterns/behavioural/newtype.md:90 +msgid "" +"- restricting functionality (reduce the functions exposed or traits " +"implemented),\r\n" +"- making a type with copy semantics have move semantics,\r\n" +"- abstraction by providing a more concrete type and thus hiding internal " +"types,\r\n" +" e.g.,\r\n" +"\r" +msgstr "" + +#: .\patterns/behavioural/newtype.md:95 +msgid "" +"```rust,ignore\r\n" +"pub struct Foo(Bar);\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/newtype.md:99 +msgid "" +"Here, `Bar` might be some public, generic type and `T1` and `T2` are some " +"internal\r\n" +"types. Users of our module shouldn't know that we implement `Foo` by using a " +"`Bar`,\r\n" +"but what we're really hiding here is the types `T1` and `T2`, and how they " +"are used\r\n" +"with `Bar`." +msgstr "" + +#: .\patterns/behavioural/newtype.md:106 +msgid "" +"- [Advanced Types in the book](https://doc.rust-lang.org/book/ch19-04-" +"advanced-types.html?highlight=newtype#using-the-newtype-pattern-for-type-" +"safety-and-abstraction)\r\n" +"- [Newtypes in Haskell](https://wiki.haskell.org/Newtype)\r\n" +"- [Type aliases](https://doc.rust-lang.org/stable/book/ch19-04-advanced-" +"types.html#creating-type-synonyms-with-type-aliases)\r\n" +"- [derive_more](https://crates.io/crates/derive_more), a crate for deriving " +"many\r\n" +" builtin traits on newtypes.\r\n" +"- [The Newtype Pattern In Rust](https://www.worthe-it.co.za/blog/2020-10-31-" +"newtype-pattern-in-rust.html)\r" +msgstr "" + +#: .\patterns/behavioural/RAII.md:1 +msgid "# RAII with guards\r" +msgstr "" + +#: .\patterns/behavioural/RAII.md:5 +msgid "" +"[RAII][wikipedia] stands for \"Resource Acquisition is Initialisation\" " +"which is a\r\n" +"terrible name. The essence of the pattern is that resource initialisation is " +"done\r\n" +"in the constructor of an object and finalisation in the destructor. This " +"pattern\r\n" +"is extended in Rust by using a RAII object as a guard of some resource and " +"relying\r\n" +"on the type system to ensure that access is always mediated by the guard " +"object." +msgstr "" + +#: .\patterns/behavioural/RAII.md:13 +msgid "" +"Mutex guards are the classic example of this pattern from the std library " +"(this\r\n" +"is a simplified version of the real implementation):" +msgstr "" + +#: .\patterns/behavioural/RAII.md:16 +msgid "" +"```rust,ignore\r\n" +"use std::ops::Deref;\r\n" +"\r\n" +"struct Foo {}\r\n" +"\r\n" +"struct Mutex {\r\n" +" // We keep a reference to our data: T here.\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"struct MutexGuard<'a, T: 'a> {\r\n" +" data: &'a T,\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"// Locking the mutex is explicit.\r\n" +"impl Mutex {\r\n" +" fn lock(&self) -> MutexGuard {\r\n" +" // Lock the underlying OS mutex.\r\n" +" //..\r\n" +"\r\n" +" // MutexGuard keeps a reference to self\r\n" +" MutexGuard {\r\n" +" data: self,\r\n" +" //..\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// Destructor for unlocking the mutex.\r\n" +"impl<'a, T> Drop for MutexGuard<'a, T> {\r\n" +" fn drop(&mut self) {\r\n" +" // Unlock the underlying OS mutex.\r\n" +" //..\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// Implementing Deref means we can treat MutexGuard like a pointer to T.\r\n" +"impl<'a, T> Deref for MutexGuard<'a, T> {\r\n" +" type Target = T;\r\n" +"\r\n" +" fn deref(&self) -> &T {\r\n" +" self.data\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn baz(x: Mutex) {\r\n" +" let xx = x.lock();\r\n" +" xx.foo(); // foo is a method on Foo.\r\n" +" // The borrow checker ensures we can't store a reference to the " +"underlying\r\n" +" // Foo which will outlive the guard xx.\r\n" +"\r\n" +" // x is unlocked when we exit this function and xx's destructor is " +"executed.\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/RAII.md:74 +msgid "" +"Where a resource must be finalised after use, RAII can be used to do this\r\n" +"finalisation. If it is an error to access that resource after finalisation, " +"then\r\n" +"this pattern can be used to prevent such errors." +msgstr "" + +#: .\patterns/behavioural/RAII.md:80 +msgid "" +"Prevents errors where a resource is not finalised and where a resource is " +"used\r\n" +"after finalisation." +msgstr "" + +#: .\patterns/behavioural/RAII.md:85 +msgid "" +"RAII is a useful pattern for ensuring resources are properly deallocated or" +"\r\n" +"finalised. We can make use of the borrow checker in Rust to statically " +"prevent\r\n" +"errors stemming from using resources after finalisation takes place." +msgstr "" + +#: .\patterns/behavioural/RAII.md:89 +msgid "" +"The core aim of the borrow checker is to ensure that references to data do " +"not\r\n" +"outlive that data. The RAII guard pattern works because the guard object\r\n" +"contains a reference to the underlying resource and only exposes such\r\n" +"references. Rust ensures that the guard cannot outlive the underlying " +"resource\r\n" +"and that references to the resource mediated by the guard cannot outlive the" +"\r\n" +"guard. To see how this works it is helpful to examine the signature of " +"`deref`\r\n" +"without lifetime elision:" +msgstr "" + +#: .\patterns/behavioural/RAII.md:97 +msgid "" +"```rust,ignore\r\n" +"fn deref<'a>(&'a self) -> &'a T {\r\n" +" //..\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/RAII.md:103 +msgid "" +"The returned reference to the resource has the same lifetime as `self` " +"(`'a`).\r\n" +"The borrow checker therefore ensures that the lifetime of the reference to " +"`T`\r\n" +"is shorter than the lifetime of `self`." +msgstr "" + +#: .\patterns/behavioural/RAII.md:107 +msgid "" +"Note that implementing `Deref` is not a core part of this pattern, it only " +"makes\r\n" +"using the guard object more ergonomic. Implementing a `get` method on the " +"guard\r\n" +"works just as well." +msgstr "" + +#: .\patterns/behavioural/RAII.md:113 +msgid "[Finalisation in destructors idiom](../../idioms/dtor-finally.md)" +msgstr "" + +#: .\patterns/behavioural/RAII.md:115 +msgid "" +"RAII is a common pattern in C++: [cppreference.com](http://en.cppreference." +"com/w/cpp/language/raii),\r\n" +"[wikipedia][wikipedia]." +msgstr "" + +#: .\patterns/behavioural/RAII.md:120 +msgid "" +"[Style guide entry](https://doc.rust-lang.org/1.0.0/style/ownership/raii." +"html)\r\n" +"(currently just a placeholder)." +msgstr "" + +#: .\patterns/behavioural/strategy.md:1 +msgid "# Strategy (aka Policy)\r" +msgstr "" + +#: .\patterns/behavioural/strategy.md:5 +msgid "" +"The [Strategy design pattern](https://en.wikipedia.org/wiki/" +"Strategy_pattern)\r\n" +"is a technique that enables separation of concerns.\r\n" +"It also allows to decouple software modules through [Dependency Inversion]" +"(https://en.wikipedia.org/wiki/Dependency_inversion_principle)." +msgstr "" + +#: .\patterns/behavioural/strategy.md:9 +msgid "" +"The basic idea behind the Strategy pattern is that, given an algorithm " +"solving\r\n" +"a particular problem, we define only the skeleton of the algorithm at an " +"abstract\r\n" +"level, and we separate the specific algorithm’s implementation into " +"different parts." +msgstr "" + +#: .\patterns/behavioural/strategy.md:13 +msgid "" +"In this way, a client using the algorithm may choose a specific " +"implementation,\r\n" +"while the general algorithm workflow remains the same. In other words, the " +"abstract\r\n" +"specification of the class does not depend on the specific implementation of " +"the\r\n" +"derived class, but specific implementation must adhere to the abstract " +"specification.\r\n" +"This is why we call it \"Dependency Inversion\"." +msgstr "" + +#: .\patterns/behavioural/strategy.md:21 +msgid "" +"Imagine we are working on a project that generates reports every month.\r\n" +"We need the reports to be generated in different formats (strategies), e.g.," +"\r\n" +"in `JSON` or `Plain Text` formats.\r\n" +"But things vary over time, and we don't know what kind of requirement we may " +"get\r\n" +"in the future. For example, we may need to generate our report in a " +"completely new\r\n" +"format, or just modify one of the existing formats." +msgstr "" + +#: .\patterns/behavioural/strategy.md:30 +msgid "" +"In this example our invariants (or abstractions) are `Context`, `Formatter`," +"\r\n" +"and `Report`, while `Text` and `Json` are our strategy structs. These " +"strategies\r\n" +"have to implement the `Formatter` trait." +msgstr "" + +#: .\patterns/behavioural/strategy.md:34 +msgid "" +"```rust\r\n" +"use std::collections::HashMap;\r\n" +"\r\n" +"type Data = HashMap;\r\n" +"\r\n" +"trait Formatter {\r\n" +" fn format(&self, data: &Data, buf: &mut String);\r\n" +"}\r\n" +"\r\n" +"struct Report;\r\n" +"\r\n" +"impl Report {\r\n" +" // Write should be used but we kept it as String to ignore error handling" +"\r\n" +" fn generate(g: T, s: &mut String) {\r\n" +" // backend operations...\r\n" +" let mut data = HashMap::new();\r\n" +" data.insert(\"one\".to_string(), 1);\r\n" +" data.insert(\"two\".to_string(), 2);\r\n" +" // generate report\r\n" +" g.format(&data, s);\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"struct Text;\r\n" +"impl Formatter for Text {\r\n" +" fn format(&self, data: &Data, buf: &mut String) {\r\n" +" for (k, v) in data {\r\n" +" let entry = format!(\"{} {}\\n\", k, v);\r\n" +" buf.push_str(&entry);\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"struct Json;\r\n" +"impl Formatter for Json {\r\n" +" fn format(&self, data: &Data, buf: &mut String) {\r\n" +" buf.push('[');\r\n" +" for (k, v) in data.into_iter() {\r\n" +" let entry = format!(r#\"{{\"{}\":\"{}\"}}\"#, k, v);\r\n" +" buf.push_str(&entry);\r\n" +" buf.push(',');\r\n" +" }\r\n" +" if !data.is_empty() {\r\n" +" buf.pop(); // remove extra , at the end\r\n" +" }\r\n" +" buf.push(']');\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let mut s = String::from(\"\");\r\n" +" Report::generate(Text, &mut s);\r\n" +" assert!(s.contains(\"one 1\"));\r\n" +" assert!(s.contains(\"two 2\"));\r\n" +"\r\n" +" s.clear(); // reuse the same buffer\r\n" +" Report::generate(Json, &mut s);\r\n" +" assert!(s.contains(r#\"{\"one\":\"1\"}\"#));\r\n" +" assert!(s.contains(r#\"{\"two\":\"2\"}\"#));\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/strategy.md:98 +msgid "" +"The main advantage is separation of concerns. For example, in this case " +"`Report`\r\n" +"does not know anything about specific implementations of `Json` and `Text`," +"\r\n" +"whereas the output implementations does not care about how data is " +"preprocessed,\r\n" +"stored, and fetched. The only thing they have to know is context and a " +"specific\r\n" +"trait and method to implement, i.e,`Formatter` and `run`." +msgstr "" + +#: .\patterns/behavioural/strategy.md:106 +msgid "" +"For each strategy there must be implemented at least one module, so number " +"of modules\r\n" +"increases with number of strategies. If there are many strategies to choose " +"from\r\n" +"then users have to know how strategies differ from one another." +msgstr "" + +#: .\patterns/behavioural/strategy.md:112 +msgid "" +"In the previous example all strategies are implemented in a single file.\r\n" +"Ways of providing different strategies includes:" +msgstr "" + +#: .\patterns/behavioural/strategy.md:115 +msgid "" +"- All in one file (as shown in this example, similar to being separated as " +"modules)\r\n" +"- Separated as modules, E.g. `formatter::json` module, `formatter::text` " +"module\r\n" +"- Use compiler feature flags, E.g. `json` feature, `text` feature\r\n" +"- Separated as crates, E.g. `json` crate, `text` crate\r\n" +"\r" +msgstr "" + +#: .\patterns/behavioural/strategy.md:120 +msgid "" +"Serde crate is a good example of the `Strategy` pattern in action. Serde " +"allows\r\n" +"[full customization](https://serde.rs/custom-serialization.html) of the " +"serialization\r\n" +"behavior by manually implementing `Serialize` and `Deserialize` traits for " +"our\r\n" +"type. For example, we could easily swap `serde_json` with `serde_cbor` since " +"they\r\n" +"expose similar methods. Having this makes the helper crate `serde_transcode` " +"much\r\n" +"more useful and ergonomic." +msgstr "" + +#: .\patterns/behavioural/strategy.md:127 +msgid "" +"However, we don't need to use traits in order to design this pattern in Rust." +msgstr "" + +#: .\patterns/behavioural/strategy.md:129 +msgid "" +"The following toy example demonstrates the idea of the Strategy pattern " +"using Rust\r\n" +"`closures`:" +msgstr "" + +#: .\patterns/behavioural/strategy.md:132 +msgid "" +"```rust\r\n" +"struct Adder;\r\n" +"impl Adder {\r\n" +" pub fn add(x: u8, y: u8, f: F) -> u8\r\n" +" where\r\n" +" F: Fn(u8, u8) -> u8,\r\n" +" {\r\n" +" f(x, y)\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let arith_adder = |x, y| x + y;\r\n" +" let bool_adder = |x, y| {\r\n" +" if x == 1 || y == 1 {\r\n" +" 1\r\n" +" } else {\r\n" +" 0\r\n" +" }\r\n" +" };\r\n" +" let custom_adder = |x, y| 2 * x + y;\r\n" +"\r\n" +" assert_eq!(9, Adder::add(4, 5, arith_adder));\r\n" +" assert_eq!(0, Adder::add(0, 0, bool_adder));\r\n" +" assert_eq!(5, Adder::add(1, 3, custom_adder));\r\n" +"}\r\n" +"\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/strategy.md:161 +msgid "In fact, Rust already uses this idea for `Options`'s `map` method:" +msgstr "" + +#: .\patterns/behavioural/strategy.md:163 +msgid "" +"```rust\r\n" +"fn main() {\r\n" +" let val = Some(\"Rust\");\r\n" +"\r\n" +" let len_strategy = |s: &str| s.len();\r\n" +" assert_eq!(4, val.map(len_strategy).unwrap());\r\n" +"\r\n" +" let first_byte_strategy = |s: &str| s.bytes().next().unwrap();\r\n" +" assert_eq!(82, val.map(first_byte_strategy).unwrap());\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/strategy.md:177 +msgid "" +"- [Strategy Pattern](https://en.wikipedia.org/wiki/Strategy_pattern)\r\n" +"- [Dependency Injection](https://en.wikipedia.org/wiki/" +"Dependency_injection)\r\n" +"- [Policy Based Design](https://en.wikipedia.org/wiki/Modern_C+" +"+_Design#Policy-based_design)\r" +msgstr "" + +#: .\patterns/behavioural/visitor.md:1 +msgid "# Visitor\r" +msgstr "" + +#: .\patterns/behavioural/visitor.md:5 +msgid "" +"A visitor encapsulates an algorithm that operates over a heterogeneous\r\n" +"collection of objects. It allows multiple different algorithms to be written" +"\r\n" +"over the same data without having to modify the data (or their primary\r\n" +"behaviour)." +msgstr "" + +#: .\patterns/behavioural/visitor.md:10 +msgid "" +"Furthermore, the visitor pattern allows separating the traversal of\r\n" +"a collection of objects from the operations performed on each object." +msgstr "" + +#: .\patterns/behavioural/visitor.md:15 +msgid "" +"```rust,ignore\r\n" +"// The data we will visit\r\n" +"mod ast {\r\n" +" pub enum Stmt {\r\n" +" Expr(Expr),\r\n" +" Let(Name, Expr),\r\n" +" }\r\n" +"\r\n" +" pub struct Name {\r\n" +" value: String,\r\n" +" }\r\n" +"\r\n" +" pub enum Expr {\r\n" +" IntLit(i64),\r\n" +" Add(Box, Box),\r\n" +" Sub(Box, Box),\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// The abstract visitor\r\n" +"mod visit {\r\n" +" use ast::*;\r\n" +"\r\n" +" pub trait Visitor {\r\n" +" fn visit_name(&mut self, n: &Name) -> T;\r\n" +" fn visit_stmt(&mut self, s: &Stmt) -> T;\r\n" +" fn visit_expr(&mut self, e: &Expr) -> T;\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"use visit::*;\r\n" +"use ast::*;\r\n" +"\r\n" +"// An example concrete implementation - walks the AST interpreting it as " +"code.\r\n" +"struct Interpreter;\r\n" +"impl Visitor for Interpreter {\r\n" +" fn visit_name(&mut self, n: &Name) -> i64 { panic!() }\r\n" +" fn visit_stmt(&mut self, s: &Stmt) -> i64 {\r\n" +" match *s {\r\n" +" Stmt::Expr(ref e) => self.visit_expr(e),\r\n" +" Stmt::Let(..) => unimplemented!(),\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" fn visit_expr(&mut self, e: &Expr) -> i64 {\r\n" +" match *e {\r\n" +" Expr::IntLit(n) => n,\r\n" +" Expr::Add(ref lhs, ref rhs) => self.visit_expr(lhs) + self." +"visit_expr(rhs),\r\n" +" Expr::Sub(ref lhs, ref rhs) => self.visit_expr(lhs) - self." +"visit_expr(rhs),\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/visitor.md:69 +msgid "" +"One could implement further visitors, for example a type checker, without " +"having\r\n" +"to modify the AST data." +msgstr "" + +#: .\patterns/behavioural/visitor.md:74 +msgid "" +"The visitor pattern is useful anywhere that you want to apply an algorithm to" +"\r\n" +"heterogeneous data. If data is homogeneous, you can use an iterator-like " +"pattern.\r\n" +"Using a visitor object (rather than a functional approach) allows the " +"visitor to\r\n" +"be stateful and thus communicate information between nodes." +msgstr "" + +#: .\patterns/behavioural/visitor.md:81 +msgid "" +"It is common for the `visit_*` methods to return void (as opposed to in the" +"\r\n" +"example). In that case it is possible to factor out the traversal code and " +"share\r\n" +"it between algorithms (and also to provide noop default methods). In Rust, " +"the\r\n" +"common way to do this is to provide `walk_*` functions for each datum. For" +"\r\n" +"example," +msgstr "" + +#: .\patterns/behavioural/visitor.md:87 +msgid "" +"```rust,ignore\r\n" +"pub fn walk_expr(visitor: &mut Visitor, e: &Expr) {\r\n" +" match *e {\r\n" +" Expr::IntLit(_) => {},\r\n" +" Expr::Add(ref lhs, ref rhs) => {\r\n" +" visitor.visit_expr(lhs);\r\n" +" visitor.visit_expr(rhs);\r\n" +" }\r\n" +" Expr::Sub(ref lhs, ref rhs) => {\r\n" +" visitor.visit_expr(lhs);\r\n" +" visitor.visit_expr(rhs);\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/visitor.md:103 +msgid "" +"In other languages (e.g., Java) it is common for data to have an `accept` " +"method\r\n" +"which performs the same duty." +msgstr "" + +#: .\patterns/behavioural/visitor.md:108 +msgid "The visitor pattern is a common pattern in most OO languages." +msgstr "" + +#: .\patterns/behavioural/visitor.md:110 +msgid "[Wikipedia article](https://en.wikipedia.org/wiki/Visitor_pattern)" +msgstr "" + +#: .\patterns/behavioural/visitor.md:112 +msgid "" +"The [fold](../creational/fold.md) pattern is similar to visitor but produces" +"\r\n" +"a new version of the visited data structure." +msgstr "" + +#: .\patterns/creational/intro.md:1 +msgid "# Creational Patterns\r" +msgstr "" + +#: .\patterns/creational/intro.md:3 +msgid "From [Wikipedia](https://en.wikipedia.org/wiki/Creational_pattern):" +msgstr "" + +#: .\patterns/creational/intro.md:5 +msgid "" +"> Design patterns that deal with object creation mechanisms, trying to " +"create objects\r\n" +"> in a manner suitable to the situation. The basic form of object creation " +"could\r\n" +"> result in design problems or in added complexity to the design. Creational " +"design\r\n" +"> patterns solve this problem by somehow controlling this object creation.\r" +msgstr "" + +#: .\patterns/creational/builder.md:1 +msgid "# Builder\r" +msgstr "" + +#: .\patterns/creational/builder.md:5 +msgid "Construct an object with calls to a builder helper." +msgstr "" + +#: .\patterns/creational/builder.md:9 +msgid "" +"```rust\r\n" +"#[derive(Debug, PartialEq)]\r\n" +"pub struct Foo {\r\n" +" // Lots of complicated fields.\r\n" +" bar: String,\r\n" +"}\r\n" +"\r\n" +"impl Foo {\r\n" +" // This method will help users to discover the builder\r\n" +" pub fn builder() -> FooBuilder {\r\n" +" FooBuilder::default()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"#[derive(Default)]\r\n" +"pub struct FooBuilder {\r\n" +" // Probably lots of optional fields.\r\n" +" bar: String,\r\n" +"}\r\n" +"\r\n" +"impl FooBuilder {\r\n" +" pub fn new(/* ... */) -> FooBuilder {\r\n" +" // Set the minimally required fields of Foo.\r\n" +" FooBuilder {\r\n" +" bar: String::from(\"X\"),\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" pub fn name(mut self, bar: String) -> FooBuilder {\r\n" +" // Set the name on the builder itself, and return the builder by " +"value.\r\n" +" self.bar = bar;\r\n" +" self\r\n" +" }\r\n" +"\r\n" +" // If we can get away with not consuming the Builder here, that is an\r\n" +" // advantage. It means we can use the FooBuilder as a template for " +"constructing\r\n" +" // many Foos.\r\n" +" pub fn build(self) -> Foo {\r\n" +" // Create a Foo from the FooBuilder, applying all settings in " +"FooBuilder\r\n" +" // to Foo.\r\n" +" Foo { bar: self.bar }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"#[test]\r\n" +"fn builder_test() {\r\n" +" let foo = Foo {\r\n" +" bar: String::from(\"Y\"),\r\n" +" };\r\n" +" let foo_from_builder: Foo = FooBuilder::new().name(String::from(\"Y\"))." +"build();\r\n" +" assert_eq!(foo, foo_from_builder);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/creational/builder.md:65 +msgid "" +"Useful when you would otherwise require many constructors or where\r\n" +"construction has side effects." +msgstr "" + +#: .\patterns/creational/builder.md:70 +msgid "Separates methods for building from other methods." +msgstr "" + +#: .\patterns/creational/builder.md:72 +msgid "Prevents proliferation of constructors." +msgstr "" + +#: .\patterns/creational/builder.md:74 +msgid "" +"Can be used for one-liner initialisation as well as more complex " +"construction." +msgstr "" + +#: .\patterns/creational/builder.md:78 +msgid "" +"More complex than creating a struct object directly, or a simple constructor" +"\r\n" +"function." +msgstr "" + +#: .\patterns/creational/builder.md:83 +msgid "" +"This pattern is seen more frequently in Rust (and for simpler objects) than " +"in\r\n" +"many other languages because Rust lacks overloading. Since you can only have " +"a\r\n" +"single method with a given name, having multiple constructors is less nice in" +"\r\n" +"Rust than in C++, Java, or others." +msgstr "" + +#: .\patterns/creational/builder.md:88 +msgid "" +"This pattern is often used where the builder object is useful in its own " +"right,\r\n" +"rather than being just a builder. For example, see\r\n" +"[`std::process::Command`](https://doc.rust-lang.org/std/process/struct." +"Command.html)\r\n" +"is a builder for [`Child`](https://doc.rust-lang.org/std/process/struct." +"Child.html)\r\n" +"(a process). In these cases, the `T` and `TBuilder` naming pattern is not " +"used." +msgstr "" + +#: .\patterns/creational/builder.md:94 +msgid "" +"The example takes and returns the builder by value. It is often more " +"ergonomic\r\n" +"(and more efficient) to take and return the builder as a mutable reference. " +"The\r\n" +"borrow checker makes this work naturally. This approach has the advantage " +"that\r\n" +"one can write code like" +msgstr "" + +#: .\patterns/creational/builder.md:99 +msgid "" +"```rust,ignore\r\n" +"let mut fb = FooBuilder::new();\r\n" +"fb.a();\r\n" +"fb.b();\r\n" +"let f = fb.build();\r\n" +"```" +msgstr "" + +#: .\patterns/creational/builder.md:106 +msgid "as well as the `FooBuilder::new().a().b().build()` style." +msgstr "" + +#: .\patterns/creational/builder.md:110 +msgid "" +"- [Description in the style guide](https://web.archive.org/" +"web/20210104103100/https://doc.rust-lang.org/1.12.0/style/ownership/builders." +"html)\r\n" +"- [derive_builder](https://crates.io/crates/derive_builder), a crate for " +"automatically\r\n" +" implementing this pattern while avoiding the boilerplate.\r\n" +"- [Constructor pattern](../../idioms/ctor.md) for when construction is " +"simpler.\r\n" +"- [Builder pattern (wikipedia)](https://en.wikipedia.org/wiki/" +"Builder_pattern)\r\n" +"- [Construction of complex values](https://web.archive.org/" +"web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety." +"html#c-builder)\r" +msgstr "" + +#: .\patterns/creational/fold.md:1 +msgid "# Fold\r" +msgstr "" + +#: .\patterns/creational/fold.md:5 +msgid "" +"Run an algorithm over each item in a collection of data to create a new item," +"\r\n" +"thus creating a whole new collection." +msgstr "" + +#: .\patterns/creational/fold.md:8 +msgid "" +"The etymology here is unclear to me. The terms 'fold' and 'folder' are used" +"\r\n" +"in the Rust compiler, although it appears to me to be more like a map than a" +"\r\n" +"fold in the usual sense. See the discussion below for more details." +msgstr "" + +#: .\patterns/creational/fold.md:14 +msgid "" +"```rust,ignore\r\n" +"// The data we will fold, a simple AST.\r\n" +"mod ast {\r\n" +" pub enum Stmt {\r\n" +" Expr(Box),\r\n" +" Let(Box, Box),\r\n" +" }\r\n" +"\r\n" +" pub struct Name {\r\n" +" value: String,\r\n" +" }\r\n" +"\r\n" +" pub enum Expr {\r\n" +" IntLit(i64),\r\n" +" Add(Box, Box),\r\n" +" Sub(Box, Box),\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// The abstract folder\r\n" +"mod fold {\r\n" +" use ast::*;\r\n" +"\r\n" +" pub trait Folder {\r\n" +" // A leaf node just returns the node itself. In some cases, we can " +"do this\r\n" +" // to inner nodes too.\r\n" +" fn fold_name(&mut self, n: Box) -> Box { n }\r\n" +" // Create a new inner node by folding its children.\r\n" +" fn fold_stmt(&mut self, s: Box) -> Box {\r\n" +" match *s {\r\n" +" Stmt::Expr(e) => Box::new(Stmt::Expr(self.fold_expr(e))),\r\n" +" Stmt::Let(n, e) => Box::new(Stmt::Let(self.fold_name(n), " +"self.fold_expr(e))),\r\n" +" }\r\n" +" }\r\n" +" fn fold_expr(&mut self, e: Box) -> Box { ... }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"use fold::*;\r\n" +"use ast::*;\r\n" +"\r\n" +"// An example concrete implementation - renames every name to 'foo'.\r\n" +"struct Renamer;\r\n" +"impl Folder for Renamer {\r\n" +" fn fold_name(&mut self, n: Box) -> Box {\r\n" +" Box::new(Name { value: \"foo\".to_owned() })\r\n" +" }\r\n" +" // Use the default methods for the other nodes.\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/creational/fold.md:65 +msgid "" +"The result of running the `Renamer` on an AST is a new AST identical to the " +"old\r\n" +"one, but with every name changed to `foo`. A real life folder might have some" +"\r\n" +"state preserved between nodes in the struct itself." +msgstr "" + +#: .\patterns/creational/fold.md:69 +msgid "" +"A folder can also be defined to map one data structure to a different (but" +"\r\n" +"usually similar) data structure. For example, we could fold an AST into a HIR" +"\r\n" +"tree (HIR stands for high-level intermediate representation)." +msgstr "" + +#: .\patterns/creational/fold.md:75 +msgid "" +"It is common to want to map a data structure by performing some operation on" +"\r\n" +"each node in the structure. For simple operations on simple data structures," +"\r\n" +"this can be done using `Iterator::map`. For more complex operations, perhaps" +"\r\n" +"where earlier nodes can affect the operation on later nodes, or where " +"iteration\r\n" +"over the data structure is non-trivial, using the fold pattern is more\r\n" +"appropriate." +msgstr "" + +#: .\patterns/creational/fold.md:82 +msgid "" +"Like the visitor pattern, the fold pattern allows us to separate traversal " +"of a\r\n" +"data structure from the operations performed to each node." +msgstr "" + +#: .\patterns/creational/fold.md:87 +msgid "" +"Mapping data structures in this fashion is common in functional languages. " +"In OO\r\n" +"languages, it would be more common to mutate the data structure in place. The" +"\r\n" +"'functional' approach is common in Rust, mostly due to the preference for\r\n" +"immutability. Using fresh data structures, rather than mutating old ones, " +"makes\r\n" +"reasoning about the code easier in most circumstances." +msgstr "" + +#: .\patterns/creational/fold.md:93 +msgid "" +"The trade-off between efficiency and reusability can be tweaked by changing " +"how\r\n" +"nodes are accepted by the `fold_*` methods." +msgstr "" + +#: .\patterns/creational/fold.md:96 +msgid "" +"In the above example we operate on `Box` pointers. Since these own their data" +"\r\n" +"exclusively, the original copy of the data structure cannot be re-used. On " +"the\r\n" +"other hand if a node is not changed, reusing it is very efficient." +msgstr "" + +#: .\patterns/creational/fold.md:100 +msgid "" +"If we were to operate on borrowed references, the original data structure " +"can be\r\n" +"reused; however, a node must be cloned even if unchanged, which can be\r\n" +"expensive." +msgstr "" + +#: .\patterns/creational/fold.md:104 +msgid "" +"Using a reference counted pointer gives the best of both worlds - we can " +"reuse\r\n" +"the original data structure, and we don't need to clone unchanged nodes. " +"However,\r\n" +"they are less ergonomic to use and mean that the data structures cannot be" +"\r\n" +"mutable." +msgstr "" + +#: .\patterns/creational/fold.md:111 +msgid "" +"Iterators have a `fold` method, however this folds a data structure into a" +"\r\n" +"value, rather than into a new data structure. An iterator's `map` is more " +"like\r\n" +"this fold pattern." +msgstr "" + +#: .\patterns/creational/fold.md:115 +msgid "" +"In other languages, fold is usually used in the sense of Rust's iterators," +"\r\n" +"rather than this pattern. Some functional languages have powerful constructs " +"for\r\n" +"performing flexible maps over data structures." +msgstr "" + +#: .\patterns/creational/fold.md:119 +msgid "" +"The [visitor](../behavioural/visitor.md) pattern is closely related to fold." +"\r\n" +"They share the concept of walking a data structure performing an operation on" +"\r\n" +"each node. However, the visitor does not create a new data structure nor " +"consume\r\n" +"the old one." +msgstr "" + +#: .\patterns/structural/intro.md:1 +msgid "# Structural Patterns\r" +msgstr "" + +#: .\patterns/structural/intro.md:3 +msgid "From [Wikipedia](https://en.wikipedia.org/wiki/Structural_pattern):" +msgstr "" + +#: .\patterns/structural/intro.md:5 +msgid "" +"> Design patterns that ease the design by identifying a simple way to " +"realize relationships\r\n" +"> among entities.\r" +msgstr "" + +#: .\patterns/structural/compose-structs.md:1 +msgid "# Compose structs together for better borrowing\r" +msgstr "" + +#: .\patterns/structural/compose-structs.md:3 +msgid "TODO - this is not a very snappy name" +msgstr "" + +#: .\patterns/structural/compose-structs.md:7 +msgid "" +"Sometimes a large struct will cause issues with the borrow checker - although" +"\r\n" +"fields can be borrowed independently, sometimes the whole struct ends up " +"being\r\n" +"used at once, preventing other uses. A solution might be to decompose the " +"struct\r\n" +"into several smaller structs. Then compose these together into the original" +"\r\n" +"struct. Then each struct can be borrowed separately and have more flexible" +"\r\n" +"behaviour." +msgstr "" + +#: .\patterns/structural/compose-structs.md:14 +msgid "" +"This will often lead to a better design in other ways: applying this design" +"\r\n" +"pattern often reveals smaller units of functionality." +msgstr "" + +#: .\patterns/structural/compose-structs.md:19 +msgid "" +"Here is a contrived example of where the borrow checker foils us in our plan " +"to\r\n" +"use a struct:" +msgstr "" + +#: .\patterns/structural/compose-structs.md:22 +msgid "" +"```rust\r\n" +"struct A {\r\n" +" f1: u32,\r\n" +" f2: u32,\r\n" +" f3: u32,\r\n" +"}\r\n" +"\r\n" +"fn foo(a: &mut A) -> &u32 { &a.f2 }\r\n" +"fn bar(a: &mut A) -> u32 { a.f1 + a.f3 }\r\n" +"\r\n" +"fn baz(a: &mut A) {\r\n" +" // The later usage of x causes a to be borrowed for the rest of the " +"function.\r\n" +" let x = foo(a);\r\n" +" // Borrow checker error:\r\n" +" // let y = bar(a); // ~ ERROR: cannot borrow `*a` as mutable more than " +"once\r\n" +" // at a time\r\n" +" println!(\"{}\", x);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/structural/compose-structs.md:42 +msgid "" +"We can apply this design pattern and refactor `A` into two smaller structs, " +"thus\r\n" +"solving the borrow checking issue:" +msgstr "" + +#: .\patterns/structural/compose-structs.md:45 +msgid "" +"```rust\r\n" +"// A is now composed of two structs - B and C.\r\n" +"struct A {\r\n" +" b: B,\r\n" +" c: C,\r\n" +"}\r\n" +"struct B {\r\n" +" f2: u32,\r\n" +"}\r\n" +"struct C {\r\n" +" f1: u32,\r\n" +" f3: u32,\r\n" +"}\r\n" +"\r\n" +"// These functions take a B or C, rather than A.\r\n" +"fn foo(b: &mut B) -> &u32 { &b.f2 }\r\n" +"fn bar(c: &mut C) -> u32 { c.f1 + c.f3 }\r\n" +"\r\n" +"fn baz(a: &mut A) {\r\n" +" let x = foo(&mut a.b);\r\n" +" // Now it's OK!\r\n" +" let y = bar(&mut a.c);\r\n" +" println!(\"{}\", x);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/structural/compose-structs.md:73 +msgid "TODO Why and where you should use the pattern" +msgstr "" + +#: .\patterns/structural/compose-structs.md:77 +msgid "Lets you work around limitations in the borrow checker." +msgstr "" + +#: .\patterns/structural/compose-structs.md:79 +msgid "Often produces a better design." +msgstr "" + +#: .\patterns/structural/compose-structs.md:83 +msgid "Leads to more verbose code." +msgstr "" + +#: .\patterns/structural/compose-structs.md:85 +msgid "" +"Sometimes, the smaller structs are not good abstractions, and so we end up " +"with\r\n" +"a worse design. That is probably a 'code smell', indicating that the program" +"\r\n" +"should be refactored in some way." +msgstr "" + +#: .\patterns/structural/compose-structs.md:91 +msgid "" +"This pattern is not required in languages that don't have a borrow checker, " +"so\r\n" +"in that sense is unique to Rust. However, making smaller units of " +"functionality\r\n" +"often leads to cleaner code: a widely acknowledged principle of software\r\n" +"engineering, independent of the language." +msgstr "" + +#: .\patterns/structural/compose-structs.md:96 +msgid "" +"This pattern relies on Rust's borrow checker to be able to borrow fields\r\n" +"independently of each other. In the example, the borrow checker knows that " +"`a.b`\r\n" +"and `a.c` are distinct and can be borrowed independently, it does not try to" +"\r\n" +"borrow all of `a`, which would make this pattern useless." +msgstr "" + +#: .\patterns/structural/small-crates.md:1 +msgid "# Prefer small crates\r" +msgstr "" + +#: .\patterns/structural/small-crates.md:5 +msgid "Prefer small crates that do one thing well." +msgstr "" + +#: .\patterns/structural/small-crates.md:7 +msgid "" +"Cargo and crates.io make it easy to add third-party libraries, much more so " +"than\r\n" +"in say C or C++. Moreover, since packages on crates.io cannot be edited or " +"removed\r\n" +"after publication, any build that works now should continue to work in the " +"future.\r\n" +"We should take advantage of this tooling, and use smaller, more fine-grained " +"dependencies." +msgstr "" + +#: .\patterns/structural/small-crates.md:14 +msgid "" +"* Small crates are easier to understand, and encourage more modular code.\r\n" +"* Crates allow for re-using code between projects.\r\n" +" For example, the `url` crate was developed as part of the Servo browser " +"engine,\r\n" +" but has since found wide use outside the project.\r\n" +"* Since the compilation unit\r\n" +" of Rust is the crate, splitting a project into multiple crates can allow " +"more of\r\n" +" the code to be built in parallel.\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/small-crates.md:24 +msgid "" +"* This can lead to \"dependency hell\", when a project depends on multiple " +"conflicting\r\n" +" versions of a crate at the same time. For example, the `url` crate has " +"both versions\r\n" +" 1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url` from `url:0.5` " +"are\r\n" +" different types, an HTTP client that uses `url:0.5` would not accept `Url` " +"values\r\n" +" from a web scraper that uses `url:1.0`.\r\n" +"* Packages on crates.io are not curated. A crate may be poorly written, have" +"\r\n" +" unhelpful documentation, or be outright malicious.\r\n" +"* Two small crates may be less optimized than one large one, since the " +"compiler\r\n" +" does not perform link-time optimization (LTO) by default.\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/small-crates.md:36 +msgid "" +"The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides " +"functions\r\n" +"for converting `&T` to `&[T]`." +msgstr "" + +#: .\patterns/structural/small-crates.md:39 +msgid "" +"The [`url`](https://crates.io/crates/url) crate provides tools for working " +"with\r\n" +"URLs." +msgstr "" + +#: .\patterns/structural/small-crates.md:42 +msgid "" +"The [`num_cpus`](https://crates.io/crates/num_cpus) crate provides a " +"function to\r\n" +"query the number of CPUs on a machine." +msgstr "" + +#: .\patterns/structural/small-crates.md:47 +msgid "* [crates.io: The Rust community crate host](https://crates.io/)\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:1 +msgid "# Contain unsafety in small modules\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:5 +msgid "" +"If you have `unsafe` code, create the smallest possible module that can " +"uphold\r\n" +"the needed invariants to build a minimal safe interface upon the unsafety. " +"Embed\r\n" +"this into a larger module that contains only safe code and presents an " +"ergonomic\r\n" +"interface. Note that the outer module can contain unsafe functions and " +"methods\r\n" +"that call directly into the unsafe code. Users may use this to gain speed " +"benefits." +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:13 +msgid "" +"* This restricts the unsafe code that must be audited\r\n" +"* Writing the outer module is much easier, since you can count on the " +"guarantees\r\n" +"of the inner module\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:19 +msgid "" +"* Sometimes, it may be hard to find a suitable interface.\r\n" +"* The abstraction may introduce inefficiencies.\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:24 +msgid "" +"* The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe " +"operations\r\n" +" in submodules, presenting a safe interface to users.\r\n" +"* `std`'s `String` class is a wrapper over `Vec` with the added invariant" +"\r\n" +"that the contents must be valid UTF-8. The operations on `String` ensure this" +"\r\n" +"behavior.\r\n" +"However, users have the option of using an `unsafe` method to create a " +"`String`,\r\n" +"in which case the onus is on them to guarantee the validity of the contents." +"\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:34 +msgid "" +"* [Ralf Jung's Blog about invariants in unsafe code](https://www.ralfj.de/" +"blog/2018/08/22/two-kinds-of-invariants.html)\r" +msgstr "" + +#: .\patterns/ffi/intro.md:1 +msgid "# FFI Patterns\r" +msgstr "" + +#: .\patterns/ffi/intro.md:3 +msgid "" +"Writing FFI code is an entire course in itself.\r\n" +"However, there are several idioms here that can act as pointers, and avoid " +"traps\r\n" +"for inexperienced users of unsafe Rust." +msgstr "" + +#: .\patterns/ffi/intro.md:7 +msgid "" +"This section contains design patterns that may be useful when doing FFI." +msgstr "" + +#: .\patterns/ffi/intro.md:9 +msgid "" +"1. [Object-Based API](./export.md) design that has good memory safety " +"characteristics,\r\n" +" and a clean boundary of what is safe and what is unsafe\r\n" +"\r\n" +"2. [Type Consolidation into Wrappers](./wrappers.md) - group multiple Rust " +"types\r\n" +" together into an opaque \"object\"\r" +msgstr "" + +#: .\patterns/ffi/export.md:1 +msgid "# Object-Based APIs\r" +msgstr "" + +#: .\patterns/ffi/export.md:5 +msgid "" +"When designing APIs in Rust which are exposed to other languages, there are " +"some\r\n" +"important design principles which are contrary to normal Rust API design:" +msgstr "" + +#: .\patterns/ffi/export.md:8 +msgid "" +"1. All Encapsulated types should be *owned* by Rust, *managed* by the user," +"\r\n" +" and *opaque*.\r\n" +"2. All Transactional data types should be *owned* by the user, and " +"*transparent*.\r\n" +"3. All library behavior should be functions acting upon Encapsulated types." +"\r\n" +"4. All library behavior should be encapsulated into types not based on " +"structure,\r\n" +" but *provenance/lifetime*.\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:17 +msgid "" +"Rust has built-in FFI support to other languages.\r\n" +"It does this by providing a way for crate authors to provide C-compatible " +"APIs\r\n" +"through different ABIs (though that is unimportant to this practice)." +msgstr "" + +#: .\patterns/ffi/export.md:21 +msgid "" +"Well-designed Rust FFI follows C API design principles, while compromising " +"the\r\n" +"design in Rust as little as possible. There are three goals with any foreign " +"API:" +msgstr "" + +#: .\patterns/ffi/export.md:24 +msgid "" +"1. Make it easy to use in the target language.\r\n" +"2. Avoid the API dictating internal unsafety on the Rust side as much as " +"possible.\r\n" +"3. Keep the potential for memory unsafety and Rust `undefined behaviour` as " +"small\r\n" +" as possible.\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:29 +msgid "" +"Rust code must trust the memory safety of the foreign language beyond a " +"certain\r\n" +"point. However, every bit of `unsafe` code on the Rust side is an " +"opportunity for\r\n" +"bugs, or to exacerbate `undefined behaviour`." +msgstr "" + +#: .\patterns/ffi/export.md:33 +msgid "" +"For example, if a pointer provenance is wrong, that may be a segfault due to" +"\r\n" +"invalid memory access. But if it is manipulated by unsafe code, it could " +"become\r\n" +"full-blown heap corruption." +msgstr "" + +#: .\patterns/ffi/export.md:37 +msgid "" +"The Object-Based API design allows for writing shims that have good memory " +"safety\r\n" +"characteristics, and a clean boundary of what is safe and what is `unsafe`." +msgstr "" + +#: .\patterns/ffi/export.md:42 +msgid "" +"The POSIX standard defines the API to access an on-file database, known as " +"[DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/" +"ndbm.h).\r\n" +"It is an excellent example of an \"object-based\" API." +msgstr "" + +#: .\patterns/ffi/export.md:45 +msgid "" +"Here is the definition in C, which hopefully should be easy to read for those" +"\r\n" +"involved in FFI. The commentary below should help explain it for those who" +"\r\n" +"miss the subtleties." +msgstr "" + +#: .\patterns/ffi/export.md:49 +msgid "" +"```C\r\n" +"struct DBM;\r\n" +"typedef struct { void *dptr, size_t dsize } datum;\r\n" +"\r\n" +"int dbm_clearerr(DBM *);\r\n" +"void dbm_close(DBM *);\r\n" +"int dbm_delete(DBM *, datum);\r\n" +"int dbm_error(DBM *);\r\n" +"datum dbm_fetch(DBM *, datum);\r\n" +"datum dbm_firstkey(DBM *);\r\n" +"datum dbm_nextkey(DBM *);\r\n" +"DBM *dbm_open(const char *, int, mode_t);\r\n" +"int dbm_store(DBM *, datum, datum, int);\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:64 +msgid "This API defines two types: `DBM` and `datum`." +msgstr "" + +#: .\patterns/ffi/export.md:66 +msgid "" +"The `DBM` type was called an \"encapsulated\" type above.\r\n" +"It is designed to contain internal state, and acts as an entry point for the" +"\r\n" +"library's behavior." +msgstr "" + +#: .\patterns/ffi/export.md:70 +msgid "" +"It is completely opaque to the user, who cannot create a `DBM` themselves " +"since\r\n" +"they don't know its size or layout. Instead, they must call `dbm_open`, and " +"that\r\n" +"only gives them *a pointer to one*." +msgstr "" + +#: .\patterns/ffi/export.md:74 +msgid "" +"This means all `DBM`s are \"owned\" by the library in a Rust sense.\r\n" +"The internal state of unknown size is kept in memory controlled by the " +"library,\r\n" +"not the user. The user can only manage its life cycle with `open` and " +"`close`,\r\n" +"and perform operations on it with the other functions." +msgstr "" + +#: .\patterns/ffi/export.md:79 +msgid "" +"The `datum` type was called a \"transactional\" type above.\r\n" +"It is designed to facilitate the exchange of information between the library " +"and\r\n" +"its user." +msgstr "" + +#: .\patterns/ffi/export.md:83 +msgid "" +"The database is designed to store \"unstructured data\", with no pre-defined " +"length\r\n" +"or meaning. As a result, the `datum` is the C equivalent of a Rust slice: a " +"bunch\r\n" +"of bytes, and a count of how many there are. The main difference is that " +"there is\r\n" +"no type information, which is what `void` indicates." +msgstr "" + +#: .\patterns/ffi/export.md:88 +msgid "" +"Keep in mind that this header is written from the library's point of view." +"\r\n" +"The user likely has some type they are using, which has a known size.\r\n" +"But the library does not care, and by the rules of C casting, any type " +"behind a\r\n" +"pointer can be cast to `void`." +msgstr "" + +#: .\patterns/ffi/export.md:93 +msgid "" +"As noted earlier, this type is *transparent* to the user. But also, this " +"type is\r\n" +"*owned* by the user.\r\n" +"This has subtle ramifications, due to that pointer inside it.\r\n" +"The question is, who owns the memory that pointer points to?" +msgstr "" + +#: .\patterns/ffi/export.md:98 +msgid "" +"The answer for best memory safety is, \"the user\".\r\n" +"But in cases such as retrieving a value, the user does not know how to " +"allocate\r\n" +"it correctly (since they don't know how long the value is). In this case, " +"the library\r\n" +"code is expected to use the heap that the user has access to -- such as the " +"C library\r\n" +"`malloc` and `free` -- and then *transfer ownership* in the Rust sense." +msgstr "" + +#: .\patterns/ffi/export.md:104 +msgid "" +"This may all seem speculative, but this is what a pointer means in C.\r\n" +"It means the same thing as Rust: \"user defined lifetime.\"\r\n" +"The user of the library needs to read the documentation in order to use it " +"correctly.\r\n" +"That said, there are some decisions that have fewer or greater consequences " +"if users\r\n" +"do it wrong. Minimizing those are what this best practice is about, and the " +"key\r\n" +"is to *transfer ownership of everything that is transparent*." +msgstr "" + +#: .\patterns/ffi/export.md:113 +msgid "" +"This minimizes the number of memory safety guarantees the user must uphold " +"to a\r\n" +"relatively small number:" +msgstr "" + +#: .\patterns/ffi/export.md:116 +msgid "" +"1. Do not call any function with a pointer not returned by `dbm_open` " +"(invalid\r\n" +" access or corruption).\r\n" +"2. Do not call any function on a pointer after close (use after free).\r\n" +"3. The `dptr` on any `datum` must be `NULL`, or point to a valid slice of " +"memory\r\n" +" at the advertised length.\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:122 +msgid "" +"In addition, it avoids a lot of pointer provenance issues.\r\n" +"To understand why, let us consider an alternative in some depth: key " +"iteration." +msgstr "" + +#: .\patterns/ffi/export.md:125 +msgid "" +"Rust is well known for its iterators.\r\n" +"When implementing one, the programmer makes a separate type with a bounded " +"lifetime\r\n" +"to its owner, and implements the `Iterator` trait." +msgstr "" + +#: .\patterns/ffi/export.md:129 +msgid "Here is how iteration would be done in Rust for `DBM`:" +msgstr "" + +#: .\patterns/ffi/export.md:131 +msgid "" +"```rust,ignore\r\n" +"struct Dbm { ... }\r\n" +"\r\n" +"impl Dbm {\r\n" +" /* ... */\r\n" +" pub fn keys<'it>(&'it self) -> DbmKeysIter<'it> { ... }\r\n" +" /* ... */\r\n" +"}\r\n" +"\r\n" +"struct DbmKeysIter<'it> {\r\n" +" owner: &'it Dbm,\r\n" +"}\r\n" +"\r\n" +"impl<'it> Iterator for DbmKeysIter<'it> { ... }\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:147 +msgid "" +"This is clean, idiomatic, and safe. thanks to Rust's guarantees.\r\n" +"However, consider what a straightforward API translation would look like:" +msgstr "" + +#: .\patterns/ffi/export.md:150 +msgid "" +"```rust,ignore\r\n" +"#[no_mangle]\r\n" +"pub extern \"C\" fn dbm_iter_new(owner: *const Dbm) -> *mut DbmKeysIter {\r\n" +" // THIS API IS A BAD IDEA! For real applications, use object-based " +"design instead.\r\n" +"}\r\n" +"#[no_mangle]\r\n" +"pub extern \"C\" fn dbm_iter_next(\r\n" +" iter: *mut DbmKeysIter,\r\n" +" key_out: *const datum\r\n" +") -> libc::c_int {\r\n" +" // THIS API IS A BAD IDEA! For real applications, use object-based " +"design instead.\r\n" +"}\r\n" +"#[no_mangle]\r\n" +"pub extern \"C\" fn dbm_iter_del(*mut DbmKeysIter) {\r\n" +" // THIS API IS A BAD IDEA! For real applications, use object-based " +"design instead.\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:168 +msgid "" +"This API loses a key piece of information: the lifetime of the iterator must " +"not\r\n" +"exceed the lifetime of the `Dbm` object that owns it. A user of the library " +"could\r\n" +"use it in a way which causes the iterator to outlive the data it is " +"iterating on,\r\n" +"resulting in reading uninitialized memory." +msgstr "" + +#: .\patterns/ffi/export.md:173 +msgid "" +"This example written in C contains a bug that will be explained afterwards:" +msgstr "" + +#: .\patterns/ffi/export.md:175 +msgid "" +"```C\r\n" +"int count_key_sizes(DBM *db) {\r\n" +" // DO NOT USE THIS FUNCTION. IT HAS A SUBTLE BUT SERIOUS BUG!\r\n" +" datum key;\r\n" +" int len = 0;\r\n" +"\r\n" +" if (!dbm_iter_new(db)) {\r\n" +" dbm_close(db);\r\n" +" return -1;\r\n" +" }\r\n" +"\r\n" +" int l;\r\n" +" while ((l = dbm_iter_next(owner, &key)) >= 0) { // an error is indicated " +"by -1\r\n" +" free(key.dptr);\r\n" +" len += key.dsize;\r\n" +" if (l == 0) { // end of the iterator\r\n" +" dbm_close(owner);\r\n" +" }\r\n" +" }\r\n" +" if l >= 0 {\r\n" +" return -1;\r\n" +" } else {\r\n" +" return len;\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:202 +msgid "" +"This bug is a classic. Here's what happens when the iterator returns the\r\n" +"end-of-iteration marker:" +msgstr "" + +#: .\patterns/ffi/export.md:205 +msgid "" +"1. The loop condition sets `l` to zero, and enters the loop because `0 >= 0`." +"\r\n" +"2. The length is incremented, in this case by zero.\r\n" +"3. The if statement is true, so the database is closed. There should be a " +"break\r\n" +" statement here.\r\n" +"4. The loop condition executes again, causing a `next` call on the closed " +"object.\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:211 +msgid "" +"The worst part about this bug?\r\n" +"If the Rust implementation was careful, this code will work most of the time!" +"\r\n" +"If the memory for the `Dbm` object is not immediately reused, an internal " +"check\r\n" +"will almost certainly fail, resulting in the iterator returning a `-1` " +"indicating\r\n" +"an error. But occasionally, it will cause a segmentation fault, or even " +"worse,\r\n" +"nonsensical memory corruption!" +msgstr "" + +#: .\patterns/ffi/export.md:218 +msgid "" +"None of this can be avoided by Rust.\r\n" +"From its perspective, it put those objects on its heap, returned pointers to " +"them,\r\n" +"and gave up control of their lifetimes. The C code simply must \"play nice\"." +msgstr "" + +#: .\patterns/ffi/export.md:222 +msgid "" +"The programmer must read and understand the API documentation.\r\n" +"While some consider that par for the course in C, a good API design can " +"mitigate\r\n" +"this risk. The POSIX API for `DBM` did this by *consolidating the ownership* " +"of\r\n" +"the iterator with its parent:" +msgstr "" + +#: .\patterns/ffi/export.md:227 +msgid "" +"```C\r\n" +"datum dbm_firstkey(DBM *);\r\n" +"datum dbm_nextkey(DBM *);\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:232 +msgid "" +"Thus, all the lifetimes were bound together, and such unsafety was prevented." +msgstr "" + +#: .\patterns/ffi/export.md:236 +msgid "" +"However, this design choice also has a number of drawbacks, which should be" +"\r\n" +"considered as well." +msgstr "" + +#: .\patterns/ffi/export.md:239 +msgid "" +"First, the API itself becomes less expressive.\r\n" +"With POSIX DBM, there is only one iterator per object, and every call changes" +"\r\n" +"its state. This is much more restrictive than iterators in almost any " +"language,\r\n" +"even though it is safe. Perhaps with other related objects, whose lifetimes " +"are\r\n" +"less hierarchical, this limitation is more of a cost than the safety." +msgstr "" + +#: .\patterns/ffi/export.md:245 +msgid "" +"Second, depending on the relationships of the API's parts, significant " +"design effort\r\n" +"may be involved. Many of the easier design points have other patterns " +"associated\r\n" +"with them:" +msgstr "" + +#: .\patterns/ffi/export.md:249 +msgid "" +"- [Wrapper Type Consolidation](./wrappers.md) groups multiple Rust types " +"together\r\n" +" into an opaque \"object\"\r\n" +"\r\n" +"- [FFI Error Passing](../../idioms/ffi/errors.md) explains error handling " +"with integer\r\n" +" codes and sentinel return values (such as `NULL` pointers)\r\n" +"\r\n" +"- [Accepting Foreign Strings](../../idioms/ffi/accepting-strings.md) allows " +"accepting\r\n" +" strings with minimal unsafe code, and is easier to get right than\r\n" +" [Passing Strings to FFI](../../idioms/ffi/passing-strings.md)\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:259 +msgid "" +"However, not every API can be done this way.\r\n" +"It is up to the best judgement of the programmer as to who their audience is." +msgstr "" + +#: .\patterns/ffi/wrappers.md:1 +msgid "# Type Consolidation into Wrappers\r" +msgstr "" + +#: .\patterns/ffi/wrappers.md:5 +msgid "" +"This pattern is designed to allow gracefully handling multiple related types," +"\r\n" +"while minimizing the surface area for memory unsafety." +msgstr "" + +#: .\patterns/ffi/wrappers.md:8 +msgid "" +"One of the cornerstones of Rust's aliasing rules is lifetimes.\r\n" +"This ensures that many patterns of access between types can be memory safe," +"\r\n" +"data race safety included." +msgstr "" + +#: .\patterns/ffi/wrappers.md:12 +msgid "" +"However, when Rust types are exported to other languages, they are usually " +"transformed\r\n" +"into pointers. In Rust, a pointer means \"the user manages the lifetime of " +"the pointee.\"\r\n" +"It is their responsibility to avoid memory unsafety." +msgstr "" + +#: .\patterns/ffi/wrappers.md:16 +msgid "" +"Some level of trust in the user code is thus required, notably around use-" +"after-free\r\n" +"which Rust can do nothing about. However, some API designs place higher " +"burdens\r\n" +"than others on the code written in the other language." +msgstr "" + +#: .\patterns/ffi/wrappers.md:20 +msgid "" +"The lowest risk API is the \"consolidated wrapper\", where all possible " +"interactions\r\n" +"with an object are folded into a \"wrapper type\", while keeping the Rust " +"API clean." +msgstr "" + +#: .\patterns/ffi/wrappers.md:25 +msgid "" +"To understand this, let us look at a classic example of an API to export: " +"iteration\r\n" +"through a collection." +msgstr "" + +#: .\patterns/ffi/wrappers.md:28 +msgid "That API looks like this:" +msgstr "" + +#: .\patterns/ffi/wrappers.md:30 +msgid "" +"1. The iterator is initialized with `first_key`.\r\n" +"2. Each call to `next_key` will advance the iterator.\r\n" +"3. Calls to `next_key` if the iterator is at the end will do nothing.\r\n" +"4. As noted above, the iterator is \"wrapped into\" the collection (unlike " +"the native\r\n" +" Rust API).\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/wrappers.md:36 +msgid "" +"If the iterator implements `nth()` efficiently, then it is possible to make " +"it\r\n" +"ephemeral to each function call:" +msgstr "" + +#: .\patterns/ffi/wrappers.md:39 +msgid "" +"```rust,ignore\r\n" +"struct MySetWrapper {\r\n" +" myset: MySet,\r\n" +" iter_next: usize,\r\n" +"}\r\n" +"\r\n" +"impl MySetWrapper {\r\n" +" pub fn first_key(&mut self) -> Option<&Key> {\r\n" +" self.iter_next = 0;\r\n" +" self.next_key()\r\n" +" }\r\n" +" pub fn next_key(&mut self) -> Option<&Key> {\r\n" +" if let Some(next) = self.myset.keys().nth(self.iter_next) {\r\n" +" self.iter_next += 1;\r\n" +" Some(next)\r\n" +" } else {\r\n" +" None\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/wrappers.md:61 +msgid "As a result, the wrapper is simple and contains no `unsafe` code." +msgstr "" + +#: .\patterns/ffi/wrappers.md:65 +msgid "" +"This makes APIs safer to use, avoiding issues with lifetimes between types." +"\r\n" +"See [Object-Based APIs](./export.md) for more on the advantages and pitfalls" +"\r\n" +"this avoids." +msgstr "" + +#: .\patterns/ffi/wrappers.md:71 +msgid "" +"Often, wrapping types is quite difficult, and sometimes a Rust API compromise" +"\r\n" +"would make things easier." +msgstr "" + +#: .\patterns/ffi/wrappers.md:74 +msgid "" +"As an example, consider an iterator which does not efficiently implement " +"`nth()`.\r\n" +"It would definitely be worth putting in special logic to make the object " +"handle\r\n" +"iteration internally, or to support a different access pattern efficiently " +"that\r\n" +"only the Foreign Function API will use." +msgstr "" + +#: .\patterns/ffi/wrappers.md:79 +msgid "### Trying to Wrap Iterators (and Failing)\r" +msgstr "" + +#: .\patterns/ffi/wrappers.md:81 +msgid "" +"To wrap any type of iterator into the API correctly, the wrapper would need " +"to\r\n" +"do what a C version of the code would do: erase the lifetime of the iterator," +"\r\n" +"and manage it manually." +msgstr "" + +#: .\patterns/ffi/wrappers.md:85 +msgid "Suffice it to say, this is *incredibly* difficult." +msgstr "" + +#: .\patterns/ffi/wrappers.md:87 +msgid "Here is an illustration of just *one* pitfall." +msgstr "" + +#: .\patterns/ffi/wrappers.md:89 +msgid "A first version of `MySetWrapper` would look like this:" +msgstr "" + +#: .\patterns/ffi/wrappers.md:91 +msgid "" +"```rust,ignore\r\n" +"struct MySetWrapper {\r\n" +" myset: MySet,\r\n" +" iter_next: usize,\r\n" +" // created from a transmuted Box\r\n" +" iterator: Option>>,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/wrappers.md:100 +msgid "" +"With `transmute` being used to extend a lifetime, and a pointer to hide it," +"\r\n" +"it's ugly already. But it gets even worse: *any other operation can cause\r\n" +"Rust `undefined behaviour`*." +msgstr "" + +#: .\patterns/ffi/wrappers.md:104 +msgid "" +"Consider that the `MySet` in the wrapper could be manipulated by other\r\n" +"functions during iteration, such as storing a new value to the key it was\r\n" +"iterating over. The API doesn't discourage this, and in fact some similar C" +"\r\n" +"libraries expect it." +msgstr "" + +#: .\patterns/ffi/wrappers.md:109 +msgid "A simple implementation of `myset_store` would be:" +msgstr "" + +#: .\patterns/ffi/wrappers.md:111 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" pub fn myset_store(\r\n" +" myset: *mut MySetWrapper,\r\n" +" key: datum,\r\n" +" value: datum) -> libc::c_int {\r\n" +"\r\n" +" // DO NOT USE THIS CODE. IT IS UNSAFE TO DEMONSTRATE A PROLBEM.\r\n" +"\r\n" +" let myset: &mut MySet = unsafe { // SAFETY: whoops, UB occurs in " +"here!\r\n" +" &mut (*myset).myset\r\n" +" };\r\n" +"\r\n" +" /* ...check and cast key and value data... */\r\n" +"\r\n" +" match myset.store(casted_key, casted_value) {\r\n" +" Ok(_) => 0,\r\n" +" Err(e) => e.into()\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/wrappers.md:137 +msgid "" +"If the iterator exists when this function is called, we have violated one of " +"Rust's\r\n" +"aliasing rules. According to Rust, the mutable reference in this block must " +"have\r\n" +"*exclusive* access to the object. If the iterator simply exists, it's not " +"exclusive,\r\n" +"so we have `undefined behaviour`! " +msgstr "" + +#: .\patterns/ffi/wrappers.md:142 +msgid "" +"To avoid this, we must have a way of ensuring that mutable reference really " +"is exclusive.\r\n" +"That basically means clearing out the iterator's shared reference while it " +"exists,\r\n" +"and then reconstructing it. In most cases, that will still be less efficient " +"than\r\n" +"the C version." +msgstr "" + +#: .\patterns/ffi/wrappers.md:147 +msgid "" +"Some may ask: how can C do this more efficiently?\r\n" +"The answer is, it cheats. Rust's aliasing rules are the problem, and C " +"simply ignores\r\n" +"them for its pointers. In exchange, it is common to see code that is declared" +"\r\n" +"in the manual as \"not thread safe\" under some or all circumstances. In " +"fact,\r\n" +"the [GNU C library](https://manpages.debian.org/buster/manpages/attributes.7." +"en.html)\r\n" +"has an entire lexicon dedicated to concurrent behavior!" +msgstr "" + +#: .\patterns/ffi/wrappers.md:154 +msgid "" +"Rust would rather make everything memory safe all the time, for both safety " +"and\r\n" +"optimizations that C code cannot attain. Being denied access to certain " +"shortcuts\r\n" +"is the price Rust programmers need to pay." +msgstr "" + +#: .\patterns/ffi/wrappers.md:158 +msgid "" +"For the C programmers out there scratching their heads, the iterator need\r\n" +" not be read *during* this code cause the UB. The exclusivity rule also " +"enables\r\n" +" compiler optimizations which may cause inconsistent observations by the " +"iterator's\r\n" +" shared reference (e.g. stack spills or reordering instructions for " +"efficiency).\r\n" +" These observations may happen *any time after* the mutable reference is " +"created." +msgstr "" + +#: .\anti_patterns/index.md:1 +msgid "# Anti-patterns\r" +msgstr "" + +#: .\anti_patterns/index.md:3 +msgid "" +"An [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) is a solution " +"to\r\n" +"a \"recurring problem that is usually ineffective and risks being highly\r\n" +"counterproductive\". Just as valuable as knowing how to solve a problem, is" +"\r\n" +"knowing how _not_ to solve it. Anti-patterns give us great counter-examples " +"to\r\n" +"consider relative to design patterns. Anti-patterns are not confined to code." +"\r\n" +"For example, a process can be an anti-pattern, too." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:1 +msgid "# Clone to satisfy the borrow checker\r" +msgstr "" + +#: .\anti_patterns/borrow_clone.md:5 +msgid "" +"The borrow checker prevents Rust users from developing otherwise unsafe code " +"by\r\n" +"ensuring that either: only one mutable reference exists, or potentially many " +"but\r\n" +"all immutable references exist. If the code written does not hold true to " +"these\r\n" +"conditions, this anti-pattern arises when the developer resolves the compiler" +"\r\n" +"error by cloning the variable." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:13 +msgid "" +"```rust\r\n" +"// define any variable\r\n" +"let mut x = 5;\r\n" +"\r\n" +"// Borrow `x` -- but clone it first\r\n" +"let y = &mut (x.clone());\r\n" +"\r\n" +"// without the x.clone() two lines prior, this line would fail on compile as" +"\r\n" +"// x has been borrowed\r\n" +"// thanks to x.clone(), x was never borrowed, and this line will run.\r\n" +"println!(\"{}\", x);\r\n" +"\r\n" +"// perform some action on the borrow to prevent rust from optimizing this\r\n" +"//out of existence\r\n" +"*y += 1;\r\n" +"```" +msgstr "" + +#: .\anti_patterns/borrow_clone.md:32 +msgid "" +"It is tempting, particularly for beginners, to use this pattern to resolve" +"\r\n" +"confusing issues with the borrow checker. However, there are serious\r\n" +"consequences. Using `.clone()` causes a copy of the data to be made. Any " +"changes\r\n" +"between the two are not synchronized -- as if two completely separate " +"variables\r\n" +"exist." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:38 +msgid "" +"There are special cases -- `Rc` is designed to handle clones " +"intelligently.\r\n" +"It internally manages exactly one copy of the data, and cloning it will only" +"\r\n" +"clone the reference." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:42 +msgid "" +"There is also `Arc` which provides shared ownership of a value of type T" +"\r\n" +"that is allocated in the heap. Invoking `.clone()` on `Arc` produces a new " +"`Arc`\r\n" +"instance, which points to the same allocation on the heap as the source " +"`Arc`,\r\n" +"while increasing a reference count." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:47 +msgid "" +"In general, clones should be deliberate, with full understanding of the\r\n" +"consequences. If a clone is used to make a borrow checker error disappear," +"\r\n" +"that's a good indication this anti-pattern may be in use." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:51 +msgid "" +"Even though `.clone()` is an indication of a bad pattern, sometimes\r\n" +"**it is fine to write inefficient code**, in cases such as when:" +msgstr "" + +#: .\anti_patterns/borrow_clone.md:54 +msgid "" +"- the developer is still new to ownership\r\n" +"- the code doesn't have great speed or memory constraints\r\n" +" (like hackathon projects or prototypes)\r\n" +"- satisfying the borrow checker is really complicated, and you prefer to\r\n" +" optimize readability over performance\r\n" +"\r" +msgstr "" + +#: .\anti_patterns/borrow_clone.md:60 +msgid "" +"If an unnecessary clone is suspected, The [Rust Book's chapter on Ownership]" +"(https://doc.rust-lang.org/book/ownership.html)\r\n" +"should be understood fully before assessing whether the clone is required or " +"not." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:63 +msgid "" +"Also be sure to always run `cargo clippy` in your project, which will detect " +"some\r\n" +"cases in which `.clone()` is not necessary, like [1](https://rust-lang." +"github.io/rust-clippy/master/index.html#redundant_clone),\r\n" +"[2](https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy)," +"\r\n" +"[3](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) or " +"[4](https://rust-lang.github.io/rust-clippy/master/index." +"html#clone_double_ref)." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:70 +msgid "" +"- [`mem::{take(_), replace(_)}` to keep owned values in changed enums](../" +"idioms/mem-replace.md)\r\n" +"- [`Rc` documentation, which handles .clone() intelligently](http://doc." +"rust-lang.org/std/rc/)\r\n" +"- [`Arc` documentation, a thread-safe reference-counting pointer](https://" +"doc.rust-lang.org/std/sync/struct.Arc.html)\r\n" +"- [Tricks with ownership in Rust](https://web.archive.org/web/20210120233744/" +"https://xion.io/post/code/rust-borrowchk-tricks.html)\r" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:1 +msgid "# `#![deny(warnings)]`\r" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:5 +msgid "" +"A well-intentioned crate author wants to ensure their code builds without\r\n" +"warnings. So they annotate their crate root with the following:" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:10 +msgid "" +"```rust\r\n" +"#![deny(warnings)]\r\n" +"\r\n" +"// All is well.\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:18 +msgid "It is short and will stop the build if anything is amiss." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:20 +msgid "## Drawbacks\r" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:22 +msgid "" +"By disallowing the compiler to build with warnings, a crate author opts out " +"of\r\n" +"Rust's famed stability. Sometimes new features or old misfeatures need a " +"change\r\n" +"in how things are done, thus lints are written that `warn` for a certain " +"grace\r\n" +"period before being turned to `deny`." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:27 +msgid "" +"For example, it was discovered that a type could have two `impl`s with the " +"same\r\n" +"method. This was deemed a bad idea, but in order to make the transition " +"smooth,\r\n" +"the `overlapping-inherent-impls` lint was introduced to give a warning to " +"those\r\n" +"stumbling on this fact, before it becomes a hard error in a future release." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:32 +msgid "" +"Also sometimes APIs get deprecated, so their use will emit a warning where" +"\r\n" +"before there was none." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:35 +msgid "" +"All this conspires to potentially break the build whenever something changes." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:37 +msgid "" +"Furthermore, crates that supply additional lints (e.g. [rust-clippy]) can no" +"\r\n" +"longer be used unless the annotation is removed. This is mitigated with\r\n" +"[--cap-lints]. The `--cap-lints=warn` command line argument, turns all `deny`" +"\r\n" +"lint errors into warnings." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:42 +#: .\functional/generics-type-classes.md:228 +msgid "## Alternatives\r" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:44 +msgid "" +"There are two ways of tackling this problem: First, we can decouple the build" +"\r\n" +"setting from the code, and second, we can name the lints we want to deny\r\n" +"explicitly." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:48 +msgid "The following command line will build with all warnings set to `deny`:" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:50 +msgid "```RUSTFLAGS=\"-D warnings\" cargo build```" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:52 +msgid "" +"This can be done by any individual developer (or be set in a CI tool like\r\n" +"Travis, but remember that this may break the build when something " +"changes)\r\n" +"without requiring a change to the code." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:56 +msgid "" +"Alternatively, we can specify the lints that we want to `deny` in the code." +"\r\n" +"Here is a list of warning lints that is (hopefully) safe to deny (as of " +"Rustc 1.48.0):" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:59 +msgid "" +"```rust,ignore\r\n" +"#![deny(bad_style,\r\n" +" const_err,\r\n" +" dead_code,\r\n" +" improper_ctypes,\r\n" +" non_shorthand_field_patterns,\r\n" +" no_mangle_generic_items,\r\n" +" overflowing_literals,\r\n" +" path_statements,\r\n" +" patterns_in_fns_without_body,\r\n" +" private_in_public,\r\n" +" unconditional_recursion,\r\n" +" unused,\r\n" +" unused_allocation,\r\n" +" unused_comparisons,\r\n" +" unused_parens,\r\n" +" while_true)]\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:78 +msgid "" +"In addition, the following `allow`ed lints may be a good idea to `deny`:" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:80 +msgid "" +"```rust,ignore\r\n" +"#![deny(missing_debug_implementations,\r\n" +" missing_docs,\r\n" +" trivial_casts,\r\n" +" trivial_numeric_casts,\r\n" +" unused_extern_crates,\r\n" +" unused_import_braces,\r\n" +" unused_qualifications,\r\n" +" unused_results)]\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:91 +msgid "Some may also want to add `missing-copy-implementations` to their list." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:93 +msgid "" +"Note that we explicitly did not add the `deprecated` lint, as it is fairly" +"\r\n" +"certain that there will be more deprecated APIs in the future." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:98 +msgid "" +"- [A collection of all clippy lints](https://rust-lang.github.io/rust-clippy/" +"master)\r\n" +"- [deprecate attribute] documentation\r\n" +"- Type `rustc -W help` for a list of lints on your system. Also type\r\n" +"`rustc --help` for a general list of options\r\n" +"- [rust-clippy] is a collection of lints for better Rust code\r\n" +"\r" +msgstr "" + +#: .\anti_patterns/deref.md:1 +msgid "# `Deref` polymorphism\r" +msgstr "" + +#: .\anti_patterns/deref.md:5 +msgid "" +"Misuse the `Deref` trait to emulate inheritance between structs, and thus " +"reuse\r\n" +"methods." +msgstr "" + +#: .\anti_patterns/deref.md:10 +msgid "" +"Sometimes we want to emulate the following common pattern from OO languages " +"such\r\n" +"as Java:" +msgstr "" + +#: .\anti_patterns/deref.md:13 +msgid "" +"```java\r\n" +"class Foo {\r\n" +" void m() { ... }\r\n" +"}\r\n" +"\r\n" +"class Bar extends Foo {}\r\n" +"\r\n" +"public static void main(String[] args) {\r\n" +" Bar b = new Bar();\r\n" +" b.m();\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deref.md:26 +msgid "We can use the deref polymorphism anti-pattern to do so:" +msgstr "" + +#: .\anti_patterns/deref.md:28 +msgid "" +"```rust\r\n" +"use std::ops::Deref;\r\n" +"\r\n" +"struct Foo {}\r\n" +"\r\n" +"impl Foo {\r\n" +" fn m(&self) {\r\n" +" //..\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"struct Bar {\r\n" +" f: Foo,\r\n" +"}\r\n" +"\r\n" +"impl Deref for Bar {\r\n" +" type Target = Foo;\r\n" +" fn deref(&self) -> &Foo {\r\n" +" &self.f\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let b = Bar { f: Foo {} };\r\n" +" b.m();\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deref.md:56 +msgid "" +"There is no struct inheritance in Rust. Instead we use composition and " +"include\r\n" +"an instance of `Foo` in `Bar` (since the field is a value, it is stored " +"inline,\r\n" +"so if there were fields, they would have the same layout in memory as the " +"Java\r\n" +"version (probably, you should use `#[repr(C)]` if you want to be sure))." +msgstr "" + +#: .\anti_patterns/deref.md:61 +msgid "" +"In order to make the method call work we implement `Deref` for `Bar` with " +"`Foo`\r\n" +"as the target (returning the embedded `Foo` field). That means that when we" +"\r\n" +"dereference a `Bar` (for example, using `*`) then we will get a `Foo`. That " +"is\r\n" +"pretty weird. Dereferencing usually gives a `T` from a reference to `T`, " +"here we\r\n" +"have two unrelated types. However, since the dot operator does implicit\r\n" +"dereferencing, it means that the method call will search for methods on " +"`Foo` as\r\n" +"well as `Bar`." +msgstr "" + +#: .\anti_patterns/deref.md:71 +msgid "You save a little boilerplate, e.g.," +msgstr "" + +#: .\anti_patterns/deref.md:73 +msgid "" +"```rust,ignore\r\n" +"impl Bar {\r\n" +" fn m(&self) {\r\n" +" self.f.m()\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deref.md:83 +msgid "" +"Most importantly this is a surprising idiom - future programmers reading " +"this in\r\n" +"code will not expect this to happen. That's because we are misusing the " +"`Deref`\r\n" +"trait rather than using it as intended (and documented, etc.). It's also " +"because\r\n" +"the mechanism here is completely implicit." +msgstr "" + +#: .\anti_patterns/deref.md:88 +msgid "" +"This pattern does not introduce subtyping between `Foo` and `Bar` like\r\n" +"inheritance in Java or C++ does. Furthermore, traits implemented by `Foo` are" +"\r\n" +"not automatically implemented for `Bar`, so this pattern interacts badly with" +"\r\n" +"bounds checking and thus generic programming." +msgstr "" + +#: .\anti_patterns/deref.md:93 +msgid "" +"Using this pattern gives subtly different semantics from most OO languages " +"with\r\n" +"regards to `self`. Usually it remains a reference to the sub-class, with this" +"\r\n" +"pattern it will be the 'class' where the method is defined." +msgstr "" + +#: .\anti_patterns/deref.md:97 +msgid "" +"Finally, this pattern only supports single inheritance, and has no notion of" +"\r\n" +"interfaces, class-based privacy, or other inheritance-related features. So, " +"it\r\n" +"gives an experience that will be subtly surprising to programmers used to " +"Java\r\n" +"inheritance, etc." +msgstr "" + +#: .\anti_patterns/deref.md:104 +msgid "" +"There is no one good alternative. Depending on the exact circumstances it " +"might\r\n" +"be better to re-implement using traits or to write out the facade methods to" +"\r\n" +"dispatch to `Foo` manually. We do intend to add a mechanism for inheritance" +"\r\n" +"similar to this to Rust, but it is likely to be some time before it reaches" +"\r\n" +"stable Rust. See these [blog](http://aturon.github.io/blog/2015/09/18/" +"reuse/)\r\n" +"[posts](http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-" +"structs-part-4-extended-enums-and-thin-traits/)\r\n" +"and this [RFC issue](https://github.com/rust-lang/rfcs/issues/349) for more " +"details." +msgstr "" + +#: .\anti_patterns/deref.md:112 +msgid "" +"The `Deref` trait is designed for the implementation of custom pointer types." +"\r\n" +"The intention is that it will take a pointer-to-`T` to a `T`, not convert\r\n" +"between different types. It is a shame that this isn't (probably cannot " +"be)\r\n" +"enforced by the trait definition." +msgstr "" + +#: .\anti_patterns/deref.md:117 +msgid "" +"Rust tries to strike a careful balance between explicit and implicit " +"mechanisms,\r\n" +"favouring explicit conversions between types. Automatic dereferencing in the " +"dot\r\n" +"operator is a case where the ergonomics strongly favour an implicit " +"mechanism,\r\n" +"but the intention is that this is limited to degrees of indirection, not\r\n" +"conversion between arbitrary types." +msgstr "" + +#: .\anti_patterns/deref.md:125 +msgid "" +"- [Collections are smart pointers idiom](../idioms/deref.md).\r\n" +"- Delegation crates for less boilerplate like [delegate](https://crates.io/" +"crates/delegate)\r\n" +" or [ambassador](https://crates.io/crates/ambassador)\r\n" +"- [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait." +"Deref.html).\r" +msgstr "" + +#: .\functional/index.md:1 +msgid "# Functional Usage of Rust\r" +msgstr "" + +#: .\functional/index.md:3 +msgid "" +"Rust is an imperative language, but it follows many\r\n" +"[functional programming](https://en.wikipedia.org/wiki/" +"Functional_programming) paradigms." +msgstr "" + +#: .\functional/index.md:6 +msgid "" +"> In computer science, *functional programming* is a programming paradigm " +"where\r\n" +"> programs are constructed by applying and composing functions.\r\n" +"> It is a declarative programming paradigm in which function definitions are" +"\r\n" +"> trees of expressions that each return a value, rather than a sequence of" +"\r\n" +"> imperative statements which change the state of the program.\r" +msgstr "" + +#: .\functional/paradigms.md:1 +msgid "# Programming paradigms\r" +msgstr "" + +#: .\functional/paradigms.md:3 +msgid "" +"One of the biggest hurdles to understanding functional programs when coming" +"\r\n" +"from an imperative background is the shift in thinking. Imperative programs" +"\r\n" +"describe __how__ to do something, whereas declarative programs describe\r\n" +"__what__ to do. Let's sum the numbers from 1 to 10 to show this." +msgstr "" + +#: .\functional/paradigms.md:8 +msgid "## Imperative\r" +msgstr "" + +#: .\functional/paradigms.md:10 +msgid "" +"```rust\r\n" +"let mut sum = 0;\r\n" +"for i in 1..11 {\r\n" +" sum += i;\r\n" +"}\r\n" +"println!(\"{}\", sum);\r\n" +"```" +msgstr "" + +#: .\functional/paradigms.md:18 +msgid "" +"With imperative programs, we have to play compiler to see what is happening." +"\r\n" +"Here, we start with a `sum` of `0`.\r\n" +"Next, we iterate through the range from 1 to 10.\r\n" +"Each time through the loop, we add the corresponding value in the range.\r\n" +"Then we print it out." +msgstr "" + +#: .\functional/paradigms.md:24 +msgid "" +"| `i` | `sum` |\r\n" +"|:---:|:-----:|\r\n" +"| 1 | 1 |\r\n" +"| 2 | 3 |\r\n" +"| 3 | 6 |\r\n" +"| 4 | 10 |\r\n" +"| 5 | 15 |\r\n" +"| 6 | 21 |\r\n" +"| 7 | 28 |\r\n" +"| 8 | 36 |\r\n" +"| 9 | 45 |\r\n" +"| 10 | 55 |\r" +msgstr "" + +#: .\functional/paradigms.md:37 +msgid "" +"This is how most of us start out programming. We learn that a program is a " +"set\r\n" +"of steps." +msgstr "" + +#: .\functional/paradigms.md:40 +msgid "## Declarative\r" +msgstr "" + +#: .\functional/paradigms.md:42 +msgid "" +"```rust\r\n" +"println!(\"{}\", (1..11).fold(0, |a, b| a + b));\r\n" +"```" +msgstr "" + +#: .\functional/paradigms.md:46 +msgid "" +"Whoa! This is really different! What's going on here?\r\n" +"Remember that with declarative programs we are describing __what__ to do,\r\n" +"rather than __how__ to do it. `fold` is a function that [composes](https://" +"en.wikipedia.org/wiki/Function_composition)\r\n" +"functions. The name is a convention from Haskell." +msgstr "" + +#: .\functional/paradigms.md:51 +msgid "" +"Here, we are composing functions of addition (this closure: `|a, b| a + " +"b`)\r\n" +"with a range from 1 to 10. The `0` is the starting point, so `a` is `0` at" +"\r\n" +"first. `b` is the first element of the range, `1`. `0 + 1 = 1` is the result." +"\r\n" +"So now we `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the next" +"\r\n" +"result. This process continues until we get to the last element in the range," +"\r\n" +"`10`." +msgstr "" + +#: .\functional/paradigms.md:58 +msgid "" +"| `a` | `b` | result |\r\n" +"|:---:|:---:|:------:|\r\n" +"| 0 | 1 | 1 |\r\n" +"| 1 | 2 | 3 |\r\n" +"| 3 | 3 | 6 |\r\n" +"| 6 | 4 | 10 |\r\n" +"| 10 | 5 | 15 |\r\n" +"| 15 | 6 | 21 |\r\n" +"| 21 | 7 | 28 |\r\n" +"| 28 | 8 | 36 |\r\n" +"| 36 | 9 | 45 |\r\n" +"| 45 | 10 | 55 |\r" +msgstr "" + +#: .\functional/generics-type-classes.md:1 +msgid "# Generics as Type Classes\r" +msgstr "" + +#: .\functional/generics-type-classes.md:5 +msgid "" +"Rust's type system is designed more like functional languages (like " +"Haskell)\r\n" +"rather than imperative languages (like Java and C++). As a result, Rust can " +"turn\r\n" +"many kinds of programming problems into \"static typing\" problems. This is " +"one\r\n" +"of the biggest wins of choosing a functional language, and is critical to " +"many\r\n" +"of Rust's compile time guarantees." +msgstr "" + +#: .\functional/generics-type-classes.md:11 +msgid "" +"A key part of this idea is the way generic types work. In C++ and Java, for" +"\r\n" +"example, generic types are a meta-programming construct for the compiler.\r\n" +"`vector` and `vector` in C++ are just two different copies of the" +"\r\n" +"same boilerplate code for a `vector` type (known as a `template`) with two" +"\r\n" +"different types filled in." +msgstr "" + +#: .\functional/generics-type-classes.md:17 +msgid "" +"In Rust, a generic type parameter creates what is known in functional " +"languages\r\n" +"as a \"type class constraint\", and each different parameter filled in by an " +"end\r\n" +"user *actually changes the type*. In other words, `Vec` and " +"`Vec`\r\n" +"*are two different types*, which are recognized as distinct by all parts of " +"the\r\n" +"type system." +msgstr "" + +#: .\functional/generics-type-classes.md:23 +msgid "" +"This is called **monomorphization**, where different types are created from" +"\r\n" +"**polymorphic** code. This special behavior requires `impl` blocks to " +"specify\r\n" +"generic parameters. Different values for the generic type cause different " +"types,\r\n" +"and different types can have different `impl` blocks." +msgstr "" + +#: .\functional/generics-type-classes.md:28 +msgid "" +"In object-oriented languages, classes can inherit behavior from their " +"parents.\r\n" +"However, this allows the attachment of not only additional behavior to\r\n" +"particular members of a type class, but extra behavior as well." +msgstr "" + +#: .\functional/generics-type-classes.md:32 +msgid "" +"The nearest equivalent is the runtime polymorphism in Javascript and Python," +"\r\n" +"where new members can be added to objects willy-nilly by any constructor.\r\n" +"However, unlike those languages, all of Rust's additional methods can be type" +"\r\n" +"checked when they are used, because their generics are statically defined. " +"That\r\n" +"makes them more usable while remaining safe." +msgstr "" + +#: .\functional/generics-type-classes.md:40 +msgid "" +"Suppose you are designing a storage server for a series of lab machines.\r\n" +"Because of the software involved, there are two different protocols you need" +"\r\n" +"to support: BOOTP (for PXE network boot), and NFS (for remote mount storage)." +msgstr "" + +#: .\functional/generics-type-classes.md:44 +msgid "" +"Your goal is to have one program, written in Rust, which can handle both of" +"\r\n" +"them. It will have protocol handlers and listen for both kinds of requests. " +"The\r\n" +"main application logic will then allow a lab administrator to configure " +"storage\r\n" +"and security controls for the actual files." +msgstr "" + +#: .\functional/generics-type-classes.md:49 +msgid "" +"The requests from machines in the lab for files contain the same basic\r\n" +"information, no matter what protocol they came from: an authentication " +"method,\r\n" +"and a file name to retrieve. A straightforward implementation would look\r\n" +"something like this:" +msgstr "" + +#: .\functional/generics-type-classes.md:54 +msgid "" +"```rust,ignore\r\n" +"\r\n" +"enum AuthInfo {\r\n" +" Nfs(crate::nfs::AuthInfo),\r\n" +" Bootp(crate::bootp::AuthInfo),\r\n" +"}\r\n" +"\r\n" +"struct FileDownloadRequest {\r\n" +" file_name: PathBuf,\r\n" +" authentication: AuthInfo,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/generics-type-classes.md:67 +msgid "" +"This design might work well enough. But now suppose you needed to support\r\n" +"adding metadata that was *protocol specific*. For example, with NFS, you\r\n" +"wanted to determine what their mount point was in order to enforce additional" +"\r\n" +"security rules." +msgstr "" + +#: .\functional/generics-type-classes.md:72 +msgid "" +"The way the current struct is designed leaves the protocol decision until\r\n" +"runtime. That means any method that applies to one protocol and not the other" +"\r\n" +"requires the programmer to do a runtime check." +msgstr "" + +#: .\functional/generics-type-classes.md:76 +msgid "Here is how getting an NFS mount point would look:" +msgstr "" + +#: .\functional/generics-type-classes.md:78 +msgid "" +"```rust,ignore\r\n" +"struct FileDownloadRequest {\r\n" +" file_name: PathBuf,\r\n" +" authentication: AuthInfo,\r\n" +" mount_point: Option,\r\n" +"}\r\n" +"\r\n" +"impl FileDownloadRequest {\r\n" +" // ... other methods ...\r\n" +"\r\n" +" /// Gets an NFS mount point if this is an NFS request. Otherwise,\r\n" +" /// return None.\r\n" +" pub fn mount_point(&self) -> Option<&Path> {\r\n" +" self.mount_point.as_ref()\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/generics-type-classes.md:96 +msgid "" +"Every caller of `mount_point()` must check for `None` and write code to " +"handle\r\n" +"it. This is true even if they know only NFS requests are ever used in a given" +"\r\n" +"code path!" +msgstr "" + +#: .\functional/generics-type-classes.md:100 +msgid "" +"It would be far more optimal to cause a compile-time error if the different" +"\r\n" +"request types were confused. After all, the entire path of the user's code," +"\r\n" +"including what functions from the library they use, will know whether a " +"request\r\n" +"is an NFS request or a BOOTP request." +msgstr "" + +#: .\functional/generics-type-classes.md:105 +msgid "" +"In Rust, this is actually possible! The solution is to *add a generic type* " +"in\r\n" +"order to split the API." +msgstr "" + +#: .\functional/generics-type-classes.md:108 +msgid "Here is what that looks like:" +msgstr "" + +#: .\functional/generics-type-classes.md:110 +msgid "" +"```rust\r\n" +"use std::path::{Path, PathBuf};\r\n" +"\r\n" +"mod nfs {\r\n" +" #[derive(Clone)]\r\n" +" pub(crate) struct AuthInfo(String); // NFS session management omitted\r\n" +"}\r\n" +"\r\n" +"mod bootp {\r\n" +" pub(crate) struct AuthInfo(); // no authentication in bootp\r\n" +"}\r\n" +"\r\n" +"// private module, lest outside users invent their own protocol kinds!\r\n" +"mod proto_trait {\r\n" +" use std::path::{Path, PathBuf};\r\n" +" use super::{bootp, nfs};\r\n" +"\r\n" +" pub(crate) trait ProtoKind {\r\n" +" type AuthInfo;\r\n" +" fn auth_info(&self) -> Self::AuthInfo;\r\n" +" }\r\n" +"\r\n" +" pub struct Nfs {\r\n" +" auth: nfs::AuthInfo,\r\n" +" mount_point: PathBuf,\r\n" +" }\r\n" +"\r\n" +" impl Nfs {\r\n" +" pub(crate) fn mount_point(&self) -> &Path {\r\n" +" &self.mount_point\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" impl ProtoKind for Nfs {\r\n" +" type AuthInfo = nfs::AuthInfo;\r\n" +" fn auth_info(&self) -> Self::AuthInfo {\r\n" +" self.auth.clone()\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" pub struct Bootp(); // no additional metadata\r\n" +"\r\n" +" impl ProtoKind for Bootp {\r\n" +" type AuthInfo = bootp::AuthInfo;\r\n" +" fn auth_info(&self) -> Self::AuthInfo {\r\n" +" bootp::AuthInfo()\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"use proto_trait::ProtoKind; // keep internal to prevent impls\r\n" +"pub use proto_trait::{Nfs, Bootp}; // re-export so callers can see them\r\n" +"\r\n" +"struct FileDownloadRequest {\r\n" +" file_name: PathBuf,\r\n" +" protocol: P,\r\n" +"}\r\n" +"\r\n" +"// all common API parts go into a generic impl block\r\n" +"impl FileDownloadRequest

{\r\n" +" fn file_path(&self) -> &Path {\r\n" +" &self.file_name\r\n" +" }\r\n" +"\r\n" +" fn auth_info(&self) -> P::AuthInfo {\r\n" +" self.protocol.auth_info()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// all protocol-specific impls go into their own block\r\n" +"impl FileDownloadRequest {\r\n" +" fn mount_point(&self) -> &Path {\r\n" +" self.protocol.mount_point()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" // your code here\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/generics-type-classes.md:191 +msgid "" +"With this approach, if the user were to make a mistake and use the wrong\r\n" +"type;" +msgstr "" + +#: .\functional/generics-type-classes.md:194 +msgid "" +"```rust,ignore\r\n" +"fn main() {\r\n" +" let mut socket = crate::bootp::listen()?;\r\n" +" while let Some(request) = socket.next_request()? {\r\n" +" match request.mount_point().as_ref()\r\n" +" \"/secure\" => socket.send(\"Access denied\"),\r\n" +" _ => {} // continue on...\r\n" +" }\r\n" +" // Rest of the code here\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/generics-type-classes.md:207 +msgid "" +"They would get a syntax error. The type `FileDownloadRequest` does not" +"\r\n" +"implement `mount_point()`, only the type `FileDownloadRequest` does. And" +"\r\n" +"that is created by the NFS module, not the BOOTP module of course!" +msgstr "" + +#: .\functional/generics-type-classes.md:213 +msgid "" +"First, it allows fields that are common to multiple states to be de-" +"duplicated.\r\n" +"By making the non-shared fields generic, they are implemented once." +msgstr "" + +#: .\functional/generics-type-classes.md:216 +msgid "" +"Second, it makes the `impl` blocks easier to read, because they are broken " +"down\r\n" +"by state. Methods common to all states are typed once in one block, and " +"methods\r\n" +"unique to one state are in a separate block." +msgstr "" + +#: .\functional/generics-type-classes.md:220 +msgid "" +"Both of these mean there are fewer lines of code, and they are better " +"organized." +msgstr "" + +#: .\functional/generics-type-classes.md:224 +msgid "" +"This currently increases the size of the binary, due to the way " +"monomorphization\r\n" +"is implemented in the compiler. Hopefully the implementation will be able to" +"\r\n" +"improve in the future." +msgstr "" + +#: .\functional/generics-type-classes.md:230 +msgid "" +"* If a type seems to need a \"split API\" due to construction or partial\r\n" +"initialization, consider the\r\n" +"[Builder Pattern](../patterns/creational/builder.md) instead.\r\n" +"\r\n" +"* If the API between types does not change -- only the behavior does -- then" +"\r\n" +"the [Strategy Pattern](../patterns/behavioural/strategy.md) is better used" +"\r\n" +"instead.\r\n" +"\r" +msgstr "" + +#: .\functional/generics-type-classes.md:240 +msgid "This pattern is used throughout the standard library:" +msgstr "" + +#: .\functional/generics-type-classes.md:242 +msgid "" +"* `Vec` can be cast from a String, unlike every other type of `Vec`." +"[^1]\r\n" +"* They can also be cast into a binary heap, but only if they contain a type" +"\r\n" +" that implements the `Ord` trait.[^2]\r\n" +"* The `to_string` method was specialized for `Cow` only of type `str`." +"[^3]\r\n" +"\r" +msgstr "" + +#: .\functional/generics-type-classes.md:247 +msgid "It is also used by several popular crates to allow API flexibility:" +msgstr "" + +#: .\functional/generics-type-classes.md:249 +msgid "" +"* The `embedded-hal` ecosystem used for embedded devices makes extensive use " +"of\r\n" +" this pattern. For example, it allows statically verifying the " +"configuration of\r\n" +" device registers used to control embedded pins. When a pin is put into a " +"mode,\r\n" +" it returns a `Pin` struct, whose generic determines the functions\r\n" +" usable in that mode, which are not on the `Pin` itself. [^4]\r\n" +"\r\n" +"* The `hyper` HTTP client library uses this to expose rich APIs for different" +"\r\n" +" pluggable requests. Clients with different connectors have different " +"methods\r\n" +" on them as well as different trait implementations, while a core set of\r\n" +" methods apply to any connector. [^5]\r\n" +"\r\n" +"* The \"type state\" pattern -- where an object gains and loses API based on " +"an\r\n" +" internal state or invariant -- is implemented in Rust using the same basic" +"\r\n" +" concept, and a slightly different technique. [^6]\r\n" +"\r" +msgstr "" + +#: .\functional/generics-type-classes.md:264 +msgid "" +"See: [impl From\\ for Vec\\](\r\n" +"https://doc.rust-lang.org/1.59.0/src/std/ffi/c_str.rs.html#803-811)" +msgstr "" + +#: .\functional/generics-type-classes.md:267 +msgid "" +"See: [impl\\ From\\\\> for BinaryHeap\\](\r\n" +"https://doc.rust-lang.org/stable/src/alloc/collections/binary_heap.rs." +"html#1345-1354)" +msgstr "" + +#: .\functional/generics-type-classes.md:270 +msgid "" +"See: [impl\\<'\\_\\> ToString for Cow\\<'\\_, str>](\r\n" +"https://doc.rust-lang.org/stable/src/alloc/string.rs.html#2235-2240)" +msgstr "" + +#: .\functional/generics-type-classes.md:273 +msgid "" +"Example:\r\n" +"[https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0." +"html](\r\n" +"https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html)" +msgstr "" + +#: .\functional/generics-type-classes.md:277 +msgid "" +"See:\r\n" +"[https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html](\r\n" +"https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html)" +msgstr "" + +#: .\functional/generics-type-classes.md:281 +msgid "" +"See:\r\n" +"[The Case for the Type State Pattern](\r\n" +"https://web.archive.org/web/20210325065112/https://www.novatec-gmbh.de/en/" +"blog/the-case-for-the-typestate-pattern-the-typestate-pattern-itself/)\r\n" +"and\r\n" +"[Rusty Typestate Series (an extensive thesis)](\r\n" +"https://web.archive.org/web/20210328164854/https://rustype.github.io/notes/" +"notes/rust-typestate-series/rust-typestate-index)" +msgstr "" + +#: .\functional/lenses.md:1 +msgid "# Lenses and Prisms\r" +msgstr "" + +#: .\functional/lenses.md:3 +msgid "" +"This is a pure functional concept that is not frequently used in Rust.\r\n" +"Nevertheless, exploring the concept may be helpful to understand other\r\n" +"patterns in Rust APIs, such as [visitors](../patterns/behavioural/visitor." +"md).\r\n" +"They also have niche use cases." +msgstr "" + +#: .\functional/lenses.md:8 +msgid "## Lenses: Uniform Access Across Types\r" +msgstr "" + +#: .\functional/lenses.md:10 +msgid "" +"A lens is a concept from functional programming languages that allows\r\n" +"accessing parts of a data type in an abstract, unified way.[^1]\r\n" +"In basic concept, it is similar to the way Rust traits work with type " +"erasure,\r\n" +"but it has a bit more power and flexibility." +msgstr "" + +#: .\functional/lenses.md:15 +msgid "" +"For example, suppose a bank contains several JSON formats for customer\r\n" +"data.\r\n" +"This is because they come from different databases or legacy systems.\r\n" +"One database contains the data needed to perform credit checks:" +msgstr "" + +#: .\functional/lenses.md:20 +msgid "" +"```json\r\n" +"{ \"name\": \"Jane Doe\",\r\n" +" \"dob\": \"2002-02-24\",\r\n" +" [...]\r\n" +" \"customer_id\": 1048576332,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:28 +msgid "Another one contains the account information:" +msgstr "" + +#: .\functional/lenses.md:30 +msgid "" +"```json\r\n" +"{ \"customer_id\": 1048576332,\r\n" +" \"accounts\": [\r\n" +" { \"account_id\": 2121,\r\n" +" \"account_type: \"savings\",\r\n" +" \"joint_customer_ids\": [],\r\n" +" [...]\r\n" +" },\r\n" +" { \"account_id\": 2122,\r\n" +" \"account_type: \"checking\",\r\n" +" \"joint_customer_ids\": [1048576333],\r\n" +" [...]\r\n" +" },\r\n" +" ]\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:47 +msgid "" +"Notice that both types have a customer ID number which corresponds to a " +"person.\r\n" +"How would a single function handle both records of different types?" +msgstr "" + +#: .\functional/lenses.md:50 +msgid "" +"In Rust, a `struct` could represent each of these types, and a trait would " +"have\r\n" +"a `get_customer_id` function they would implement:" +msgstr "" + +#: .\functional/lenses.md:53 +msgid "" +"```rust\r\n" +"use std::collections::HashSet;\r\n" +"\r\n" +"pub struct Account {\r\n" +" account_id: u32,\r\n" +" account_type: String,\r\n" +" // other fields omitted\r\n" +"}\r\n" +"\r\n" +"pub trait CustomerId {\r\n" +" fn get_customer_id(&self) -> u64;\r\n" +"}\r\n" +"\r\n" +"pub struct CreditRecord {\r\n" +" customer_id: u64,\r\n" +" name: String,\r\n" +" dob: String,\r\n" +" // other fields omitted\r\n" +"}\r\n" +"\r\n" +"impl CustomerId for CreditRecord {\r\n" +" fn get_customer_id(&self) -> u64 {\r\n" +" self.customer_id\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub struct AccountRecord {\r\n" +" customer_id: u64,\r\n" +" accounts: Vec,\r\n" +"}\r\n" +"\r\n" +"impl CustomerId for AccountRecord {\r\n" +" fn get_customer_id(&self) -> u64 {\r\n" +" self.customer_id\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// static polymorphism: only one type, but each function call can choose it" +"\r\n" +"fn unique_ids_set(records: &[R]) -> HashSet {\r\n" +" records.iter().map(|r| r.get_customer_id()).collect()\r\n" +"}\r\n" +"\r\n" +"// dynamic dispatch: iterates over any type with a customer ID, collecting " +"all\r\n" +"// values together\r\n" +"fn unique_ids_iter(iterator: I) -> HashSet\r\n" +" where I: Iterator>\r\n" +"{\r\n" +" iterator.map(|r| r.as_ref().get_customer_id()).collect()\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:104 +msgid "" +"Lenses, however, allow the code supporting customer ID to be moved from the" +"\r\n" +"*type* to the *accessor function*.\r\n" +"Rather than implementing a trait on each type, all matching structures can" +"\r\n" +"simply be accessed the same way." +msgstr "" + +#: .\functional/lenses.md:109 +msgid "" +"While the Rust language itself does not support this (type erasure is the\r\n" +"preferred solution to this problem), the [lens-rs\r\n" +"crate](https://github.com/TOETOE55/lens-rs/blob/master/guide.md) allows code" +"\r\n" +"that feels like this to be written with macros:" +msgstr "" + +#: .\functional/lenses.md:114 +msgid "" +"```rust,ignore\r\n" +"use std::collections::HashSet;\r\n" +"\r\n" +"use lens_rs::{optics, Lens, LensRef, Optics};\r\n" +"\r\n" +"#[derive(Clone, Debug, Lens /* derive to allow lenses to work */)]\r\n" +"pub struct CreditRecord {\r\n" +" #[optic(ref)] // macro attribute to allow viewing this field\r\n" +" customer_id: u64,\r\n" +" name: String,\r\n" +" dob: String,\r\n" +" // other fields omitted\r\n" +"}\r\n" +"\r\n" +"#[derive(Clone, Debug)]\r\n" +"pub struct Account {\r\n" +" account_id: u32,\r\n" +" account_type: String,\r\n" +" // other fields omitted\r\n" +"}\r\n" +"\r\n" +"#[derive(Clone, Debug, Lens)]\r\n" +"pub struct AccountRecord {\r\n" +" #[optic(ref)]\r\n" +" customer_id: u64,\r\n" +" accounts: Vec,\r\n" +"}\r\n" +"\r\n" +"fn unique_ids_lens(iter: impl Iterator) -> HashSet\r\n" +"where\r\n" +" T: LensRef, // any type with this field\r\n" +"{\r\n" +" iter.map(|r| *r.view_ref(optics!(customer_id))).collect()\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:150 +msgid "" +"The version of `unique_ids_lens` shown here allows any type to be in the " +"iterator,\r\n" +"so long as it has an attribute called `customer_id` which can be accessed by" +"\r\n" +"the function.\r\n" +"This is how most functional programming languages operate on lenses." +msgstr "" + +#: .\functional/lenses.md:155 +msgid "" +"Rather than macros, they achieve this with a technique known as \"currying\"." +"\r\n" +"That is, they \"partially construct\" the function, leaving the type of the" +"\r\n" +"final parameter (the value being operated on) unfilled until the function is" +"\r\n" +"called.\r\n" +"Thus it can be called with different types dynamically even from one place in" +"\r\n" +"the code.\r\n" +"That is what the `optics!` and `view_ref` in the example above simulates." +msgstr "" + +#: .\functional/lenses.md:163 +msgid "" +"The functional approach need not be restricted to accessing members.\r\n" +"More powerful lenses can be created which both *set* and *get* data in a\r\n" +"structure.\r\n" +"But the concept really becomes interesting when used as a building block for" +"\r\n" +"composition.\r\n" +"That is where the concept appears more clearly in Rust." +msgstr "" + +#: .\functional/lenses.md:170 +msgid "## Prisms: A Higher-Order form of \"Optics\"\r" +msgstr "" + +#: .\functional/lenses.md:172 +msgid "" +"A simple function such as `unique_ids_lens` above operates on a single lens." +"\r\n" +"A *prism* is a function that operates on a *family* of lenses.\r\n" +"It is one conceptual level higher, using lenses as a building block, and\r\n" +"continuing the metaphor, is part of a family of \"optics\".\r\n" +"It is the main one that is useful in understanding Rust APIs, so will be the" +"\r\n" +"focus here." +msgstr "" + +#: .\functional/lenses.md:179 +msgid "" +"The same way that traits allow \"lens-like\" design with static polymorphism " +"and\r\n" +"dynamic dispatch, prism-like designs appear in Rust APIs which split problems" +"\r\n" +"into multiple associated types to be composed.\r\n" +"A good example of this is the traits in the parsing crate *Serde*." +msgstr "" + +#: .\functional/lenses.md:184 +msgid "" +"Trying to understand the way *Serde* works by only reading the API is a\r\n" +"challenge, especially the first time.\r\n" +"Consider the `Deserializer` trait, implemented by some type in any library" +"\r\n" +"which parses a new format:" +msgstr "" + +#: .\functional/lenses.md:189 +msgid "" +"```rust,ignore\r\n" +"pub trait Deserializer<'de>: Sized {\r\n" +" type Error: Error;\r\n" +"\r\n" +" fn deserialize_any(self, visitor: V) -> Result" +"\r\n" +" where\r\n" +" V: Visitor<'de>;\r\n" +"\r\n" +" fn deserialize_bool(self, visitor: V) -> Result" +"\r\n" +" where\r\n" +" V: Visitor<'de>;\r\n" +"\r\n" +" // remainder ommitted\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:205 +msgid "" +"For a trait that is just supposed to parse data from a format and return a" +"\r\n" +"value, this looks odd." +msgstr "" + +#: .\functional/lenses.md:208 +msgid "Why are all the return types type erased?" +msgstr "" + +#: .\functional/lenses.md:210 +msgid "" +"To understand that, we need to keep the lens concept in mind and look at\r\n" +"the definition of the `Visitor` type that is passed in generically:" +msgstr "" + +#: .\functional/lenses.md:213 +msgid "" +"```rust,ignore\r\n" +"pub trait Visitor<'de>: Sized {\r\n" +" type Value;\r\n" +"\r\n" +" fn visit_bool(self, v: bool) -> Result\r\n" +" where\r\n" +" E: Error;\r\n" +"\r\n" +" fn visit_u64(self, v: u64) -> Result\r\n" +" where\r\n" +" E: Error;\r\n" +"\r\n" +" fn visit_str(self, v: &str) -> Result\r\n" +" where\r\n" +" E: Error;\r\n" +"\r\n" +" // remainder omitted\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:233 +msgid "" +"The job of the `Visitor` type is to construct values in the *Serde* data " +"model,\r\n" +"which are represented by its associated `Value` type." +msgstr "" + +#: .\functional/lenses.md:236 +msgid "" +"These values represent parts of the Rust value being deserialized.\r\n" +"If this fails, it returns an `Error` type - an error type determined by the" +"\r\n" +"`Deserializer` when its methods were called." +msgstr "" + +#: .\functional/lenses.md:240 +msgid "" +"This highlights that `Deserializer` is similar to `CustomerId` from earlier," +"\r\n" +"allowing any format parser which implements it to create `Value`s based on " +"what\r\n" +"it parsed.\r\n" +"The `Value` trait is acting like a lens in functional programming languages." +msgstr "" + +#: .\functional/lenses.md:245 +msgid "" +"But unlike the `CustomerId` trait, the return types of `Visitor` methods are" +"\r\n" +"*generic*, and the concrete `Value` type is *determined by the Visitor " +"itself*." +msgstr "" + +#: .\functional/lenses.md:248 +msgid "" +"Instead of acting as one lens, it effectively acts as a family of\r\n" +"lenses, one for each concrete type of `Visitor`." +msgstr "" + +#: .\functional/lenses.md:251 +msgid "" +"The `Deserializer` API is based on having a generic set of \"lenses\" work " +"across\r\n" +"a set of other generic types for \"observation\".\r\n" +"It is a *prism*." +msgstr "" + +#: .\functional/lenses.md:255 +msgid "For example, consider the identity record from earlier but simplified:" +msgstr "" + +#: .\functional/lenses.md:257 +msgid "" +"```json\r\n" +"{ \"name\": \"Jane Doe\",\r\n" +" \"customer_id\": 1048576332,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:263 +msgid "" +"How would the *Serde* library deserialize this JSON into `struct " +"CreditRecord`?" +msgstr "" + +#: .\functional/lenses.md:265 +msgid "" +"1. The user would call a library function to deserialize the data. This would" +"\r\n" +" create a `Deserializer` based on the JSON format.\r\n" +"1. Based on the fields in the struct, a `Visitor` would be created (more on" +"\r\n" +" that in a moment) which knows how to create each type in a generic data" +"\r\n" +" model that was needed to represent it: `u64` and `String`.\r\n" +"1. The deserializer would make calls to the `Visitor` as it parsed items.\r\n" +"1. The `Visitor` would indicate if the items found were expected, and if not," +"\r\n" +" raise an error to indicate deserialization has failed.\r\n" +"\r" +msgstr "" + +#: .\functional/lenses.md:274 +msgid "For our very simple structure above, the expected pattern would be:" +msgstr "" + +#: .\functional/lenses.md:276 +msgid "" +"1. Visit a map (*Serde*'s equvialent to `HashMap` or JSON's dictionary).\r\n" +"1. Visit a string key called \"name\".\r\n" +"1. Visit a string value, which will go into the `name` field.\r\n" +"1. Visit a string key called \"customer_id\".\r\n" +"1. Visit a string value, which will go into the `customer_id` field.\r\n" +"1. Visit the end of the map.\r\n" +"\r" +msgstr "" + +#: .\functional/lenses.md:283 +msgid "But what determines which \"observation\" pattern is expected?" +msgstr "" + +#: .\functional/lenses.md:285 +msgid "" +"A functional programming language would be able to use currying to create\r\n" +"reflection of each type based on the type itself.\r\n" +"Rust does not support that, so every single type would need to have its own" +"\r\n" +"code written based on its fields and their properties." +msgstr "" + +#: .\functional/lenses.md:290 +msgid "*Serde* solves this usability challenge with a derive macro:" +msgstr "" + +#: .\functional/lenses.md:292 +msgid "" +"```rust,ignore\r\n" +"use serde::Deserialize;\r\n" +"\r\n" +"#[derive(Deserialize)]\r\n" +"struct IdRecord {\r\n" +" name: String,\r\n" +" customer_id: String,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:302 +msgid "" +"That macro simply generates an impl block causing the struct to implement a" +"\r\n" +"trait called `Deserialize`." +msgstr "" + +#: .\functional/lenses.md:305 +msgid "It is defined this way:" +msgstr "" + +#: .\functional/lenses.md:307 +msgid "" +"```rust,ignore\r\n" +"pub trait Deserialize<'de>: Sized {\r\n" +" fn deserialize(deserializer: D) -> Result\r\n" +" where\r\n" +" D: Deserializer<'de>;\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:315 +msgid "" +"This is the function that determines how to create the struct itself.\r\n" +"Code is generated based on the struct's fields.\r\n" +"When the parsing library is called - in our example, a JSON parsing library -" +"\r\n" +"it creates a `Deserializer` and calls `Type::deserialize` with it as a\r\n" +"parameter." +msgstr "" + +#: .\functional/lenses.md:321 +msgid "" +"The `deserialize` code will then create a `Visitor` which will have its calls" +"\r\n" +"\"refracted\" by the `Deserializer`.\r\n" +"If everything goes well, eventually that `Visitor` will construct a value\r\n" +"corresponding to the type being parsed and return it." +msgstr "" + +#: .\functional/lenses.md:326 +msgid "" +"For a complete example, see the [*Serde*\r\n" +"documentation](https://serde.rs/deserialize-struct.html)." +msgstr "" + +#: .\functional/lenses.md:329 +msgid "To wrap up, this is the power of *Serde*:" +msgstr "" + +#: .\functional/lenses.md:331 +msgid "" +"1. The structure being parsed is represented by an `impl` block for " +"`Deserialize`\r\n" +"1. The input data format (e.g. JSON) is represented by a `Deserializer` " +"called\r\n" +" by `Deserialize`\r\n" +"1. The `Deserializer` acts like a prism which \"refracts\" lens-like " +"`Visitor`\r\n" +" calls which actually build the data value\r\n" +"\r" +msgstr "" + +#: .\functional/lenses.md:337 +msgid "" +"The result is that types to be deserialized only implement the \"top layer\" " +"of\r\n" +"the API, and file formats only need to implement the \"bottom layer\".\r\n" +"Each piece can then \"just work\" with the rest of the ecosystem, since " +"generic\r\n" +"types will bridge them." +msgstr "" + +#: .\functional/lenses.md:342 +msgid "" +"To emphasize, the only reason this model works on any format and any type is" +"\r\n" +"because the `Deserializer` trait's output type **is specified by the\r\n" +"implementor of `Visitor` it is passed**, rather than being tied to one " +"specific\r\n" +"type.\r\n" +"This was not true in the account example earlier." +msgstr "" + +#: .\functional/lenses.md:348 +msgid "" +"Rust's generic-inspired type system can bring it close to these concepts and" +"\r\n" +"use their power, as shown in this API design.\r\n" +"But it may also need procedural macros to create bridges for its generics." +msgstr "" + +#: .\functional/lenses.md:352 +msgid "## See Also\r" +msgstr "" + +#: .\functional/lenses.md:354 +msgid "" +"- [lens-rs crate](https://crates.io/crates/lens-rs) for a pre-built lenses" +"\r\n" +" implementation, with a cleaner interface than these examples\r\n" +"- [serde](https://serde.rs) itself, which makes these concepts intuitive for" +"\r\n" +" end users (i.e. defining the structs) without needing to undestand the\r\n" +" details\r\n" +"- [luminance](https://github.com/phaazon/luminance-rs) is a crate for drawing" +"\r\n" +" computer graphics that uses lens API design, including proceducal macros to" +"\r\n" +" create full prisms for buffers of different pixel types that remain generic" +"\r\n" +"- [An Article about Lenses in\r\n" +" Scala](https://web.archive.org/web/20221128185849/https://medium.com/" +"zyseme-technology/functional-references-lens-and-other-optics-in-scala-" +"e5f7e2fdafe)\r\n" +" that is very readable even without Scala expertise.\r\n" +"- [Paper: Profunctor Optics: Modular Data\r\n" +" Accessors](https://web.archive.org/web/20220701102832/https://arxiv.org/" +"ftp/arxiv/papers/1703/1703.10857.pdf)\r\n" +"\r" +msgstr "" + +#: .\functional/lenses.md:368 +msgid "" +"[School of Haskell: A Little Lens Starter Tutorial](https://web.archive.org/" +"web/20221128190041/https://www.schoolofhaskell.com/school/to-infinity-and-" +"beyond/pick-of-the-week/a-little-lens-starter-tutorial)" +msgstr "" + +#: .\additional_resources/index.md:1 +msgid "# Additional resources\r" +msgstr "" + +#: .\additional_resources/index.md:3 +msgid "A collection of complementary helpful content" +msgstr "" + +#: .\additional_resources/index.md:5 +msgid "## Talks\r" +msgstr "" + +#: .\additional_resources/index.md:7 +msgid "" +"- [Design Patterns in Rust](https://www.youtube.com/watch?v=Pm_oO0N5B9k) by" +"\r\n" +" Nicholas Cameron at the PDRust (2016)\r\n" +"- [Writing Idiomatic Libraries in Rust](https://www.youtube.com/watch?" +"v=0zOg8_B71gE)\r\n" +" by Pascal Hertleif at RustFest (2017)\r\n" +"- [Rust Programming Techniques](https://www.youtube.com/watch?v=vqavdUGKeb4) " +"by\r\n" +" Nicholas Cameron at LinuxConfAu (2018)\r\n" +"\r" +msgstr "" + +#: .\additional_resources/index.md:14 +msgid "## Books (Online)\r" +msgstr "" + +#: .\additional_resources/index.md:16 +msgid "" +"- [The Rust API Guidelines](https://rust-lang.github.io/api-guidelines)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:1 +msgid "# Design principles\r" +msgstr "" + +#: .\additional_resources/design-principles.md:3 +msgid "## A brief overview over common design principles\r" +msgstr "" + +#: .\additional_resources/design-principles.md:7 +msgid "## [SOLID](https://en.wikipedia.org/wiki/SOLID)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:9 +msgid "" +"- [Single Responsibility Principle (SRP)](https://en.wikipedia.org/wiki/" +"Single-responsibility_principle):\r\n" +" A class should only have a single responsibility, that is, only changes to" +"\r\n" +" one part of the software's specification should be able to affect the\r\n" +" specification of the class.\r\n" +"- [Open/Closed Principle (OCP)](https://en.wikipedia.org/wiki/Open" +"%E2%80%93closed_principle):\r\n" +" \"Software entities ... should be open for extension, but closed for\r\n" +" modification.\"\r\n" +"- [Liskov Substitution Principle (LSP)](https://en.wikipedia.org/wiki/" +"Liskov_substitution_principle):\r\n" +" \"Objects in a program should be replaceable with instances of their " +"subtypes\r\n" +" without altering the correctness of that program.\"\r\n" +"- [Interface Segregation Principle (ISP)](https://en.wikipedia.org/wiki/" +"Interface_segregation_principle):\r\n" +" \"Many client-specific interfaces are better than one general-purpose\r\n" +" interface.\"\r\n" +"- [Dependency Inversion Principle (DIP)](https://en.wikipedia.org/wiki/" +"Dependency_inversion_principle):\r\n" +" One should \"depend upon abstractions, [not] concretions.\"\r\n" +"\r" +msgstr "" + +#: .\additional_resources/design-principles.md:25 +msgid "" +"## [DRY (Don’t Repeat Yourself)](https://en.wikipedia.org/wiki/Don" +"%27t_repeat_yourself)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:27 +msgid "" +"\"Every piece of knowledge must have a single, unambiguous, authoritative\r\n" +"representation within a system\"" +msgstr "" + +#: .\additional_resources/design-principles.md:30 +msgid "## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:32 +msgid "" +"most systems work best if they are kept simple rather than made complicated;" +"\r\n" +"therefore, simplicity should be a key goal in design, and unnecessary\r\n" +"complexity should be avoided" +msgstr "" + +#: .\additional_resources/design-principles.md:36 +msgid "" +"## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:38 +msgid "" +"a given object should assume as little as possible about the structure or\r\n" +"properties of anything else (including its subcomponents), in accordance with" +"\r\n" +"the principle of \"information hiding\"" +msgstr "" + +#: .\additional_resources/design-principles.md:42 +msgid "" +"## [Design by contract (DbC)](https://en.wikipedia.org/wiki/" +"Design_by_contract)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:44 +msgid "" +"software designers should define formal, precise and verifiable interface\r\n" +"specifications for software components, which extend the ordinary definition " +"of\r\n" +"abstract data types with preconditions, postconditions and invariants" +msgstr "" + +#: .\additional_resources/design-principles.md:48 +msgid "" +"## [Encapsulation](https://en.wikipedia.org/wiki/" +"Encapsulation_(computer_programming))\r" +msgstr "" + +#: .\additional_resources/design-principles.md:50 +msgid "" +"bundling of data with the methods that operate on that data, or the " +"restricting\r\n" +"of direct access to some of an object's components. Encapsulation is used to" +"\r\n" +"hide the values or state of a structured data object inside a class, " +"preventing\r\n" +"unauthorized parties' direct access to them." +msgstr "" + +#: .\additional_resources/design-principles.md:55 +msgid "" +"## [Command-Query-Separation(CQS)](https://en.wikipedia.org/wiki/Command" +"%E2%80%93query_separation)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:57 +msgid "" +"“Functions should not produce abstract side effects...only commands\r\n" +"(procedures) will be permitted to produce side effects.” - Bertrand Meyer:" +"\r\n" +"Object-Oriented Software Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:61 +msgid "" +"## [Principle of least astonishment (POLA)](https://en.wikipedia.org/wiki/" +"Principle_of_least_astonishment)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:63 +msgid "" +"a component of a system should behave in a way that most users will expect it" +"\r\n" +"to behave. The behavior should not astonish or surprise users" +msgstr "" + +#: .\additional_resources/design-principles.md:66 +msgid "## Linguistic-Modular-Units\r" +msgstr "" + +#: .\additional_resources/design-principles.md:68 +msgid "" +"“Modules must correspond to syntactic units in the language used.” - Bertrand" +"\r\n" +"Meyer: Object-Oriented Software Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:71 +msgid "## Self-Documentation\r" +msgstr "" + +#: .\additional_resources/design-principles.md:73 +msgid "" +"“The designer of a module should strive to make all information about the\r\n" +"module part of the module itself.” - Bertrand Meyer: Object-Oriented Software" +"\r\n" +"Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:77 +msgid "## Uniform-Access\r" +msgstr "" + +#: .\additional_resources/design-principles.md:79 +msgid "" +"“All services offered by a module should be available through a uniform\r\n" +"notation, which does not betray whether they are implemented through storage " +"or\r\n" +"through computation.” - Bertrand Meyer: Object-Oriented Software Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:83 +msgid "## Single-Choice\r" +msgstr "" + +#: .\additional_resources/design-principles.md:85 +msgid "" +"“Whenever a software system must support a set of alternatives, one and only" +"\r\n" +"one module in the system should know their exhaustive list.” - Bertrand " +"Meyer:\r\n" +"Object-Oriented Software Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:89 +msgid "## Persistence-Closure\r" +msgstr "" + +#: .\additional_resources/design-principles.md:91 +msgid "" +"“Whenever a storage mechanism stores an object, it must store with it the\r\n" +"dependents of that object. Whenever a retrieval mechanism retrieves a\r\n" +"previously stored object, it must also retrieve any dependent of that object" +"\r\n" +"that has not yet been retrieved.” - Bertrand Meyer: Object-Oriented Software" +"\r\n" +"Construction" +msgstr "" diff --git a/po/messages.pot b/po/messages.pot new file mode 100644 index 00000000..0e403f95 --- /dev/null +++ b/po/messages.pot @@ -0,0 +1,7400 @@ + +msgid "" +msgstr "" +"Project-Id-Version: Rust Design Patterns\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: .\SUMMARY.md:3 +msgid "Introduction" +msgstr "" + +#: .\SUMMARY.md:4 +msgid "Translations" +msgstr "" + +#: .\SUMMARY.md:5 +msgid "Idioms" +msgstr "" + +#: .\SUMMARY.md:6 +msgid "Use borrowed types for arguments" +msgstr "" + +#: .\SUMMARY.md:7 +msgid "Concatenating Strings with format!" +msgstr "" + +#: .\SUMMARY.md:8 +msgid "Constructor" +msgstr "" + +#: .\SUMMARY.md:9 +msgid "The Default Trait" +msgstr "" + +#: .\SUMMARY.md:10 +msgid "Collections Are Smart Pointers" +msgstr "" + +#: .\SUMMARY.md:11 +msgid "Finalisation in Destructors" +msgstr "" + +#: .\SUMMARY.md:12 +msgid "mem::{take(_), replace(_)}" +msgstr "" + +#: .\SUMMARY.md:13 +msgid "On-Stack Dynamic Dispatch" +msgstr "" + +#: .\SUMMARY.md:14 +#: .\SUMMARY.md:40 +msgid "Foreign function interface (FFI)" +msgstr "" + +#: .\SUMMARY.md:15 +msgid "Idiomatic Errors" +msgstr "" + +#: .\SUMMARY.md:16 +msgid "Accepting Strings" +msgstr "" + +#: .\SUMMARY.md:17 +msgid "Passing Strings" +msgstr "" + +#: .\SUMMARY.md:18 +msgid "Iterating over an Option" +msgstr "" + +#: .\SUMMARY.md:19 +msgid "Pass Variables to Closure" +msgstr "" + +#: .\SUMMARY.md:20 +msgid "Privacy For Extensibility" +msgstr "" + +#: .\SUMMARY.md:21 +msgid "Easy doc initialization" +msgstr "" + +#: .\SUMMARY.md:22 +msgid "Temporary mutability" +msgstr "" + +#: .\SUMMARY.md:23 +msgid "Return consumed arg on error" +msgstr "" + +#: .\SUMMARY.md:25 +msgid "Design Patterns" +msgstr "" + +#: .\SUMMARY.md:26 +msgid "Behavioural" +msgstr "" + +#: .\SUMMARY.md:27 +msgid "Command" +msgstr "" + +#: .\SUMMARY.md:28 +msgid "Interpreter" +msgstr "" + +#: .\SUMMARY.md:29 +msgid "Newtype" +msgstr "" + +#: .\SUMMARY.md:30 +msgid "RAII Guards" +msgstr "" + +#: .\SUMMARY.md:31 +msgid "Strategy" +msgstr "" + +#: .\SUMMARY.md:32 +msgid "Visitor" +msgstr "" + +#: .\SUMMARY.md:33 +msgid "Creational" +msgstr "" + +#: .\SUMMARY.md:34 +msgid "Builder" +msgstr "" + +#: .\SUMMARY.md:35 +msgid "Fold" +msgstr "" + +#: .\SUMMARY.md:36 +msgid "Structural" +msgstr "" + +#: .\SUMMARY.md:37 +msgid "Compose Structs" +msgstr "" + +#: .\SUMMARY.md:38 +msgid "Prefer Small Crates" +msgstr "" + +#: .\SUMMARY.md:39 +msgid "Contain unsafety in small modules" +msgstr "" + +#: .\SUMMARY.md:41 +msgid "Object-Based APIs" +msgstr "" + +#: .\SUMMARY.md:42 +msgid "Type Consolidation into Wrappers" +msgstr "" + +#: .\SUMMARY.md:44 +msgid "Anti-patterns" +msgstr "" + +#: .\SUMMARY.md:45 +msgid "Clone to satisfy the borrow checker" +msgstr "" + +#: .\SUMMARY.md:46 +msgid "#[deny(warnings)]" +msgstr "" + +#: .\SUMMARY.md:47 +msgid "Deref Polymorphism" +msgstr "" + +#: .\SUMMARY.md:49 +msgid "Functional Programming" +msgstr "" + +#: .\SUMMARY.md:50 +msgid "Programming paradigms" +msgstr "" + +#: .\SUMMARY.md:51 +msgid "Generics as Type Classes" +msgstr "" + +#: .\SUMMARY.md:52 +msgid "Lenses and Prisms" +msgstr "" + +#: .\SUMMARY.md:54 +msgid "Additional Resources" +msgstr "" + +#: .\SUMMARY.md:55 +msgid "Design principles" +msgstr "" + +#: .\intro.md:1 +msgid "# Introduction\r" +msgstr "" + +#: .\intro.md:3 +msgid "## Participation\r" +msgstr "" + +#: .\intro.md:5 +msgid "" +"If you are interested in contributing to this book, check out the\r\n" +"[contribution " +"guidelines](https://github.com/rust-unofficial/patterns/blob/master/CONTRIBUTING.md)." +msgstr "" + +#: .\intro.md:8 +msgid "## Design patterns\r" +msgstr "" + +#: .\intro.md:10 +msgid "" +"In software development, we often come across problems that share\r\n" +"similarities regardless of the environment they appear in. Although the\r\n" +"implementation details are crucial to solve the task at hand, we may\r\n" +"abstract from these particularities to find the common practices that\r\n" +"are generically applicable." +msgstr "" + +#: .\intro.md:16 +msgid "" +"Design patterns are a collection of reusable and tested solutions to\r\n" +"recurring problems in engineering. They make our software more modular,\r\n" +"maintainable, and extensible. Moreover, these patterns provide a common\r\n" +"language for developers, making them an excellent tool for effective\r\n" +"communication when problem-solving in teams." +msgstr "" + +#: .\intro.md:22 +#: .\patterns/index.md:14 +msgid "## Design patterns in Rust\r" +msgstr "" + +#: .\intro.md:24 +msgid "" +"Rust is not object-oriented, and the combination of all its " +"characteristics,\r\n" +"such as functional elements, a strong type system, and the borrow " +"checker,\r\n" +"makes it unique.\r\n" +"Because of this, Rust design patterns vary with respect to other\r\n" +"traditional object-oriented programming languages.\r\n" +"That's why we decided to write this book. We hope you enjoy reading it!\r\n" +"The book is divided in three main chapters:" +msgstr "" + +#: .\intro.md:32 +msgid "" +"- [Idioms](./idioms/index.md): guidelines to follow when coding.\r\n" +" They are the social norms of the community.\r\n" +" You should break them only if you have a good reason for it.\r\n" +"- [Design patterns](./patterns/index.md): methods to solve common " +"problems\r\n" +" when coding.\r\n" +"- [Anti-patterns](./anti_patterns/index.md): methods to solve common " +"problems\r\n" +" when coding.\r\n" +" However, while design patterns give us benefits,\r\n" +" anti-patterns create more problems.\r" +msgstr "" + +#: .\translations.md:1 +msgid "# Translations\r" +msgstr "" + +#: .\translations.md:3 +msgid "- [简体中文](https://fomalhauthmj.github.io/patterns/)\r\n\r" +msgstr "" + +#: .\translations.md:5 +msgid "" +"If you want to add a translation, please open an issue in the\r\n" +"[main repository](https://github.com/rust-unofficial/patterns)." +msgstr "" + +#: .\idioms/index.md:1 +msgid "# Idioms\r" +msgstr "" + +#: .\idioms/index.md:3 +msgid "" +"[Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly " +"used\r\n" +"styles, guidelines and patterns largely agreed upon by a community.\r\n" +"Writing idiomatic code allows other developers to understand better what " +"is\r\n" +"happening." +msgstr "" + +#: .\idioms/index.md:8 +msgid "" +"After all, the computer only cares about the machine code that is " +"generated\r\n" +"by the compiler.\r\n" +"Instead, the source code is mainly beneficial to the developer.\r\n" +"So, since we have this abstraction layer, why not make it more readable?" +msgstr "" + +#: .\idioms/index.md:13 +msgid "" +"Remember the [KISS " +"principle](https://en.wikipedia.org/wiki/KISS_principle):\r\n" +"\"Keep It Simple, Stupid\". It claims that \"most systems work best if they " +"are\r\n" +"kept simple rather than made complicated; therefore, simplicity should be a " +"key\r\n" +"goal in design, and unnecessary complexity should be avoided\"." +msgstr "" + +#: .\idioms/index.md:18 +msgid "> Code is there for humans, not computers, to understand.\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:1 +msgid "# Use borrowed types for arguments\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:3 +#: .\idioms/concat-format.md:3 +#: .\idioms/ctor.md:3 +#: .\idioms/default.md:3 +#: .\idioms/deref.md:3 +#: .\idioms/dtor-finally.md:3 +#: .\idioms/mem-replace.md:3 +#: .\idioms/on-stack-dyn-dispatch.md:3 +#: .\idioms/ffi/errors.md:3 +#: .\idioms/ffi/accepting-strings.md:3 +#: .\idioms/ffi/passing-strings.md:3 +#: .\idioms/option-iter.md:3 +#: .\idioms/pass-var-to-closure.md:3 +#: .\idioms/priv-extend.md:3 +#: .\idioms/rustdoc-init.md:3 +#: .\idioms/temporary-mutability.md:3 +#: .\idioms/return-consumed-arg-on-error.md:3 +#: .\patterns/behavioural/command.md:3 +#: .\patterns/behavioural/interpreter.md:3 +#: .\patterns/behavioural/newtype.md:13 +#: .\patterns/behavioural/RAII.md:3 +#: .\patterns/behavioural/strategy.md:3 +#: .\patterns/behavioural/visitor.md:3 +#: .\patterns/creational/builder.md:3 +#: .\patterns/creational/fold.md:3 +#: .\patterns/structural/compose-structs.md:5 +#: .\patterns/structural/small-crates.md:3 +#: .\patterns/structural/unsafe-mods.md:3 +#: .\patterns/ffi/export.md:3 +#: .\patterns/ffi/wrappers.md:3 +#: .\anti_patterns/borrow_clone.md:3 +#: .\anti_patterns/deny-warnings.md:3 +#: .\anti_patterns/deref.md:3 +#: .\functional/generics-type-classes.md:3 +msgid "## Description\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:5 +msgid "" +"Using a target of a deref coercion can increase the flexibility of your " +"code\r\n" +"when you are deciding which argument type to use for a function argument.\r\n" +"In this way, the function will accept more input types." +msgstr "" + +#: .\idioms/coercion-arguments.md:9 +msgid "" +"This is not limited to slice-able or fat pointer types.\r\n" +"In fact, you should always prefer using the __borrowed type__ over\r\n" +"__borrowing the owned type__.\r\n" +"Such as `&str` over `&String`, `&[T]` over `&Vec`, or `&T` over `&Box`." +msgstr "" + +#: .\idioms/coercion-arguments.md:14 +msgid "" +"Using borrowed types you can avoid layers of indirection for those " +"instances\r\n" +"where the owned type already provides a layer of indirection. For instance, " +"a\r\n" +"`String` has a layer of indirection, so a `&String` will have two layers " +"of\r\n" +"indirection. We can avoid this by using `&str` instead, and letting " +"`&String`\r\n" +"coerce to a `&str` whenever the function is invoked." +msgstr "" + +#: .\idioms/coercion-arguments.md:20 +#: .\idioms/concat-format.md:10 +#: .\idioms/default.md:20 +#: .\idioms/deref.md:9 +#: .\idioms/dtor-finally.md:9 +#: .\idioms/mem-replace.md:11 +#: .\idioms/on-stack-dyn-dispatch.md:10 +#: .\idioms/pass-var-to-closure.md:12 +#: .\idioms/priv-extend.md:18 +#: .\idioms/rustdoc-init.md:45 +#: .\idioms/temporary-mutability.md:12 +#: .\idioms/return-consumed-arg-on-error.md:8 +#: .\patterns/behavioural/command.md:18 +#: .\patterns/behavioural/newtype.md:18 +#: .\patterns/behavioural/RAII.md:11 +#: .\patterns/behavioural/strategy.md:28 +#: .\patterns/behavioural/visitor.md:13 +#: .\patterns/creational/builder.md:7 +#: .\patterns/creational/fold.md:12 +#: .\patterns/structural/compose-structs.md:17 +#: .\anti_patterns/borrow_clone.md:11 +#: .\anti_patterns/deny-warnings.md:8 +#: .\anti_patterns/deref.md:8 +#: .\functional/generics-type-classes.md:38 +msgid "## Example\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:22 +msgid "" +"For this example, we will illustrate some differences for using `&String` as " +"a\r\n" +"function argument versus using a `&str`, but the ideas apply as well to " +"using\r\n" +"`&Vec` versus using a `&[T]` or using a `&Box` versus a `&T`." +msgstr "" + +#: .\idioms/coercion-arguments.md:26 +msgid "" +"Consider an example where we wish to determine if a word contains three\r\n" +"consecutive vowels. We don't need to own the string to determine this, so " +"we\r\n" +"will take a reference." +msgstr "" + +#: .\idioms/coercion-arguments.md:30 +msgid "The code might look something like this:" +msgstr "" + +#: .\idioms/coercion-arguments.md:32 +msgid "" +"```rust\r\n" +"fn three_vowels(word: &String) -> bool {\r\n" +" let mut vowel_count = 0;\r\n" +" for c in word.chars() {\r\n" +" match c {\r\n" +" 'a' | 'e' | 'i' | 'o' | 'u' => {\r\n" +" vowel_count += 1;\r\n" +" if vowel_count >= 3 {\r\n" +" return true\r\n" +" }\r\n" +" }\r\n" +" _ => vowel_count = 0\r\n" +" }\r\n" +" }\r\n" +" false\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let ferris = \"Ferris\".to_string();\r\n" +" let curious = \"Curious\".to_string();\r\n" +" println!(\"{}: {}\", ferris, three_vowels(&ferris));\r\n" +" println!(\"{}: {}\", curious, three_vowels(&curious));\r\n" +"\r\n" +" // This works fine, but the following two lines would fail:\r\n" +" // println!(\"Ferris: {}\", three_vowels(\"Ferris\"));\r\n" +" // println!(\"Curious: {}\", three_vowels(\"Curious\"));\r\n" +"\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:62 +msgid "" +"This works fine because we are passing a `&String` type as a parameter.\r\n" +"If we remove the comments on the last two lines, the example will fail. " +"This\r\n" +"is because a `&str` type will not coerce to a `&String` type. We can fix " +"this\r\n" +"by simply modifying the type for our argument." +msgstr "" + +#: .\idioms/coercion-arguments.md:67 +msgid "For instance, if we change our function declaration to:" +msgstr "" + +#: .\idioms/coercion-arguments.md:69 +msgid "" +"```rust, ignore\r\n" +"fn three_vowels(word: &str) -> bool {\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:73 +msgid "then both versions will compile and print the same output." +msgstr "" + +#: .\idioms/coercion-arguments.md:75 +msgid "" +"```bash\r\n" +"Ferris: false\r\n" +"Curious: true\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:80 +msgid "" +"But wait, that's not all! There is more to this story.\r\n" +"It's likely that you may say to yourself: that doesn't matter, I will never " +"be\r\n" +"using a `&'static str` as an input anyways (as we did when we used " +"`\"Ferris\"`).\r\n" +"Even ignoring this special example, you may still find that using `&str` " +"will\r\n" +"give you more flexibility than using a `&String`." +msgstr "" + +#: .\idioms/coercion-arguments.md:86 +msgid "" +"Let's now take an example where someone gives us a sentence, and we want " +"to\r\n" +"determine if any of the words in the sentence contain three consecutive " +"vowels.\r\n" +"We probably should make use of the function we have already defined and " +"simply\r\n" +"feed in each word from the sentence." +msgstr "" + +#: .\idioms/coercion-arguments.md:91 +msgid "An example of this could look like this:" +msgstr "" + +#: .\idioms/coercion-arguments.md:93 +msgid "" +"```rust\r\n" +"fn three_vowels(word: &str) -> bool {\r\n" +" let mut vowel_count = 0;\r\n" +" for c in word.chars() {\r\n" +" match c {\r\n" +" 'a' | 'e' | 'i' | 'o' | 'u' => {\r\n" +" vowel_count += 1;\r\n" +" if vowel_count >= 3 {\r\n" +" return true\r\n" +" }\r\n" +" }\r\n" +" _ => vowel_count = 0\r\n" +" }\r\n" +" }\r\n" +" false\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let sentence_string =\r\n" +" \"Once upon a time, there was a friendly curious crab named " +"Ferris\".to_string();\r\n" +" for word in sentence_string.split(' ') {\r\n" +" if three_vowels(word) {\r\n" +" println!(\"{} has three consecutive vowels!\", word);\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:121 +msgid "" +"Running this example using our function declared with an argument type " +"`&str`\r\n" +"will yield" +msgstr "" + +#: .\idioms/coercion-arguments.md:124 +msgid "" +"```bash\r\n" +"curious has three consecutive vowels!\r\n" +"```" +msgstr "" + +#: .\idioms/coercion-arguments.md:128 +msgid "" +"However, this example will not run when our function is declared with an\r\n" +"argument type `&String`. This is because string slices are a `&str` and not " +"a\r\n" +"`&String` which would require an allocation to be converted to `&String` " +"which\r\n" +"is not implicit, whereas converting from `String` to `&str` is cheap and " +"implicit." +msgstr "" + +#: .\idioms/coercion-arguments.md:133 +#: .\idioms/ctor.md:101 +#: .\idioms/default.md:58 +#: .\idioms/deref.md:76 +#: .\idioms/dtor-finally.md:88 +#: .\idioms/mem-replace.md:110 +#: .\idioms/on-stack-dyn-dispatch.md:84 +#: .\idioms/option-iter.md:46 +#: .\idioms/priv-extend.md:120 +#: .\patterns/behavioural/command.md:218 +#: .\patterns/behavioural/interpreter.md:143 +#: .\patterns/behavioural/newtype.md:104 +#: .\patterns/behavioural/RAII.md:111 +#: .\patterns/behavioural/strategy.md:175 +#: .\patterns/behavioural/visitor.md:106 +#: .\patterns/creational/builder.md:108 +#: .\patterns/creational/fold.md:109 +#: .\patterns/structural/small-crates.md:45 +#: .\patterns/structural/unsafe-mods.md:32 +#: .\anti_patterns/borrow_clone.md:68 +#: .\anti_patterns/deny-warnings.md:96 +#: .\anti_patterns/deref.md:123 +#: .\functional/generics-type-classes.md:238 +msgid "## See also\r" +msgstr "" + +#: .\idioms/coercion-arguments.md:135 +msgid "" +"- [Rust Language Reference on Type " +"Coercions](https://doc.rust-lang.org/reference/type-coercions.html)\r\n" +"- For more discussion on how to handle `String` and `&str` see\r\n" +" [this blog series " +"(2015)](https://web.archive.org/web/20201112023149/https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html)\r\n" +" by Herman J. Radtke III\r" +msgstr "" + +#: .\idioms/concat-format.md:1 +msgid "# Concatenating strings with `format!`\r" +msgstr "" + +#: .\idioms/concat-format.md:5 +msgid "" +"It is possible to build up strings using the `push` and `push_str` methods " +"on a\r\n" +"mutable `String`, or using its `+` operator. However, it is often more\r\n" +"convenient to use `format!`, especially where there is a mix of literal " +"and\r\n" +"non-literal strings." +msgstr "" + +#: .\idioms/concat-format.md:12 +msgid "" +"```rust\r\n" +"fn say_hello(name: &str) -> String {\r\n" +" // We could construct the result string manually.\r\n" +" // let mut result = \"Hello \".to_owned();\r\n" +" // result.push_str(name);\r\n" +" // result.push('!');\r\n" +" // result\r\n" +"\r\n" +" // But using format! is better.\r\n" +" format!(\"Hello {}!\", name)\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/concat-format.md:25 +#: .\idioms/deref.md:43 +#: .\idioms/dtor-finally.md:42 +#: .\idioms/mem-replace.md:84 +#: .\idioms/on-stack-dyn-dispatch.md:48 +#: .\idioms/ffi/errors.md:131 +#: .\idioms/ffi/accepting-strings.md:68 +#: .\idioms/ffi/passing-strings.md:68 +#: .\idioms/pass-var-to-closure.md:48 +#: .\idioms/rustdoc-init.md:77 +#: .\idioms/temporary-mutability.md:38 +#: .\idioms/return-consumed-arg-on-error.md:55 +#: .\patterns/behavioural/newtype.md:66 +#: .\patterns/behavioural/RAII.md:78 +#: .\patterns/behavioural/strategy.md:96 +#: .\patterns/creational/builder.md:68 +#: .\patterns/structural/compose-structs.md:75 +#: .\patterns/structural/small-crates.md:12 +#: .\patterns/structural/unsafe-mods.md:11 +#: .\patterns/ffi/export.md:111 +#: .\patterns/ffi/wrappers.md:63 +#: .\anti_patterns/deny-warnings.md:16 +#: .\anti_patterns/deref.md:69 +#: .\functional/generics-type-classes.md:211 +msgid "## Advantages\r" +msgstr "" + +#: .\idioms/concat-format.md:27 +msgid "" +"Using `format!` is usually the most succinct and readable way to combine " +"strings." +msgstr "" + +#: .\idioms/concat-format.md:29 +#: .\idioms/deref.md:50 +#: .\idioms/dtor-finally.md:47 +#: .\idioms/mem-replace.md:88 +#: .\idioms/on-stack-dyn-dispatch.md:54 +#: .\idioms/ffi/errors.md:136 +#: .\idioms/ffi/accepting-strings.md:141 +#: .\idioms/ffi/passing-strings.md:103 +#: .\idioms/pass-var-to-closure.md:57 +#: .\idioms/rustdoc-init.md:81 +#: .\idioms/temporary-mutability.md:42 +#: .\idioms/return-consumed-arg-on-error.md:59 +#: .\patterns/behavioural/newtype.md:77 +#: .\patterns/behavioural/strategy.md:104 +#: .\patterns/creational/builder.md:76 +#: .\patterns/structural/compose-structs.md:81 +#: .\patterns/structural/small-crates.md:22 +#: .\patterns/structural/unsafe-mods.md:17 +#: .\patterns/ffi/export.md:234 +#: .\patterns/ffi/wrappers.md:69 +#: .\anti_patterns/deref.md:81 +#: .\functional/generics-type-classes.md:222 +msgid "## Disadvantages\r" +msgstr "" + +#: .\idioms/concat-format.md:31 +msgid "" +"It is usually not the most efficient way to combine strings - a series of " +"`push`\r\n" +"operations on a mutable string is usually the most efficient (especially if " +"the\r\n" +"string has been pre-allocated to the expected size)." +msgstr "" + +#: .\idioms/ctor.md:1 +msgid "# Constructors\r" +msgstr "" + +#: .\idioms/ctor.md:5 +msgid "" +"Rust does not have constructors as a language construct. Instead, the\r\n" +"convention is to use an [associated function][] `new` to create an object:" +msgstr "" + +#: .\idioms/ctor.md:8 +msgid "" +"```rust\r\n" +"/// Time in seconds.\r\n" +"///\r\n" +"/// # Example\r\n" +"///\r\n" +"/// ```\r\n" +"/// let s = Second::new(42);\r\n" +"/// assert_eq!(42, s.value());\r\n" +"/// ```\r\n" +"pub struct Second {\r\n" +" value: u64\r\n" +"}\r\n" +"\r\n" +"impl Second {\r\n" +" // Constructs a new instance of [`Second`].\r\n" +" // Note this is an associated function - no self.\r\n" +" pub fn new(value: u64) -> Self {\r\n" +" Self { value }\r\n" +" }\r\n" +"\r\n" +" /// Returns the value in seconds.\r\n" +" pub fn value(&self) -> u64 {\r\n" +" self.value\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ctor.md:35 +msgid "## Default Constructors\r" +msgstr "" + +#: .\idioms/ctor.md:37 +msgid "" +"Rust supports default constructors with the [`Default`][std-default] trait:" +msgstr "" + +#: .\idioms/ctor.md:39 +msgid "" +"```rust\r\n" +"/// Time in seconds.\r\n" +"///\r\n" +"/// # Example\r\n" +"///\r\n" +"/// ```\r\n" +"/// let s = Second::default();\r\n" +"/// assert_eq!(0, s.value());\r\n" +"/// ```\r\n" +"pub struct Second {\r\n" +" value: u64\r\n" +"}\r\n" +"\r\n" +"impl Second {\r\n" +" /// Returns the value in seconds.\r\n" +" pub fn value(&self) -> u64 {\r\n" +" self.value\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"impl Default for Second {\r\n" +" fn default() -> Self {\r\n" +" Self { value: 0 }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ctor.md:66 +msgid "" +"`Default` can also be derived if all types of all fields implement " +"`Default`,\r\n" +"like they do with `Second`:" +msgstr "" + +#: .\idioms/ctor.md:69 +msgid "" +"```rust\r\n" +"/// Time in seconds.\r\n" +"///\r\n" +"/// # Example\r\n" +"///\r\n" +"/// ```\r\n" +"/// let s = Second::default();\r\n" +"/// assert_eq!(0, s.value());\r\n" +"/// ```\r\n" +"#[derive(Default)]\r\n" +"pub struct Second {\r\n" +" value: u64\r\n" +"}\r\n" +"\r\n" +"impl Second {\r\n" +" /// Returns the value in seconds.\r\n" +" pub fn value(&self) -> u64 {\r\n" +" self.value\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ctor.md:91 +msgid "" +"**Note:** It is common and expected for types to implement both\r\n" +"`Default` and an empty `new` constructor. `new` is the constructor\r\n" +"convention in Rust, and users expect it to exist, so if it is\r\n" +"reasonable for the basic constructor to take no arguments, then it\r\n" +"should, even if it is functionally identical to default." +msgstr "" + +#: .\idioms/ctor.md:97 +msgid "" +"**Hint:** The advantage of implementing or deriving `Default` is that your " +"type\r\n" +"can now be used where a `Default` implementation is required, most " +"prominently,\r\n" +"any of the [`*or_default` functions in the standard library][std-or-default]." +msgstr "" + +#: .\idioms/ctor.md:103 +msgid "" +"- The [default idiom](default.md) for a more in-depth description of the\r\n" +" `Default` trait.\r\n" +"\r\n" +"- The [builder pattern](../patterns/creational/builder.md) for " +"constructing\r\n" +" objects where there are multiple configurations.\r\n" +"\r\n" +"- [API Guidelines/C-COMMON-TRAITS][API Guidelines/C-COMMON-TRAITS] for\r\n" +" implementing both, `Default` and `new`.\r\n" +"\r" +msgstr "" + +#: .\idioms/default.md:1 +msgid "# The `Default` Trait\r" +msgstr "" + +#: .\idioms/default.md:5 +msgid "" +"Many types in Rust have a [constructor]. However, this is *specific* to " +"the\r\n" +"type; Rust cannot abstract over \"everything that has a `new()` method\". " +"To\r\n" +"allow this, the [`Default`] trait was conceived, which can be used with\r\n" +"containers and other generic types (e.g. see " +"[`Option::unwrap_or_default()`]).\r\n" +"Notably, some containers already implement it where applicable." +msgstr "" + +#: .\idioms/default.md:11 +msgid "" +"Not only do one-element containers like `Cow`, `Box` or `Arc` implement\r\n" +"`Default` for contained `Default` types, one can automatically\r\n" +"`#[derive(Default)]` for structs whose fields all implement it, so the " +"more\r\n" +"types implement `Default`, the more useful it becomes." +msgstr "" + +#: .\idioms/default.md:16 +msgid "" +"On the other hand, constructors can take multiple arguments, while the\r\n" +"`default()` method does not. There can even be multiple constructors with\r\n" +"different names, but there can only be one `Default` implementation per type." +msgstr "" + +#: .\idioms/default.md:22 +msgid "" +"```rust\r\n" +"use std::{path::PathBuf, time::Duration};\r\n" +"\r\n" +"// note that we can simply auto-derive Default here.\r\n" +"#[derive(Default, Debug, PartialEq)]\r\n" +"struct MyConfiguration {\r\n" +" // Option defaults to None\r\n" +" output: Option,\r\n" +" // Vecs default to empty vector\r\n" +" search_path: Vec,\r\n" +" // Duration defaults to zero time\r\n" +" timeout: Duration,\r\n" +" // bool defaults to false\r\n" +" check: bool,\r\n" +"}\r\n" +"\r\n" +"impl MyConfiguration {\r\n" +" // add setters here\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" // construct a new instance with default values\r\n" +" let mut conf = MyConfiguration::default();\r\n" +" // do something with conf here\r\n" +" conf.check = true;\r\n" +" println!(\"conf = {:#?}\", conf);\r\n" +" \r\n" +" // partial initialization with default values, creates the same " +"instance\r\n" +" let conf1 = MyConfiguration {\r\n" +" check: true,\r\n" +" ..Default::default()\r\n" +" };\r\n" +" assert_eq!(conf, conf1);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/default.md:60 +msgid "" +"- The [constructor] idiom is another way to generate instances that may or " +"may\r\n" +"not be \"default\"\r\n" +"- The [`Default`] documentation (scroll down for the list of " +"implementors)\r\n" +"- [`Option::unwrap_or_default()`]\r\n" +"- [`derive(new)`]\r\n" +"\r" +msgstr "" + +#: .\idioms/deref.md:1 +msgid "# Collections are smart pointers\r" +msgstr "" + +#: .\idioms/deref.md:5 +msgid "" +"Use the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html)\r\n" +"trait to treat collections like smart pointers, offering owning\r\n" +"and borrowed views of data." +msgstr "" + +#: .\idioms/deref.md:11 +msgid "" +"```rust,ignore\r\n" +"use std::ops::Deref;\r\n" +"\r\n" +"struct Vec {\r\n" +" data: RawVec,\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"impl Deref for Vec {\r\n" +" type Target = [T];\r\n" +"\r\n" +" fn deref(&self) -> &[T] {\r\n" +" //..\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/deref.md:28 +msgid "" +"A `Vec` is an owning collection of `T`s, while a slice (`&[T]`) is a " +"borrowed\r\n" +"collection of `T`s. Implementing `Deref` for `Vec` allows implicit " +"dereferencing\r\n" +"from `&Vec` to `&[T]` and includes the relationship in " +"auto-derefencing\r\n" +"searches. Most methods you might expect to be implemented for `Vec`s are " +"instead\r\n" +"implemented for slices." +msgstr "" + +#: .\idioms/deref.md:34 +msgid "Also `String` and `&str` have a similar relation." +msgstr "" + +#: .\idioms/deref.md:36 +#: .\idioms/dtor-finally.md:32 +#: .\idioms/mem-replace.md:57 +#: .\idioms/on-stack-dyn-dispatch.md:37 +#: .\idioms/ffi/accepting-strings.md:12 +#: .\idioms/ffi/passing-strings.md:14 +#: .\idioms/rustdoc-init.md:9 +#: .\idioms/return-consumed-arg-on-error.md:43 +#: .\patterns/behavioural/command.md:8 +#: .\patterns/behavioural/interpreter.md:16 +#: .\patterns/behavioural/newtype.md:56 +#: .\patterns/behavioural/RAII.md:72 +#: .\patterns/behavioural/strategy.md:19 +#: .\patterns/behavioural/visitor.md:72 +#: .\patterns/creational/builder.md:63 +#: .\patterns/creational/fold.md:73 +#: .\patterns/structural/compose-structs.md:71 +#: .\patterns/ffi/export.md:15 +#: .\anti_patterns/borrow_clone.md:30 +msgid "## Motivation\r" +msgstr "" + +#: .\idioms/deref.md:38 +msgid "" +"Ownership and borrowing are key aspects of the Rust language. Data " +"structures\r\n" +"must account for these semantics properly to give a good user\r\n" +"experience. When implementing a data structure that owns its data, offering " +"a\r\n" +"borrowed view of that data allows for more flexible APIs." +msgstr "" + +#: .\idioms/deref.md:45 +msgid "" +"Most methods can be implemented only for the borrowed view, they are then\r\n" +"implicitly available for the owning view." +msgstr "" + +#: .\idioms/deref.md:48 +msgid "Gives clients a choice between borrowing or taking ownership of data." +msgstr "" + +#: .\idioms/deref.md:52 +msgid "" +"Methods and traits only available via dereferencing are not taken into " +"account\r\n" +"when bounds checking, so generic programming with data structures using " +"this\r\n" +"pattern can get complex (see the `Borrow` and `AsRef` traits, etc.)." +msgstr "" + +#: .\idioms/deref.md:56 +#: .\idioms/dtor-finally.md:61 +#: .\idioms/mem-replace.md:99 +#: .\idioms/on-stack-dyn-dispatch.md:68 +#: .\idioms/priv-extend.md:85 +#: .\idioms/rustdoc-init.md:87 +#: .\patterns/behavioural/command.md:203 +#: .\patterns/behavioural/interpreter.md:104 +#: .\patterns/behavioural/newtype.md:85 +#: .\patterns/behavioural/RAII.md:83 +#: .\patterns/behavioural/strategy.md:110 +#: .\patterns/behavioural/visitor.md:79 +#: .\patterns/creational/builder.md:81 +#: .\patterns/creational/fold.md:85 +#: .\patterns/structural/compose-structs.md:89 +#: .\anti_patterns/deref.md:102 +msgid "## Discussion\r" +msgstr "" + +#: .\idioms/deref.md:58 +msgid "" +"Smart pointers and collections are analogous: a smart pointer points to a " +"single\r\n" +"object, whereas a collection points to many objects. From the point of view " +"of\r\n" +"the type system, there is little difference between the two. A collection " +"owns\r\n" +"its data if the only way to access each datum is via the collection and " +"the\r\n" +"collection is responsible for deleting the data (even in cases of shared\r\n" +"ownership, some kind of borrowed view may be appropriate). If a collection " +"owns\r\n" +"its data, it is usually useful to provide a view of the data as borrowed so " +"that\r\n" +"it can be referenced multiple times." +msgstr "" + +#: .\idioms/deref.md:67 +msgid "" +"Most smart pointers (e.g., `Foo`) implement `Deref`. " +"However,\r\n" +"collections will usually dereference to a custom type. `[T]` and `str` have " +"some\r\n" +"language support, but in the general case, this is not necessary. `Foo` " +"can\r\n" +"implement `Deref>` where `Bar` is a dynamically sized type " +"and\r\n" +"`&Bar` is a borrowed view of the data in `Foo`." +msgstr "" + +#: .\idioms/deref.md:73 +msgid "" +"Commonly, ordered collections will implement `Index` for `Range`s to " +"provide\r\n" +"slicing syntax. The target will be the borrowed view." +msgstr "" + +#: .\idioms/deref.md:78 +msgid "" +"- [Deref polymorphism anti-pattern](../anti_patterns/deref.md).\r\n" +"- [Documentation for `Deref` " +"trait](https://doc.rust-lang.org/std/ops/trait.Deref.html).\r" +msgstr "" + +#: .\idioms/dtor-finally.md:1 +msgid "# Finalisation in destructors\r" +msgstr "" + +#: .\idioms/dtor-finally.md:5 +msgid "" +"Rust does not provide the equivalent to `finally` blocks - code that will " +"be\r\n" +"executed no matter how a function is exited. Instead, an object's destructor " +"can\r\n" +"be used to run code that must be run before exit." +msgstr "" + +#: .\idioms/dtor-finally.md:11 +msgid "" +"```rust,ignore\r\n" +"fn bar() -> Result<(), ()> {\r\n" +" // These don't need to be defined inside the function.\r\n" +" struct Foo;\r\n" +"\r\n" +" // Implement a destructor for Foo.\r\n" +" impl Drop for Foo {\r\n" +" fn drop(&mut self) {\r\n" +" println!(\"exit\");\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" // The dtor of _exit will run however the function `bar` is exited.\r\n" +" let _exit = Foo;\r\n" +" // Implicit return with `?` operator.\r\n" +" baz()?;\r\n" +" // Normal return.\r\n" +" Ok(())\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/dtor-finally.md:34 +msgid "" +"If a function has multiple return points, then executing code on exit " +"becomes\r\n" +"difficult and repetitive (and thus bug-prone). This is especially the case " +"where\r\n" +"return is implicit due to a macro. A common case is the `?` operator " +"which\r\n" +"returns if the result is an `Err`, but continues if it is `Ok`. `?` is used " +"as\r\n" +"an exception handling mechanism, but unlike Java (which has `finally`), " +"there is\r\n" +"no way to schedule code to run in both the normal and exceptional cases.\r\n" +"Panicking will also exit a function early." +msgstr "" + +#: .\idioms/dtor-finally.md:44 +msgid "" +"Code in destructors will (nearly) always be run - copes with panics, " +"early\r\n" +"returns, etc." +msgstr "" + +#: .\idioms/dtor-finally.md:49 +msgid "" +"It is not guaranteed that destructors will run. For example, if there is " +"an\r\n" +"infinite loop in a function or if running a function crashes before exit.\r\n" +"Destructors are also not run in the case of a panic in an already " +"panicking\r\n" +"thread. Therefore, destructors cannot be relied on as finalizers where it " +"is\r\n" +"absolutely essential that finalisation happens." +msgstr "" + +#: .\idioms/dtor-finally.md:55 +msgid "" +"This pattern introduces some hard to notice, implicit code. Reading a " +"function\r\n" +"gives no clear indication of destructors to be run on exit. This can make\r\n" +"debugging tricky." +msgstr "" + +#: .\idioms/dtor-finally.md:59 +msgid "" +"Requiring an object and `Drop` impl just for finalisation is heavy on " +"boilerplate." +msgstr "" + +#: .\idioms/dtor-finally.md:63 +msgid "" +"There is some subtlety about how exactly to store the object used as a\r\n" +"finalizer. It must be kept alive until the end of the function and must then " +"be\r\n" +"destroyed. The object must always be a value or uniquely owned pointer " +"(e.g.,\r\n" +"`Box`). If a shared pointer (such as `Rc`) is used, then the finalizer " +"can\r\n" +"be kept alive beyond the lifetime of the function. For similar reasons, " +"the\r\n" +"finalizer should not be moved or returned." +msgstr "" + +#: .\idioms/dtor-finally.md:70 +msgid "" +"The finalizer must be assigned into a variable, otherwise it will be " +"destroyed\r\n" +"immediately, rather than when it goes out of scope. The variable name must " +"start\r\n" +"with `_` if the variable is only used as a finalizer, otherwise the " +"compiler\r\n" +"will warn that the finalizer is never used. However, do not call the " +"variable\r\n" +"`_` with no suffix - in that case it will be destroyed immediately." +msgstr "" + +#: .\idioms/dtor-finally.md:76 +msgid "" +"In Rust, destructors are run when an object goes out of scope. This " +"happens\r\n" +"whether we reach the end of block, there is an early return, or the " +"program\r\n" +"panics. When panicking, Rust unwinds the stack running destructors for " +"each\r\n" +"object in each stack frame. So, destructors get called even if the panic " +"happens\r\n" +"in a function being called." +msgstr "" + +#: .\idioms/dtor-finally.md:82 +msgid "" +"If a destructor panics while unwinding, there is no good action to take, so " +"Rust\r\n" +"aborts the thread immediately, without running further destructors. This " +"means\r\n" +"that destructors are not absolutely guaranteed to run. It also means that " +"you\r\n" +"must take extra care in your destructors not to panic, since it could " +"leave\r\n" +"resources in an unexpected state." +msgstr "" + +#: .\idioms/dtor-finally.md:90 +msgid "[RAII guards](../patterns/behavioural/RAII.md)." +msgstr "" + +#: .\idioms/mem-replace.md:1 +msgid "# `mem::{take(_), replace(_)}` to keep owned values in changed enums\r" +msgstr "" + +#: .\idioms/mem-replace.md:5 +msgid "" +"Say we have a `&mut MyEnum` which has (at least) two variants,\r\n" +"`A { name: String, x: u8 }` and `B { name: String }`. Now we want to " +"change\r\n" +"`MyEnum::A` to a `B` if `x` is zero, while keeping `MyEnum::B` intact." +msgstr "" + +#: .\idioms/mem-replace.md:9 +msgid "We can do this without cloning the `name`." +msgstr "" + +#: .\idioms/mem-replace.md:13 +msgid "" +"```rust\r\n" +"use std::mem;\r\n" +"\r\n" +"enum MyEnum {\r\n" +" A { name: String, x: u8 },\r\n" +" B { name: String }\r\n" +"}\r\n" +"\r\n" +"fn a_to_b(e: &mut MyEnum) {\r\n" +" if let MyEnum::A { name, x: 0 } = e {\r\n" +" // this takes out our `name` and put in an empty String instead\r\n" +" // (note that empty strings don't allocate).\r\n" +" // Then, construct the new enum variant (which will\r\n" +" // be assigned to `*e`).\r\n" +" *e = MyEnum::B { name: mem::take(name) }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/mem-replace.md:32 +msgid "This also works with more variants:" +msgstr "" + +#: .\idioms/mem-replace.md:34 +msgid "" +"```rust\r\n" +"use std::mem;\r\n" +"\r\n" +"enum MultiVariateEnum {\r\n" +" A { name: String },\r\n" +" B { name: String },\r\n" +" C,\r\n" +" D\r\n" +"}\r\n" +"\r\n" +"fn swizzle(e: &mut MultiVariateEnum) {\r\n" +" use MultiVariateEnum::*;\r\n" +" *e = match e {\r\n" +" // Ownership rules do not allow taking `name` by value, but we " +"cannot\r\n" +" // take the value out of a mutable reference, unless we replace " +"it:\r\n" +" A { name } => B { name: mem::take(name) },\r\n" +" B { name } => A { name: mem::take(name) },\r\n" +" C => D,\r\n" +" D => C\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/mem-replace.md:59 +msgid "" +"When working with enums, we may want to change an enum value in place, " +"perhaps\r\n" +"to another variant. This is usually done in two phases to keep the borrow\r\n" +"checker happy. In the first phase, we observe the existing value and look " +"at\r\n" +"its parts to decide what to do next. In the second phase we may " +"conditionally\r\n" +"change the value (as in the example above)." +msgstr "" + +#: .\idioms/mem-replace.md:65 +msgid "" +"The borrow checker won't allow us to take out `name` of the enum (because\r\n" +"*something* must be there.) We could of course `.clone()` name and put the " +"clone\r\n" +"into our `MyEnum::B`, but that would be an instance of the [Clone to " +"satisfy\r\n" +"the borrow checker](../anti_patterns/borrow_clone.md) anti-pattern. Anyway, " +"we\r\n" +"can avoid the extra allocation by changing `e` with only a mutable borrow." +msgstr "" + +#: .\idioms/mem-replace.md:71 +msgid "" +"`mem::take` lets us swap out the value, replacing it with it's default " +"value,\r\n" +"and returning the previous value. For `String`, the default value is an " +"empty\r\n" +"`String`, which does not need to allocate. As a result, we get the " +"original\r\n" +"`name` *as an owned value*. We can then wrap this in another enum." +msgstr "" + +#: .\idioms/mem-replace.md:76 +msgid "" +"__NOTE:__ `mem::replace` is very similar, but allows us to specify what " +"to\r\n" +"replace the value with. An equivalent to our `mem::take` line would be\r\n" +"`mem::replace(name, String::new())`." +msgstr "" + +#: .\idioms/mem-replace.md:80 +msgid "" +"Note, however, that if we are using an `Option` and want to replace its\r\n" +"value with a `None`, `Option`’s `take()` method provides a shorter and\r\n" +"more idiomatic alternative." +msgstr "" + +#: .\idioms/mem-replace.md:86 +msgid "" +"Look ma, no allocation! Also you may feel like Indiana Jones while doing it." +msgstr "" + +#: .\idioms/mem-replace.md:90 +msgid "" +"This gets a bit wordy. Getting it wrong repeatedly will make you hate the\r\n" +"borrow checker. The compiler may fail to optimize away the double store,\r\n" +"resulting in reduced performance as opposed to what you'd do in unsafe\r\n" +"languages." +msgstr "" + +#: .\idioms/mem-replace.md:95 +msgid "" +"Furthermore, the type you are taking needs to implement the [`Default`\r\n" +"trait](./default.md). However, if the type you're working with doesn't\r\n" +"implement this, you can instead use `mem::replace`." +msgstr "" + +#: .\idioms/mem-replace.md:101 +msgid "" +"This pattern is only of interest in Rust. In GC'd languages, you'd take " +"the\r\n" +"reference to the value by default (and the GC would keep track of refs), and " +"in\r\n" +"other low-level languages like C you'd simply alias the pointer and fix " +"things\r\n" +"later." +msgstr "" + +#: .\idioms/mem-replace.md:106 +msgid "" +"However, in Rust, we have to do a little more work to do this. An owned " +"value\r\n" +"may only have one owner, so to take it out, we need to put something back in " +"–\r\n" +"like Indiana Jones, replacing the artifact with a bag of sand." +msgstr "" + +#: .\idioms/mem-replace.md:112 +msgid "" +"This gets rid of the [Clone to satisfy the borrow " +"checker](../anti_patterns/borrow_clone.md)\r\n" +"anti-pattern in a specific case." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:1 +msgid "# On-Stack Dynamic Dispatch\r" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:5 +msgid "" +"We can dynamically dispatch over multiple values, however, to do so, we " +"need\r\n" +"to declare multiple variables to bind differently-typed objects. To extend " +"the\r\n" +"lifetime as necessary, we can use deferred conditional initialization, as " +"seen\r\n" +"below:" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:12 +msgid "" +"```rust\r\n" +"use std::io;\r\n" +"use std::fs;\r\n" +"\r\n" +"# fn main() -> Result<(), Box> {\r\n" +"# let arg = \"-\";\r\n" +"\r\n" +"// These must live longer than `readable`, and thus are declared first:\r\n" +"let (mut stdin_read, mut file_read);\r\n" +"\r\n" +"// We need to ascribe the type to get dynamic dispatch.\r\n" +"let readable: &mut dyn io::Read = if arg == \"-\" {\r\n" +" stdin_read = io::stdin();\r\n" +" &mut stdin_read\r\n" +"} else {\r\n" +" file_read = fs::File::open(arg)?;\r\n" +" &mut file_read\r\n" +"};\r\n" +"\r\n" +"// Read from `readable` here.\r\n" +"\r\n" +"# Ok(())\r\n" +"# }\r\n" +"```" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:39 +msgid "" +"Rust monomorphises code by default. This means a copy of the code will be\r\n" +"generated for each type it is used with and optimized independently. While " +"this\r\n" +"allows for very fast code on the hot path, it also bloats the code in " +"places\r\n" +"where performance is not of the essence, thus costing compile time and " +"cache\r\n" +"usage." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:45 +msgid "" +"Luckily, Rust allows us to use dynamic dispatch, but we have to explicitly " +"ask\r\n" +"for it." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:50 +msgid "" +"We do not need to allocate anything on the heap. Neither do we need to\r\n" +"initialize something we won't use later, nor do we need to monomorphize " +"the\r\n" +"whole code that follows to work with both `File` or `Stdin`." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:56 +msgid "The code needs more moving parts than the `Box`-based version:" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:58 +msgid "" +"```rust,ignore\r\n" +"// We still need to ascribe the type for dynamic dispatch.\r\n" +"let readable: Box = if arg == \"-\" {\r\n" +" Box::new(io::stdin())\r\n" +"} else {\r\n" +" Box::new(fs::File::open(arg)?)\r\n" +"};\r\n" +"// Read from `readable` here.\r\n" +"```" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:70 +msgid "" +"Rust newcomers will usually learn that Rust requires all variables to be\r\n" +"initialized *before use*, so it's easy to overlook the fact that *unused*\r\n" +"variables may well be uninitialized. Rust works quite hard to ensure that " +"this\r\n" +"works out fine and only the initialized values are dropped at the end of " +"their\r\n" +"scope." +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:76 +msgid "The example meets all the constraints Rust places on us:" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:78 +msgid "" +"* All variables are initialized before using (in this case borrowing) " +"them\r\n" +"* Each variable only holds values of a single type. In our example, `stdin` " +"is\r\n" +"of type `Stdin`, `file` is of type `File` and `readable` is of type `&mut " +"dyn\r\n" +"Read`\r\n" +"* Each borrowed value outlives all the references borrowed from it\r\n" +"\r" +msgstr "" + +#: .\idioms/on-stack-dyn-dispatch.md:86 +msgid "" +"* [Finalisation in destructors](dtor-finally.md) and\r\n" +"[RAII guards](../patterns/behavioural/RAII.md) can benefit from tight " +"control over\r\n" +"lifetimes.\r\n" +"* For conditionally filled `Option<&T>`s of (mutable) references, one can\r\n" +"initialize an `Option` directly and use its [`.as_ref()`] method to get " +"an\r\n" +"optional reference.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/intro.md:1 +msgid "# FFI Idioms\r" +msgstr "" + +#: .\idioms/ffi/intro.md:3 +msgid "" +"Writing FFI code is an entire course in itself.\r\n" +"However, there are several idioms here that can act as pointers, and " +"avoid\r\n" +"traps for inexperienced users of `unsafe` Rust." +msgstr "" + +#: .\idioms/ffi/intro.md:7 +msgid "This section contains idioms that may be useful when doing FFI." +msgstr "" + +#: .\idioms/ffi/intro.md:9 +msgid "" +"1. [Idiomatic Errors](./errors.md) - Error handling with integer codes " +"and\r\n" +" sentinel return values (such as `NULL` pointers)\r\n" +"\r\n" +"2. [Accepting Strings](./accepting-strings.md) with minimal unsafe code\r\n" +"\r\n" +"3. [Passing Strings](./passing-strings.md) to FFI functions\r" +msgstr "" + +#: .\idioms/ffi/errors.md:1 +msgid "# Error Handling in FFI\r" +msgstr "" + +#: .\idioms/ffi/errors.md:5 +msgid "" +"In foreign languages like C, errors are represented by return codes.\r\n" +"However, Rust's type system allows much more rich error information to be\r\n" +"captured and propogated through a full type." +msgstr "" + +#: .\idioms/ffi/errors.md:9 +msgid "" +"This best practice shows different kinds of error codes, and how to expose " +"them\r\n" +"in a usable way:" +msgstr "" + +#: .\idioms/ffi/errors.md:12 +msgid "" +"1. Flat Enums should be converted to integers and returned as codes.\r\n" +"2. Structured Enums should be converted to an integer code with a string " +"error\r\n" +" message for detail.\r\n" +"3. Custom Error Types should become \"transparent\", with a C " +"representation.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/errors.md:17 +#: .\idioms/ffi/accepting-strings.md:29 +#: .\idioms/ffi/passing-strings.md:26 +#: .\patterns/ffi/export.md:40 +#: .\patterns/ffi/wrappers.md:23 +msgid "## Code Example\r" +msgstr "" + +#: .\idioms/ffi/errors.md:19 +msgid "### Flat Enums\r" +msgstr "" + +#: .\idioms/ffi/errors.md:21 +msgid "" +"```rust,ignore\r\n" +"enum DatabaseError {\r\n" +" IsReadOnly = 1, // user attempted a write operation\r\n" +" IOError = 2, // user should read the C errno() for what it was\r\n" +" FileCorrupted = 3, // user should run a repair tool to recover it\r\n" +"}\r\n" +"\r\n" +"impl From for libc::c_int {\r\n" +" fn from(e: DatabaseError) -> libc::c_int {\r\n" +" (e as i8).into()\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/errors.md:35 +msgid "### Structured Enums\r" +msgstr "" + +#: .\idioms/ffi/errors.md:37 +msgid "" +"```rust,ignore\r\n" +"pub mod errors {\r\n" +" enum DatabaseError {\r\n" +" IsReadOnly,\r\n" +" IOError(std::io::Error),\r\n" +" FileCorrupted(String), // message describing the issue\r\n" +" }\r\n" +"\r\n" +" impl From for libc::c_int {\r\n" +" fn from(e: DatabaseError) -> libc::c_int {\r\n" +" match e {\r\n" +" DatabaseError::IsReadOnly => 1,\r\n" +" DatabaseError::IOError(_) => 2,\r\n" +" DatabaseError::FileCorrupted(_) => 3,\r\n" +" }\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub mod c_api {\r\n" +" use super::errors::DatabaseError;\r\n" +"\r\n" +" #[no_mangle]\r\n" +" pub extern \"C\" fn db_error_description(\r\n" +" e: *const DatabaseError\r\n" +" ) -> *mut libc::c_char {\r\n" +"\r\n" +" let error: &DatabaseError = unsafe {\r\n" +" // SAFETY: pointer lifetime is greater than the current stack " +"frame\r\n" +" &*e\r\n" +" };\r\n" +"\r\n" +" let error_str: String = match error {\r\n" +" DatabaseError::IsReadOnly => {\r\n" +" format!(\"cannot write to read-only database\");\r\n" +" }\r\n" +" DatabaseError::IOError(e) => {\r\n" +" format!(\"I/O Error: {}\", e);\r\n" +" }\r\n" +" DatabaseError::FileCorrupted(s) => {\r\n" +" format!(\"File corrupted, run repair: {}\", &s);\r\n" +" }\r\n" +" };\r\n" +"\r\n" +" let c_error = unsafe {\r\n" +" // SAFETY: copying error_str to an allocated buffer with a " +"NUL\r\n" +" // character at the end\r\n" +" let mut malloc: *mut u8 = libc::malloc(error_str.len() + 1) as " +"*mut _;\r\n" +"\r\n" +" if malloc.is_null() {\r\n" +" return std::ptr::null_mut();\r\n" +" }\r\n" +"\r\n" +" let src = error_str.as_bytes().as_ptr();\r\n" +"\r\n" +" std::ptr::copy_nonoverlapping(src, malloc, error_str.len());\r\n" +"\r\n" +" std::ptr::write(malloc.add(error_str.len()), 0);\r\n" +"\r\n" +" malloc as *mut libc::c_char\r\n" +" };\r\n" +"\r\n" +" c_error\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/errors.md:104 +msgid "### Custom Error Types\r" +msgstr "" + +#: .\idioms/ffi/errors.md:106 +msgid "" +"```rust,ignore\r\n" +"struct ParseError {\r\n" +" expected: char,\r\n" +" line: u32,\r\n" +" ch: u16\r\n" +"}\r\n" +"\r\n" +"impl ParseError { /* ... */ }\r\n" +"\r\n" +"/* Create a second version which is exposed as a C structure */\r\n" +"#[repr(C)]\r\n" +"pub struct parse_error {\r\n" +" pub expected: libc::c_char,\r\n" +" pub line: u32,\r\n" +" pub ch: u16\r\n" +"}\r\n" +"\r\n" +"impl From for parse_error {\r\n" +" fn from(e: ParseError) -> parse_error {\r\n" +" let ParseError { expected, line, ch } = e;\r\n" +" parse_error { expected, line, ch }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/errors.md:133 +msgid "" +"This ensures that the foreign language has clear access to error " +"information\r\n" +"while not compromising the Rust code's API at all." +msgstr "" + +#: .\idioms/ffi/errors.md:138 +msgid "" +"It's a lot of typing, and some types may not be able to be converted " +"easily\r\n" +"to C." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:1 +msgid "# Accepting Strings\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:5 +msgid "" +"When accepting strings via FFI through pointers, there are two principles " +"that\r\n" +"should be followed:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:8 +msgid "" +"1. Keep foreign strings \"borrowed\", rather than copying them directly.\r\n" +"2. Minimize the amount of complexity and `unsafe` code involved in " +"converting\r\n" +" from a C-style string to native Rust strings.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:14 +msgid "" +"The strings used in C have different behaviours to those used in Rust, " +"namely:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:16 +msgid "" +"- C strings are null-terminated while Rust strings store their length\r\n" +"- C strings can contain any arbitrary non-zero byte while Rust strings must " +"be\r\n" +" UTF-8\r\n" +"- C strings are accessed and manipulated using `unsafe` pointer " +"operations\r\n" +" while interactions with Rust strings go through safe methods\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:22 +msgid "" +"The Rust standard library comes with C equivalents of Rust's `String` and " +"`&str`\r\n" +"called `CString` and `&CStr`, that allow us to avoid a lot of the " +"complexity\r\n" +"and `unsafe` code involved in converting between C strings and Rust strings." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:26 +msgid "" +"The `&CStr` type also allows us to work with borrowed data, meaning " +"passing\r\n" +"strings between Rust and C is a zero-cost operation." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:31 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" /// Log a message at the specified level.\r\n" +" ///\r\n" +" /// # Safety\r\n" +" ///\r\n" +" /// It is the caller's guarantee to ensure `msg`:\r\n" +" ///\r\n" +" /// - is not a null pointer\r\n" +" /// - points to valid, initialized data\r\n" +" /// - points to memory ending in a null byte\r\n" +" /// - won't be mutated for the duration of this function call\r\n" +" #[no_mangle]\r\n" +" pub unsafe extern \"C\" fn mylib_log(\r\n" +" msg: *const libc::c_char,\r\n" +" level: libc::c_int\r\n" +" ) {\r\n" +" let level: crate::LogLevel = match level { /* ... */ };\r\n" +"\r\n" +" // SAFETY: The caller has already guaranteed this is okay (see " +"the\r\n" +" // `# Safety` section of the doc-comment).\r\n" +" let msg_str: &str = match std::ffi::CStr::from_ptr(msg).to_str() " +"{\r\n" +" Ok(s) => s,\r\n" +" Err(e) => {\r\n" +" crate::log_error(\"FFI string conversion failed\");\r\n" +" return;\r\n" +" }\r\n" +" };\r\n" +"\r\n" +" crate::log(msg_str, level);\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:70 +msgid "The example is is written to ensure that:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:72 +msgid "" +"1. The `unsafe` block is as small as possible.\r\n" +"2. The pointer with an \"untracked\" lifetime becomes a \"tracked\" " +"shared\r\n" +" reference\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:76 +msgid "Consider an alternative, where the string is actually copied:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:78 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" pub extern \"C\" fn mylib_log(msg: *const libc::c_char, level: " +"libc::c_int) {\r\n" +" // DO NOT USE THIS CODE.\r\n" +" // IT IS UGLY, VERBOSE, AND CONTAINS A SUBTLE BUG.\r\n" +"\r\n" +" let level: crate::LogLevel = match level { /* ... */ };\r\n" +"\r\n" +" let msg_len = unsafe { /* SAFETY: strlen is what it is, I guess? " +"*/\r\n" +" libc::strlen(msg)\r\n" +" };\r\n" +"\r\n" +" let mut msg_data = Vec::with_capacity(msg_len + 1);\r\n" +"\r\n" +" let msg_cstr: std::ffi::CString = unsafe {\r\n" +" // SAFETY: copying from a foreign pointer expected to live\r\n" +" // for the entire stack frame into owned memory\r\n" +" std::ptr::copy_nonoverlapping(msg, msg_data.as_mut(), " +"msg_len);\r\n" +"\r\n" +" msg_data.set_len(msg_len + 1);\r\n" +"\r\n" +" std::ffi::CString::from_vec_with_nul(msg_data).unwrap()\r\n" +" }\r\n" +"\r\n" +" let msg_str: String = unsafe {\r\n" +" match msg_cstr.into_string() {\r\n" +" Ok(s) => s,\r\n" +" Err(e) => {\r\n" +" crate::log_error(\"FFI string conversion failed\");\r\n" +" return;\r\n" +" }\r\n" +" }\r\n" +" };\r\n" +"\r\n" +" crate::log(&msg_str, level);\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:120 +msgid "This code in inferior to the original in two respects:" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:122 +msgid "" +"1. There is much more `unsafe` code, and more importantly, more invariants " +"it\r\n" +" must uphold.\r\n" +"2. Due to the extensive arithmetic required, there is a bug in this " +"version\r\n" +" that cases Rust `undefined behaviour`.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:127 +msgid "" +"The bug here is a simple mistake in pointer arithmetic: the string was " +"copied,\r\n" +"all `msg_len` bytes of it. However, the `NUL` terminator at the end was not." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:130 +msgid "" +"The Vector then had its size *set* to the length of the *zero padded string* " +"--\r\n" +"rather than *resized* to it, which could have added a zero at the end.\r\n" +"As a result, the last byte in the Vector is uninitialized memory.\r\n" +"When the `CString` is created at the bottom of the block, its read of the\r\n" +"Vector will cause `undefined behaviour`!" +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:136 +msgid "" +"Like many such issues, this would be difficult issue to track down.\r\n" +"Sometimes it would panic because the string was not `UTF-8`, sometimes it " +"would\r\n" +"put a weird character at the end of the string, sometimes it would just\r\n" +"completely crash." +msgstr "" + +#: .\idioms/ffi/accepting-strings.md:143 +#: .\idioms/ffi/passing-strings.md:105 +msgid "None?" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:1 +msgid "# Passing Strings\r" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:5 +msgid "" +"When passing strings to FFI functions, there are four principles that should " +"be\r\n" +"followed:" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:8 +msgid "" +"1. Make the lifetime of owned strings as long as possible.\r\n" +"2. Minimize `unsafe` code during the conversion.\r\n" +"3. If the C code can modify the string data, use `Vec` instead of " +"`CString`.\r\n" +"4. Unless the Foreign Function API requires it, the ownership of the " +"string\r\n" +" should not transfer to the callee.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:16 +msgid "" +"Rust has built-in support for C-style strings with its `CString` and " +"`CStr`\r\n" +"types. However, there are different approaches one can take with strings " +"that\r\n" +"are being sent to a foreign function call from a Rust function." +msgstr "" + +#: .\idioms/ffi/passing-strings.md:20 +msgid "" +"The best practice is simple: use `CString` in such a way as to minimize\r\n" +"`unsafe` code. However, a secondary caveat is that\r\n" +"*the object must live long enough*, meaning the lifetime should be " +"maximized.\r\n" +"In addition, the documentation explains that \"round-tripping\" a `CString` " +"after\r\n" +"modification is UB, so additional work is necessary in that case." +msgstr "" + +#: .\idioms/ffi/passing-strings.md:28 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" extern \"C\" {\r\n" +" fn seterr(message: *const libc::c_char);\r\n" +" fn geterr(buffer: *mut libc::c_char, size: libc::c_int) -> " +"libc::c_int;\r\n" +" }\r\n" +"\r\n" +" fn report_error_to_ffi>(\r\n" +" err: S\r\n" +" ) -> Result<(), std::ffi::NulError>{\r\n" +" let c_err = std::ffi::CString::new(err.into())?;\r\n" +"\r\n" +" unsafe {\r\n" +" // SAFETY: calling an FFI whose documentation says the pointer " +"is\r\n" +" // const, so no modification should occur\r\n" +" seterr(c_err.as_ptr());\r\n" +" }\r\n" +"\r\n" +" Ok(())\r\n" +" // The lifetime of c_err continues until here\r\n" +" }\r\n" +"\r\n" +" fn get_error_from_ffi() -> Result " +"{\r\n" +" let mut buffer = vec![0u8; 1024];\r\n" +" unsafe {\r\n" +" // SAFETY: calling an FFI whose documentation implies\r\n" +" // that the input need only live as long as the call\r\n" +" let written: usize = geterr(buffer.as_mut_ptr(), " +"1023).into();\r\n" +"\r\n" +" buffer.truncate(written + 1);\r\n" +" }\r\n" +"\r\n" +" std::ffi::CString::new(buffer).unwrap().into_string()\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:70 +msgid "The example is written in a way to ensure that:" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:72 +msgid "" +"1. The `unsafe` block is as small as possible.\r\n" +"2. The `CString` lives long enough.\r\n" +"3. Errors with typecasts are always propagated when possible.\r\n" +"\r" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:76 +msgid "" +"A common mistake (so common it's in the documentation) is to not use the\r\n" +"variable in the first block:" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:79 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" fn report_error>(err: S) -> Result<(), " +"std::ffi::NulError> {\r\n" +" unsafe {\r\n" +" // SAFETY: whoops, this contains a dangling pointer!\r\n" +" seterr(std::ffi::CString::new(err.into())?.as_ptr());\r\n" +" }\r\n" +" Ok(())\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/ffi/passing-strings.md:94 +msgid "" +"This code will result in a dangling pointer, because the lifetime of the\r\n" +"`CString` is not extended by the pointer creation, unlike if a reference " +"were\r\n" +"created." +msgstr "" + +#: .\idioms/ffi/passing-strings.md:98 +msgid "" +"Another issue frequently raised is that the initialization of a 1k vector " +"of\r\n" +"zeroes is \"slow\". However, recent versions of Rust actually optimize " +"that\r\n" +"particular macro to a call to `zmalloc`, meaning it is as fast as the " +"operating\r\n" +"system's ability to return zeroed memory (which is quite fast)." +msgstr "" + +#: .\idioms/option-iter.md:1 +msgid "# Iterating over an `Option`\r" +msgstr "" + +#: .\idioms/option-iter.md:5 +msgid "" +"`Option` can be viewed as a container that contains either zero or one\r\n" +"element. In particular, it implements the `IntoIterator` trait, and as " +"such\r\n" +"can be used with generic code that needs such a type." +msgstr "" + +#: .\idioms/option-iter.md:9 +#: .\patterns/structural/small-crates.md:34 +#: .\patterns/structural/unsafe-mods.md:22 +msgid "## Examples\r" +msgstr "" + +#: .\idioms/option-iter.md:11 +msgid "" +"Since `Option` implements `IntoIterator`, it can be used as an argument " +"to\r\n" +"[`.extend()`](https://doc.rust-lang.org/std/iter/trait.Extend.html#tymethod.extend):" +msgstr "" + +#: .\idioms/option-iter.md:14 +msgid "" +"```rust\r\n" +"let turing = Some(\"Turing\");\r\n" +"let mut logicians = vec![\"Curry\", \"Kleene\", \"Markov\"];\r\n" +"\r\n" +"logicians.extend(turing);\r\n" +"\r\n" +"// equivalent to\r\n" +"if let Some(turing_inner) = turing {\r\n" +" logicians.push(turing_inner);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/option-iter.md:26 +msgid "" +"If you need to tack an `Option` to the end of an existing iterator, you " +"can\r\n" +"pass it to " +"[`.chain()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain):" +msgstr "" + +#: .\idioms/option-iter.md:29 +msgid "" +"```rust\r\n" +"let turing = Some(\"Turing\");\r\n" +"let logicians = vec![\"Curry\", \"Kleene\", \"Markov\"];\r\n" +"\r\n" +"for logician in logicians.iter().chain(turing.iter()) {\r\n" +" println!(\"{} is a logician\", logician);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/option-iter.md:38 +msgid "" +"Note that if the `Option` is always `Some`, then it is more idiomatic to " +"use\r\n" +"[`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) on " +"the\r\n" +"element instead." +msgstr "" + +#: .\idioms/option-iter.md:42 +msgid "" +"Also, since `Option` implements `IntoIterator`, it's possible to iterate " +"over\r\n" +"it using a `for` loop. This is equivalent to matching it with `if let " +"Some(..)`,\r\n" +"and in most cases you should prefer the latter." +msgstr "" + +#: .\idioms/option-iter.md:48 +msgid "" +"* [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is " +"an\r\n" +"iterator which yields exactly one element. It's a more readable alternative " +"to\r\n" +"`Some(foo).into_iter()`.\r\n" +"\r\n" +"* " +"[`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map)\r\n" +" is a version of " +"[`Iterator::map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map),\r\n" +" specialized to mapping functions which return `Option`.\r\n" +"\r\n" +"* The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides " +"functions\r\n" +" for converting an `Option` to a zero- or one-element slice.\r\n" +"\r\n" +"* [Documentation for " +"`Option`](https://doc.rust-lang.org/std/option/enum.Option.html)\r" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:1 +msgid "# Pass variables to closure\r" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:5 +msgid "" +"By default, closures capture their environment by borrowing. Or you can " +"use\r\n" +"`move`-closure to move whole environment. However, often you want to move " +"just\r\n" +"some variables to closure, give it copy of some data, pass it by reference, " +"or\r\n" +"perform some other transformation." +msgstr "" + +#: .\idioms/pass-var-to-closure.md:10 +msgid "Use variable rebinding in separate scope for that." +msgstr "" + +#: .\idioms/pass-var-to-closure.md:14 +msgid "Use" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:16 +msgid "" +"```rust\r\n" +"use std::rc::Rc;\r\n" +"\r\n" +"let num1 = Rc::new(1);\r\n" +"let num2 = Rc::new(2);\r\n" +"let num3 = Rc::new(3);\r\n" +"let closure = {\r\n" +" // `num1` is moved\r\n" +" let num2 = num2.clone(); // `num2` is cloned\r\n" +" let num3 = num3.as_ref(); // `num3` is borrowed\r\n" +" move || {\r\n" +" *num1 + *num2 + *num3;\r\n" +" }\r\n" +"};\r\n" +"```" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:32 +msgid "instead of" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:34 +msgid "" +"```rust\r\n" +"use std::rc::Rc;\r\n" +"\r\n" +"let num1 = Rc::new(1);\r\n" +"let num2 = Rc::new(2);\r\n" +"let num3 = Rc::new(3);\r\n" +"\r\n" +"let num2_cloned = num2.clone();\r\n" +"let num3_borrowed = num3.as_ref();\r\n" +"let closure = move || {\r\n" +" *num1 + *num2_cloned + *num3_borrowed;\r\n" +"};\r\n" +"```" +msgstr "" + +#: .\idioms/pass-var-to-closure.md:50 +msgid "" +"Copied data are grouped together with closure definition, so their purpose " +"is\r\n" +"more clear, and they will be dropped immediately even if they are not " +"consumed\r\n" +"by closure." +msgstr "" + +#: .\idioms/pass-var-to-closure.md:54 +msgid "" +"Closure uses same variable names as surrounding code whether data are copied " +"or\r\n" +"moved." +msgstr "" + +#: .\idioms/pass-var-to-closure.md:59 +msgid "Additional indentation of closure body." +msgstr "" + +#: .\idioms/priv-extend.md:1 +msgid "# `#[non_exhaustive]` and private fields for extensibility\r" +msgstr "" + +#: .\idioms/priv-extend.md:5 +msgid "" +"A small set of scenarios exist where a library author may want to add " +"public\r\n" +"fields to a public struct or new variants to an enum without breaking " +"backwards\r\n" +"compatibility." +msgstr "" + +#: .\idioms/priv-extend.md:9 +msgid "Rust offers two solutions to this problem:" +msgstr "" + +#: .\idioms/priv-extend.md:11 +msgid "" +"- Use `#[non_exhaustive]` on `struct`s, `enum`s, and `enum` variants.\r\n" +" For extensive documentation on all the places where `#[non_exhaustive]` " +"can be\r\n" +" used, see [the " +"docs](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute).\r\n" +"\r\n" +"- You may add a private field to a struct to prevent it from being " +"directly\r\n" +" instantiated or matched against (see Alternative)\r\n" +"\r" +msgstr "" + +#: .\idioms/priv-extend.md:20 +msgid "" +"```rust\r\n" +"mod a {\r\n" +" // Public struct.\r\n" +" #[non_exhaustive]\r\n" +" pub struct S {\r\n" +" pub foo: i32,\r\n" +" }\r\n" +" \r\n" +" #[non_exhaustive]\r\n" +" pub enum AdmitMoreVariants {\r\n" +" VariantA,\r\n" +" VariantB,\r\n" +" #[non_exhaustive]\r\n" +" VariantC { a: String }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn print_matched_variants(s: a::S) {\r\n" +" // Because S is `#[non_exhaustive]`, it cannot be named here and\r\n" +" // we must use `..` in the pattern.\r\n" +" let a::S { foo: _, ..} = s;\r\n" +" \r\n" +" let some_enum = a::AdmitMoreVariants::VariantA;\r\n" +" match some_enum {\r\n" +" a::AdmitMoreVariants::VariantA => println!(\"it's an A\"),\r\n" +" a::AdmitMoreVariants::VariantB => println!(\"it's a b\"),\r\n" +"\r\n" +" // .. required because this variant is non-exhaustive as well\r\n" +" a::AdmitMoreVariants::VariantC { a, .. } => println!(\"it's a " +"c\"),\r\n" +"\r\n" +" // The wildcard match is required because more variants may be\r\n" +" // added in the future\r\n" +" _ => println!(\"it's a new variant\")\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/priv-extend.md:57 +msgid "## Alternative: `Private fields` for structs\r" +msgstr "" + +#: .\idioms/priv-extend.md:59 +msgid "" +"`#[non_exhaustive]` only works across crate boundaries.\r\n" +"Within a crate, the private field method may be used." +msgstr "" + +#: .\idioms/priv-extend.md:62 +msgid "" +"Adding a field to a struct is a mostly backwards compatible change.\r\n" +"However, if a client uses a pattern to deconstruct a struct instance, " +"they\r\n" +"might name all the fields in the struct and adding a new one would break " +"that\r\n" +"pattern.\r\n" +"The client could name some fields and use `..` in the pattern, in which case " +"adding\r\n" +"another field is backwards compatible.\r\n" +"Making at least one of the struct's fields private forces clients to use the " +"latter\r\n" +"form of patterns, ensuring that the struct is future-proof." +msgstr "" + +#: .\idioms/priv-extend.md:71 +msgid "" +"The downside of this approach is that you might need to add an otherwise " +"unneeded\r\n" +"field to the struct.\r\n" +"You can use the `()` type so that there is no runtime overhead and prepend " +"`_` to\r\n" +"the field name to avoid the unused field warning." +msgstr "" + +#: .\idioms/priv-extend.md:76 +msgid "" +"```rust\r\n" +"pub struct S {\r\n" +" pub a: i32,\r\n" +" // Because `b` is private, you cannot match on `S` without using `..` " +"and `S`\r\n" +" // cannot be directly instantiated or matched against\r\n" +" _b: ()\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/priv-extend.md:87 +msgid "" +"On `struct`s, `#[non_exhaustive]` allows adding additional fields in a " +"backwards\r\n" +"compatible way.\r\n" +"It will also prevent clients from using the struct constructor, even if all " +"the\r\n" +"fields are public.\r\n" +"This may be helpful, but it's worth considering if you _want_ an additional " +"field\r\n" +"to be found by clients as a compiler error rather than something that may be " +"silently\r\n" +"undiscovered." +msgstr "" + +#: .\idioms/priv-extend.md:95 +msgid "" +"`#[non_exhaustive]` can be applied to enum variants as well.\r\n" +"A `#[non_exhaustive]` variant behaves in the same way as a " +"`#[non_exhaustive]` struct." +msgstr "" + +#: .\idioms/priv-extend.md:98 +msgid "" +"Use this deliberately and with caution: incrementing the major version when " +"adding\r\n" +"fields or variants is often a better option.\r\n" +"`#[non_exhaustive]` may be appropriate in scenarios where you're modeling an " +"external\r\n" +"resource that may change out-of-sync with your library, but is not a general " +"purpose\r\n" +"tool." +msgstr "" + +#: .\idioms/priv-extend.md:104 +msgid "### Disadvantages\r" +msgstr "" + +#: .\idioms/priv-extend.md:106 +msgid "" +"`#[non_exhaustive]` can make your code much less ergonomic to use, " +"especially when\r\n" +"forced to handle unknown enum variants.\r\n" +"It should only be used when these sorts of evolutions are required " +"**without**\r\n" +"incrementing the major version." +msgstr "" + +#: .\idioms/priv-extend.md:111 +msgid "" +"When `#[non_exhaustive]` is applied to `enum`s, it forces clients to handle " +"a\r\n" +"wildcard variant.\r\n" +"If there is no sensible action to take in this case, this may lead to " +"awkward\r\n" +"code and code paths that are only executed in extremely rare " +"circumstances.\r\n" +"If a client decides to `panic!()` in this scenario, it may have been better " +"to\r\n" +"expose this error at compile time.\r\n" +"In fact, `#[non_exhaustive]` forces clients to handle the \"Something else\" " +"case;\r\n" +"there is rarely a sensible action to take in this scenario." +msgstr "" + +#: .\idioms/priv-extend.md:122 +msgid "" +"- [RFC introducing #[non_exhaustive] attribute for enums and " +"structs](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md)\r" +msgstr "" + +#: .\idioms/rustdoc-init.md:1 +msgid "# Easy doc initialization\r" +msgstr "" + +#: .\idioms/rustdoc-init.md:5 +msgid "" +"If a struct takes significant effort to initialize when writing docs, it can " +"be\r\n" +"quicker to wrap your example with a helper function which takes the struct " +"as an\r\n" +"argument." +msgstr "" + +#: .\idioms/rustdoc-init.md:11 +msgid "" +"Sometimes there is a struct with multiple or complicated parameters and " +"several\r\n" +"methods. Each of these methods should have examples." +msgstr "" + +#: .\idioms/rustdoc-init.md:14 +msgid "For example:" +msgstr "" + +#: .\idioms/rustdoc-init.md:16 +msgid "" +"```rust,ignore\r\n" +"struct Connection {\r\n" +" name: String,\r\n" +" stream: TcpStream,\r\n" +"}\r\n" +"\r\n" +"impl Connection {\r\n" +" /// Sends a request over the connection.\r\n" +" ///\r\n" +" /// # Example\r\n" +" /// ```no_run\r\n" +" /// # // Boilerplate are required to get an example working.\r\n" +" /// # let stream = TcpStream::connect(\"127.0.0.1:34254\");\r\n" +" /// # let connection = Connection { name: \"foo\".to_owned(), stream " +"};\r\n" +" /// # let request = Request::new(\"RequestId\", RequestType::Get, " +"\"payload\");\r\n" +" /// let response = connection.send_request(request);\r\n" +" /// assert!(response.is_ok());\r\n" +" /// ```\r\n" +" fn send_request(&self, request: Request) -> Result {\r\n" +" // ...\r\n" +" }\r\n" +"\r\n" +" /// Oh no, all that boilerplate needs to be repeated here!\r\n" +" fn check_status(&self) -> Status {\r\n" +" // ...\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/rustdoc-init.md:47 +msgid "" +"Instead of typing all of this boilerplate to create a `Connection` and\r\n" +"`Request`, it is easier to just create a wrapping helper function which " +"takes\r\n" +"them as arguments:" +msgstr "" + +#: .\idioms/rustdoc-init.md:51 +msgid "" +"```rust,ignore\r\n" +"struct Connection {\r\n" +" name: String,\r\n" +" stream: TcpStream,\r\n" +"}\r\n" +"\r\n" +"impl Connection {\r\n" +" /// Sends a request over the connection.\r\n" +" ///\r\n" +" /// # Example\r\n" +" /// ```\r\n" +" /// # fn call_send(connection: Connection, request: Request) {\r\n" +" /// let response = connection.send_request(request);\r\n" +" /// assert!(response.is_ok());\r\n" +" /// # }\r\n" +" /// ```\r\n" +" fn send_request(&self, request: Request) {\r\n" +" // ...\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/rustdoc-init.md:73 +msgid "" +"**Note** in the above example the line `assert!(response.is_ok());` will " +"not\r\n" +"actually run while testing because it is inside a function which is never\r\n" +"invoked." +msgstr "" + +#: .\idioms/rustdoc-init.md:79 +msgid "This is much more concise and avoids repetitive code in examples." +msgstr "" + +#: .\idioms/rustdoc-init.md:83 +msgid "" +"As example is in a function, the code will not be tested. Though it will " +"still be\r\n" +"checked to make sure it compiles when running a `cargo test`. So this " +"pattern is\r\n" +"most useful when you need `no_run`. With this, you do not need to add " +"`no_run`." +msgstr "" + +#: .\idioms/rustdoc-init.md:89 +msgid "If assertions are not required this pattern works well." +msgstr "" + +#: .\idioms/rustdoc-init.md:91 +msgid "" +"If they are, an alternative can be to create a public method to create a " +"helper\r\n" +"instance which is annotated with `#[doc(hidden)]` (so that users won't see " +"it).\r\n" +"Then this method can be called inside of rustdoc because it is part of " +"the\r\n" +"crate's public API." +msgstr "" + +#: .\idioms/temporary-mutability.md:1 +msgid "# Temporary mutability\r" +msgstr "" + +#: .\idioms/temporary-mutability.md:5 +msgid "" +"Often it is necessary to prepare and process some data, but after that data " +"are\r\n" +"only inspected and never modified. The intention can be made explicit by " +"redefining\r\n" +"the mutable variable as immutable." +msgstr "" + +#: .\idioms/temporary-mutability.md:9 +msgid "" +"It can be done either by processing data within a nested block or by " +"redefining\r\n" +"the variable." +msgstr "" + +#: .\idioms/temporary-mutability.md:14 +msgid "Say, vector must be sorted before usage." +msgstr "" + +#: .\idioms/temporary-mutability.md:16 +msgid "Using nested block:" +msgstr "" + +#: .\idioms/temporary-mutability.md:18 +msgid "" +"```rust,ignore\r\n" +"let data = {\r\n" +" let mut data = get_vec();\r\n" +" data.sort();\r\n" +" data\r\n" +"};\r\n" +"\r\n" +"// Here `data` is immutable.\r\n" +"```" +msgstr "" + +#: .\idioms/temporary-mutability.md:28 +msgid "Using variable rebinding:" +msgstr "" + +#: .\idioms/temporary-mutability.md:30 +msgid "" +"```rust,ignore\r\n" +"let mut data = get_vec();\r\n" +"data.sort();\r\n" +"let data = data;\r\n" +"\r\n" +"// Here `data` is immutable.\r\n" +"```" +msgstr "" + +#: .\idioms/temporary-mutability.md:40 +msgid "" +"Compiler ensures that you don't accidentally mutate data after some point." +msgstr "" + +#: .\idioms/temporary-mutability.md:44 +msgid "" +"Nested block requires additional indentation of block body.\r\n" +"One more line to return data from block or redefine variable." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:1 +msgid "# Return consumed argument on error\r" +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:5 +msgid "" +"If a fallible function consumes (moves) an argument, return that argument " +"back inside\r\n" +"an error." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:10 +msgid "" +"```rust\r\n" +"pub fn send(value: String) -> Result<(), SendError> {\r\n" +" println!(\"using {value} in a meaningful way\");\r\n" +" // Simulate non-deterministic fallible action.\r\n" +" use std::time::SystemTime;\r\n" +" let period = " +"SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();\r\n" +" if period.subsec_nanos() % 2 == 1 {\r\n" +" Ok(())\r\n" +" } else {\r\n" +" Err(SendError(value))\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub struct SendError(String);\r\n" +"\r\n" +"fn main() {\r\n" +" let mut value = \"imagine this is very long string\".to_string();\r\n" +"\r\n" +" let success = 's: {\r\n" +" // Try to send value two times.\r\n" +" for _ in 0..2 {\r\n" +" value = match send(value) {\r\n" +" Ok(()) => break 's true,\r\n" +" Err(SendError(value)) => value,\r\n" +" }\r\n" +" }\r\n" +" false\r\n" +" };\r\n" +"\r\n" +" println!(\"success: {}\", success);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:45 +msgid "" +"In case of error you may want to try some alternative way or to\r\n" +"retry action in case of non-deterministic function. But if the argument\r\n" +"is always consumed, you are forced to clone it on every call, which\r\n" +"is not very efficient." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:50 +msgid "" +"The standard library uses this approach in e.g. `String::from_utf8` " +"method.\r\n" +"When given a vector that doesn't contain valid UTF-8, a `FromUtf8Error`\r\n" +"is returned.\r\n" +"You can get original vector back using `FromUtf8Error::into_bytes` method." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:57 +msgid "Better performance because of moving arguments whenever possible." +msgstr "" + +#: .\idioms/return-consumed-arg-on-error.md:61 +msgid "Slightly more complex error types." +msgstr "" + +#: .\patterns/index.md:1 +msgid "# Design Patterns\r" +msgstr "" + +#: .\patterns/index.md:3 +msgid "" +"[Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) " +"are\r\n" +"\"general reusable solutions to a commonly occurring problem within a " +"given\r\n" +"context in software design\". Design patterns are a great way to describe " +"the\r\n" +"culture of a programming language. Design patterns are very " +"language-specific -\r\n" +"what is a pattern in one language may be unnecessary in another due to a\r\n" +"language feature, or impossible to express due to a missing feature." +msgstr "" + +#: .\patterns/index.md:10 +msgid "" +"If overused, design patterns can add unnecessary complexity to programs.\r\n" +"However, they are a great way to share intermediate and advanced level " +"knowledge\r\n" +"about a programming language." +msgstr "" + +#: .\patterns/index.md:16 +msgid "" +"Rust has many unique features. These features give us great benefit by " +"removing\r\n" +"whole classes of problems. Some of them are also patterns that are _unique_ " +"to Rust." +msgstr "" + +#: .\patterns/index.md:19 +msgid "## YAGNI\r" +msgstr "" + +#: .\patterns/index.md:21 +msgid "" +"YAGNI is an acronym that stands for `You Aren't Going to Need It`.\r\n" +"It's a vital software design principle to apply as you write code." +msgstr "" + +#: .\patterns/index.md:24 +msgid "> The best code I ever wrote was code I never wrote.\r" +msgstr "" + +#: .\patterns/index.md:26 +msgid "" +"If we apply YAGNI to design patterns, we see that the features of Rust allow " +"us to\r\n" +"throw out many patterns. For instance, there is no need for the [strategy " +"pattern](https://en.wikipedia.org/wiki/Strategy_pattern)\r\n" +"in Rust because we can just use " +"[traits](https://doc.rust-lang.org/book/traits.html)." +msgstr "" + +#: .\patterns/behavioural/intro.md:1 +msgid "# Behavioural Patterns\r" +msgstr "" + +#: .\patterns/behavioural/intro.md:3 +msgid "From [Wikipedia](https://en.wikipedia.org/wiki/Behavioral_pattern):" +msgstr "" + +#: .\patterns/behavioural/intro.md:5 +msgid "" +"> Design patterns that identify common communication patterns among " +"objects.\r\n" +"> By doing so, these patterns increase flexibility in carrying out " +"communication.\r" +msgstr "" + +#: .\patterns/behavioural/command.md:1 +msgid "# Command\r" +msgstr "" + +#: .\patterns/behavioural/command.md:5 +msgid "" +"The basic idea of the Command pattern is to separate out actions into its " +"own\r\n" +"objects and pass them as parameters." +msgstr "" + +#: .\patterns/behavioural/command.md:10 +msgid "" +"Suppose we have a sequence of actions or transactions encapsulated as " +"objects.\r\n" +"We want these actions or commands to be executed or invoked in some order " +"later\r\n" +"at different time. These commands may also be triggered as a result of some " +"event.\r\n" +"For example, when a user pushes a button, or on arrival of a data packet.\r\n" +"In addition, these commands might be undoable. This may come in useful " +"for\r\n" +"operations of an editor. We might want to store logs of executed commands so " +"that\r\n" +"we could reapply the changes later if the system crashes." +msgstr "" + +#: .\patterns/behavioural/command.md:20 +msgid "" +"Define two database operations `create table` and `add field`. Each of " +"these\r\n" +"operations is a command which knows how to undo the command, e.g., `drop " +"table`\r\n" +"and `remove field`. When a user invokes a database migration operation then " +"each\r\n" +"command is executed in the defined order, and when the user invokes the " +"rollback\r\n" +"operation then the whole set of commands is invoked in reverse order." +msgstr "" + +#: .\patterns/behavioural/command.md:26 +msgid "## Approach: Using trait objects\r" +msgstr "" + +#: .\patterns/behavioural/command.md:28 +msgid "" +"We define a common trait which encapsulates our command with two " +"operations\r\n" +"`execute` and `rollback`. All command `structs` must implement this trait." +msgstr "" + +#: .\patterns/behavioural/command.md:31 +msgid "" +"```rust\r\n" +"pub trait Migration {\r\n" +" fn execute(&self) -> &str;\r\n" +" fn rollback(&self) -> &str;\r\n" +"}\r\n" +"\r\n" +"pub struct CreateTable;\r\n" +"impl Migration for CreateTable {\r\n" +" fn execute(&self) -> &str {\r\n" +" \"create table\"\r\n" +" }\r\n" +" fn rollback(&self) -> &str {\r\n" +" \"drop table\"\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub struct AddField;\r\n" +"impl Migration for AddField {\r\n" +" fn execute(&self) -> &str {\r\n" +" \"add field\"\r\n" +" }\r\n" +" fn rollback(&self) -> &str {\r\n" +" \"remove field\"\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"struct Schema {\r\n" +" commands: Vec>,\r\n" +"}\r\n" +"\r\n" +"impl Schema {\r\n" +" fn new() -> Self {\r\n" +" Self { commands: vec![] }\r\n" +" }\r\n" +"\r\n" +" fn add_migration(&mut self, cmd: Box) {\r\n" +" self.commands.push(cmd);\r\n" +" }\r\n" +"\r\n" +" fn execute(&self) -> Vec<&str> {\r\n" +" self.commands.iter().map(|cmd| cmd.execute()).collect()\r\n" +" }\r\n" +" fn rollback(&self) -> Vec<&str> {\r\n" +" self.commands\r\n" +" .iter()\r\n" +" .rev() // reverse iterator's direction\r\n" +" .map(|cmd| cmd.rollback())\r\n" +" .collect()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let mut schema = Schema::new();\r\n" +"\r\n" +" let cmd = Box::new(CreateTable);\r\n" +" schema.add_migration(cmd);\r\n" +" let cmd = Box::new(AddField);\r\n" +" schema.add_migration(cmd);\r\n" +"\r\n" +" assert_eq!(vec![\"create table\", \"add field\"], schema.execute());\r\n" +" assert_eq!(vec![\"remove field\", \"drop table\"], " +"schema.rollback());\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/command.md:95 +msgid "## Approach: Using function pointers\r" +msgstr "" + +#: .\patterns/behavioural/command.md:97 +msgid "" +"We could follow another approach by creating each individual command as\r\n" +"a different function and store function pointers to invoke these functions " +"later\r\n" +"at a different time. Since function pointers implement all three traits " +"`Fn`,\r\n" +"`FnMut`, and `FnOnce` we could as well pass and store closures instead of\r\n" +"function pointers." +msgstr "" + +#: .\patterns/behavioural/command.md:103 +msgid "" +"```rust\r\n" +"type FnPtr = fn() -> String;\r\n" +"struct Command {\r\n" +" execute: FnPtr,\r\n" +" rollback: FnPtr,\r\n" +"}\r\n" +"\r\n" +"struct Schema {\r\n" +" commands: Vec,\r\n" +"}\r\n" +"\r\n" +"impl Schema {\r\n" +" fn new() -> Self {\r\n" +" Self { commands: vec![] }\r\n" +" }\r\n" +" fn add_migration(&mut self, execute: FnPtr, rollback: FnPtr) {\r\n" +" self.commands.push(Command { execute, rollback });\r\n" +" }\r\n" +" fn execute(&self) -> Vec {\r\n" +" self.commands.iter().map(|cmd| (cmd.execute)()).collect()\r\n" +" }\r\n" +" fn rollback(&self) -> Vec {\r\n" +" self.commands\r\n" +" .iter()\r\n" +" .rev()\r\n" +" .map(|cmd| (cmd.rollback)())\r\n" +" .collect()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn add_field() -> String {\r\n" +" \"add field\".to_string()\r\n" +"}\r\n" +"\r\n" +"fn remove_field() -> String {\r\n" +" \"remove field\".to_string()\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let mut schema = Schema::new();\r\n" +" schema.add_migration(|| \"create table\".to_string(), || \"drop " +"table\".to_string());\r\n" +" schema.add_migration(add_field, remove_field);\r\n" +" assert_eq!(vec![\"create table\", \"add field\"], schema.execute());\r\n" +" assert_eq!(vec![\"remove field\", \"drop table\"], " +"schema.rollback());\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/command.md:150 +msgid "## Approach: Using `Fn` trait objects\r" +msgstr "" + +#: .\patterns/behavioural/command.md:152 +msgid "" +"Finally, instead of defining a common command trait we could store\r\n" +"each command implementing the `Fn` trait separately in vectors." +msgstr "" + +#: .\patterns/behavioural/command.md:155 +msgid "" +"```rust\r\n" +"type Migration<'a> = Box &'a str>;\r\n" +"\r\n" +"struct Schema<'a> {\r\n" +" executes: Vec>,\r\n" +" rollbacks: Vec>,\r\n" +"}\r\n" +"\r\n" +"impl<'a> Schema<'a> {\r\n" +" fn new() -> Self {\r\n" +" Self {\r\n" +" executes: vec![],\r\n" +" rollbacks: vec![],\r\n" +" }\r\n" +" }\r\n" +" fn add_migration(&mut self, execute: E, rollback: R)\r\n" +" where\r\n" +" E: Fn() -> &'a str + 'static,\r\n" +" R: Fn() -> &'a str + 'static,\r\n" +" {\r\n" +" self.executes.push(Box::new(execute));\r\n" +" self.rollbacks.push(Box::new(rollback));\r\n" +" }\r\n" +" fn execute(&self) -> Vec<&str> {\r\n" +" self.executes.iter().map(|cmd| cmd()).collect()\r\n" +" }\r\n" +" fn rollback(&self) -> Vec<&str> {\r\n" +" self.rollbacks.iter().rev().map(|cmd| cmd()).collect()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn add_field() -> &'static str {\r\n" +" \"add field\"\r\n" +"}\r\n" +"\r\n" +"fn remove_field() -> &'static str {\r\n" +" \"remove field\"\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let mut schema = Schema::new();\r\n" +" schema.add_migration(|| \"create table\", || \"drop table\");\r\n" +" schema.add_migration(add_field, remove_field);\r\n" +" assert_eq!(vec![\"create table\", \"add field\"], schema.execute());\r\n" +" assert_eq!(vec![\"remove field\", \"drop table\"], " +"schema.rollback());\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/command.md:205 +msgid "" +"If our commands are small and may be defined as functions or passed as a " +"closure\r\n" +"then using function pointers might be preferable since it does not " +"exploit\r\n" +"dynamic dispatch. But if our command is a whole struct with a bunch of " +"functions\r\n" +"and variables defined as seperated module then using trait objects would " +"be\r\n" +"more suitable. A case of application can be found in " +"[`actix`](https://actix.rs/),\r\n" +"which uses trait objects when it registers a handler function for routes.\r\n" +"In case of using `Fn` trait objects we can create and use commands in the " +"same\r\n" +"way as we used in case of function pointers." +msgstr "" + +#: .\patterns/behavioural/command.md:214 +msgid "" +"As performance, there is always a trade-off between performance and code\r\n" +"simplicity and organisation. Static dispatch gives faster performance, " +"while\r\n" +"dynamic dispatch provides flexibility when we structure our application." +msgstr "" + +#: .\patterns/behavioural/command.md:220 +msgid "" +"- [Command pattern](https://en.wikipedia.org/wiki/Command_pattern)\r\n" +"\r\n" +"- [Another example for the `command` " +"pattern](https://web.archive.org/web/20210223131236/https://chercher.tech/rust/command-design-pattern-rust)\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:1 +msgid "# Interpreter\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:5 +msgid "" +"If a problem occurs very often and requires long and repetitive steps to " +"solve\r\n" +"it, then the problem instances might be expressed in a simple language and " +"an\r\n" +"interpreter object could solve it by interpreting the sentences written in " +"this\r\n" +"simple language." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:10 +msgid "Basically, for any kind of problems we define:" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:12 +msgid "" +"- A [domain specific " +"language](https://en.wikipedia.org/wiki/Domain-specific_language),\r\n" +"- A grammar for this language,\r\n" +"- An interpreter that solves the problem instances.\r\n" +"\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:18 +msgid "" +"Our goal is to translate simple mathematical expressions into postfix " +"expressions\r\n" +"(or [Reverse Polish " +"notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation))\r\n" +"For simplicity, our expressions consist of ten digits `0`, ..., `9` and " +"two\r\n" +"operations `+`, `-`. For example, the expression `2 + 4` is translated " +"into\r\n" +"`2 4 +`." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:24 +msgid "## Context Free Grammar for our problem\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:26 +msgid "" +"Our task is translating infix expressions into postfix ones. Let's define a " +"context\r\n" +"free grammar for a set of infix expressions over `0`, ..., `9`, `+`, and " +"`-`,\r\n" +"where:" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:30 +msgid "" +"- Terminal symbols: `0`, `...`, `9`, `+`, `-`\r\n" +"- Non-terminal symbols: `exp`, `term`\r\n" +"- Start symbol is `exp`\r\n" +"- And the following are production rules\r\n" +"\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:35 +msgid "" +"```ignore\r\n" +"exp -> exp + term\r\n" +"exp -> exp - term\r\n" +"exp -> term\r\n" +"term -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:42 +msgid "" +"__NOTE:__ This grammar should be further transformed depending on what we " +"are going\r\n" +"to do with it. For example, we might need to remove left recursion. For " +"more\r\n" +"details please see [Compilers: Principles,Techniques, and Tools\r\n" +"](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools)\r\n" +"(aka Dragon Book)." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:48 +msgid "## Solution\r" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:50 +msgid "" +"We simply implement a recursive descent parser. For simplicity's sake, the " +"code\r\n" +"panics when an expression is syntactically wrong (for example `2-34` or " +"`2+5-`\r\n" +"are wrong according to the grammar definition)." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:54 +msgid "" +"```rust\r\n" +"pub struct Interpreter<'a> {\r\n" +" it: std::str::Chars<'a>,\r\n" +"}\r\n" +"\r\n" +"impl<'a> Interpreter<'a> {\r\n" +"\r\n" +" pub fn new(infix: &'a str) -> Self {\r\n" +" Self { it: infix.chars() }\r\n" +" }\r\n" +"\r\n" +" fn next_char(&mut self) -> Option {\r\n" +" self.it.next()\r\n" +" }\r\n" +"\r\n" +" pub fn interpret(&mut self, out: &mut String) {\r\n" +" self.term(out);\r\n" +"\r\n" +" while let Some(op) = self.next_char() {\r\n" +" if op == '+' || op == '-' {\r\n" +" self.term(out);\r\n" +" out.push(op);\r\n" +" } else {\r\n" +" panic!(\"Unexpected symbol '{}'\", op);\r\n" +" }\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" fn term(&mut self, out: &mut String) {\r\n" +" match self.next_char() {\r\n" +" Some(ch) if ch.is_digit(10) => out.push(ch),\r\n" +" Some(ch) => panic!(\"Unexpected symbol '{}'\", ch),\r\n" +" None => panic!(\"Unexpected end of string\"),\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub fn main() {\r\n" +" let mut intr = Interpreter::new(\"2+3\");\r\n" +" let mut postfix = String::new();\r\n" +" intr.interpret(&mut postfix);\r\n" +" assert_eq!(postfix, \"23+\");\r\n" +"\r\n" +" intr = Interpreter::new(\"1-2+3-4\");\r\n" +" postfix.clear();\r\n" +" intr.interpret(&mut postfix);\r\n" +" assert_eq!(postfix, \"12-3+4-\");\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:106 +msgid "" +"There may be a wrong perception that the Interpreter design pattern is about " +"design\r\n" +"grammars for formal languages and implementation of parsers for these " +"grammars.\r\n" +"In fact, this pattern is about expressing problem instances in a more " +"specific\r\n" +"way and implementing functions/classes/structs that solve these problem " +"instances.\r\n" +"Rust language has `macro_rules!` that allow us to define special syntax and " +"rules\r\n" +"on how to expand this syntax into source code." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:113 +msgid "" +"In the following example we create a simple `macro_rules!` that computes\r\n" +"[Euclidean length](https://en.wikipedia.org/wiki/Euclidean_distance) of " +"`n`\r\n" +"dimensional vectors. Writing `norm!(x,1,2)` might be easier to express and " +"more\r\n" +"efficient than packing `x,1,2` into a `Vec` and calling a function " +"computing\r\n" +"the length." +msgstr "" + +#: .\patterns/behavioural/interpreter.md:119 +msgid "" +"```rust\r\n" +"macro_rules! norm {\r\n" +" ($($element:expr),*) => {\r\n" +" {\r\n" +" let mut n = 0.0;\r\n" +" $(\r\n" +" n += ($element as f64)*($element as f64);\r\n" +" )*\r\n" +" n.sqrt()\r\n" +" }\r\n" +" };\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let x = -3f64;\r\n" +" let y = 4f64;\r\n" +"\r\n" +" assert_eq!(3f64, norm!(x));\r\n" +" assert_eq!(5f64, norm!(x, y));\r\n" +" assert_eq!(0f64, norm!(0, 0, 0)); \r\n" +" assert_eq!(1f64, norm!(0.5, -0.5, 0.5, -0.5));\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/interpreter.md:145 +msgid "" +"- [Interpreter " +"pattern](https://en.wikipedia.org/wiki/Interpreter_pattern)\r\n" +"- [Context free " +"grammar](https://en.wikipedia.org/wiki/Context-free_grammar)\r\n" +"- [macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html)\r" +msgstr "" + +#: .\patterns/behavioural/newtype.md:1 +msgid "# Newtype\r" +msgstr "" + +#: .\patterns/behavioural/newtype.md:3 +msgid "" +"What if in some cases we want a type to behave similar to another type or\r\n" +"enforce some behaviour at compile time when using only type aliases would\r\n" +"not be enough?" +msgstr "" + +#: .\patterns/behavioural/newtype.md:7 +msgid "" +"For example, if we want to create a custom `Display` implementation for " +"`String`\r\n" +"due to security considerations (e.g. passwords)." +msgstr "" + +#: .\patterns/behavioural/newtype.md:10 +msgid "" +"For such cases we could use the `Newtype` pattern to provide __type " +"safety__\r\n" +"and __encapsulation__." +msgstr "" + +#: .\patterns/behavioural/newtype.md:15 +msgid "" +"Use a tuple struct with a single field to make an opaque wrapper for a " +"type.\r\n" +"This creates a new type, rather than an alias to a type (`type` items)." +msgstr "" + +#: .\patterns/behavioural/newtype.md:20 +msgid "" +"```rust,ignore\r\n" +"// Some type, not necessarily in the same module or even crate.\r\n" +"struct Foo {\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"impl Foo {\r\n" +" // These functions are not present on Bar.\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"// The newtype.\r\n" +"pub struct Bar(Foo);\r\n" +"\r\n" +"impl Bar {\r\n" +" // Constructor.\r\n" +" pub fn new(\r\n" +" //..\r\n" +" ) -> Self {\r\n" +"\r\n" +" //..\r\n" +"\r\n" +" }\r\n" +"\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let b = Bar::new(...);\r\n" +"\r\n" +" // Foo and Bar are type incompatible, the following do not type " +"check.\r\n" +" // let f: Foo = b;\r\n" +" // let b: Bar = Foo { ... };\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/newtype.md:58 +msgid "" +"The primary motivation for newtypes is abstraction. It allows you to " +"share\r\n" +"implementation details between types while precisely controlling the " +"interface.\r\n" +"By using a newtype rather than exposing the implementation type as part of " +"an\r\n" +"API, it allows you to change implementation backwards compatibly." +msgstr "" + +#: .\patterns/behavioural/newtype.md:63 +msgid "" +"Newtypes can be used for distinguishing units, e.g., wrapping `f64` to " +"give\r\n" +"distinguishable `Miles` and `Kilometres`." +msgstr "" + +#: .\patterns/behavioural/newtype.md:68 +msgid "" +"The wrapped and wrapper types are not type compatible (as opposed to " +"using\r\n" +"`type`), so users of the newtype will never 'confuse' the wrapped and " +"wrapper\r\n" +"types." +msgstr "" + +#: .\patterns/behavioural/newtype.md:72 +msgid "Newtypes are a zero-cost abstraction - there is no runtime overhead." +msgstr "" + +#: .\patterns/behavioural/newtype.md:74 +msgid "" +"The privacy system ensures that users cannot access the wrapped type (if " +"the\r\n" +"field is private, which it is by default)." +msgstr "" + +#: .\patterns/behavioural/newtype.md:79 +msgid "" +"The downside of newtypes (especially compared with type aliases), is that " +"there\r\n" +"is no special language support. This means there can be *a lot* of " +"boilerplate.\r\n" +"You need a 'pass through' method for every method you want to expose on " +"the\r\n" +"wrapped type, and an impl for every trait you want to also be implemented " +"for\r\n" +"the wrapper type." +msgstr "" + +#: .\patterns/behavioural/newtype.md:87 +msgid "" +"Newtypes are very common in Rust code. Abstraction or representing units are " +"the\r\n" +"most common uses, but they can be used for other reasons:" +msgstr "" + +#: .\patterns/behavioural/newtype.md:90 +msgid "" +"- restricting functionality (reduce the functions exposed or traits " +"implemented),\r\n" +"- making a type with copy semantics have move semantics,\r\n" +"- abstraction by providing a more concrete type and thus hiding internal " +"types,\r\n" +" e.g.,\r\n" +"\r" +msgstr "" + +#: .\patterns/behavioural/newtype.md:95 +msgid "" +"```rust,ignore\r\n" +"pub struct Foo(Bar);\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/newtype.md:99 +msgid "" +"Here, `Bar` might be some public, generic type and `T1` and `T2` are some " +"internal\r\n" +"types. Users of our module shouldn't know that we implement `Foo` by using a " +"`Bar`,\r\n" +"but what we're really hiding here is the types `T1` and `T2`, and how they " +"are used\r\n" +"with `Bar`." +msgstr "" + +#: .\patterns/behavioural/newtype.md:106 +msgid "" +"- [Advanced Types in the " +"book](https://doc.rust-lang.org/book/ch19-04-advanced-types.html?highlight=newtype#using-the-newtype-pattern-for-type-safety-and-abstraction)\r\n" +"- [Newtypes in Haskell](https://wiki.haskell.org/Newtype)\r\n" +"- [Type " +"aliases](https://doc.rust-lang.org/stable/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases)\r\n" +"- [derive_more](https://crates.io/crates/derive_more), a crate for deriving " +"many\r\n" +" builtin traits on newtypes.\r\n" +"- [The Newtype Pattern In " +"Rust](https://www.worthe-it.co.za/blog/2020-10-31-newtype-pattern-in-rust.html)\r" +msgstr "" + +#: .\patterns/behavioural/RAII.md:1 +msgid "# RAII with guards\r" +msgstr "" + +#: .\patterns/behavioural/RAII.md:5 +msgid "" +"[RAII][wikipedia] stands for \"Resource Acquisition is Initialisation\" " +"which is a\r\n" +"terrible name. The essence of the pattern is that resource initialisation is " +"done\r\n" +"in the constructor of an object and finalisation in the destructor. This " +"pattern\r\n" +"is extended in Rust by using a RAII object as a guard of some resource and " +"relying\r\n" +"on the type system to ensure that access is always mediated by the guard " +"object." +msgstr "" + +#: .\patterns/behavioural/RAII.md:13 +msgid "" +"Mutex guards are the classic example of this pattern from the std library " +"(this\r\n" +"is a simplified version of the real implementation):" +msgstr "" + +#: .\patterns/behavioural/RAII.md:16 +msgid "" +"```rust,ignore\r\n" +"use std::ops::Deref;\r\n" +"\r\n" +"struct Foo {}\r\n" +"\r\n" +"struct Mutex {\r\n" +" // We keep a reference to our data: T here.\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"struct MutexGuard<'a, T: 'a> {\r\n" +" data: &'a T,\r\n" +" //..\r\n" +"}\r\n" +"\r\n" +"// Locking the mutex is explicit.\r\n" +"impl Mutex {\r\n" +" fn lock(&self) -> MutexGuard {\r\n" +" // Lock the underlying OS mutex.\r\n" +" //..\r\n" +"\r\n" +" // MutexGuard keeps a reference to self\r\n" +" MutexGuard {\r\n" +" data: self,\r\n" +" //..\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// Destructor for unlocking the mutex.\r\n" +"impl<'a, T> Drop for MutexGuard<'a, T> {\r\n" +" fn drop(&mut self) {\r\n" +" // Unlock the underlying OS mutex.\r\n" +" //..\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// Implementing Deref means we can treat MutexGuard like a pointer to T.\r\n" +"impl<'a, T> Deref for MutexGuard<'a, T> {\r\n" +" type Target = T;\r\n" +"\r\n" +" fn deref(&self) -> &T {\r\n" +" self.data\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn baz(x: Mutex) {\r\n" +" let xx = x.lock();\r\n" +" xx.foo(); // foo is a method on Foo.\r\n" +" // The borrow checker ensures we can't store a reference to the " +"underlying\r\n" +" // Foo which will outlive the guard xx.\r\n" +"\r\n" +" // x is unlocked when we exit this function and xx's destructor is " +"executed.\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/RAII.md:74 +msgid "" +"Where a resource must be finalised after use, RAII can be used to do this\r\n" +"finalisation. If it is an error to access that resource after finalisation, " +"then\r\n" +"this pattern can be used to prevent such errors." +msgstr "" + +#: .\patterns/behavioural/RAII.md:80 +msgid "" +"Prevents errors where a resource is not finalised and where a resource is " +"used\r\n" +"after finalisation." +msgstr "" + +#: .\patterns/behavioural/RAII.md:85 +msgid "" +"RAII is a useful pattern for ensuring resources are properly deallocated " +"or\r\n" +"finalised. We can make use of the borrow checker in Rust to statically " +"prevent\r\n" +"errors stemming from using resources after finalisation takes place." +msgstr "" + +#: .\patterns/behavioural/RAII.md:89 +msgid "" +"The core aim of the borrow checker is to ensure that references to data do " +"not\r\n" +"outlive that data. The RAII guard pattern works because the guard object\r\n" +"contains a reference to the underlying resource and only exposes such\r\n" +"references. Rust ensures that the guard cannot outlive the underlying " +"resource\r\n" +"and that references to the resource mediated by the guard cannot outlive " +"the\r\n" +"guard. To see how this works it is helpful to examine the signature of " +"`deref`\r\n" +"without lifetime elision:" +msgstr "" + +#: .\patterns/behavioural/RAII.md:97 +msgid "" +"```rust,ignore\r\n" +"fn deref<'a>(&'a self) -> &'a T {\r\n" +" //..\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/RAII.md:103 +msgid "" +"The returned reference to the resource has the same lifetime as `self` " +"(`'a`).\r\n" +"The borrow checker therefore ensures that the lifetime of the reference to " +"`T`\r\n" +"is shorter than the lifetime of `self`." +msgstr "" + +#: .\patterns/behavioural/RAII.md:107 +msgid "" +"Note that implementing `Deref` is not a core part of this pattern, it only " +"makes\r\n" +"using the guard object more ergonomic. Implementing a `get` method on the " +"guard\r\n" +"works just as well." +msgstr "" + +#: .\patterns/behavioural/RAII.md:113 +msgid "[Finalisation in destructors idiom](../../idioms/dtor-finally.md)" +msgstr "" + +#: .\patterns/behavioural/RAII.md:115 +msgid "" +"RAII is a common pattern in C++: " +"[cppreference.com](http://en.cppreference.com/w/cpp/language/raii),\r\n" +"[wikipedia][wikipedia]." +msgstr "" + +#: .\patterns/behavioural/RAII.md:120 +msgid "" +"[Style guide " +"entry](https://doc.rust-lang.org/1.0.0/style/ownership/raii.html)\r\n" +"(currently just a placeholder)." +msgstr "" + +#: .\patterns/behavioural/strategy.md:1 +msgid "# Strategy (aka Policy)\r" +msgstr "" + +#: .\patterns/behavioural/strategy.md:5 +msgid "" +"The [Strategy design " +"pattern](https://en.wikipedia.org/wiki/Strategy_pattern)\r\n" +"is a technique that enables separation of concerns.\r\n" +"It also allows to decouple software modules through [Dependency " +"Inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle)." +msgstr "" + +#: .\patterns/behavioural/strategy.md:9 +msgid "" +"The basic idea behind the Strategy pattern is that, given an algorithm " +"solving\r\n" +"a particular problem, we define only the skeleton of the algorithm at an " +"abstract\r\n" +"level, and we separate the specific algorithm’s implementation into " +"different parts." +msgstr "" + +#: .\patterns/behavioural/strategy.md:13 +msgid "" +"In this way, a client using the algorithm may choose a specific " +"implementation,\r\n" +"while the general algorithm workflow remains the same. In other words, the " +"abstract\r\n" +"specification of the class does not depend on the specific implementation of " +"the\r\n" +"derived class, but specific implementation must adhere to the abstract " +"specification.\r\n" +"This is why we call it \"Dependency Inversion\"." +msgstr "" + +#: .\patterns/behavioural/strategy.md:21 +msgid "" +"Imagine we are working on a project that generates reports every month.\r\n" +"We need the reports to be generated in different formats (strategies), " +"e.g.,\r\n" +"in `JSON` or `Plain Text` formats.\r\n" +"But things vary over time, and we don't know what kind of requirement we may " +"get\r\n" +"in the future. For example, we may need to generate our report in a " +"completely new\r\n" +"format, or just modify one of the existing formats." +msgstr "" + +#: .\patterns/behavioural/strategy.md:30 +msgid "" +"In this example our invariants (or abstractions) are `Context`, " +"`Formatter`,\r\n" +"and `Report`, while `Text` and `Json` are our strategy structs. These " +"strategies\r\n" +"have to implement the `Formatter` trait." +msgstr "" + +#: .\patterns/behavioural/strategy.md:34 +msgid "" +"```rust\r\n" +"use std::collections::HashMap;\r\n" +"\r\n" +"type Data = HashMap;\r\n" +"\r\n" +"trait Formatter {\r\n" +" fn format(&self, data: &Data, buf: &mut String);\r\n" +"}\r\n" +"\r\n" +"struct Report;\r\n" +"\r\n" +"impl Report {\r\n" +" // Write should be used but we kept it as String to ignore error " +"handling\r\n" +" fn generate(g: T, s: &mut String) {\r\n" +" // backend operations...\r\n" +" let mut data = HashMap::new();\r\n" +" data.insert(\"one\".to_string(), 1);\r\n" +" data.insert(\"two\".to_string(), 2);\r\n" +" // generate report\r\n" +" g.format(&data, s);\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"struct Text;\r\n" +"impl Formatter for Text {\r\n" +" fn format(&self, data: &Data, buf: &mut String) {\r\n" +" for (k, v) in data {\r\n" +" let entry = format!(\"{} {}\\n" +"\", k, v);\r\n" +" buf.push_str(&entry);\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"struct Json;\r\n" +"impl Formatter for Json {\r\n" +" fn format(&self, data: &Data, buf: &mut String) {\r\n" +" buf.push('[');\r\n" +" for (k, v) in data.into_iter() {\r\n" +" let entry = format!(r#\"{{\"{}\":\"{}\"}}\"#, k, v);\r\n" +" buf.push_str(&entry);\r\n" +" buf.push(',');\r\n" +" }\r\n" +" if !data.is_empty() {\r\n" +" buf.pop(); // remove extra , at the end\r\n" +" }\r\n" +" buf.push(']');\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let mut s = String::from(\"\");\r\n" +" Report::generate(Text, &mut s);\r\n" +" assert!(s.contains(\"one 1\"));\r\n" +" assert!(s.contains(\"two 2\"));\r\n" +"\r\n" +" s.clear(); // reuse the same buffer\r\n" +" Report::generate(Json, &mut s);\r\n" +" assert!(s.contains(r#\"{\"one\":\"1\"}\"#));\r\n" +" assert!(s.contains(r#\"{\"two\":\"2\"}\"#));\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/strategy.md:98 +msgid "" +"The main advantage is separation of concerns. For example, in this case " +"`Report`\r\n" +"does not know anything about specific implementations of `Json` and " +"`Text`,\r\n" +"whereas the output implementations does not care about how data is " +"preprocessed,\r\n" +"stored, and fetched. The only thing they have to know is context and a " +"specific\r\n" +"trait and method to implement, i.e,`Formatter` and `run`." +msgstr "" + +#: .\patterns/behavioural/strategy.md:106 +msgid "" +"For each strategy there must be implemented at least one module, so number " +"of modules\r\n" +"increases with number of strategies. If there are many strategies to choose " +"from\r\n" +"then users have to know how strategies differ from one another." +msgstr "" + +#: .\patterns/behavioural/strategy.md:112 +msgid "" +"In the previous example all strategies are implemented in a single file.\r\n" +"Ways of providing different strategies includes:" +msgstr "" + +#: .\patterns/behavioural/strategy.md:115 +msgid "" +"- All in one file (as shown in this example, similar to being separated as " +"modules)\r\n" +"- Separated as modules, E.g. `formatter::json` module, `formatter::text` " +"module\r\n" +"- Use compiler feature flags, E.g. `json` feature, `text` feature\r\n" +"- Separated as crates, E.g. `json` crate, `text` crate\r\n" +"\r" +msgstr "" + +#: .\patterns/behavioural/strategy.md:120 +msgid "" +"Serde crate is a good example of the `Strategy` pattern in action. Serde " +"allows\r\n" +"[full customization](https://serde.rs/custom-serialization.html) of the " +"serialization\r\n" +"behavior by manually implementing `Serialize` and `Deserialize` traits for " +"our\r\n" +"type. For example, we could easily swap `serde_json` with `serde_cbor` since " +"they\r\n" +"expose similar methods. Having this makes the helper crate `serde_transcode` " +"much\r\n" +"more useful and ergonomic." +msgstr "" + +#: .\patterns/behavioural/strategy.md:127 +msgid "" +"However, we don't need to use traits in order to design this pattern in Rust." +msgstr "" + +#: .\patterns/behavioural/strategy.md:129 +msgid "" +"The following toy example demonstrates the idea of the Strategy pattern " +"using Rust\r\n" +"`closures`:" +msgstr "" + +#: .\patterns/behavioural/strategy.md:132 +msgid "" +"```rust\r\n" +"struct Adder;\r\n" +"impl Adder {\r\n" +" pub fn add(x: u8, y: u8, f: F) -> u8\r\n" +" where\r\n" +" F: Fn(u8, u8) -> u8,\r\n" +" {\r\n" +" f(x, y)\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let arith_adder = |x, y| x + y;\r\n" +" let bool_adder = |x, y| {\r\n" +" if x == 1 || y == 1 {\r\n" +" 1\r\n" +" } else {\r\n" +" 0\r\n" +" }\r\n" +" };\r\n" +" let custom_adder = |x, y| 2 * x + y;\r\n" +"\r\n" +" assert_eq!(9, Adder::add(4, 5, arith_adder));\r\n" +" assert_eq!(0, Adder::add(0, 0, bool_adder));\r\n" +" assert_eq!(5, Adder::add(1, 3, custom_adder));\r\n" +"}\r\n" +"\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/strategy.md:161 +msgid "In fact, Rust already uses this idea for `Options`'s `map` method:" +msgstr "" + +#: .\patterns/behavioural/strategy.md:163 +msgid "" +"```rust\r\n" +"fn main() {\r\n" +" let val = Some(\"Rust\");\r\n" +"\r\n" +" let len_strategy = |s: &str| s.len();\r\n" +" assert_eq!(4, val.map(len_strategy).unwrap());\r\n" +"\r\n" +" let first_byte_strategy = |s: &str| s.bytes().next().unwrap();\r\n" +" assert_eq!(82, val.map(first_byte_strategy).unwrap());\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/strategy.md:177 +msgid "" +"- [Strategy Pattern](https://en.wikipedia.org/wiki/Strategy_pattern)\r\n" +"- [Dependency " +"Injection](https://en.wikipedia.org/wiki/Dependency_injection)\r\n" +"- [Policy Based " +"Design](https://en.wikipedia.org/wiki/Modern_C++_Design#Policy-based_design)\r" +msgstr "" + +#: .\patterns/behavioural/visitor.md:1 +msgid "# Visitor\r" +msgstr "" + +#: .\patterns/behavioural/visitor.md:5 +msgid "" +"A visitor encapsulates an algorithm that operates over a heterogeneous\r\n" +"collection of objects. It allows multiple different algorithms to be " +"written\r\n" +"over the same data without having to modify the data (or their primary\r\n" +"behaviour)." +msgstr "" + +#: .\patterns/behavioural/visitor.md:10 +msgid "" +"Furthermore, the visitor pattern allows separating the traversal of\r\n" +"a collection of objects from the operations performed on each object." +msgstr "" + +#: .\patterns/behavioural/visitor.md:15 +msgid "" +"```rust,ignore\r\n" +"// The data we will visit\r\n" +"mod ast {\r\n" +" pub enum Stmt {\r\n" +" Expr(Expr),\r\n" +" Let(Name, Expr),\r\n" +" }\r\n" +"\r\n" +" pub struct Name {\r\n" +" value: String,\r\n" +" }\r\n" +"\r\n" +" pub enum Expr {\r\n" +" IntLit(i64),\r\n" +" Add(Box, Box),\r\n" +" Sub(Box, Box),\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// The abstract visitor\r\n" +"mod visit {\r\n" +" use ast::*;\r\n" +"\r\n" +" pub trait Visitor {\r\n" +" fn visit_name(&mut self, n: &Name) -> T;\r\n" +" fn visit_stmt(&mut self, s: &Stmt) -> T;\r\n" +" fn visit_expr(&mut self, e: &Expr) -> T;\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"use visit::*;\r\n" +"use ast::*;\r\n" +"\r\n" +"// An example concrete implementation - walks the AST interpreting it as " +"code.\r\n" +"struct Interpreter;\r\n" +"impl Visitor for Interpreter {\r\n" +" fn visit_name(&mut self, n: &Name) -> i64 { panic!() }\r\n" +" fn visit_stmt(&mut self, s: &Stmt) -> i64 {\r\n" +" match *s {\r\n" +" Stmt::Expr(ref e) => self.visit_expr(e),\r\n" +" Stmt::Let(..) => unimplemented!(),\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" fn visit_expr(&mut self, e: &Expr) -> i64 {\r\n" +" match *e {\r\n" +" Expr::IntLit(n) => n,\r\n" +" Expr::Add(ref lhs, ref rhs) => self.visit_expr(lhs) + " +"self.visit_expr(rhs),\r\n" +" Expr::Sub(ref lhs, ref rhs) => self.visit_expr(lhs) - " +"self.visit_expr(rhs),\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/visitor.md:69 +msgid "" +"One could implement further visitors, for example a type checker, without " +"having\r\n" +"to modify the AST data." +msgstr "" + +#: .\patterns/behavioural/visitor.md:74 +msgid "" +"The visitor pattern is useful anywhere that you want to apply an algorithm " +"to\r\n" +"heterogeneous data. If data is homogeneous, you can use an iterator-like " +"pattern.\r\n" +"Using a visitor object (rather than a functional approach) allows the " +"visitor to\r\n" +"be stateful and thus communicate information between nodes." +msgstr "" + +#: .\patterns/behavioural/visitor.md:81 +msgid "" +"It is common for the `visit_*` methods to return void (as opposed to in " +"the\r\n" +"example). In that case it is possible to factor out the traversal code and " +"share\r\n" +"it between algorithms (and also to provide noop default methods). In Rust, " +"the\r\n" +"common way to do this is to provide `walk_*` functions for each datum. " +"For\r\n" +"example," +msgstr "" + +#: .\patterns/behavioural/visitor.md:87 +msgid "" +"```rust,ignore\r\n" +"pub fn walk_expr(visitor: &mut Visitor, e: &Expr) {\r\n" +" match *e {\r\n" +" Expr::IntLit(_) => {},\r\n" +" Expr::Add(ref lhs, ref rhs) => {\r\n" +" visitor.visit_expr(lhs);\r\n" +" visitor.visit_expr(rhs);\r\n" +" }\r\n" +" Expr::Sub(ref lhs, ref rhs) => {\r\n" +" visitor.visit_expr(lhs);\r\n" +" visitor.visit_expr(rhs);\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/behavioural/visitor.md:103 +msgid "" +"In other languages (e.g., Java) it is common for data to have an `accept` " +"method\r\n" +"which performs the same duty." +msgstr "" + +#: .\patterns/behavioural/visitor.md:108 +msgid "The visitor pattern is a common pattern in most OO languages." +msgstr "" + +#: .\patterns/behavioural/visitor.md:110 +msgid "[Wikipedia article](https://en.wikipedia.org/wiki/Visitor_pattern)" +msgstr "" + +#: .\patterns/behavioural/visitor.md:112 +msgid "" +"The [fold](../creational/fold.md) pattern is similar to visitor but " +"produces\r\n" +"a new version of the visited data structure." +msgstr "" + +#: .\patterns/creational/intro.md:1 +msgid "# Creational Patterns\r" +msgstr "" + +#: .\patterns/creational/intro.md:3 +msgid "From [Wikipedia](https://en.wikipedia.org/wiki/Creational_pattern):" +msgstr "" + +#: .\patterns/creational/intro.md:5 +msgid "" +"> Design patterns that deal with object creation mechanisms, trying to " +"create objects\r\n" +"> in a manner suitable to the situation. The basic form of object creation " +"could\r\n" +"> result in design problems or in added complexity to the design. Creational " +"design\r\n" +"> patterns solve this problem by somehow controlling this object creation.\r" +msgstr "" + +#: .\patterns/creational/builder.md:1 +msgid "# Builder\r" +msgstr "" + +#: .\patterns/creational/builder.md:5 +msgid "Construct an object with calls to a builder helper." +msgstr "" + +#: .\patterns/creational/builder.md:9 +msgid "" +"```rust\r\n" +"#[derive(Debug, PartialEq)]\r\n" +"pub struct Foo {\r\n" +" // Lots of complicated fields.\r\n" +" bar: String,\r\n" +"}\r\n" +"\r\n" +"impl Foo {\r\n" +" // This method will help users to discover the builder\r\n" +" pub fn builder() -> FooBuilder {\r\n" +" FooBuilder::default()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"#[derive(Default)]\r\n" +"pub struct FooBuilder {\r\n" +" // Probably lots of optional fields.\r\n" +" bar: String,\r\n" +"}\r\n" +"\r\n" +"impl FooBuilder {\r\n" +" pub fn new(/* ... */) -> FooBuilder {\r\n" +" // Set the minimally required fields of Foo.\r\n" +" FooBuilder {\r\n" +" bar: String::from(\"X\"),\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" pub fn name(mut self, bar: String) -> FooBuilder {\r\n" +" // Set the name on the builder itself, and return the builder by " +"value.\r\n" +" self.bar = bar;\r\n" +" self\r\n" +" }\r\n" +"\r\n" +" // If we can get away with not consuming the Builder here, that is an\r\n" +" // advantage. It means we can use the FooBuilder as a template for " +"constructing\r\n" +" // many Foos.\r\n" +" pub fn build(self) -> Foo {\r\n" +" // Create a Foo from the FooBuilder, applying all settings in " +"FooBuilder\r\n" +" // to Foo.\r\n" +" Foo { bar: self.bar }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"#[test]\r\n" +"fn builder_test() {\r\n" +" let foo = Foo {\r\n" +" bar: String::from(\"Y\"),\r\n" +" };\r\n" +" let foo_from_builder: Foo = " +"FooBuilder::new().name(String::from(\"Y\")).build();\r\n" +" assert_eq!(foo, foo_from_builder);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/creational/builder.md:65 +msgid "" +"Useful when you would otherwise require many constructors or where\r\n" +"construction has side effects." +msgstr "" + +#: .\patterns/creational/builder.md:70 +msgid "Separates methods for building from other methods." +msgstr "" + +#: .\patterns/creational/builder.md:72 +msgid "Prevents proliferation of constructors." +msgstr "" + +#: .\patterns/creational/builder.md:74 +msgid "" +"Can be used for one-liner initialisation as well as more complex " +"construction." +msgstr "" + +#: .\patterns/creational/builder.md:78 +msgid "" +"More complex than creating a struct object directly, or a simple " +"constructor\r\n" +"function." +msgstr "" + +#: .\patterns/creational/builder.md:83 +msgid "" +"This pattern is seen more frequently in Rust (and for simpler objects) than " +"in\r\n" +"many other languages because Rust lacks overloading. Since you can only have " +"a\r\n" +"single method with a given name, having multiple constructors is less nice " +"in\r\n" +"Rust than in C++, Java, or others." +msgstr "" + +#: .\patterns/creational/builder.md:88 +msgid "" +"This pattern is often used where the builder object is useful in its own " +"right,\r\n" +"rather than being just a builder. For example, see\r\n" +"[`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html)\r\n" +"is a builder for " +"[`Child`](https://doc.rust-lang.org/std/process/struct.Child.html)\r\n" +"(a process). In these cases, the `T` and `TBuilder` naming pattern is not " +"used." +msgstr "" + +#: .\patterns/creational/builder.md:94 +msgid "" +"The example takes and returns the builder by value. It is often more " +"ergonomic\r\n" +"(and more efficient) to take and return the builder as a mutable reference. " +"The\r\n" +"borrow checker makes this work naturally. This approach has the advantage " +"that\r\n" +"one can write code like" +msgstr "" + +#: .\patterns/creational/builder.md:99 +msgid "" +"```rust,ignore\r\n" +"let mut fb = FooBuilder::new();\r\n" +"fb.a();\r\n" +"fb.b();\r\n" +"let f = fb.build();\r\n" +"```" +msgstr "" + +#: .\patterns/creational/builder.md:106 +msgid "as well as the `FooBuilder::new().a().b().build()` style." +msgstr "" + +#: .\patterns/creational/builder.md:110 +msgid "" +"- [Description in the style " +"guide](https://web.archive.org/web/20210104103100/https://doc.rust-lang.org/1.12.0/style/ownership/builders.html)\r\n" +"- [derive_builder](https://crates.io/crates/derive_builder), a crate for " +"automatically\r\n" +" implementing this pattern while avoiding the boilerplate.\r\n" +"- [Constructor pattern](../../idioms/ctor.md) for when construction is " +"simpler.\r\n" +"- [Builder pattern " +"(wikipedia)](https://en.wikipedia.org/wiki/Builder_pattern)\r\n" +"- [Construction of complex " +"values](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder)\r" +msgstr "" + +#: .\patterns/creational/fold.md:1 +msgid "# Fold\r" +msgstr "" + +#: .\patterns/creational/fold.md:5 +msgid "" +"Run an algorithm over each item in a collection of data to create a new " +"item,\r\n" +"thus creating a whole new collection." +msgstr "" + +#: .\patterns/creational/fold.md:8 +msgid "" +"The etymology here is unclear to me. The terms 'fold' and 'folder' are " +"used\r\n" +"in the Rust compiler, although it appears to me to be more like a map than " +"a\r\n" +"fold in the usual sense. See the discussion below for more details." +msgstr "" + +#: .\patterns/creational/fold.md:14 +msgid "" +"```rust,ignore\r\n" +"// The data we will fold, a simple AST.\r\n" +"mod ast {\r\n" +" pub enum Stmt {\r\n" +" Expr(Box),\r\n" +" Let(Box, Box),\r\n" +" }\r\n" +"\r\n" +" pub struct Name {\r\n" +" value: String,\r\n" +" }\r\n" +"\r\n" +" pub enum Expr {\r\n" +" IntLit(i64),\r\n" +" Add(Box, Box),\r\n" +" Sub(Box, Box),\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// The abstract folder\r\n" +"mod fold {\r\n" +" use ast::*;\r\n" +"\r\n" +" pub trait Folder {\r\n" +" // A leaf node just returns the node itself. In some cases, we can " +"do this\r\n" +" // to inner nodes too.\r\n" +" fn fold_name(&mut self, n: Box) -> Box { n }\r\n" +" // Create a new inner node by folding its children.\r\n" +" fn fold_stmt(&mut self, s: Box) -> Box {\r\n" +" match *s {\r\n" +" Stmt::Expr(e) => Box::new(Stmt::Expr(self.fold_expr(e))),\r\n" +" Stmt::Let(n, e) => Box::new(Stmt::Let(self.fold_name(n), " +"self.fold_expr(e))),\r\n" +" }\r\n" +" }\r\n" +" fn fold_expr(&mut self, e: Box) -> Box { ... }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"use fold::*;\r\n" +"use ast::*;\r\n" +"\r\n" +"// An example concrete implementation - renames every name to 'foo'.\r\n" +"struct Renamer;\r\n" +"impl Folder for Renamer {\r\n" +" fn fold_name(&mut self, n: Box) -> Box {\r\n" +" Box::new(Name { value: \"foo\".to_owned() })\r\n" +" }\r\n" +" // Use the default methods for the other nodes.\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/creational/fold.md:65 +msgid "" +"The result of running the `Renamer` on an AST is a new AST identical to the " +"old\r\n" +"one, but with every name changed to `foo`. A real life folder might have " +"some\r\n" +"state preserved between nodes in the struct itself." +msgstr "" + +#: .\patterns/creational/fold.md:69 +msgid "" +"A folder can also be defined to map one data structure to a different " +"(but\r\n" +"usually similar) data structure. For example, we could fold an AST into a " +"HIR\r\n" +"tree (HIR stands for high-level intermediate representation)." +msgstr "" + +#: .\patterns/creational/fold.md:75 +msgid "" +"It is common to want to map a data structure by performing some operation " +"on\r\n" +"each node in the structure. For simple operations on simple data " +"structures,\r\n" +"this can be done using `Iterator::map`. For more complex operations, " +"perhaps\r\n" +"where earlier nodes can affect the operation on later nodes, or where " +"iteration\r\n" +"over the data structure is non-trivial, using the fold pattern is more\r\n" +"appropriate." +msgstr "" + +#: .\patterns/creational/fold.md:82 +msgid "" +"Like the visitor pattern, the fold pattern allows us to separate traversal " +"of a\r\n" +"data structure from the operations performed to each node." +msgstr "" + +#: .\patterns/creational/fold.md:87 +msgid "" +"Mapping data structures in this fashion is common in functional languages. " +"In OO\r\n" +"languages, it would be more common to mutate the data structure in place. " +"The\r\n" +"'functional' approach is common in Rust, mostly due to the preference for\r\n" +"immutability. Using fresh data structures, rather than mutating old ones, " +"makes\r\n" +"reasoning about the code easier in most circumstances." +msgstr "" + +#: .\patterns/creational/fold.md:93 +msgid "" +"The trade-off between efficiency and reusability can be tweaked by changing " +"how\r\n" +"nodes are accepted by the `fold_*` methods." +msgstr "" + +#: .\patterns/creational/fold.md:96 +msgid "" +"In the above example we operate on `Box` pointers. Since these own their " +"data\r\n" +"exclusively, the original copy of the data structure cannot be re-used. On " +"the\r\n" +"other hand if a node is not changed, reusing it is very efficient." +msgstr "" + +#: .\patterns/creational/fold.md:100 +msgid "" +"If we were to operate on borrowed references, the original data structure " +"can be\r\n" +"reused; however, a node must be cloned even if unchanged, which can be\r\n" +"expensive." +msgstr "" + +#: .\patterns/creational/fold.md:104 +msgid "" +"Using a reference counted pointer gives the best of both worlds - we can " +"reuse\r\n" +"the original data structure, and we don't need to clone unchanged nodes. " +"However,\r\n" +"they are less ergonomic to use and mean that the data structures cannot " +"be\r\n" +"mutable." +msgstr "" + +#: .\patterns/creational/fold.md:111 +msgid "" +"Iterators have a `fold` method, however this folds a data structure into " +"a\r\n" +"value, rather than into a new data structure. An iterator's `map` is more " +"like\r\n" +"this fold pattern." +msgstr "" + +#: .\patterns/creational/fold.md:115 +msgid "" +"In other languages, fold is usually used in the sense of Rust's " +"iterators,\r\n" +"rather than this pattern. Some functional languages have powerful constructs " +"for\r\n" +"performing flexible maps over data structures." +msgstr "" + +#: .\patterns/creational/fold.md:119 +msgid "" +"The [visitor](../behavioural/visitor.md) pattern is closely related to " +"fold.\r\n" +"They share the concept of walking a data structure performing an operation " +"on\r\n" +"each node. However, the visitor does not create a new data structure nor " +"consume\r\n" +"the old one." +msgstr "" + +#: .\patterns/structural/intro.md:1 +msgid "# Structural Patterns\r" +msgstr "" + +#: .\patterns/structural/intro.md:3 +msgid "From [Wikipedia](https://en.wikipedia.org/wiki/Structural_pattern):" +msgstr "" + +#: .\patterns/structural/intro.md:5 +msgid "" +"> Design patterns that ease the design by identifying a simple way to " +"realize relationships\r\n" +"> among entities.\r" +msgstr "" + +#: .\patterns/structural/compose-structs.md:1 +msgid "# Compose structs together for better borrowing\r" +msgstr "" + +#: .\patterns/structural/compose-structs.md:3 +msgid "TODO - this is not a very snappy name" +msgstr "" + +#: .\patterns/structural/compose-structs.md:7 +msgid "" +"Sometimes a large struct will cause issues with the borrow checker - " +"although\r\n" +"fields can be borrowed independently, sometimes the whole struct ends up " +"being\r\n" +"used at once, preventing other uses. A solution might be to decompose the " +"struct\r\n" +"into several smaller structs. Then compose these together into the " +"original\r\n" +"struct. Then each struct can be borrowed separately and have more " +"flexible\r\n" +"behaviour." +msgstr "" + +#: .\patterns/structural/compose-structs.md:14 +msgid "" +"This will often lead to a better design in other ways: applying this " +"design\r\n" +"pattern often reveals smaller units of functionality." +msgstr "" + +#: .\patterns/structural/compose-structs.md:19 +msgid "" +"Here is a contrived example of where the borrow checker foils us in our plan " +"to\r\n" +"use a struct:" +msgstr "" + +#: .\patterns/structural/compose-structs.md:22 +msgid "" +"```rust\r\n" +"struct A {\r\n" +" f1: u32,\r\n" +" f2: u32,\r\n" +" f3: u32,\r\n" +"}\r\n" +"\r\n" +"fn foo(a: &mut A) -> &u32 { &a.f2 }\r\n" +"fn bar(a: &mut A) -> u32 { a.f1 + a.f3 }\r\n" +"\r\n" +"fn baz(a: &mut A) {\r\n" +" // The later usage of x causes a to be borrowed for the rest of the " +"function.\r\n" +" let x = foo(a);\r\n" +" // Borrow checker error:\r\n" +" // let y = bar(a); // ~ ERROR: cannot borrow `*a` as mutable more than " +"once\r\n" +" // at a time\r\n" +" println!(\"{}\", x);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/structural/compose-structs.md:42 +msgid "" +"We can apply this design pattern and refactor `A` into two smaller structs, " +"thus\r\n" +"solving the borrow checking issue:" +msgstr "" + +#: .\patterns/structural/compose-structs.md:45 +msgid "" +"```rust\r\n" +"// A is now composed of two structs - B and C.\r\n" +"struct A {\r\n" +" b: B,\r\n" +" c: C,\r\n" +"}\r\n" +"struct B {\r\n" +" f2: u32,\r\n" +"}\r\n" +"struct C {\r\n" +" f1: u32,\r\n" +" f3: u32,\r\n" +"}\r\n" +"\r\n" +"// These functions take a B or C, rather than A.\r\n" +"fn foo(b: &mut B) -> &u32 { &b.f2 }\r\n" +"fn bar(c: &mut C) -> u32 { c.f1 + c.f3 }\r\n" +"\r\n" +"fn baz(a: &mut A) {\r\n" +" let x = foo(&mut a.b);\r\n" +" // Now it's OK!\r\n" +" let y = bar(&mut a.c);\r\n" +" println!(\"{}\", x);\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/structural/compose-structs.md:73 +msgid "TODO Why and where you should use the pattern" +msgstr "" + +#: .\patterns/structural/compose-structs.md:77 +msgid "Lets you work around limitations in the borrow checker." +msgstr "" + +#: .\patterns/structural/compose-structs.md:79 +msgid "Often produces a better design." +msgstr "" + +#: .\patterns/structural/compose-structs.md:83 +msgid "Leads to more verbose code." +msgstr "" + +#: .\patterns/structural/compose-structs.md:85 +msgid "" +"Sometimes, the smaller structs are not good abstractions, and so we end up " +"with\r\n" +"a worse design. That is probably a 'code smell', indicating that the " +"program\r\n" +"should be refactored in some way." +msgstr "" + +#: .\patterns/structural/compose-structs.md:91 +msgid "" +"This pattern is not required in languages that don't have a borrow checker, " +"so\r\n" +"in that sense is unique to Rust. However, making smaller units of " +"functionality\r\n" +"often leads to cleaner code: a widely acknowledged principle of software\r\n" +"engineering, independent of the language." +msgstr "" + +#: .\patterns/structural/compose-structs.md:96 +msgid "" +"This pattern relies on Rust's borrow checker to be able to borrow fields\r\n" +"independently of each other. In the example, the borrow checker knows that " +"`a.b`\r\n" +"and `a.c` are distinct and can be borrowed independently, it does not try " +"to\r\n" +"borrow all of `a`, which would make this pattern useless." +msgstr "" + +#: .\patterns/structural/small-crates.md:1 +msgid "# Prefer small crates\r" +msgstr "" + +#: .\patterns/structural/small-crates.md:5 +msgid "Prefer small crates that do one thing well." +msgstr "" + +#: .\patterns/structural/small-crates.md:7 +msgid "" +"Cargo and crates.io make it easy to add third-party libraries, much more so " +"than\r\n" +"in say C or C++. Moreover, since packages on crates.io cannot be edited or " +"removed\r\n" +"after publication, any build that works now should continue to work in the " +"future.\r\n" +"We should take advantage of this tooling, and use smaller, more fine-grained " +"dependencies." +msgstr "" + +#: .\patterns/structural/small-crates.md:14 +msgid "" +"* Small crates are easier to understand, and encourage more modular code.\r\n" +"* Crates allow for re-using code between projects.\r\n" +" For example, the `url` crate was developed as part of the Servo browser " +"engine,\r\n" +" but has since found wide use outside the project.\r\n" +"* Since the compilation unit\r\n" +" of Rust is the crate, splitting a project into multiple crates can allow " +"more of\r\n" +" the code to be built in parallel.\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/small-crates.md:24 +msgid "" +"* This can lead to \"dependency hell\", when a project depends on multiple " +"conflicting\r\n" +" versions of a crate at the same time. For example, the `url` crate has " +"both versions\r\n" +" 1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url` from `url:0.5` " +"are\r\n" +" different types, an HTTP client that uses `url:0.5` would not accept `Url` " +"values\r\n" +" from a web scraper that uses `url:1.0`.\r\n" +"* Packages on crates.io are not curated. A crate may be poorly written, " +"have\r\n" +" unhelpful documentation, or be outright malicious.\r\n" +"* Two small crates may be less optimized than one large one, since the " +"compiler\r\n" +" does not perform link-time optimization (LTO) by default.\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/small-crates.md:36 +msgid "" +"The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides " +"functions\r\n" +"for converting `&T` to `&[T]`." +msgstr "" + +#: .\patterns/structural/small-crates.md:39 +msgid "" +"The [`url`](https://crates.io/crates/url) crate provides tools for working " +"with\r\n" +"URLs." +msgstr "" + +#: .\patterns/structural/small-crates.md:42 +msgid "" +"The [`num_cpus`](https://crates.io/crates/num_cpus) crate provides a " +"function to\r\n" +"query the number of CPUs on a machine." +msgstr "" + +#: .\patterns/structural/small-crates.md:47 +msgid "* [crates.io: The Rust community crate host](https://crates.io/)\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:1 +msgid "# Contain unsafety in small modules\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:5 +msgid "" +"If you have `unsafe` code, create the smallest possible module that can " +"uphold\r\n" +"the needed invariants to build a minimal safe interface upon the unsafety. " +"Embed\r\n" +"this into a larger module that contains only safe code and presents an " +"ergonomic\r\n" +"interface. Note that the outer module can contain unsafe functions and " +"methods\r\n" +"that call directly into the unsafe code. Users may use this to gain speed " +"benefits." +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:13 +msgid "" +"* This restricts the unsafe code that must be audited\r\n" +"* Writing the outer module is much easier, since you can count on the " +"guarantees\r\n" +"of the inner module\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:19 +msgid "" +"* Sometimes, it may be hard to find a suitable interface.\r\n" +"* The abstraction may introduce inefficiencies.\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:24 +msgid "" +"* The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe " +"operations\r\n" +" in submodules, presenting a safe interface to users.\r\n" +"* `std`'s `String` class is a wrapper over `Vec` with the added " +"invariant\r\n" +"that the contents must be valid UTF-8. The operations on `String` ensure " +"this\r\n" +"behavior.\r\n" +"However, users have the option of using an `unsafe` method to create a " +"`String`,\r\n" +"in which case the onus is on them to guarantee the validity of the " +"contents.\r\n" +"\r" +msgstr "" + +#: .\patterns/structural/unsafe-mods.md:34 +msgid "" +"* [Ralf Jung's Blog about invariants in unsafe " +"code](https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html)\r" +msgstr "" + +#: .\patterns/ffi/intro.md:1 +msgid "# FFI Patterns\r" +msgstr "" + +#: .\patterns/ffi/intro.md:3 +msgid "" +"Writing FFI code is an entire course in itself.\r\n" +"However, there are several idioms here that can act as pointers, and avoid " +"traps\r\n" +"for inexperienced users of unsafe Rust." +msgstr "" + +#: .\patterns/ffi/intro.md:7 +msgid "This section contains design patterns that may be useful when doing FFI." +msgstr "" + +#: .\patterns/ffi/intro.md:9 +msgid "" +"1. [Object-Based API](./export.md) design that has good memory safety " +"characteristics,\r\n" +" and a clean boundary of what is safe and what is unsafe\r\n" +"\r\n" +"2. [Type Consolidation into Wrappers](./wrappers.md) - group multiple Rust " +"types\r\n" +" together into an opaque \"object\"\r" +msgstr "" + +#: .\patterns/ffi/export.md:1 +msgid "# Object-Based APIs\r" +msgstr "" + +#: .\patterns/ffi/export.md:5 +msgid "" +"When designing APIs in Rust which are exposed to other languages, there are " +"some\r\n" +"important design principles which are contrary to normal Rust API design:" +msgstr "" + +#: .\patterns/ffi/export.md:8 +msgid "" +"1. All Encapsulated types should be *owned* by Rust, *managed* by the " +"user,\r\n" +" and *opaque*.\r\n" +"2. All Transactional data types should be *owned* by the user, and " +"*transparent*.\r\n" +"3. All library behavior should be functions acting upon Encapsulated " +"types.\r\n" +"4. All library behavior should be encapsulated into types not based on " +"structure,\r\n" +" but *provenance/lifetime*.\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:17 +msgid "" +"Rust has built-in FFI support to other languages.\r\n" +"It does this by providing a way for crate authors to provide C-compatible " +"APIs\r\n" +"through different ABIs (though that is unimportant to this practice)." +msgstr "" + +#: .\patterns/ffi/export.md:21 +msgid "" +"Well-designed Rust FFI follows C API design principles, while compromising " +"the\r\n" +"design in Rust as little as possible. There are three goals with any foreign " +"API:" +msgstr "" + +#: .\patterns/ffi/export.md:24 +msgid "" +"1. Make it easy to use in the target language.\r\n" +"2. Avoid the API dictating internal unsafety on the Rust side as much as " +"possible.\r\n" +"3. Keep the potential for memory unsafety and Rust `undefined behaviour` as " +"small\r\n" +" as possible.\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:29 +msgid "" +"Rust code must trust the memory safety of the foreign language beyond a " +"certain\r\n" +"point. However, every bit of `unsafe` code on the Rust side is an " +"opportunity for\r\n" +"bugs, or to exacerbate `undefined behaviour`." +msgstr "" + +#: .\patterns/ffi/export.md:33 +msgid "" +"For example, if a pointer provenance is wrong, that may be a segfault due " +"to\r\n" +"invalid memory access. But if it is manipulated by unsafe code, it could " +"become\r\n" +"full-blown heap corruption." +msgstr "" + +#: .\patterns/ffi/export.md:37 +msgid "" +"The Object-Based API design allows for writing shims that have good memory " +"safety\r\n" +"characteristics, and a clean boundary of what is safe and what is `unsafe`." +msgstr "" + +#: .\patterns/ffi/export.md:42 +msgid "" +"The POSIX standard defines the API to access an on-file database, known as " +"[DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/ndbm.h).\r\n" +"It is an excellent example of an \"object-based\" API." +msgstr "" + +#: .\patterns/ffi/export.md:45 +msgid "" +"Here is the definition in C, which hopefully should be easy to read for " +"those\r\n" +"involved in FFI. The commentary below should help explain it for those " +"who\r\n" +"miss the subtleties." +msgstr "" + +#: .\patterns/ffi/export.md:49 +msgid "" +"```C\r\n" +"struct DBM;\r\n" +"typedef struct { void *dptr, size_t dsize } datum;\r\n" +"\r\n" +"int dbm_clearerr(DBM *);\r\n" +"void dbm_close(DBM *);\r\n" +"int dbm_delete(DBM *, datum);\r\n" +"int dbm_error(DBM *);\r\n" +"datum dbm_fetch(DBM *, datum);\r\n" +"datum dbm_firstkey(DBM *);\r\n" +"datum dbm_nextkey(DBM *);\r\n" +"DBM *dbm_open(const char *, int, mode_t);\r\n" +"int dbm_store(DBM *, datum, datum, int);\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:64 +msgid "This API defines two types: `DBM` and `datum`." +msgstr "" + +#: .\patterns/ffi/export.md:66 +msgid "" +"The `DBM` type was called an \"encapsulated\" type above.\r\n" +"It is designed to contain internal state, and acts as an entry point for " +"the\r\n" +"library's behavior." +msgstr "" + +#: .\patterns/ffi/export.md:70 +msgid "" +"It is completely opaque to the user, who cannot create a `DBM` themselves " +"since\r\n" +"they don't know its size or layout. Instead, they must call `dbm_open`, and " +"that\r\n" +"only gives them *a pointer to one*." +msgstr "" + +#: .\patterns/ffi/export.md:74 +msgid "" +"This means all `DBM`s are \"owned\" by the library in a Rust sense.\r\n" +"The internal state of unknown size is kept in memory controlled by the " +"library,\r\n" +"not the user. The user can only manage its life cycle with `open` and " +"`close`,\r\n" +"and perform operations on it with the other functions." +msgstr "" + +#: .\patterns/ffi/export.md:79 +msgid "" +"The `datum` type was called a \"transactional\" type above.\r\n" +"It is designed to facilitate the exchange of information between the library " +"and\r\n" +"its user." +msgstr "" + +#: .\patterns/ffi/export.md:83 +msgid "" +"The database is designed to store \"unstructured data\", with no pre-defined " +"length\r\n" +"or meaning. As a result, the `datum` is the C equivalent of a Rust slice: a " +"bunch\r\n" +"of bytes, and a count of how many there are. The main difference is that " +"there is\r\n" +"no type information, which is what `void` indicates." +msgstr "" + +#: .\patterns/ffi/export.md:88 +msgid "" +"Keep in mind that this header is written from the library's point of " +"view.\r\n" +"The user likely has some type they are using, which has a known size.\r\n" +"But the library does not care, and by the rules of C casting, any type " +"behind a\r\n" +"pointer can be cast to `void`." +msgstr "" + +#: .\patterns/ffi/export.md:93 +msgid "" +"As noted earlier, this type is *transparent* to the user. But also, this " +"type is\r\n" +"*owned* by the user.\r\n" +"This has subtle ramifications, due to that pointer inside it.\r\n" +"The question is, who owns the memory that pointer points to?" +msgstr "" + +#: .\patterns/ffi/export.md:98 +msgid "" +"The answer for best memory safety is, \"the user\".\r\n" +"But in cases such as retrieving a value, the user does not know how to " +"allocate\r\n" +"it correctly (since they don't know how long the value is). In this case, " +"the library\r\n" +"code is expected to use the heap that the user has access to -- such as the " +"C library\r\n" +"`malloc` and `free` -- and then *transfer ownership* in the Rust sense." +msgstr "" + +#: .\patterns/ffi/export.md:104 +msgid "" +"This may all seem speculative, but this is what a pointer means in C.\r\n" +"It means the same thing as Rust: \"user defined lifetime.\"\r\n" +"The user of the library needs to read the documentation in order to use it " +"correctly.\r\n" +"That said, there are some decisions that have fewer or greater consequences " +"if users\r\n" +"do it wrong. Minimizing those are what this best practice is about, and the " +"key\r\n" +"is to *transfer ownership of everything that is transparent*." +msgstr "" + +#: .\patterns/ffi/export.md:113 +msgid "" +"This minimizes the number of memory safety guarantees the user must uphold " +"to a\r\n" +"relatively small number:" +msgstr "" + +#: .\patterns/ffi/export.md:116 +msgid "" +"1. Do not call any function with a pointer not returned by `dbm_open` " +"(invalid\r\n" +" access or corruption).\r\n" +"2. Do not call any function on a pointer after close (use after free).\r\n" +"3. The `dptr` on any `datum` must be `NULL`, or point to a valid slice of " +"memory\r\n" +" at the advertised length.\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:122 +msgid "" +"In addition, it avoids a lot of pointer provenance issues.\r\n" +"To understand why, let us consider an alternative in some depth: key " +"iteration." +msgstr "" + +#: .\patterns/ffi/export.md:125 +msgid "" +"Rust is well known for its iterators.\r\n" +"When implementing one, the programmer makes a separate type with a bounded " +"lifetime\r\n" +"to its owner, and implements the `Iterator` trait." +msgstr "" + +#: .\patterns/ffi/export.md:129 +msgid "Here is how iteration would be done in Rust for `DBM`:" +msgstr "" + +#: .\patterns/ffi/export.md:131 +msgid "" +"```rust,ignore\r\n" +"struct Dbm { ... }\r\n" +"\r\n" +"impl Dbm {\r\n" +" /* ... */\r\n" +" pub fn keys<'it>(&'it self) -> DbmKeysIter<'it> { ... }\r\n" +" /* ... */\r\n" +"}\r\n" +"\r\n" +"struct DbmKeysIter<'it> {\r\n" +" owner: &'it Dbm,\r\n" +"}\r\n" +"\r\n" +"impl<'it> Iterator for DbmKeysIter<'it> { ... }\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:147 +msgid "" +"This is clean, idiomatic, and safe. thanks to Rust's guarantees.\r\n" +"However, consider what a straightforward API translation would look like:" +msgstr "" + +#: .\patterns/ffi/export.md:150 +msgid "" +"```rust,ignore\r\n" +"#[no_mangle]\r\n" +"pub extern \"C\" fn dbm_iter_new(owner: *const Dbm) -> *mut DbmKeysIter {\r\n" +" // THIS API IS A BAD IDEA! For real applications, use object-based " +"design instead.\r\n" +"}\r\n" +"#[no_mangle]\r\n" +"pub extern \"C\" fn dbm_iter_next(\r\n" +" iter: *mut DbmKeysIter,\r\n" +" key_out: *const datum\r\n" +") -> libc::c_int {\r\n" +" // THIS API IS A BAD IDEA! For real applications, use object-based " +"design instead.\r\n" +"}\r\n" +"#[no_mangle]\r\n" +"pub extern \"C\" fn dbm_iter_del(*mut DbmKeysIter) {\r\n" +" // THIS API IS A BAD IDEA! For real applications, use object-based " +"design instead.\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:168 +msgid "" +"This API loses a key piece of information: the lifetime of the iterator must " +"not\r\n" +"exceed the lifetime of the `Dbm` object that owns it. A user of the library " +"could\r\n" +"use it in a way which causes the iterator to outlive the data it is " +"iterating on,\r\n" +"resulting in reading uninitialized memory." +msgstr "" + +#: .\patterns/ffi/export.md:173 +msgid "" +"This example written in C contains a bug that will be explained afterwards:" +msgstr "" + +#: .\patterns/ffi/export.md:175 +msgid "" +"```C\r\n" +"int count_key_sizes(DBM *db) {\r\n" +" // DO NOT USE THIS FUNCTION. IT HAS A SUBTLE BUT SERIOUS BUG!\r\n" +" datum key;\r\n" +" int len = 0;\r\n" +"\r\n" +" if (!dbm_iter_new(db)) {\r\n" +" dbm_close(db);\r\n" +" return -1;\r\n" +" }\r\n" +"\r\n" +" int l;\r\n" +" while ((l = dbm_iter_next(owner, &key)) >= 0) { // an error is indicated " +"by -1\r\n" +" free(key.dptr);\r\n" +" len += key.dsize;\r\n" +" if (l == 0) { // end of the iterator\r\n" +" dbm_close(owner);\r\n" +" }\r\n" +" }\r\n" +" if l >= 0 {\r\n" +" return -1;\r\n" +" } else {\r\n" +" return len;\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:202 +msgid "" +"This bug is a classic. Here's what happens when the iterator returns the\r\n" +"end-of-iteration marker:" +msgstr "" + +#: .\patterns/ffi/export.md:205 +msgid "" +"1. The loop condition sets `l` to zero, and enters the loop because `0 >= " +"0`.\r\n" +"2. The length is incremented, in this case by zero.\r\n" +"3. The if statement is true, so the database is closed. There should be a " +"break\r\n" +" statement here.\r\n" +"4. The loop condition executes again, causing a `next` call on the closed " +"object.\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:211 +msgid "" +"The worst part about this bug?\r\n" +"If the Rust implementation was careful, this code will work most of the " +"time!\r\n" +"If the memory for the `Dbm` object is not immediately reused, an internal " +"check\r\n" +"will almost certainly fail, resulting in the iterator returning a `-1` " +"indicating\r\n" +"an error. But occasionally, it will cause a segmentation fault, or even " +"worse,\r\n" +"nonsensical memory corruption!" +msgstr "" + +#: .\patterns/ffi/export.md:218 +msgid "" +"None of this can be avoided by Rust.\r\n" +"From its perspective, it put those objects on its heap, returned pointers to " +"them,\r\n" +"and gave up control of their lifetimes. The C code simply must \"play nice\"." +msgstr "" + +#: .\patterns/ffi/export.md:222 +msgid "" +"The programmer must read and understand the API documentation.\r\n" +"While some consider that par for the course in C, a good API design can " +"mitigate\r\n" +"this risk. The POSIX API for `DBM` did this by *consolidating the ownership* " +"of\r\n" +"the iterator with its parent:" +msgstr "" + +#: .\patterns/ffi/export.md:227 +msgid "" +"```C\r\n" +"datum dbm_firstkey(DBM *);\r\n" +"datum dbm_nextkey(DBM *);\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/export.md:232 +msgid "" +"Thus, all the lifetimes were bound together, and such unsafety was prevented." +msgstr "" + +#: .\patterns/ffi/export.md:236 +msgid "" +"However, this design choice also has a number of drawbacks, which should " +"be\r\n" +"considered as well." +msgstr "" + +#: .\patterns/ffi/export.md:239 +msgid "" +"First, the API itself becomes less expressive.\r\n" +"With POSIX DBM, there is only one iterator per object, and every call " +"changes\r\n" +"its state. This is much more restrictive than iterators in almost any " +"language,\r\n" +"even though it is safe. Perhaps with other related objects, whose lifetimes " +"are\r\n" +"less hierarchical, this limitation is more of a cost than the safety." +msgstr "" + +#: .\patterns/ffi/export.md:245 +msgid "" +"Second, depending on the relationships of the API's parts, significant " +"design effort\r\n" +"may be involved. Many of the easier design points have other patterns " +"associated\r\n" +"with them:" +msgstr "" + +#: .\patterns/ffi/export.md:249 +msgid "" +"- [Wrapper Type Consolidation](./wrappers.md) groups multiple Rust types " +"together\r\n" +" into an opaque \"object\"\r\n" +"\r\n" +"- [FFI Error Passing](../../idioms/ffi/errors.md) explains error handling " +"with integer\r\n" +" codes and sentinel return values (such as `NULL` pointers)\r\n" +"\r\n" +"- [Accepting Foreign Strings](../../idioms/ffi/accepting-strings.md) allows " +"accepting\r\n" +" strings with minimal unsafe code, and is easier to get right than\r\n" +" [Passing Strings to FFI](../../idioms/ffi/passing-strings.md)\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/export.md:259 +msgid "" +"However, not every API can be done this way.\r\n" +"It is up to the best judgement of the programmer as to who their audience is." +msgstr "" + +#: .\patterns/ffi/wrappers.md:1 +msgid "# Type Consolidation into Wrappers\r" +msgstr "" + +#: .\patterns/ffi/wrappers.md:5 +msgid "" +"This pattern is designed to allow gracefully handling multiple related " +"types,\r\n" +"while minimizing the surface area for memory unsafety." +msgstr "" + +#: .\patterns/ffi/wrappers.md:8 +msgid "" +"One of the cornerstones of Rust's aliasing rules is lifetimes.\r\n" +"This ensures that many patterns of access between types can be memory " +"safe,\r\n" +"data race safety included." +msgstr "" + +#: .\patterns/ffi/wrappers.md:12 +msgid "" +"However, when Rust types are exported to other languages, they are usually " +"transformed\r\n" +"into pointers. In Rust, a pointer means \"the user manages the lifetime of " +"the pointee.\"\r\n" +"It is their responsibility to avoid memory unsafety." +msgstr "" + +#: .\patterns/ffi/wrappers.md:16 +msgid "" +"Some level of trust in the user code is thus required, notably around " +"use-after-free\r\n" +"which Rust can do nothing about. However, some API designs place higher " +"burdens\r\n" +"than others on the code written in the other language." +msgstr "" + +#: .\patterns/ffi/wrappers.md:20 +msgid "" +"The lowest risk API is the \"consolidated wrapper\", where all possible " +"interactions\r\n" +"with an object are folded into a \"wrapper type\", while keeping the Rust " +"API clean." +msgstr "" + +#: .\patterns/ffi/wrappers.md:25 +msgid "" +"To understand this, let us look at a classic example of an API to export: " +"iteration\r\n" +"through a collection." +msgstr "" + +#: .\patterns/ffi/wrappers.md:28 +msgid "That API looks like this:" +msgstr "" + +#: .\patterns/ffi/wrappers.md:30 +msgid "" +"1. The iterator is initialized with `first_key`.\r\n" +"2. Each call to `next_key` will advance the iterator.\r\n" +"3. Calls to `next_key` if the iterator is at the end will do nothing.\r\n" +"4. As noted above, the iterator is \"wrapped into\" the collection (unlike " +"the native\r\n" +" Rust API).\r\n" +"\r" +msgstr "" + +#: .\patterns/ffi/wrappers.md:36 +msgid "" +"If the iterator implements `nth()` efficiently, then it is possible to make " +"it\r\n" +"ephemeral to each function call:" +msgstr "" + +#: .\patterns/ffi/wrappers.md:39 +msgid "" +"```rust,ignore\r\n" +"struct MySetWrapper {\r\n" +" myset: MySet,\r\n" +" iter_next: usize,\r\n" +"}\r\n" +"\r\n" +"impl MySetWrapper {\r\n" +" pub fn first_key(&mut self) -> Option<&Key> {\r\n" +" self.iter_next = 0;\r\n" +" self.next_key()\r\n" +" }\r\n" +" pub fn next_key(&mut self) -> Option<&Key> {\r\n" +" if let Some(next) = self.myset.keys().nth(self.iter_next) {\r\n" +" self.iter_next += 1;\r\n" +" Some(next)\r\n" +" } else {\r\n" +" None\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/wrappers.md:61 +msgid "As a result, the wrapper is simple and contains no `unsafe` code." +msgstr "" + +#: .\patterns/ffi/wrappers.md:65 +msgid "" +"This makes APIs safer to use, avoiding issues with lifetimes between " +"types.\r\n" +"See [Object-Based APIs](./export.md) for more on the advantages and " +"pitfalls\r\n" +"this avoids." +msgstr "" + +#: .\patterns/ffi/wrappers.md:71 +msgid "" +"Often, wrapping types is quite difficult, and sometimes a Rust API " +"compromise\r\n" +"would make things easier." +msgstr "" + +#: .\patterns/ffi/wrappers.md:74 +msgid "" +"As an example, consider an iterator which does not efficiently implement " +"`nth()`.\r\n" +"It would definitely be worth putting in special logic to make the object " +"handle\r\n" +"iteration internally, or to support a different access pattern efficiently " +"that\r\n" +"only the Foreign Function API will use." +msgstr "" + +#: .\patterns/ffi/wrappers.md:79 +msgid "### Trying to Wrap Iterators (and Failing)\r" +msgstr "" + +#: .\patterns/ffi/wrappers.md:81 +msgid "" +"To wrap any type of iterator into the API correctly, the wrapper would need " +"to\r\n" +"do what a C version of the code would do: erase the lifetime of the " +"iterator,\r\n" +"and manage it manually." +msgstr "" + +#: .\patterns/ffi/wrappers.md:85 +msgid "Suffice it to say, this is *incredibly* difficult." +msgstr "" + +#: .\patterns/ffi/wrappers.md:87 +msgid "Here is an illustration of just *one* pitfall." +msgstr "" + +#: .\patterns/ffi/wrappers.md:89 +msgid "A first version of `MySetWrapper` would look like this:" +msgstr "" + +#: .\patterns/ffi/wrappers.md:91 +msgid "" +"```rust,ignore\r\n" +"struct MySetWrapper {\r\n" +" myset: MySet,\r\n" +" iter_next: usize,\r\n" +" // created from a transmuted Box\r\n" +" iterator: Option>>,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/wrappers.md:100 +msgid "" +"With `transmute` being used to extend a lifetime, and a pointer to hide " +"it,\r\n" +"it's ugly already. But it gets even worse: *any other operation can cause\r\n" +"Rust `undefined behaviour`*." +msgstr "" + +#: .\patterns/ffi/wrappers.md:104 +msgid "" +"Consider that the `MySet` in the wrapper could be manipulated by other\r\n" +"functions during iteration, such as storing a new value to the key it was\r\n" +"iterating over. The API doesn't discourage this, and in fact some similar " +"C\r\n" +"libraries expect it." +msgstr "" + +#: .\patterns/ffi/wrappers.md:109 +msgid "A simple implementation of `myset_store` would be:" +msgstr "" + +#: .\patterns/ffi/wrappers.md:111 +msgid "" +"```rust,ignore\r\n" +"pub mod unsafe_module {\r\n" +"\r\n" +" // other module content\r\n" +"\r\n" +" pub fn myset_store(\r\n" +" myset: *mut MySetWrapper,\r\n" +" key: datum,\r\n" +" value: datum) -> libc::c_int {\r\n" +"\r\n" +" // DO NOT USE THIS CODE. IT IS UNSAFE TO DEMONSTRATE A PROLBEM.\r\n" +"\r\n" +" let myset: &mut MySet = unsafe { // SAFETY: whoops, UB occurs in " +"here!\r\n" +" &mut (*myset).myset\r\n" +" };\r\n" +"\r\n" +" /* ...check and cast key and value data... */\r\n" +"\r\n" +" match myset.store(casted_key, casted_value) {\r\n" +" Ok(_) => 0,\r\n" +" Err(e) => e.into()\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\patterns/ffi/wrappers.md:137 +msgid "" +"If the iterator exists when this function is called, we have violated one of " +"Rust's\r\n" +"aliasing rules. According to Rust, the mutable reference in this block must " +"have\r\n" +"*exclusive* access to the object. If the iterator simply exists, it's not " +"exclusive,\r\n" +"so we have `undefined behaviour`! " +msgstr "" + +#: .\patterns/ffi/wrappers.md:142 +msgid "" +"To avoid this, we must have a way of ensuring that mutable reference really " +"is exclusive.\r\n" +"That basically means clearing out the iterator's shared reference while it " +"exists,\r\n" +"and then reconstructing it. In most cases, that will still be less efficient " +"than\r\n" +"the C version." +msgstr "" + +#: .\patterns/ffi/wrappers.md:147 +msgid "" +"Some may ask: how can C do this more efficiently?\r\n" +"The answer is, it cheats. Rust's aliasing rules are the problem, and C " +"simply ignores\r\n" +"them for its pointers. In exchange, it is common to see code that is " +"declared\r\n" +"in the manual as \"not thread safe\" under some or all circumstances. In " +"fact,\r\n" +"the [GNU C " +"library](https://manpages.debian.org/buster/manpages/attributes.7.en.html)\r\n" +"has an entire lexicon dedicated to concurrent behavior!" +msgstr "" + +#: .\patterns/ffi/wrappers.md:154 +msgid "" +"Rust would rather make everything memory safe all the time, for both safety " +"and\r\n" +"optimizations that C code cannot attain. Being denied access to certain " +"shortcuts\r\n" +"is the price Rust programmers need to pay." +msgstr "" + +#: .\patterns/ffi/wrappers.md:158 +msgid "" +"For the C programmers out there scratching their heads, the iterator need\r\n" +" not be read *during* this code cause the UB. The exclusivity rule also " +"enables\r\n" +" compiler optimizations which may cause inconsistent observations by the " +"iterator's\r\n" +" shared reference (e.g. stack spills or reordering instructions for " +"efficiency).\r\n" +" These observations may happen *any time after* the mutable reference is " +"created." +msgstr "" + +#: .\anti_patterns/index.md:1 +msgid "# Anti-patterns\r" +msgstr "" + +#: .\anti_patterns/index.md:3 +msgid "" +"An [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) is a solution " +"to\r\n" +"a \"recurring problem that is usually ineffective and risks being highly\r\n" +"counterproductive\". Just as valuable as knowing how to solve a problem, " +"is\r\n" +"knowing how _not_ to solve it. Anti-patterns give us great counter-examples " +"to\r\n" +"consider relative to design patterns. Anti-patterns are not confined to " +"code.\r\n" +"For example, a process can be an anti-pattern, too." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:1 +msgid "# Clone to satisfy the borrow checker\r" +msgstr "" + +#: .\anti_patterns/borrow_clone.md:5 +msgid "" +"The borrow checker prevents Rust users from developing otherwise unsafe code " +"by\r\n" +"ensuring that either: only one mutable reference exists, or potentially many " +"but\r\n" +"all immutable references exist. If the code written does not hold true to " +"these\r\n" +"conditions, this anti-pattern arises when the developer resolves the " +"compiler\r\n" +"error by cloning the variable." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:13 +msgid "" +"```rust\r\n" +"// define any variable\r\n" +"let mut x = 5;\r\n" +"\r\n" +"// Borrow `x` -- but clone it first\r\n" +"let y = &mut (x.clone());\r\n" +"\r\n" +"// without the x.clone() two lines prior, this line would fail on compile " +"as\r\n" +"// x has been borrowed\r\n" +"// thanks to x.clone(), x was never borrowed, and this line will run.\r\n" +"println!(\"{}\", x);\r\n" +"\r\n" +"// perform some action on the borrow to prevent rust from optimizing this\r\n" +"//out of existence\r\n" +"*y += 1;\r\n" +"```" +msgstr "" + +#: .\anti_patterns/borrow_clone.md:32 +msgid "" +"It is tempting, particularly for beginners, to use this pattern to " +"resolve\r\n" +"confusing issues with the borrow checker. However, there are serious\r\n" +"consequences. Using `.clone()` causes a copy of the data to be made. Any " +"changes\r\n" +"between the two are not synchronized -- as if two completely separate " +"variables\r\n" +"exist." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:38 +msgid "" +"There are special cases -- `Rc` is designed to handle clones " +"intelligently.\r\n" +"It internally manages exactly one copy of the data, and cloning it will " +"only\r\n" +"clone the reference." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:42 +msgid "" +"There is also `Arc` which provides shared ownership of a value of type " +"T\r\n" +"that is allocated in the heap. Invoking `.clone()` on `Arc` produces a new " +"`Arc`\r\n" +"instance, which points to the same allocation on the heap as the source " +"`Arc`,\r\n" +"while increasing a reference count." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:47 +msgid "" +"In general, clones should be deliberate, with full understanding of the\r\n" +"consequences. If a clone is used to make a borrow checker error " +"disappear,\r\n" +"that's a good indication this anti-pattern may be in use." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:51 +msgid "" +"Even though `.clone()` is an indication of a bad pattern, sometimes\r\n" +"**it is fine to write inefficient code**, in cases such as when:" +msgstr "" + +#: .\anti_patterns/borrow_clone.md:54 +msgid "" +"- the developer is still new to ownership\r\n" +"- the code doesn't have great speed or memory constraints\r\n" +" (like hackathon projects or prototypes)\r\n" +"- satisfying the borrow checker is really complicated, and you prefer to\r\n" +" optimize readability over performance\r\n" +"\r" +msgstr "" + +#: .\anti_patterns/borrow_clone.md:60 +msgid "" +"If an unnecessary clone is suspected, The [Rust Book's chapter on " +"Ownership](https://doc.rust-lang.org/book/ownership.html)\r\n" +"should be understood fully before assessing whether the clone is required or " +"not." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:63 +msgid "" +"Also be sure to always run `cargo clippy` in your project, which will detect " +"some\r\n" +"cases in which `.clone()` is not necessary, like " +"[1](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone),\r\n" +"[2](https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy),\r\n" +"[3](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) or " +"[4](https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref)." +msgstr "" + +#: .\anti_patterns/borrow_clone.md:70 +msgid "" +"- [`mem::{take(_), replace(_)}` to keep owned values in changed " +"enums](../idioms/mem-replace.md)\r\n" +"- [`Rc` documentation, which handles .clone() " +"intelligently](http://doc.rust-lang.org/std/rc/)\r\n" +"- [`Arc` documentation, a thread-safe reference-counting " +"pointer](https://doc.rust-lang.org/std/sync/struct.Arc.html)\r\n" +"- [Tricks with ownership in " +"Rust](https://web.archive.org/web/20210120233744/https://xion.io/post/code/rust-borrowchk-tricks.html)\r" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:1 +msgid "# `#![deny(warnings)]`\r" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:5 +msgid "" +"A well-intentioned crate author wants to ensure their code builds without\r\n" +"warnings. So they annotate their crate root with the following:" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:10 +msgid "" +"```rust\r\n" +"#![deny(warnings)]\r\n" +"\r\n" +"// All is well.\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:18 +msgid "It is short and will stop the build if anything is amiss." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:20 +msgid "## Drawbacks\r" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:22 +msgid "" +"By disallowing the compiler to build with warnings, a crate author opts out " +"of\r\n" +"Rust's famed stability. Sometimes new features or old misfeatures need a " +"change\r\n" +"in how things are done, thus lints are written that `warn` for a certain " +"grace\r\n" +"period before being turned to `deny`." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:27 +msgid "" +"For example, it was discovered that a type could have two `impl`s with the " +"same\r\n" +"method. This was deemed a bad idea, but in order to make the transition " +"smooth,\r\n" +"the `overlapping-inherent-impls` lint was introduced to give a warning to " +"those\r\n" +"stumbling on this fact, before it becomes a hard error in a future release." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:32 +msgid "" +"Also sometimes APIs get deprecated, so their use will emit a warning " +"where\r\n" +"before there was none." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:35 +msgid "" +"All this conspires to potentially break the build whenever something changes." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:37 +msgid "" +"Furthermore, crates that supply additional lints (e.g. [rust-clippy]) can " +"no\r\n" +"longer be used unless the annotation is removed. This is mitigated with\r\n" +"[--cap-lints]. The `--cap-lints=warn` command line argument, turns all " +"`deny`\r\n" +"lint errors into warnings." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:42 +#: .\functional/generics-type-classes.md:228 +msgid "## Alternatives\r" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:44 +msgid "" +"There are two ways of tackling this problem: First, we can decouple the " +"build\r\n" +"setting from the code, and second, we can name the lints we want to deny\r\n" +"explicitly." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:48 +msgid "The following command line will build with all warnings set to `deny`:" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:50 +msgid "```RUSTFLAGS=\"-D warnings\" cargo build```" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:52 +msgid "" +"This can be done by any individual developer (or be set in a CI tool like\r\n" +"Travis, but remember that this may break the build when something " +"changes)\r\n" +"without requiring a change to the code." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:56 +msgid "" +"Alternatively, we can specify the lints that we want to `deny` in the " +"code.\r\n" +"Here is a list of warning lints that is (hopefully) safe to deny (as of " +"Rustc 1.48.0):" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:59 +msgid "" +"```rust,ignore\r\n" +"#![deny(bad_style,\r\n" +" const_err,\r\n" +" dead_code,\r\n" +" improper_ctypes,\r\n" +" non_shorthand_field_patterns,\r\n" +" no_mangle_generic_items,\r\n" +" overflowing_literals,\r\n" +" path_statements,\r\n" +" patterns_in_fns_without_body,\r\n" +" private_in_public,\r\n" +" unconditional_recursion,\r\n" +" unused,\r\n" +" unused_allocation,\r\n" +" unused_comparisons,\r\n" +" unused_parens,\r\n" +" while_true)]\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:78 +msgid "In addition, the following `allow`ed lints may be a good idea to `deny`:" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:80 +msgid "" +"```rust,ignore\r\n" +"#![deny(missing_debug_implementations,\r\n" +" missing_docs,\r\n" +" trivial_casts,\r\n" +" trivial_numeric_casts,\r\n" +" unused_extern_crates,\r\n" +" unused_import_braces,\r\n" +" unused_qualifications,\r\n" +" unused_results)]\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deny-warnings.md:91 +msgid "Some may also want to add `missing-copy-implementations` to their list." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:93 +msgid "" +"Note that we explicitly did not add the `deprecated` lint, as it is " +"fairly\r\n" +"certain that there will be more deprecated APIs in the future." +msgstr "" + +#: .\anti_patterns/deny-warnings.md:98 +msgid "" +"- [A collection of all clippy " +"lints](https://rust-lang.github.io/rust-clippy/master)\r\n" +"- [deprecate attribute] documentation\r\n" +"- Type `rustc -W help` for a list of lints on your system. Also type\r\n" +"`rustc --help` for a general list of options\r\n" +"- [rust-clippy] is a collection of lints for better Rust code\r\n" +"\r" +msgstr "" + +#: .\anti_patterns/deref.md:1 +msgid "# `Deref` polymorphism\r" +msgstr "" + +#: .\anti_patterns/deref.md:5 +msgid "" +"Misuse the `Deref` trait to emulate inheritance between structs, and thus " +"reuse\r\n" +"methods." +msgstr "" + +#: .\anti_patterns/deref.md:10 +msgid "" +"Sometimes we want to emulate the following common pattern from OO languages " +"such\r\n" +"as Java:" +msgstr "" + +#: .\anti_patterns/deref.md:13 +msgid "" +"```java\r\n" +"class Foo {\r\n" +" void m() { ... }\r\n" +"}\r\n" +"\r\n" +"class Bar extends Foo {}\r\n" +"\r\n" +"public static void main(String[] args) {\r\n" +" Bar b = new Bar();\r\n" +" b.m();\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deref.md:26 +msgid "We can use the deref polymorphism anti-pattern to do so:" +msgstr "" + +#: .\anti_patterns/deref.md:28 +msgid "" +"```rust\r\n" +"use std::ops::Deref;\r\n" +"\r\n" +"struct Foo {}\r\n" +"\r\n" +"impl Foo {\r\n" +" fn m(&self) {\r\n" +" //..\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"struct Bar {\r\n" +" f: Foo,\r\n" +"}\r\n" +"\r\n" +"impl Deref for Bar {\r\n" +" type Target = Foo;\r\n" +" fn deref(&self) -> &Foo {\r\n" +" &self.f\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" let b = Bar { f: Foo {} };\r\n" +" b.m();\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deref.md:56 +msgid "" +"There is no struct inheritance in Rust. Instead we use composition and " +"include\r\n" +"an instance of `Foo` in `Bar` (since the field is a value, it is stored " +"inline,\r\n" +"so if there were fields, they would have the same layout in memory as the " +"Java\r\n" +"version (probably, you should use `#[repr(C)]` if you want to be sure))." +msgstr "" + +#: .\anti_patterns/deref.md:61 +msgid "" +"In order to make the method call work we implement `Deref` for `Bar` with " +"`Foo`\r\n" +"as the target (returning the embedded `Foo` field). That means that when " +"we\r\n" +"dereference a `Bar` (for example, using `*`) then we will get a `Foo`. That " +"is\r\n" +"pretty weird. Dereferencing usually gives a `T` from a reference to `T`, " +"here we\r\n" +"have two unrelated types. However, since the dot operator does implicit\r\n" +"dereferencing, it means that the method call will search for methods on " +"`Foo` as\r\n" +"well as `Bar`." +msgstr "" + +#: .\anti_patterns/deref.md:71 +msgid "You save a little boilerplate, e.g.," +msgstr "" + +#: .\anti_patterns/deref.md:73 +msgid "" +"```rust,ignore\r\n" +"impl Bar {\r\n" +" fn m(&self) {\r\n" +" self.f.m()\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\anti_patterns/deref.md:83 +msgid "" +"Most importantly this is a surprising idiom - future programmers reading " +"this in\r\n" +"code will not expect this to happen. That's because we are misusing the " +"`Deref`\r\n" +"trait rather than using it as intended (and documented, etc.). It's also " +"because\r\n" +"the mechanism here is completely implicit." +msgstr "" + +#: .\anti_patterns/deref.md:88 +msgid "" +"This pattern does not introduce subtyping between `Foo` and `Bar` like\r\n" +"inheritance in Java or C++ does. Furthermore, traits implemented by `Foo` " +"are\r\n" +"not automatically implemented for `Bar`, so this pattern interacts badly " +"with\r\n" +"bounds checking and thus generic programming." +msgstr "" + +#: .\anti_patterns/deref.md:93 +msgid "" +"Using this pattern gives subtly different semantics from most OO languages " +"with\r\n" +"regards to `self`. Usually it remains a reference to the sub-class, with " +"this\r\n" +"pattern it will be the 'class' where the method is defined." +msgstr "" + +#: .\anti_patterns/deref.md:97 +msgid "" +"Finally, this pattern only supports single inheritance, and has no notion " +"of\r\n" +"interfaces, class-based privacy, or other inheritance-related features. So, " +"it\r\n" +"gives an experience that will be subtly surprising to programmers used to " +"Java\r\n" +"inheritance, etc." +msgstr "" + +#: .\anti_patterns/deref.md:104 +msgid "" +"There is no one good alternative. Depending on the exact circumstances it " +"might\r\n" +"be better to re-implement using traits or to write out the facade methods " +"to\r\n" +"dispatch to `Foo` manually. We do intend to add a mechanism for " +"inheritance\r\n" +"similar to this to Rust, but it is likely to be some time before it " +"reaches\r\n" +"stable Rust. See these " +"[blog](http://aturon.github.io/blog/2015/09/18/reuse/)\r\n" +"[posts](http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-structs-part-4-extended-enums-and-thin-traits/)\r\n" +"and this [RFC issue](https://github.com/rust-lang/rfcs/issues/349) for more " +"details." +msgstr "" + +#: .\anti_patterns/deref.md:112 +msgid "" +"The `Deref` trait is designed for the implementation of custom pointer " +"types.\r\n" +"The intention is that it will take a pointer-to-`T` to a `T`, not convert\r\n" +"between different types. It is a shame that this isn't (probably cannot " +"be)\r\n" +"enforced by the trait definition." +msgstr "" + +#: .\anti_patterns/deref.md:117 +msgid "" +"Rust tries to strike a careful balance between explicit and implicit " +"mechanisms,\r\n" +"favouring explicit conversions between types. Automatic dereferencing in the " +"dot\r\n" +"operator is a case where the ergonomics strongly favour an implicit " +"mechanism,\r\n" +"but the intention is that this is limited to degrees of indirection, not\r\n" +"conversion between arbitrary types." +msgstr "" + +#: .\anti_patterns/deref.md:125 +msgid "" +"- [Collections are smart pointers idiom](../idioms/deref.md).\r\n" +"- Delegation crates for less boilerplate like " +"[delegate](https://crates.io/crates/delegate)\r\n" +" or [ambassador](https://crates.io/crates/ambassador)\r\n" +"- [Documentation for `Deref` " +"trait](https://doc.rust-lang.org/std/ops/trait.Deref.html).\r" +msgstr "" + +#: .\functional/index.md:1 +msgid "# Functional Usage of Rust\r" +msgstr "" + +#: .\functional/index.md:3 +msgid "" +"Rust is an imperative language, but it follows many\r\n" +"[functional " +"programming](https://en.wikipedia.org/wiki/Functional_programming) paradigms." +msgstr "" + +#: .\functional/index.md:6 +msgid "" +"> In computer science, *functional programming* is a programming paradigm " +"where\r\n" +"> programs are constructed by applying and composing functions.\r\n" +"> It is a declarative programming paradigm in which function definitions " +"are\r\n" +"> trees of expressions that each return a value, rather than a sequence " +"of\r\n" +"> imperative statements which change the state of the program.\r" +msgstr "" + +#: .\functional/paradigms.md:1 +msgid "# Programming paradigms\r" +msgstr "" + +#: .\functional/paradigms.md:3 +msgid "" +"One of the biggest hurdles to understanding functional programs when " +"coming\r\n" +"from an imperative background is the shift in thinking. Imperative " +"programs\r\n" +"describe __how__ to do something, whereas declarative programs describe\r\n" +"__what__ to do. Let's sum the numbers from 1 to 10 to show this." +msgstr "" + +#: .\functional/paradigms.md:8 +msgid "## Imperative\r" +msgstr "" + +#: .\functional/paradigms.md:10 +msgid "" +"```rust\r\n" +"let mut sum = 0;\r\n" +"for i in 1..11 {\r\n" +" sum += i;\r\n" +"}\r\n" +"println!(\"{}\", sum);\r\n" +"```" +msgstr "" + +#: .\functional/paradigms.md:18 +msgid "" +"With imperative programs, we have to play compiler to see what is " +"happening.\r\n" +"Here, we start with a `sum` of `0`.\r\n" +"Next, we iterate through the range from 1 to 10.\r\n" +"Each time through the loop, we add the corresponding value in the range.\r\n" +"Then we print it out." +msgstr "" + +#: .\functional/paradigms.md:24 +msgid "" +"| `i` | `sum` |\r\n" +"|:---:|:-----:|\r\n" +"| 1 | 1 |\r\n" +"| 2 | 3 |\r\n" +"| 3 | 6 |\r\n" +"| 4 | 10 |\r\n" +"| 5 | 15 |\r\n" +"| 6 | 21 |\r\n" +"| 7 | 28 |\r\n" +"| 8 | 36 |\r\n" +"| 9 | 45 |\r\n" +"| 10 | 55 |\r" +msgstr "" + +#: .\functional/paradigms.md:37 +msgid "" +"This is how most of us start out programming. We learn that a program is a " +"set\r\n" +"of steps." +msgstr "" + +#: .\functional/paradigms.md:40 +msgid "## Declarative\r" +msgstr "" + +#: .\functional/paradigms.md:42 +msgid "" +"```rust\r\n" +"println!(\"{}\", (1..11).fold(0, |a, b| a + b));\r\n" +"```" +msgstr "" + +#: .\functional/paradigms.md:46 +msgid "" +"Whoa! This is really different! What's going on here?\r\n" +"Remember that with declarative programs we are describing __what__ to do,\r\n" +"rather than __how__ to do it. `fold` is a function that " +"[composes](https://en.wikipedia.org/wiki/Function_composition)\r\n" +"functions. The name is a convention from Haskell." +msgstr "" + +#: .\functional/paradigms.md:51 +msgid "" +"Here, we are composing functions of addition (this closure: `|a, b| a + " +"b`)\r\n" +"with a range from 1 to 10. The `0` is the starting point, so `a` is `0` " +"at\r\n" +"first. `b` is the first element of the range, `1`. `0 + 1 = 1` is the " +"result.\r\n" +"So now we `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the " +"next\r\n" +"result. This process continues until we get to the last element in the " +"range,\r\n" +"`10`." +msgstr "" + +#: .\functional/paradigms.md:58 +msgid "" +"| `a` | `b` | result |\r\n" +"|:---:|:---:|:------:|\r\n" +"| 0 | 1 | 1 |\r\n" +"| 1 | 2 | 3 |\r\n" +"| 3 | 3 | 6 |\r\n" +"| 6 | 4 | 10 |\r\n" +"| 10 | 5 | 15 |\r\n" +"| 15 | 6 | 21 |\r\n" +"| 21 | 7 | 28 |\r\n" +"| 28 | 8 | 36 |\r\n" +"| 36 | 9 | 45 |\r\n" +"| 45 | 10 | 55 |\r" +msgstr "" + +#: .\functional/generics-type-classes.md:1 +msgid "# Generics as Type Classes\r" +msgstr "" + +#: .\functional/generics-type-classes.md:5 +msgid "" +"Rust's type system is designed more like functional languages (like " +"Haskell)\r\n" +"rather than imperative languages (like Java and C++). As a result, Rust can " +"turn\r\n" +"many kinds of programming problems into \"static typing\" problems. This is " +"one\r\n" +"of the biggest wins of choosing a functional language, and is critical to " +"many\r\n" +"of Rust's compile time guarantees." +msgstr "" + +#: .\functional/generics-type-classes.md:11 +msgid "" +"A key part of this idea is the way generic types work. In C++ and Java, " +"for\r\n" +"example, generic types are a meta-programming construct for the compiler.\r\n" +"`vector` and `vector` in C++ are just two different copies of " +"the\r\n" +"same boilerplate code for a `vector` type (known as a `template`) with " +"two\r\n" +"different types filled in." +msgstr "" + +#: .\functional/generics-type-classes.md:17 +msgid "" +"In Rust, a generic type parameter creates what is known in functional " +"languages\r\n" +"as a \"type class constraint\", and each different parameter filled in by an " +"end\r\n" +"user *actually changes the type*. In other words, `Vec` and " +"`Vec`\r\n" +"*are two different types*, which are recognized as distinct by all parts of " +"the\r\n" +"type system." +msgstr "" + +#: .\functional/generics-type-classes.md:23 +msgid "" +"This is called **monomorphization**, where different types are created " +"from\r\n" +"**polymorphic** code. This special behavior requires `impl` blocks to " +"specify\r\n" +"generic parameters. Different values for the generic type cause different " +"types,\r\n" +"and different types can have different `impl` blocks." +msgstr "" + +#: .\functional/generics-type-classes.md:28 +msgid "" +"In object-oriented languages, classes can inherit behavior from their " +"parents.\r\n" +"However, this allows the attachment of not only additional behavior to\r\n" +"particular members of a type class, but extra behavior as well." +msgstr "" + +#: .\functional/generics-type-classes.md:32 +msgid "" +"The nearest equivalent is the runtime polymorphism in Javascript and " +"Python,\r\n" +"where new members can be added to objects willy-nilly by any constructor.\r\n" +"However, unlike those languages, all of Rust's additional methods can be " +"type\r\n" +"checked when they are used, because their generics are statically defined. " +"That\r\n" +"makes them more usable while remaining safe." +msgstr "" + +#: .\functional/generics-type-classes.md:40 +msgid "" +"Suppose you are designing a storage server for a series of lab machines.\r\n" +"Because of the software involved, there are two different protocols you " +"need\r\n" +"to support: BOOTP (for PXE network boot), and NFS (for remote mount storage)." +msgstr "" + +#: .\functional/generics-type-classes.md:44 +msgid "" +"Your goal is to have one program, written in Rust, which can handle both " +"of\r\n" +"them. It will have protocol handlers and listen for both kinds of requests. " +"The\r\n" +"main application logic will then allow a lab administrator to configure " +"storage\r\n" +"and security controls for the actual files." +msgstr "" + +#: .\functional/generics-type-classes.md:49 +msgid "" +"The requests from machines in the lab for files contain the same basic\r\n" +"information, no matter what protocol they came from: an authentication " +"method,\r\n" +"and a file name to retrieve. A straightforward implementation would look\r\n" +"something like this:" +msgstr "" + +#: .\functional/generics-type-classes.md:54 +msgid "" +"```rust,ignore\r\n" +"\r\n" +"enum AuthInfo {\r\n" +" Nfs(crate::nfs::AuthInfo),\r\n" +" Bootp(crate::bootp::AuthInfo),\r\n" +"}\r\n" +"\r\n" +"struct FileDownloadRequest {\r\n" +" file_name: PathBuf,\r\n" +" authentication: AuthInfo,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/generics-type-classes.md:67 +msgid "" +"This design might work well enough. But now suppose you needed to support\r\n" +"adding metadata that was *protocol specific*. For example, with NFS, you\r\n" +"wanted to determine what their mount point was in order to enforce " +"additional\r\n" +"security rules." +msgstr "" + +#: .\functional/generics-type-classes.md:72 +msgid "" +"The way the current struct is designed leaves the protocol decision until\r\n" +"runtime. That means any method that applies to one protocol and not the " +"other\r\n" +"requires the programmer to do a runtime check." +msgstr "" + +#: .\functional/generics-type-classes.md:76 +msgid "Here is how getting an NFS mount point would look:" +msgstr "" + +#: .\functional/generics-type-classes.md:78 +msgid "" +"```rust,ignore\r\n" +"struct FileDownloadRequest {\r\n" +" file_name: PathBuf,\r\n" +" authentication: AuthInfo,\r\n" +" mount_point: Option,\r\n" +"}\r\n" +"\r\n" +"impl FileDownloadRequest {\r\n" +" // ... other methods ...\r\n" +"\r\n" +" /// Gets an NFS mount point if this is an NFS request. Otherwise,\r\n" +" /// return None.\r\n" +" pub fn mount_point(&self) -> Option<&Path> {\r\n" +" self.mount_point.as_ref()\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/generics-type-classes.md:96 +msgid "" +"Every caller of `mount_point()` must check for `None` and write code to " +"handle\r\n" +"it. This is true even if they know only NFS requests are ever used in a " +"given\r\n" +"code path!" +msgstr "" + +#: .\functional/generics-type-classes.md:100 +msgid "" +"It would be far more optimal to cause a compile-time error if the " +"different\r\n" +"request types were confused. After all, the entire path of the user's " +"code,\r\n" +"including what functions from the library they use, will know whether a " +"request\r\n" +"is an NFS request or a BOOTP request." +msgstr "" + +#: .\functional/generics-type-classes.md:105 +msgid "" +"In Rust, this is actually possible! The solution is to *add a generic type* " +"in\r\n" +"order to split the API." +msgstr "" + +#: .\functional/generics-type-classes.md:108 +msgid "Here is what that looks like:" +msgstr "" + +#: .\functional/generics-type-classes.md:110 +msgid "" +"```rust\r\n" +"use std::path::{Path, PathBuf};\r\n" +"\r\n" +"mod nfs {\r\n" +" #[derive(Clone)]\r\n" +" pub(crate) struct AuthInfo(String); // NFS session management omitted\r\n" +"}\r\n" +"\r\n" +"mod bootp {\r\n" +" pub(crate) struct AuthInfo(); // no authentication in bootp\r\n" +"}\r\n" +"\r\n" +"// private module, lest outside users invent their own protocol kinds!\r\n" +"mod proto_trait {\r\n" +" use std::path::{Path, PathBuf};\r\n" +" use super::{bootp, nfs};\r\n" +"\r\n" +" pub(crate) trait ProtoKind {\r\n" +" type AuthInfo;\r\n" +" fn auth_info(&self) -> Self::AuthInfo;\r\n" +" }\r\n" +"\r\n" +" pub struct Nfs {\r\n" +" auth: nfs::AuthInfo,\r\n" +" mount_point: PathBuf,\r\n" +" }\r\n" +"\r\n" +" impl Nfs {\r\n" +" pub(crate) fn mount_point(&self) -> &Path {\r\n" +" &self.mount_point\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" impl ProtoKind for Nfs {\r\n" +" type AuthInfo = nfs::AuthInfo;\r\n" +" fn auth_info(&self) -> Self::AuthInfo {\r\n" +" self.auth.clone()\r\n" +" }\r\n" +" }\r\n" +"\r\n" +" pub struct Bootp(); // no additional metadata\r\n" +"\r\n" +" impl ProtoKind for Bootp {\r\n" +" type AuthInfo = bootp::AuthInfo;\r\n" +" fn auth_info(&self) -> Self::AuthInfo {\r\n" +" bootp::AuthInfo()\r\n" +" }\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"use proto_trait::ProtoKind; // keep internal to prevent impls\r\n" +"pub use proto_trait::{Nfs, Bootp}; // re-export so callers can see them\r\n" +"\r\n" +"struct FileDownloadRequest {\r\n" +" file_name: PathBuf,\r\n" +" protocol: P,\r\n" +"}\r\n" +"\r\n" +"// all common API parts go into a generic impl block\r\n" +"impl FileDownloadRequest

{\r\n" +" fn file_path(&self) -> &Path {\r\n" +" &self.file_name\r\n" +" }\r\n" +"\r\n" +" fn auth_info(&self) -> P::AuthInfo {\r\n" +" self.protocol.auth_info()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// all protocol-specific impls go into their own block\r\n" +"impl FileDownloadRequest {\r\n" +" fn mount_point(&self) -> &Path {\r\n" +" self.protocol.mount_point()\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"fn main() {\r\n" +" // your code here\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/generics-type-classes.md:191 +msgid "" +"With this approach, if the user were to make a mistake and use the wrong\r\n" +"type;" +msgstr "" + +#: .\functional/generics-type-classes.md:194 +msgid "" +"```rust,ignore\r\n" +"fn main() {\r\n" +" let mut socket = crate::bootp::listen()?;\r\n" +" while let Some(request) = socket.next_request()? {\r\n" +" match request.mount_point().as_ref()\r\n" +" \"/secure\" => socket.send(\"Access denied\"),\r\n" +" _ => {} // continue on...\r\n" +" }\r\n" +" // Rest of the code here\r\n" +" }\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/generics-type-classes.md:207 +msgid "" +"They would get a syntax error. The type `FileDownloadRequest` does " +"not\r\n" +"implement `mount_point()`, only the type `FileDownloadRequest` does. " +"And\r\n" +"that is created by the NFS module, not the BOOTP module of course!" +msgstr "" + +#: .\functional/generics-type-classes.md:213 +msgid "" +"First, it allows fields that are common to multiple states to be " +"de-duplicated.\r\n" +"By making the non-shared fields generic, they are implemented once." +msgstr "" + +#: .\functional/generics-type-classes.md:216 +msgid "" +"Second, it makes the `impl` blocks easier to read, because they are broken " +"down\r\n" +"by state. Methods common to all states are typed once in one block, and " +"methods\r\n" +"unique to one state are in a separate block." +msgstr "" + +#: .\functional/generics-type-classes.md:220 +msgid "" +"Both of these mean there are fewer lines of code, and they are better " +"organized." +msgstr "" + +#: .\functional/generics-type-classes.md:224 +msgid "" +"This currently increases the size of the binary, due to the way " +"monomorphization\r\n" +"is implemented in the compiler. Hopefully the implementation will be able " +"to\r\n" +"improve in the future." +msgstr "" + +#: .\functional/generics-type-classes.md:230 +msgid "" +"* If a type seems to need a \"split API\" due to construction or partial\r\n" +"initialization, consider the\r\n" +"[Builder Pattern](../patterns/creational/builder.md) instead.\r\n" +"\r\n" +"* If the API between types does not change -- only the behavior does -- " +"then\r\n" +"the [Strategy Pattern](../patterns/behavioural/strategy.md) is better " +"used\r\n" +"instead.\r\n" +"\r" +msgstr "" + +#: .\functional/generics-type-classes.md:240 +msgid "This pattern is used throughout the standard library:" +msgstr "" + +#: .\functional/generics-type-classes.md:242 +msgid "" +"* `Vec` can be cast from a String, unlike every other type of " +"`Vec`.[^1]\r\n" +"* They can also be cast into a binary heap, but only if they contain a " +"type\r\n" +" that implements the `Ord` trait.[^2]\r\n" +"* The `to_string` method was specialized for `Cow` only of type " +"`str`.[^3]\r\n" +"\r" +msgstr "" + +#: .\functional/generics-type-classes.md:247 +msgid "It is also used by several popular crates to allow API flexibility:" +msgstr "" + +#: .\functional/generics-type-classes.md:249 +msgid "" +"* The `embedded-hal` ecosystem used for embedded devices makes extensive use " +"of\r\n" +" this pattern. For example, it allows statically verifying the " +"configuration of\r\n" +" device registers used to control embedded pins. When a pin is put into a " +"mode,\r\n" +" it returns a `Pin` struct, whose generic determines the functions\r\n" +" usable in that mode, which are not on the `Pin` itself. [^4]\r\n" +"\r\n" +"* The `hyper` HTTP client library uses this to expose rich APIs for " +"different\r\n" +" pluggable requests. Clients with different connectors have different " +"methods\r\n" +" on them as well as different trait implementations, while a core set of\r\n" +" methods apply to any connector. [^5]\r\n" +"\r\n" +"* The \"type state\" pattern -- where an object gains and loses API based on " +"an\r\n" +" internal state or invariant -- is implemented in Rust using the same " +"basic\r\n" +" concept, and a slightly different technique. [^6]\r\n" +"\r" +msgstr "" + +#: .\functional/generics-type-classes.md:264 +msgid "" +"See: [impl From\\ for Vec\\](\r\n" +"https://doc.rust-lang.org/1.59.0/src/std/ffi/c_str.rs.html#803-811)" +msgstr "" + +#: .\functional/generics-type-classes.md:267 +msgid "" +"See: [impl\\ From\\\\> for BinaryHeap\\](\r\n" +"https://doc.rust-lang.org/stable/src/alloc/collections/binary_heap.rs.html#1345-1354)" +msgstr "" + +#: .\functional/generics-type-classes.md:270 +msgid "" +"See: [impl\\<'\\_\\> ToString for Cow\\<'\\_, str>](\r\n" +"https://doc.rust-lang.org/stable/src/alloc/string.rs.html#2235-2240)" +msgstr "" + +#: .\functional/generics-type-classes.md:273 +msgid "" +"Example:\r\n" +"[https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html](\r\n" +"https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html)" +msgstr "" + +#: .\functional/generics-type-classes.md:277 +msgid "" +"See:\r\n" +"[https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html](\r\n" +"https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html)" +msgstr "" + +#: .\functional/generics-type-classes.md:281 +msgid "" +"See:\r\n" +"[The Case for the Type State Pattern](\r\n" +"https://web.archive.org/web/20210325065112/https://www.novatec-gmbh.de/en/blog/the-case-for-the-typestate-pattern-the-typestate-pattern-itself/)\r\n" +"and\r\n" +"[Rusty Typestate Series (an extensive thesis)](\r\n" +"https://web.archive.org/web/20210328164854/https://rustype.github.io/notes/notes/rust-typestate-series/rust-typestate-index)" +msgstr "" + +#: .\functional/lenses.md:1 +msgid "# Lenses and Prisms\r" +msgstr "" + +#: .\functional/lenses.md:3 +msgid "" +"This is a pure functional concept that is not frequently used in Rust.\r\n" +"Nevertheless, exploring the concept may be helpful to understand other\r\n" +"patterns in Rust APIs, such as " +"[visitors](../patterns/behavioural/visitor.md).\r\n" +"They also have niche use cases." +msgstr "" + +#: .\functional/lenses.md:8 +msgid "## Lenses: Uniform Access Across Types\r" +msgstr "" + +#: .\functional/lenses.md:10 +msgid "" +"A lens is a concept from functional programming languages that allows\r\n" +"accessing parts of a data type in an abstract, unified way.[^1]\r\n" +"In basic concept, it is similar to the way Rust traits work with type " +"erasure,\r\n" +"but it has a bit more power and flexibility." +msgstr "" + +#: .\functional/lenses.md:15 +msgid "" +"For example, suppose a bank contains several JSON formats for customer\r\n" +"data.\r\n" +"This is because they come from different databases or legacy systems.\r\n" +"One database contains the data needed to perform credit checks:" +msgstr "" + +#: .\functional/lenses.md:20 +msgid "" +"```json\r\n" +"{ \"name\": \"Jane Doe\",\r\n" +" \"dob\": \"2002-02-24\",\r\n" +" [...]\r\n" +" \"customer_id\": 1048576332,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:28 +msgid "Another one contains the account information:" +msgstr "" + +#: .\functional/lenses.md:30 +msgid "" +"```json\r\n" +"{ \"customer_id\": 1048576332,\r\n" +" \"accounts\": [\r\n" +" { \"account_id\": 2121,\r\n" +" \"account_type: \"savings\",\r\n" +" \"joint_customer_ids\": [],\r\n" +" [...]\r\n" +" },\r\n" +" { \"account_id\": 2122,\r\n" +" \"account_type: \"checking\",\r\n" +" \"joint_customer_ids\": [1048576333],\r\n" +" [...]\r\n" +" },\r\n" +" ]\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:47 +msgid "" +"Notice that both types have a customer ID number which corresponds to a " +"person.\r\n" +"How would a single function handle both records of different types?" +msgstr "" + +#: .\functional/lenses.md:50 +msgid "" +"In Rust, a `struct` could represent each of these types, and a trait would " +"have\r\n" +"a `get_customer_id` function they would implement:" +msgstr "" + +#: .\functional/lenses.md:53 +msgid "" +"```rust\r\n" +"use std::collections::HashSet;\r\n" +"\r\n" +"pub struct Account {\r\n" +" account_id: u32,\r\n" +" account_type: String,\r\n" +" // other fields omitted\r\n" +"}\r\n" +"\r\n" +"pub trait CustomerId {\r\n" +" fn get_customer_id(&self) -> u64;\r\n" +"}\r\n" +"\r\n" +"pub struct CreditRecord {\r\n" +" customer_id: u64,\r\n" +" name: String,\r\n" +" dob: String,\r\n" +" // other fields omitted\r\n" +"}\r\n" +"\r\n" +"impl CustomerId for CreditRecord {\r\n" +" fn get_customer_id(&self) -> u64 {\r\n" +" self.customer_id\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"pub struct AccountRecord {\r\n" +" customer_id: u64,\r\n" +" accounts: Vec,\r\n" +"}\r\n" +"\r\n" +"impl CustomerId for AccountRecord {\r\n" +" fn get_customer_id(&self) -> u64 {\r\n" +" self.customer_id\r\n" +" }\r\n" +"}\r\n" +"\r\n" +"// static polymorphism: only one type, but each function call can choose " +"it\r\n" +"fn unique_ids_set(records: &[R]) -> HashSet {\r\n" +" records.iter().map(|r| r.get_customer_id()).collect()\r\n" +"}\r\n" +"\r\n" +"// dynamic dispatch: iterates over any type with a customer ID, collecting " +"all\r\n" +"// values together\r\n" +"fn unique_ids_iter(iterator: I) -> HashSet\r\n" +" where I: Iterator>\r\n" +"{\r\n" +" iterator.map(|r| r.as_ref().get_customer_id()).collect()\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:104 +msgid "" +"Lenses, however, allow the code supporting customer ID to be moved from " +"the\r\n" +"*type* to the *accessor function*.\r\n" +"Rather than implementing a trait on each type, all matching structures " +"can\r\n" +"simply be accessed the same way." +msgstr "" + +#: .\functional/lenses.md:109 +msgid "" +"While the Rust language itself does not support this (type erasure is the\r\n" +"preferred solution to this problem), the [lens-rs\r\n" +"crate](https://github.com/TOETOE55/lens-rs/blob/master/guide.md) allows " +"code\r\n" +"that feels like this to be written with macros:" +msgstr "" + +#: .\functional/lenses.md:114 +msgid "" +"```rust,ignore\r\n" +"use std::collections::HashSet;\r\n" +"\r\n" +"use lens_rs::{optics, Lens, LensRef, Optics};\r\n" +"\r\n" +"#[derive(Clone, Debug, Lens /* derive to allow lenses to work */)]\r\n" +"pub struct CreditRecord {\r\n" +" #[optic(ref)] // macro attribute to allow viewing this field\r\n" +" customer_id: u64,\r\n" +" name: String,\r\n" +" dob: String,\r\n" +" // other fields omitted\r\n" +"}\r\n" +"\r\n" +"#[derive(Clone, Debug)]\r\n" +"pub struct Account {\r\n" +" account_id: u32,\r\n" +" account_type: String,\r\n" +" // other fields omitted\r\n" +"}\r\n" +"\r\n" +"#[derive(Clone, Debug, Lens)]\r\n" +"pub struct AccountRecord {\r\n" +" #[optic(ref)]\r\n" +" customer_id: u64,\r\n" +" accounts: Vec,\r\n" +"}\r\n" +"\r\n" +"fn unique_ids_lens(iter: impl Iterator) -> HashSet\r\n" +"where\r\n" +" T: LensRef, // any type with this field\r\n" +"{\r\n" +" iter.map(|r| *r.view_ref(optics!(customer_id))).collect()\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:150 +msgid "" +"The version of `unique_ids_lens` shown here allows any type to be in the " +"iterator,\r\n" +"so long as it has an attribute called `customer_id` which can be accessed " +"by\r\n" +"the function.\r\n" +"This is how most functional programming languages operate on lenses." +msgstr "" + +#: .\functional/lenses.md:155 +msgid "" +"Rather than macros, they achieve this with a technique known as " +"\"currying\".\r\n" +"That is, they \"partially construct\" the function, leaving the type of " +"the\r\n" +"final parameter (the value being operated on) unfilled until the function " +"is\r\n" +"called.\r\n" +"Thus it can be called with different types dynamically even from one place " +"in\r\n" +"the code.\r\n" +"That is what the `optics!` and `view_ref` in the example above simulates." +msgstr "" + +#: .\functional/lenses.md:163 +msgid "" +"The functional approach need not be restricted to accessing members.\r\n" +"More powerful lenses can be created which both *set* and *get* data in a\r\n" +"structure.\r\n" +"But the concept really becomes interesting when used as a building block " +"for\r\n" +"composition.\r\n" +"That is where the concept appears more clearly in Rust." +msgstr "" + +#: .\functional/lenses.md:170 +msgid "## Prisms: A Higher-Order form of \"Optics\"\r" +msgstr "" + +#: .\functional/lenses.md:172 +msgid "" +"A simple function such as `unique_ids_lens` above operates on a single " +"lens.\r\n" +"A *prism* is a function that operates on a *family* of lenses.\r\n" +"It is one conceptual level higher, using lenses as a building block, and\r\n" +"continuing the metaphor, is part of a family of \"optics\".\r\n" +"It is the main one that is useful in understanding Rust APIs, so will be " +"the\r\n" +"focus here." +msgstr "" + +#: .\functional/lenses.md:179 +msgid "" +"The same way that traits allow \"lens-like\" design with static polymorphism " +"and\r\n" +"dynamic dispatch, prism-like designs appear in Rust APIs which split " +"problems\r\n" +"into multiple associated types to be composed.\r\n" +"A good example of this is the traits in the parsing crate *Serde*." +msgstr "" + +#: .\functional/lenses.md:184 +msgid "" +"Trying to understand the way *Serde* works by only reading the API is a\r\n" +"challenge, especially the first time.\r\n" +"Consider the `Deserializer` trait, implemented by some type in any " +"library\r\n" +"which parses a new format:" +msgstr "" + +#: .\functional/lenses.md:189 +msgid "" +"```rust,ignore\r\n" +"pub trait Deserializer<'de>: Sized {\r\n" +" type Error: Error;\r\n" +"\r\n" +" fn deserialize_any(self, visitor: V) -> Result\r\n" +" where\r\n" +" V: Visitor<'de>;\r\n" +"\r\n" +" fn deserialize_bool(self, visitor: V) -> Result\r\n" +" where\r\n" +" V: Visitor<'de>;\r\n" +"\r\n" +" // remainder ommitted\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:205 +msgid "" +"For a trait that is just supposed to parse data from a format and return " +"a\r\n" +"value, this looks odd." +msgstr "" + +#: .\functional/lenses.md:208 +msgid "Why are all the return types type erased?" +msgstr "" + +#: .\functional/lenses.md:210 +msgid "" +"To understand that, we need to keep the lens concept in mind and look at\r\n" +"the definition of the `Visitor` type that is passed in generically:" +msgstr "" + +#: .\functional/lenses.md:213 +msgid "" +"```rust,ignore\r\n" +"pub trait Visitor<'de>: Sized {\r\n" +" type Value;\r\n" +"\r\n" +" fn visit_bool(self, v: bool) -> Result\r\n" +" where\r\n" +" E: Error;\r\n" +"\r\n" +" fn visit_u64(self, v: u64) -> Result\r\n" +" where\r\n" +" E: Error;\r\n" +"\r\n" +" fn visit_str(self, v: &str) -> Result\r\n" +" where\r\n" +" E: Error;\r\n" +"\r\n" +" // remainder omitted\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:233 +msgid "" +"The job of the `Visitor` type is to construct values in the *Serde* data " +"model,\r\n" +"which are represented by its associated `Value` type." +msgstr "" + +#: .\functional/lenses.md:236 +msgid "" +"These values represent parts of the Rust value being deserialized.\r\n" +"If this fails, it returns an `Error` type - an error type determined by " +"the\r\n" +"`Deserializer` when its methods were called." +msgstr "" + +#: .\functional/lenses.md:240 +msgid "" +"This highlights that `Deserializer` is similar to `CustomerId` from " +"earlier,\r\n" +"allowing any format parser which implements it to create `Value`s based on " +"what\r\n" +"it parsed.\r\n" +"The `Value` trait is acting like a lens in functional programming languages." +msgstr "" + +#: .\functional/lenses.md:245 +msgid "" +"But unlike the `CustomerId` trait, the return types of `Visitor` methods " +"are\r\n" +"*generic*, and the concrete `Value` type is *determined by the Visitor " +"itself*." +msgstr "" + +#: .\functional/lenses.md:248 +msgid "" +"Instead of acting as one lens, it effectively acts as a family of\r\n" +"lenses, one for each concrete type of `Visitor`." +msgstr "" + +#: .\functional/lenses.md:251 +msgid "" +"The `Deserializer` API is based on having a generic set of \"lenses\" work " +"across\r\n" +"a set of other generic types for \"observation\".\r\n" +"It is a *prism*." +msgstr "" + +#: .\functional/lenses.md:255 +msgid "For example, consider the identity record from earlier but simplified:" +msgstr "" + +#: .\functional/lenses.md:257 +msgid "" +"```json\r\n" +"{ \"name\": \"Jane Doe\",\r\n" +" \"customer_id\": 1048576332,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:263 +msgid "" +"How would the *Serde* library deserialize this JSON into `struct " +"CreditRecord`?" +msgstr "" + +#: .\functional/lenses.md:265 +msgid "" +"1. The user would call a library function to deserialize the data. This " +"would\r\n" +" create a `Deserializer` based on the JSON format.\r\n" +"1. Based on the fields in the struct, a `Visitor` would be created (more " +"on\r\n" +" that in a moment) which knows how to create each type in a generic " +"data\r\n" +" model that was needed to represent it: `u64` and `String`.\r\n" +"1. The deserializer would make calls to the `Visitor` as it parsed items.\r\n" +"1. The `Visitor` would indicate if the items found were expected, and if " +"not,\r\n" +" raise an error to indicate deserialization has failed.\r\n" +"\r" +msgstr "" + +#: .\functional/lenses.md:274 +msgid "For our very simple structure above, the expected pattern would be:" +msgstr "" + +#: .\functional/lenses.md:276 +msgid "" +"1. Visit a map (*Serde*'s equvialent to `HashMap` or JSON's dictionary).\r\n" +"1. Visit a string key called \"name\".\r\n" +"1. Visit a string value, which will go into the `name` field.\r\n" +"1. Visit a string key called \"customer_id\".\r\n" +"1. Visit a string value, which will go into the `customer_id` field.\r\n" +"1. Visit the end of the map.\r\n" +"\r" +msgstr "" + +#: .\functional/lenses.md:283 +msgid "But what determines which \"observation\" pattern is expected?" +msgstr "" + +#: .\functional/lenses.md:285 +msgid "" +"A functional programming language would be able to use currying to create\r\n" +"reflection of each type based on the type itself.\r\n" +"Rust does not support that, so every single type would need to have its " +"own\r\n" +"code written based on its fields and their properties." +msgstr "" + +#: .\functional/lenses.md:290 +msgid "*Serde* solves this usability challenge with a derive macro:" +msgstr "" + +#: .\functional/lenses.md:292 +msgid "" +"```rust,ignore\r\n" +"use serde::Deserialize;\r\n" +"\r\n" +"#[derive(Deserialize)]\r\n" +"struct IdRecord {\r\n" +" name: String,\r\n" +" customer_id: String,\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:302 +msgid "" +"That macro simply generates an impl block causing the struct to implement " +"a\r\n" +"trait called `Deserialize`." +msgstr "" + +#: .\functional/lenses.md:305 +msgid "It is defined this way:" +msgstr "" + +#: .\functional/lenses.md:307 +msgid "" +"```rust,ignore\r\n" +"pub trait Deserialize<'de>: Sized {\r\n" +" fn deserialize(deserializer: D) -> Result\r\n" +" where\r\n" +" D: Deserializer<'de>;\r\n" +"}\r\n" +"```" +msgstr "" + +#: .\functional/lenses.md:315 +msgid "" +"This is the function that determines how to create the struct itself.\r\n" +"Code is generated based on the struct's fields.\r\n" +"When the parsing library is called - in our example, a JSON parsing library " +"-\r\n" +"it creates a `Deserializer` and calls `Type::deserialize` with it as a\r\n" +"parameter." +msgstr "" + +#: .\functional/lenses.md:321 +msgid "" +"The `deserialize` code will then create a `Visitor` which will have its " +"calls\r\n" +"\"refracted\" by the `Deserializer`.\r\n" +"If everything goes well, eventually that `Visitor` will construct a value\r\n" +"corresponding to the type being parsed and return it." +msgstr "" + +#: .\functional/lenses.md:326 +msgid "" +"For a complete example, see the [*Serde*\r\n" +"documentation](https://serde.rs/deserialize-struct.html)." +msgstr "" + +#: .\functional/lenses.md:329 +msgid "To wrap up, this is the power of *Serde*:" +msgstr "" + +#: .\functional/lenses.md:331 +msgid "" +"1. The structure being parsed is represented by an `impl` block for " +"`Deserialize`\r\n" +"1. The input data format (e.g. JSON) is represented by a `Deserializer` " +"called\r\n" +" by `Deserialize`\r\n" +"1. The `Deserializer` acts like a prism which \"refracts\" lens-like " +"`Visitor`\r\n" +" calls which actually build the data value\r\n" +"\r" +msgstr "" + +#: .\functional/lenses.md:337 +msgid "" +"The result is that types to be deserialized only implement the \"top layer\" " +"of\r\n" +"the API, and file formats only need to implement the \"bottom layer\".\r\n" +"Each piece can then \"just work\" with the rest of the ecosystem, since " +"generic\r\n" +"types will bridge them." +msgstr "" + +#: .\functional/lenses.md:342 +msgid "" +"To emphasize, the only reason this model works on any format and any type " +"is\r\n" +"because the `Deserializer` trait's output type **is specified by the\r\n" +"implementor of `Visitor` it is passed**, rather than being tied to one " +"specific\r\n" +"type.\r\n" +"This was not true in the account example earlier." +msgstr "" + +#: .\functional/lenses.md:348 +msgid "" +"Rust's generic-inspired type system can bring it close to these concepts " +"and\r\n" +"use their power, as shown in this API design.\r\n" +"But it may also need procedural macros to create bridges for its generics." +msgstr "" + +#: .\functional/lenses.md:352 +msgid "## See Also\r" +msgstr "" + +#: .\functional/lenses.md:354 +msgid "" +"- [lens-rs crate](https://crates.io/crates/lens-rs) for a pre-built " +"lenses\r\n" +" implementation, with a cleaner interface than these examples\r\n" +"- [serde](https://serde.rs) itself, which makes these concepts intuitive " +"for\r\n" +" end users (i.e. defining the structs) without needing to undestand the\r\n" +" details\r\n" +"- [luminance](https://github.com/phaazon/luminance-rs) is a crate for " +"drawing\r\n" +" computer graphics that uses lens API design, including proceducal macros " +"to\r\n" +" create full prisms for buffers of different pixel types that remain " +"generic\r\n" +"- [An Article about Lenses in\r\n" +" " +"Scala](https://web.archive.org/web/20221128185849/https://medium.com/zyseme-technology/functional-references-lens-and-other-optics-in-scala-e5f7e2fdafe)\r\n" +" that is very readable even without Scala expertise.\r\n" +"- [Paper: Profunctor Optics: Modular Data\r\n" +" " +"Accessors](https://web.archive.org/web/20220701102832/https://arxiv.org/ftp/arxiv/papers/1703/1703.10857.pdf)\r\n" +"\r" +msgstr "" + +#: .\functional/lenses.md:368 +msgid "" +"[School of Haskell: A Little Lens Starter " +"Tutorial](https://web.archive.org/web/20221128190041/https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/a-little-lens-starter-tutorial)" +msgstr "" + +#: .\additional_resources/index.md:1 +msgid "# Additional resources\r" +msgstr "" + +#: .\additional_resources/index.md:3 +msgid "A collection of complementary helpful content" +msgstr "" + +#: .\additional_resources/index.md:5 +msgid "## Talks\r" +msgstr "" + +#: .\additional_resources/index.md:7 +msgid "" +"- [Design Patterns in Rust](https://www.youtube.com/watch?v=Pm_oO0N5B9k) " +"by\r\n" +" Nicholas Cameron at the PDRust (2016)\r\n" +"- [Writing Idiomatic Libraries in " +"Rust](https://www.youtube.com/watch?v=0zOg8_B71gE)\r\n" +" by Pascal Hertleif at RustFest (2017)\r\n" +"- [Rust Programming Techniques](https://www.youtube.com/watch?v=vqavdUGKeb4) " +"by\r\n" +" Nicholas Cameron at LinuxConfAu (2018)\r\n" +"\r" +msgstr "" + +#: .\additional_resources/index.md:14 +msgid "## Books (Online)\r" +msgstr "" + +#: .\additional_resources/index.md:16 +msgid "- [The Rust API Guidelines](https://rust-lang.github.io/api-guidelines)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:1 +msgid "# Design principles\r" +msgstr "" + +#: .\additional_resources/design-principles.md:3 +msgid "## A brief overview over common design principles\r" +msgstr "" + +#: .\additional_resources/design-principles.md:7 +msgid "## [SOLID](https://en.wikipedia.org/wiki/SOLID)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:9 +msgid "" +"- [Single Responsibility Principle " +"(SRP)](https://en.wikipedia.org/wiki/Single-responsibility_principle):\r\n" +" A class should only have a single responsibility, that is, only changes " +"to\r\n" +" one part of the software's specification should be able to affect the\r\n" +" specification of the class.\r\n" +"- [Open/Closed Principle " +"(OCP)](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle):\r\n" +" \"Software entities ... should be open for extension, but closed for\r\n" +" modification.\"\r\n" +"- [Liskov Substitution Principle " +"(LSP)](https://en.wikipedia.org/wiki/Liskov_substitution_principle):\r\n" +" \"Objects in a program should be replaceable with instances of their " +"subtypes\r\n" +" without altering the correctness of that program.\"\r\n" +"- [Interface Segregation Principle " +"(ISP)](https://en.wikipedia.org/wiki/Interface_segregation_principle):\r\n" +" \"Many client-specific interfaces are better than one general-purpose\r\n" +" interface.\"\r\n" +"- [Dependency Inversion Principle " +"(DIP)](https://en.wikipedia.org/wiki/Dependency_inversion_principle):\r\n" +" One should \"depend upon abstractions, [not] concretions.\"\r\n" +"\r" +msgstr "" + +#: .\additional_resources/design-principles.md:25 +msgid "" +"## [DRY (Don’t Repeat " +"Yourself)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:27 +msgid "" +"\"Every piece of knowledge must have a single, unambiguous, authoritative\r\n" +"representation within a system\"" +msgstr "" + +#: .\additional_resources/design-principles.md:30 +msgid "## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:32 +msgid "" +"most systems work best if they are kept simple rather than made " +"complicated;\r\n" +"therefore, simplicity should be a key goal in design, and unnecessary\r\n" +"complexity should be avoided" +msgstr "" + +#: .\additional_resources/design-principles.md:36 +msgid "## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:38 +msgid "" +"a given object should assume as little as possible about the structure or\r\n" +"properties of anything else (including its subcomponents), in accordance " +"with\r\n" +"the principle of \"information hiding\"" +msgstr "" + +#: .\additional_resources/design-principles.md:42 +msgid "" +"## [Design by contract " +"(DbC)](https://en.wikipedia.org/wiki/Design_by_contract)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:44 +msgid "" +"software designers should define formal, precise and verifiable interface\r\n" +"specifications for software components, which extend the ordinary definition " +"of\r\n" +"abstract data types with preconditions, postconditions and invariants" +msgstr "" + +#: .\additional_resources/design-principles.md:48 +msgid "" +"## " +"[Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))\r" +msgstr "" + +#: .\additional_resources/design-principles.md:50 +msgid "" +"bundling of data with the methods that operate on that data, or the " +"restricting\r\n" +"of direct access to some of an object's components. Encapsulation is used " +"to\r\n" +"hide the values or state of a structured data object inside a class, " +"preventing\r\n" +"unauthorized parties' direct access to them." +msgstr "" + +#: .\additional_resources/design-principles.md:55 +msgid "" +"## " +"[Command-Query-Separation(CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:57 +msgid "" +"“Functions should not produce abstract side effects...only commands\r\n" +"(procedures) will be permitted to produce side effects.” - Bertrand " +"Meyer:\r\n" +"Object-Oriented Software Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:61 +msgid "" +"## [Principle of least astonishment " +"(POLA)](https://en.wikipedia.org/wiki/Principle_of_least_astonishment)\r" +msgstr "" + +#: .\additional_resources/design-principles.md:63 +msgid "" +"a component of a system should behave in a way that most users will expect " +"it\r\n" +"to behave. The behavior should not astonish or surprise users" +msgstr "" + +#: .\additional_resources/design-principles.md:66 +msgid "## Linguistic-Modular-Units\r" +msgstr "" + +#: .\additional_resources/design-principles.md:68 +msgid "" +"“Modules must correspond to syntactic units in the language used.” - " +"Bertrand\r\n" +"Meyer: Object-Oriented Software Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:71 +msgid "## Self-Documentation\r" +msgstr "" + +#: .\additional_resources/design-principles.md:73 +msgid "" +"“The designer of a module should strive to make all information about the\r\n" +"module part of the module itself.” - Bertrand Meyer: Object-Oriented " +"Software\r\n" +"Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:77 +msgid "## Uniform-Access\r" +msgstr "" + +#: .\additional_resources/design-principles.md:79 +msgid "" +"“All services offered by a module should be available through a uniform\r\n" +"notation, which does not betray whether they are implemented through storage " +"or\r\n" +"through computation.” - Bertrand Meyer: Object-Oriented Software Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:83 +msgid "## Single-Choice\r" +msgstr "" + +#: .\additional_resources/design-principles.md:85 +msgid "" +"“Whenever a software system must support a set of alternatives, one and " +"only\r\n" +"one module in the system should know their exhaustive list.” - Bertrand " +"Meyer:\r\n" +"Object-Oriented Software Construction" +msgstr "" + +#: .\additional_resources/design-principles.md:89 +msgid "## Persistence-Closure\r" +msgstr "" + +#: .\additional_resources/design-principles.md:91 +msgid "" +"“Whenever a storage mechanism stores an object, it must store with it the\r\n" +"dependents of that object. Whenever a retrieval mechanism retrieves a\r\n" +"previously stored object, it must also retrieve any dependent of that " +"object\r\n" +"that has not yet been retrieved.” - Bertrand Meyer: Object-Oriented " +"Software\r\n" +"Construction" +msgstr "" + diff --git a/src/SUMMARY.md b/src/SUMMARY.md new file mode 100644 index 00000000..c2771940 --- /dev/null +++ b/src/SUMMARY.md @@ -0,0 +1,55 @@ +# Summary + +- [Introduction](./intro.md) + - [Translations](./translations.md) +- [Idioms](./idioms/index.md) + - [Use borrowed types for arguments](./idioms/coercion-arguments.md) + - [Concatenating Strings with format!](./idioms/concat-format.md) + - [Constructor](./idioms/ctor.md) + - [The Default Trait](./idioms/default.md) + - [Collections Are Smart Pointers](./idioms/deref.md) + - [Finalisation in Destructors](./idioms/dtor-finally.md) + - [`mem::{take(_), replace(_)}`](./idioms/mem-replace.md) + - [On-Stack Dynamic Dispatch](./idioms/on-stack-dyn-dispatch.md) + - [Foreign function interface (FFI)](./idioms/ffi/intro.md) + - [Idiomatic Errors](./idioms/ffi/errors.md) + - [Accepting Strings](./idioms/ffi/accepting-strings.md) + - [Passing Strings](./idioms/ffi/passing-strings.md) + - [Iterating over an Option](./idioms/option-iter.md) + - [Pass Variables to Closure](./idioms/pass-var-to-closure.md) + - [Privacy For Extensibility](./idioms/priv-extend.md) + - [Easy doc initialization](./idioms/rustdoc-init.md) + - [Temporary mutability](./idioms/temporary-mutability.md) + - [Return consumed arg on error](./idioms/return-consumed-arg-on-error.md) + +- [Design Patterns](./patterns/index.md) + - [Behavioural](./patterns/behavioural/intro.md) + - [Command](./patterns/behavioural/command.md) + - [Interpreter](./patterns/behavioural/interpreter.md) + - [Newtype](./patterns/behavioural/newtype.md) + - [RAII Guards](./patterns/behavioural/RAII.md) + - [Strategy](./patterns/behavioural/strategy.md) + - [Visitor](./patterns/behavioural/visitor.md) + - [Creational](./patterns/creational/intro.md) + - [Builder](./patterns/creational/builder.md) + - [Fold](./patterns/creational/fold.md) + - [Structural](./patterns/structural/intro.md) + - [Compose Structs](./patterns/structural/compose-structs.md) + - [Prefer Small Crates](./patterns/structural/small-crates.md) + - [Contain unsafety in small modules](./patterns/structural/unsafe-mods.md) + - [Foreign function interface (FFI)](./patterns/ffi/intro.md) + - [Object-Based APIs](./patterns/ffi/export.md) + - [Type Consolidation into Wrappers](./patterns/ffi/wrappers.md) + +- [Anti-patterns](./anti_patterns/index.md) + - [Clone to satisfy the borrow checker](./anti_patterns/borrow_clone.md) + - [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md) + - [Deref Polymorphism](./anti_patterns/deref.md) + +- [Functional Programming](./functional/index.md) + - [Programming paradigms](./functional/paradigms.md) + - [Generics as Type Classes](./functional/generics-type-classes.md) + - [Lenses and Prisms](./functional/lenses.md) + +- [Additional Resources](./additional_resources/index.md) + - [Design principles](./additional_resources/design-principles.md) diff --git a/src/additional_resources/design-principles.md b/src/additional_resources/design-principles.md new file mode 100644 index 00000000..d20ff914 --- /dev/null +++ b/src/additional_resources/design-principles.md @@ -0,0 +1,95 @@ +# Design principles + +## A brief overview over common design principles + +--- + +## [SOLID](https://en.wikipedia.org/wiki/SOLID) + +- [Single Responsibility Principle (SRP)](https://en.wikipedia.org/wiki/Single-responsibility_principle): + A class should only have a single responsibility, that is, only changes to + one part of the software's specification should be able to affect the + specification of the class. +- [Open/Closed Principle (OCP)](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle): + "Software entities ... should be open for extension, but closed for + modification." +- [Liskov Substitution Principle (LSP)](https://en.wikipedia.org/wiki/Liskov_substitution_principle): + "Objects in a program should be replaceable with instances of their subtypes + without altering the correctness of that program." +- [Interface Segregation Principle (ISP)](https://en.wikipedia.org/wiki/Interface_segregation_principle): + "Many client-specific interfaces are better than one general-purpose + interface." +- [Dependency Inversion Principle (DIP)](https://en.wikipedia.org/wiki/Dependency_inversion_principle): + One should "depend upon abstractions, [not] concretions." + +## [DRY (Don’t Repeat Yourself)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) + +"Every piece of knowledge must have a single, unambiguous, authoritative +representation within a system" + +## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle) + +most systems work best if they are kept simple rather than made complicated; +therefore, simplicity should be a key goal in design, and unnecessary +complexity should be avoided + +## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter) + +a given object should assume as little as possible about the structure or +properties of anything else (including its subcomponents), in accordance with +the principle of "information hiding" + +## [Design by contract (DbC)](https://en.wikipedia.org/wiki/Design_by_contract) + +software designers should define formal, precise and verifiable interface +specifications for software components, which extend the ordinary definition of +abstract data types with preconditions, postconditions and invariants + +## [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)) + +bundling of data with the methods that operate on that data, or the restricting +of direct access to some of an object's components. Encapsulation is used to +hide the values or state of a structured data object inside a class, preventing +unauthorized parties' direct access to them. + +## [Command-Query-Separation(CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation) + +“Functions should not produce abstract side effects...only commands +(procedures) will be permitted to produce side effects.” - Bertrand Meyer: +Object-Oriented Software Construction + +## [Principle of least astonishment (POLA)](https://en.wikipedia.org/wiki/Principle_of_least_astonishment) + +a component of a system should behave in a way that most users will expect it +to behave. The behavior should not astonish or surprise users + +## Linguistic-Modular-Units + +“Modules must correspond to syntactic units in the language used.” - Bertrand +Meyer: Object-Oriented Software Construction + +## Self-Documentation + +“The designer of a module should strive to make all information about the +module part of the module itself.” - Bertrand Meyer: Object-Oriented Software +Construction + +## Uniform-Access + +“All services offered by a module should be available through a uniform +notation, which does not betray whether they are implemented through storage or +through computation.” - Bertrand Meyer: Object-Oriented Software Construction + +## Single-Choice + +“Whenever a software system must support a set of alternatives, one and only +one module in the system should know their exhaustive list.” - Bertrand Meyer: +Object-Oriented Software Construction + +## Persistence-Closure + +“Whenever a storage mechanism stores an object, it must store with it the +dependents of that object. Whenever a retrieval mechanism retrieves a +previously stored object, it must also retrieve any dependent of that object +that has not yet been retrieved.” - Bertrand Meyer: Object-Oriented Software +Construction diff --git a/src/additional_resources/index.md b/src/additional_resources/index.md new file mode 100644 index 00000000..29022937 --- /dev/null +++ b/src/additional_resources/index.md @@ -0,0 +1,16 @@ +# Additional resources + +A collection of complementary helpful content + +## Talks + +- [Design Patterns in Rust](https://www.youtube.com/watch?v=Pm_oO0N5B9k) by + Nicholas Cameron at the PDRust (2016) +- [Writing Idiomatic Libraries in Rust](https://www.youtube.com/watch?v=0zOg8_B71gE) + by Pascal Hertleif at RustFest (2017) +- [Rust Programming Techniques](https://www.youtube.com/watch?v=vqavdUGKeb4) by + Nicholas Cameron at LinuxConfAu (2018) + +## Books (Online) + +- [The Rust API Guidelines](https://rust-lang.github.io/api-guidelines) diff --git a/src/anti_patterns/borrow_clone.md b/src/anti_patterns/borrow_clone.md new file mode 100644 index 00000000..f5806aa2 --- /dev/null +++ b/src/anti_patterns/borrow_clone.md @@ -0,0 +1,73 @@ +# Clone to satisfy the borrow checker + +## Description + +The borrow checker prevents Rust users from developing otherwise unsafe code by +ensuring that either: only one mutable reference exists, or potentially many but +all immutable references exist. If the code written does not hold true to these +conditions, this anti-pattern arises when the developer resolves the compiler +error by cloning the variable. + +## Example + +```rust +// define any variable +let mut x = 5; + +// Borrow `x` -- but clone it first +let y = &mut (x.clone()); + +// without the x.clone() two lines prior, this line would fail on compile as +// x has been borrowed +// thanks to x.clone(), x was never borrowed, and this line will run. +println!("{}", x); + +// perform some action on the borrow to prevent rust from optimizing this +//out of existence +*y += 1; +``` + +## Motivation + +It is tempting, particularly for beginners, to use this pattern to resolve +confusing issues with the borrow checker. However, there are serious +consequences. Using `.clone()` causes a copy of the data to be made. Any changes +between the two are not synchronized -- as if two completely separate variables +exist. + +There are special cases -- `Rc` is designed to handle clones intelligently. +It internally manages exactly one copy of the data, and cloning it will only +clone the reference. + +There is also `Arc` which provides shared ownership of a value of type T +that is allocated in the heap. Invoking `.clone()` on `Arc` produces a new `Arc` +instance, which points to the same allocation on the heap as the source `Arc`, +while increasing a reference count. + +In general, clones should be deliberate, with full understanding of the +consequences. If a clone is used to make a borrow checker error disappear, +that's a good indication this anti-pattern may be in use. + +Even though `.clone()` is an indication of a bad pattern, sometimes +**it is fine to write inefficient code**, in cases such as when: + +- the developer is still new to ownership +- the code doesn't have great speed or memory constraints + (like hackathon projects or prototypes) +- satisfying the borrow checker is really complicated, and you prefer to + optimize readability over performance + +If an unnecessary clone is suspected, The [Rust Book's chapter on Ownership](https://doc.rust-lang.org/book/ownership.html) +should be understood fully before assessing whether the clone is required or not. + +Also be sure to always run `cargo clippy` in your project, which will detect some +cases in which `.clone()` is not necessary, like [1](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone), +[2](https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy), +[3](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) or [4](https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref). + +## See also + +- [`mem::{take(_), replace(_)}` to keep owned values in changed enums](../idioms/mem-replace.md) +- [`Rc` documentation, which handles .clone() intelligently](http://doc.rust-lang.org/std/rc/) +- [`Arc` documentation, a thread-safe reference-counting pointer](https://doc.rust-lang.org/std/sync/struct.Arc.html) +- [Tricks with ownership in Rust](https://web.archive.org/web/20210120233744/https://xion.io/post/code/rust-borrowchk-tricks.html) diff --git a/src/anti_patterns/deny-warnings.md b/src/anti_patterns/deny-warnings.md new file mode 100644 index 00000000..1dae9d29 --- /dev/null +++ b/src/anti_patterns/deny-warnings.md @@ -0,0 +1,106 @@ +# `#![deny(warnings)]` + +## Description + +A well-intentioned crate author wants to ensure their code builds without +warnings. So they annotate their crate root with the following: + +## Example + +```rust +#![deny(warnings)] + +// All is well. +``` + +## Advantages + +It is short and will stop the build if anything is amiss. + +## Drawbacks + +By disallowing the compiler to build with warnings, a crate author opts out of +Rust's famed stability. Sometimes new features or old misfeatures need a change +in how things are done, thus lints are written that `warn` for a certain grace +period before being turned to `deny`. + +For example, it was discovered that a type could have two `impl`s with the same +method. This was deemed a bad idea, but in order to make the transition smooth, +the `overlapping-inherent-impls` lint was introduced to give a warning to those +stumbling on this fact, before it becomes a hard error in a future release. + +Also sometimes APIs get deprecated, so their use will emit a warning where +before there was none. + +All this conspires to potentially break the build whenever something changes. + +Furthermore, crates that supply additional lints (e.g. [rust-clippy]) can no +longer be used unless the annotation is removed. This is mitigated with +[--cap-lints]. The `--cap-lints=warn` command line argument, turns all `deny` +lint errors into warnings. + +## Alternatives + +There are two ways of tackling this problem: First, we can decouple the build +setting from the code, and second, we can name the lints we want to deny +explicitly. + +The following command line will build with all warnings set to `deny`: + +```RUSTFLAGS="-D warnings" cargo build``` + +This can be done by any individual developer (or be set in a CI tool like +Travis, but remember that this may break the build when something changes) +without requiring a change to the code. + +Alternatively, we can specify the lints that we want to `deny` in the code. +Here is a list of warning lints that is (hopefully) safe to deny (as of Rustc 1.48.0): + +```rust,ignore +#![deny(bad_style, + const_err, + dead_code, + improper_ctypes, + non_shorthand_field_patterns, + no_mangle_generic_items, + overflowing_literals, + path_statements, + patterns_in_fns_without_body, + private_in_public, + unconditional_recursion, + unused, + unused_allocation, + unused_comparisons, + unused_parens, + while_true)] +``` + +In addition, the following `allow`ed lints may be a good idea to `deny`: + +```rust,ignore +#![deny(missing_debug_implementations, + missing_docs, + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_qualifications, + unused_results)] +``` + +Some may also want to add `missing-copy-implementations` to their list. + +Note that we explicitly did not add the `deprecated` lint, as it is fairly +certain that there will be more deprecated APIs in the future. + +## See also + +- [A collection of all clippy lints](https://rust-lang.github.io/rust-clippy/master) +- [deprecate attribute] documentation +- Type `rustc -W help` for a list of lints on your system. Also type +`rustc --help` for a general list of options +- [rust-clippy] is a collection of lints for better Rust code + +[rust-clippy]: https://github.com/Manishearth/rust-clippy +[deprecate attribute]: https://doc.rust-lang.org/reference/attributes.html#deprecation +[--cap-lints]: https://doc.rust-lang.org/rustc/lints/levels.html#capping-lints diff --git a/src/anti_patterns/deref.md b/src/anti_patterns/deref.md new file mode 100644 index 00000000..b6f8f789 --- /dev/null +++ b/src/anti_patterns/deref.md @@ -0,0 +1,128 @@ +# `Deref` polymorphism + +## Description + +Misuse the `Deref` trait to emulate inheritance between structs, and thus reuse +methods. + +## Example + +Sometimes we want to emulate the following common pattern from OO languages such +as Java: + +```java +class Foo { + void m() { ... } +} + +class Bar extends Foo {} + +public static void main(String[] args) { + Bar b = new Bar(); + b.m(); +} +``` + +We can use the deref polymorphism anti-pattern to do so: + +```rust +use std::ops::Deref; + +struct Foo {} + +impl Foo { + fn m(&self) { + //.. + } +} + +struct Bar { + f: Foo, +} + +impl Deref for Bar { + type Target = Foo; + fn deref(&self) -> &Foo { + &self.f + } +} + +fn main() { + let b = Bar { f: Foo {} }; + b.m(); +} +``` + +There is no struct inheritance in Rust. Instead we use composition and include +an instance of `Foo` in `Bar` (since the field is a value, it is stored inline, +so if there were fields, they would have the same layout in memory as the Java +version (probably, you should use `#[repr(C)]` if you want to be sure)). + +In order to make the method call work we implement `Deref` for `Bar` with `Foo` +as the target (returning the embedded `Foo` field). That means that when we +dereference a `Bar` (for example, using `*`) then we will get a `Foo`. That is +pretty weird. Dereferencing usually gives a `T` from a reference to `T`, here we +have two unrelated types. However, since the dot operator does implicit +dereferencing, it means that the method call will search for methods on `Foo` as +well as `Bar`. + +## Advantages + +You save a little boilerplate, e.g., + +```rust,ignore +impl Bar { + fn m(&self) { + self.f.m() + } +} +``` + +## Disadvantages + +Most importantly this is a surprising idiom - future programmers reading this in +code will not expect this to happen. That's because we are misusing the `Deref` +trait rather than using it as intended (and documented, etc.). It's also because +the mechanism here is completely implicit. + +This pattern does not introduce subtyping between `Foo` and `Bar` like +inheritance in Java or C++ does. Furthermore, traits implemented by `Foo` are +not automatically implemented for `Bar`, so this pattern interacts badly with +bounds checking and thus generic programming. + +Using this pattern gives subtly different semantics from most OO languages with +regards to `self`. Usually it remains a reference to the sub-class, with this +pattern it will be the 'class' where the method is defined. + +Finally, this pattern only supports single inheritance, and has no notion of +interfaces, class-based privacy, or other inheritance-related features. So, it +gives an experience that will be subtly surprising to programmers used to Java +inheritance, etc. + +## Discussion + +There is no one good alternative. Depending on the exact circumstances it might +be better to re-implement using traits or to write out the facade methods to +dispatch to `Foo` manually. We do intend to add a mechanism for inheritance +similar to this to Rust, but it is likely to be some time before it reaches +stable Rust. See these [blog](http://aturon.github.io/blog/2015/09/18/reuse/) +[posts](http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-structs-part-4-extended-enums-and-thin-traits/) +and this [RFC issue](https://github.com/rust-lang/rfcs/issues/349) for more details. + +The `Deref` trait is designed for the implementation of custom pointer types. +The intention is that it will take a pointer-to-`T` to a `T`, not convert +between different types. It is a shame that this isn't (probably cannot be) +enforced by the trait definition. + +Rust tries to strike a careful balance between explicit and implicit mechanisms, +favouring explicit conversions between types. Automatic dereferencing in the dot +operator is a case where the ergonomics strongly favour an implicit mechanism, +but the intention is that this is limited to degrees of indirection, not +conversion between arbitrary types. + +## See also + +- [Collections are smart pointers idiom](../idioms/deref.md). +- Delegation crates for less boilerplate like [delegate](https://crates.io/crates/delegate) + or [ambassador](https://crates.io/crates/ambassador) +- [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html). diff --git a/src/anti_patterns/index.md b/src/anti_patterns/index.md new file mode 100644 index 00000000..84107e04 --- /dev/null +++ b/src/anti_patterns/index.md @@ -0,0 +1,8 @@ +# Anti-patterns + +An [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) is a solution to +a "recurring problem that is usually ineffective and risks being highly +counterproductive". Just as valuable as knowing how to solve a problem, is +knowing how _not_ to solve it. Anti-patterns give us great counter-examples to +consider relative to design patterns. Anti-patterns are not confined to code. +For example, a process can be an anti-pattern, too. diff --git a/src/functional/generics-type-classes.md b/src/functional/generics-type-classes.md new file mode 100644 index 00000000..df80a08b --- /dev/null +++ b/src/functional/generics-type-classes.md @@ -0,0 +1,286 @@ +# Generics as Type Classes + +## Description + +Rust's type system is designed more like functional languages (like Haskell) +rather than imperative languages (like Java and C++). As a result, Rust can turn +many kinds of programming problems into "static typing" problems. This is one +of the biggest wins of choosing a functional language, and is critical to many +of Rust's compile time guarantees. + +A key part of this idea is the way generic types work. In C++ and Java, for +example, generic types are a meta-programming construct for the compiler. +`vector` and `vector` in C++ are just two different copies of the +same boilerplate code for a `vector` type (known as a `template`) with two +different types filled in. + +In Rust, a generic type parameter creates what is known in functional languages +as a "type class constraint", and each different parameter filled in by an end +user *actually changes the type*. In other words, `Vec` and `Vec` +*are two different types*, which are recognized as distinct by all parts of the +type system. + +This is called **monomorphization**, where different types are created from +**polymorphic** code. This special behavior requires `impl` blocks to specify +generic parameters. Different values for the generic type cause different types, +and different types can have different `impl` blocks. + +In object-oriented languages, classes can inherit behavior from their parents. +However, this allows the attachment of not only additional behavior to +particular members of a type class, but extra behavior as well. + +The nearest equivalent is the runtime polymorphism in Javascript and Python, +where new members can be added to objects willy-nilly by any constructor. +However, unlike those languages, all of Rust's additional methods can be type +checked when they are used, because their generics are statically defined. That +makes them more usable while remaining safe. + +## Example + +Suppose you are designing a storage server for a series of lab machines. +Because of the software involved, there are two different protocols you need +to support: BOOTP (for PXE network boot), and NFS (for remote mount storage). + +Your goal is to have one program, written in Rust, which can handle both of +them. It will have protocol handlers and listen for both kinds of requests. The +main application logic will then allow a lab administrator to configure storage +and security controls for the actual files. + +The requests from machines in the lab for files contain the same basic +information, no matter what protocol they came from: an authentication method, +and a file name to retrieve. A straightforward implementation would look +something like this: + +```rust,ignore + +enum AuthInfo { + Nfs(crate::nfs::AuthInfo), + Bootp(crate::bootp::AuthInfo), +} + +struct FileDownloadRequest { + file_name: PathBuf, + authentication: AuthInfo, +} +``` + +This design might work well enough. But now suppose you needed to support +adding metadata that was *protocol specific*. For example, with NFS, you +wanted to determine what their mount point was in order to enforce additional +security rules. + +The way the current struct is designed leaves the protocol decision until +runtime. That means any method that applies to one protocol and not the other +requires the programmer to do a runtime check. + +Here is how getting an NFS mount point would look: + +```rust,ignore +struct FileDownloadRequest { + file_name: PathBuf, + authentication: AuthInfo, + mount_point: Option, +} + +impl FileDownloadRequest { + // ... other methods ... + + /// Gets an NFS mount point if this is an NFS request. Otherwise, + /// return None. + pub fn mount_point(&self) -> Option<&Path> { + self.mount_point.as_ref() + } +} +``` + +Every caller of `mount_point()` must check for `None` and write code to handle +it. This is true even if they know only NFS requests are ever used in a given +code path! + +It would be far more optimal to cause a compile-time error if the different +request types were confused. After all, the entire path of the user's code, +including what functions from the library they use, will know whether a request +is an NFS request or a BOOTP request. + +In Rust, this is actually possible! The solution is to *add a generic type* in +order to split the API. + +Here is what that looks like: + +```rust +use std::path::{Path, PathBuf}; + +mod nfs { + #[derive(Clone)] + pub(crate) struct AuthInfo(String); // NFS session management omitted +} + +mod bootp { + pub(crate) struct AuthInfo(); // no authentication in bootp +} + +// private module, lest outside users invent their own protocol kinds! +mod proto_trait { + use std::path::{Path, PathBuf}; + use super::{bootp, nfs}; + + pub(crate) trait ProtoKind { + type AuthInfo; + fn auth_info(&self) -> Self::AuthInfo; + } + + pub struct Nfs { + auth: nfs::AuthInfo, + mount_point: PathBuf, + } + + impl Nfs { + pub(crate) fn mount_point(&self) -> &Path { + &self.mount_point + } + } + + impl ProtoKind for Nfs { + type AuthInfo = nfs::AuthInfo; + fn auth_info(&self) -> Self::AuthInfo { + self.auth.clone() + } + } + + pub struct Bootp(); // no additional metadata + + impl ProtoKind for Bootp { + type AuthInfo = bootp::AuthInfo; + fn auth_info(&self) -> Self::AuthInfo { + bootp::AuthInfo() + } + } +} + +use proto_trait::ProtoKind; // keep internal to prevent impls +pub use proto_trait::{Nfs, Bootp}; // re-export so callers can see them + +struct FileDownloadRequest { + file_name: PathBuf, + protocol: P, +} + +// all common API parts go into a generic impl block +impl FileDownloadRequest

{ + fn file_path(&self) -> &Path { + &self.file_name + } + + fn auth_info(&self) -> P::AuthInfo { + self.protocol.auth_info() + } +} + +// all protocol-specific impls go into their own block +impl FileDownloadRequest { + fn mount_point(&self) -> &Path { + self.protocol.mount_point() + } +} + +fn main() { + // your code here +} +``` + +With this approach, if the user were to make a mistake and use the wrong +type; + +```rust,ignore +fn main() { + let mut socket = crate::bootp::listen()?; + while let Some(request) = socket.next_request()? { + match request.mount_point().as_ref() + "/secure" => socket.send("Access denied"), + _ => {} // continue on... + } + // Rest of the code here + } +} +``` + +They would get a syntax error. The type `FileDownloadRequest` does not +implement `mount_point()`, only the type `FileDownloadRequest` does. And +that is created by the NFS module, not the BOOTP module of course! + +## Advantages + +First, it allows fields that are common to multiple states to be de-duplicated. +By making the non-shared fields generic, they are implemented once. + +Second, it makes the `impl` blocks easier to read, because they are broken down +by state. Methods common to all states are typed once in one block, and methods +unique to one state are in a separate block. + +Both of these mean there are fewer lines of code, and they are better organized. + +## Disadvantages + +This currently increases the size of the binary, due to the way monomorphization +is implemented in the compiler. Hopefully the implementation will be able to +improve in the future. + +## Alternatives + +* If a type seems to need a "split API" due to construction or partial +initialization, consider the +[Builder Pattern](../patterns/creational/builder.md) instead. + +* If the API between types does not change -- only the behavior does -- then +the [Strategy Pattern](../patterns/behavioural/strategy.md) is better used +instead. + +## See also + +This pattern is used throughout the standard library: + +* `Vec` can be cast from a String, unlike every other type of `Vec`.[^1] +* They can also be cast into a binary heap, but only if they contain a type + that implements the `Ord` trait.[^2] +* The `to_string` method was specialized for `Cow` only of type `str`.[^3] + +It is also used by several popular crates to allow API flexibility: + +* The `embedded-hal` ecosystem used for embedded devices makes extensive use of + this pattern. For example, it allows statically verifying the configuration of + device registers used to control embedded pins. When a pin is put into a mode, + it returns a `Pin` struct, whose generic determines the functions + usable in that mode, which are not on the `Pin` itself. [^4] + +* The `hyper` HTTP client library uses this to expose rich APIs for different + pluggable requests. Clients with different connectors have different methods + on them as well as different trait implementations, while a core set of + methods apply to any connector. [^5] + +* The "type state" pattern -- where an object gains and loses API based on an + internal state or invariant -- is implemented in Rust using the same basic + concept, and a slightly different technique. [^6] + +[^1]: See: [impl From\ for Vec\]( +https://doc.rust-lang.org/1.59.0/src/std/ffi/c_str.rs.html#803-811) + +[^2]: See: [impl\ From\\> for BinaryHeap\]( +https://doc.rust-lang.org/stable/src/alloc/collections/binary_heap.rs.html#1345-1354) + +[^3]: See: [impl\<'\_\> ToString for Cow\<'\_, str>]( +https://doc.rust-lang.org/stable/src/alloc/string.rs.html#2235-2240) + +[^4]: Example: +[https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html]( +https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html) + +[^5]: See: +[https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html]( +https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html) + +[^6]: See: +[The Case for the Type State Pattern]( +https://web.archive.org/web/20210325065112/https://www.novatec-gmbh.de/en/blog/the-case-for-the-typestate-pattern-the-typestate-pattern-itself/) +and +[Rusty Typestate Series (an extensive thesis)]( +https://web.archive.org/web/20210328164854/https://rustype.github.io/notes/notes/rust-typestate-series/rust-typestate-index) diff --git a/src/functional/index.md b/src/functional/index.md new file mode 100644 index 00000000..b29e9caf --- /dev/null +++ b/src/functional/index.md @@ -0,0 +1,10 @@ +# Functional Usage of Rust + +Rust is an imperative language, but it follows many +[functional programming](https://en.wikipedia.org/wiki/Functional_programming) paradigms. + +> In computer science, *functional programming* is a programming paradigm where +> programs are constructed by applying and composing functions. +> It is a declarative programming paradigm in which function definitions are +> trees of expressions that each return a value, rather than a sequence of +> imperative statements which change the state of the program. diff --git a/src/functional/lenses.md b/src/functional/lenses.md new file mode 100644 index 00000000..c5f1ab59 --- /dev/null +++ b/src/functional/lenses.md @@ -0,0 +1,368 @@ +# Lenses and Prisms + +This is a pure functional concept that is not frequently used in Rust. +Nevertheless, exploring the concept may be helpful to understand other +patterns in Rust APIs, such as [visitors](../patterns/behavioural/visitor.md). +They also have niche use cases. + +## Lenses: Uniform Access Across Types + +A lens is a concept from functional programming languages that allows +accessing parts of a data type in an abstract, unified way.[^1] +In basic concept, it is similar to the way Rust traits work with type erasure, +but it has a bit more power and flexibility. + +For example, suppose a bank contains several JSON formats for customer +data. +This is because they come from different databases or legacy systems. +One database contains the data needed to perform credit checks: + +```json +{ "name": "Jane Doe", + "dob": "2002-02-24", + [...] + "customer_id": 1048576332, +} +``` + +Another one contains the account information: + +```json +{ "customer_id": 1048576332, + "accounts": [ + { "account_id": 2121, + "account_type: "savings", + "joint_customer_ids": [], + [...] + }, + { "account_id": 2122, + "account_type: "checking", + "joint_customer_ids": [1048576333], + [...] + }, + ] +} +``` + +Notice that both types have a customer ID number which corresponds to a person. +How would a single function handle both records of different types? + +In Rust, a `struct` could represent each of these types, and a trait would have +a `get_customer_id` function they would implement: + +```rust +use std::collections::HashSet; + +pub struct Account { + account_id: u32, + account_type: String, + // other fields omitted +} + +pub trait CustomerId { + fn get_customer_id(&self) -> u64; +} + +pub struct CreditRecord { + customer_id: u64, + name: String, + dob: String, + // other fields omitted +} + +impl CustomerId for CreditRecord { + fn get_customer_id(&self) -> u64 { + self.customer_id + } +} + +pub struct AccountRecord { + customer_id: u64, + accounts: Vec, +} + +impl CustomerId for AccountRecord { + fn get_customer_id(&self) -> u64 { + self.customer_id + } +} + +// static polymorphism: only one type, but each function call can choose it +fn unique_ids_set(records: &[R]) -> HashSet { + records.iter().map(|r| r.get_customer_id()).collect() +} + +// dynamic dispatch: iterates over any type with a customer ID, collecting all +// values together +fn unique_ids_iter(iterator: I) -> HashSet + where I: Iterator> +{ + iterator.map(|r| r.as_ref().get_customer_id()).collect() +} +``` + +Lenses, however, allow the code supporting customer ID to be moved from the +*type* to the *accessor function*. +Rather than implementing a trait on each type, all matching structures can +simply be accessed the same way. + +While the Rust language itself does not support this (type erasure is the +preferred solution to this problem), the [lens-rs +crate](https://github.com/TOETOE55/lens-rs/blob/master/guide.md) allows code +that feels like this to be written with macros: + +```rust,ignore +use std::collections::HashSet; + +use lens_rs::{optics, Lens, LensRef, Optics}; + +#[derive(Clone, Debug, Lens /* derive to allow lenses to work */)] +pub struct CreditRecord { + #[optic(ref)] // macro attribute to allow viewing this field + customer_id: u64, + name: String, + dob: String, + // other fields omitted +} + +#[derive(Clone, Debug)] +pub struct Account { + account_id: u32, + account_type: String, + // other fields omitted +} + +#[derive(Clone, Debug, Lens)] +pub struct AccountRecord { + #[optic(ref)] + customer_id: u64, + accounts: Vec, +} + +fn unique_ids_lens(iter: impl Iterator) -> HashSet +where + T: LensRef, // any type with this field +{ + iter.map(|r| *r.view_ref(optics!(customer_id))).collect() +} +``` + +The version of `unique_ids_lens` shown here allows any type to be in the iterator, +so long as it has an attribute called `customer_id` which can be accessed by +the function. +This is how most functional programming languages operate on lenses. + +Rather than macros, they achieve this with a technique known as "currying". +That is, they "partially construct" the function, leaving the type of the +final parameter (the value being operated on) unfilled until the function is +called. +Thus it can be called with different types dynamically even from one place in +the code. +That is what the `optics!` and `view_ref` in the example above simulates. + +The functional approach need not be restricted to accessing members. +More powerful lenses can be created which both *set* and *get* data in a +structure. +But the concept really becomes interesting when used as a building block for +composition. +That is where the concept appears more clearly in Rust. + +## Prisms: A Higher-Order form of "Optics" + +A simple function such as `unique_ids_lens` above operates on a single lens. +A *prism* is a function that operates on a *family* of lenses. +It is one conceptual level higher, using lenses as a building block, and +continuing the metaphor, is part of a family of "optics". +It is the main one that is useful in understanding Rust APIs, so will be the +focus here. + +The same way that traits allow "lens-like" design with static polymorphism and +dynamic dispatch, prism-like designs appear in Rust APIs which split problems +into multiple associated types to be composed. +A good example of this is the traits in the parsing crate *Serde*. + +Trying to understand the way *Serde* works by only reading the API is a +challenge, especially the first time. +Consider the `Deserializer` trait, implemented by some type in any library +which parses a new format: + +```rust,ignore +pub trait Deserializer<'de>: Sized { + type Error: Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>; + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>; + + // remainder ommitted +} +``` + +For a trait that is just supposed to parse data from a format and return a +value, this looks odd. + +Why are all the return types type erased? + +To understand that, we need to keep the lens concept in mind and look at +the definition of the `Visitor` type that is passed in generically: + +```rust,ignore +pub trait Visitor<'de>: Sized { + type Value; + + fn visit_bool(self, v: bool) -> Result + where + E: Error; + + fn visit_u64(self, v: u64) -> Result + where + E: Error; + + fn visit_str(self, v: &str) -> Result + where + E: Error; + + // remainder omitted +} +``` + +The job of the `Visitor` type is to construct values in the *Serde* data model, +which are represented by its associated `Value` type. + +These values represent parts of the Rust value being deserialized. +If this fails, it returns an `Error` type - an error type determined by the +`Deserializer` when its methods were called. + +This highlights that `Deserializer` is similar to `CustomerId` from earlier, +allowing any format parser which implements it to create `Value`s based on what +it parsed. +The `Value` trait is acting like a lens in functional programming languages. + +But unlike the `CustomerId` trait, the return types of `Visitor` methods are +*generic*, and the concrete `Value` type is *determined by the Visitor itself*. + +Instead of acting as one lens, it effectively acts as a family of +lenses, one for each concrete type of `Visitor`. + +The `Deserializer` API is based on having a generic set of "lenses" work across +a set of other generic types for "observation". +It is a *prism*. + +For example, consider the identity record from earlier but simplified: + +```json +{ "name": "Jane Doe", + "customer_id": 1048576332, +} +``` + +How would the *Serde* library deserialize this JSON into `struct CreditRecord`? + +1. The user would call a library function to deserialize the data. This would + create a `Deserializer` based on the JSON format. +1. Based on the fields in the struct, a `Visitor` would be created (more on + that in a moment) which knows how to create each type in a generic data + model that was needed to represent it: `u64` and `String`. +1. The deserializer would make calls to the `Visitor` as it parsed items. +1. The `Visitor` would indicate if the items found were expected, and if not, + raise an error to indicate deserialization has failed. + +For our very simple structure above, the expected pattern would be: + +1. Visit a map (*Serde*'s equvialent to `HashMap` or JSON's dictionary). +1. Visit a string key called "name". +1. Visit a string value, which will go into the `name` field. +1. Visit a string key called "customer_id". +1. Visit a string value, which will go into the `customer_id` field. +1. Visit the end of the map. + +But what determines which "observation" pattern is expected? + +A functional programming language would be able to use currying to create +reflection of each type based on the type itself. +Rust does not support that, so every single type would need to have its own +code written based on its fields and their properties. + +*Serde* solves this usability challenge with a derive macro: + +```rust,ignore +use serde::Deserialize; + +#[derive(Deserialize)] +struct IdRecord { + name: String, + customer_id: String, +} +``` + +That macro simply generates an impl block causing the struct to implement a +trait called `Deserialize`. + +It is defined this way: + +```rust,ignore +pub trait Deserialize<'de>: Sized { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>; +} +``` + +This is the function that determines how to create the struct itself. +Code is generated based on the struct's fields. +When the parsing library is called - in our example, a JSON parsing library - +it creates a `Deserializer` and calls `Type::deserialize` with it as a +parameter. + +The `deserialize` code will then create a `Visitor` which will have its calls +"refracted" by the `Deserializer`. +If everything goes well, eventually that `Visitor` will construct a value +corresponding to the type being parsed and return it. + +For a complete example, see the [*Serde* +documentation](https://serde.rs/deserialize-struct.html). + +To wrap up, this is the power of *Serde*: + +1. The structure being parsed is represented by an `impl` block for `Deserialize` +1. The input data format (e.g. JSON) is represented by a `Deserializer` called + by `Deserialize` +1. The `Deserializer` acts like a prism which "refracts" lens-like `Visitor` + calls which actually build the data value + +The result is that types to be deserialized only implement the "top layer" of +the API, and file formats only need to implement the "bottom layer". +Each piece can then "just work" with the rest of the ecosystem, since generic +types will bridge them. + +To emphasize, the only reason this model works on any format and any type is +because the `Deserializer` trait's output type **is specified by the +implementor of `Visitor` it is passed**, rather than being tied to one specific +type. +This was not true in the account example earlier. + +Rust's generic-inspired type system can bring it close to these concepts and +use their power, as shown in this API design. +But it may also need procedural macros to create bridges for its generics. + +## See Also + +- [lens-rs crate](https://crates.io/crates/lens-rs) for a pre-built lenses + implementation, with a cleaner interface than these examples +- [serde](https://serde.rs) itself, which makes these concepts intuitive for + end users (i.e. defining the structs) without needing to undestand the + details +- [luminance](https://github.com/phaazon/luminance-rs) is a crate for drawing + computer graphics that uses lens API design, including proceducal macros to + create full prisms for buffers of different pixel types that remain generic +- [An Article about Lenses in + Scala](https://web.archive.org/web/20221128185849/https://medium.com/zyseme-technology/functional-references-lens-and-other-optics-in-scala-e5f7e2fdafe) + that is very readable even without Scala expertise. +- [Paper: Profunctor Optics: Modular Data + Accessors](https://web.archive.org/web/20220701102832/https://arxiv.org/ftp/arxiv/papers/1703/1703.10857.pdf) + +[^1]: [School of Haskell: A Little Lens Starter Tutorial](https://web.archive.org/web/20221128190041/https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/a-little-lens-starter-tutorial) diff --git a/src/functional/paradigms.md b/src/functional/paradigms.md new file mode 100644 index 00000000..9fc3b99e --- /dev/null +++ b/src/functional/paradigms.md @@ -0,0 +1,69 @@ +# Programming paradigms + +One of the biggest hurdles to understanding functional programs when coming +from an imperative background is the shift in thinking. Imperative programs +describe __how__ to do something, whereas declarative programs describe +__what__ to do. Let's sum the numbers from 1 to 10 to show this. + +## Imperative + +```rust +let mut sum = 0; +for i in 1..11 { + sum += i; +} +println!("{}", sum); +``` + +With imperative programs, we have to play compiler to see what is happening. +Here, we start with a `sum` of `0`. +Next, we iterate through the range from 1 to 10. +Each time through the loop, we add the corresponding value in the range. +Then we print it out. + +| `i` | `sum` | +|:---:|:-----:| +| 1 | 1 | +| 2 | 3 | +| 3 | 6 | +| 4 | 10 | +| 5 | 15 | +| 6 | 21 | +| 7 | 28 | +| 8 | 36 | +| 9 | 45 | +| 10 | 55 | + +This is how most of us start out programming. We learn that a program is a set +of steps. + +## Declarative + +```rust +println!("{}", (1..11).fold(0, |a, b| a + b)); +``` + +Whoa! This is really different! What's going on here? +Remember that with declarative programs we are describing __what__ to do, +rather than __how__ to do it. `fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition) +functions. The name is a convention from Haskell. + +Here, we are composing functions of addition (this closure: `|a, b| a + b`) +with a range from 1 to 10. The `0` is the starting point, so `a` is `0` at +first. `b` is the first element of the range, `1`. `0 + 1 = 1` is the result. +So now we `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the next +result. This process continues until we get to the last element in the range, +`10`. + +| `a` | `b` | result | +|:---:|:---:|:------:| +| 0 | 1 | 1 | +| 1 | 2 | 3 | +| 3 | 3 | 6 | +| 6 | 4 | 10 | +| 10 | 5 | 15 | +| 15 | 6 | 21 | +| 21 | 7 | 28 | +| 28 | 8 | 36 | +| 36 | 9 | 45 | +| 45 | 10 | 55 | diff --git a/src/idioms/coercion-arguments.md b/src/idioms/coercion-arguments.md new file mode 100644 index 00000000..2ca63ab5 --- /dev/null +++ b/src/idioms/coercion-arguments.md @@ -0,0 +1,138 @@ +# Use borrowed types for arguments + +## Description + +Using a target of a deref coercion can increase the flexibility of your code +when you are deciding which argument type to use for a function argument. +In this way, the function will accept more input types. + +This is not limited to slice-able or fat pointer types. +In fact, you should always prefer using the __borrowed type__ over +__borrowing the owned type__. +Such as `&str` over `&String`, `&[T]` over `&Vec`, or `&T` over `&Box`. + +Using borrowed types you can avoid layers of indirection for those instances +where the owned type already provides a layer of indirection. For instance, a +`String` has a layer of indirection, so a `&String` will have two layers of +indirection. We can avoid this by using `&str` instead, and letting `&String` +coerce to a `&str` whenever the function is invoked. + +## Example + +For this example, we will illustrate some differences for using `&String` as a +function argument versus using a `&str`, but the ideas apply as well to using +`&Vec` versus using a `&[T]` or using a `&Box` versus a `&T`. + +Consider an example where we wish to determine if a word contains three +consecutive vowels. We don't need to own the string to determine this, so we +will take a reference. + +The code might look something like this: + +```rust +fn three_vowels(word: &String) -> bool { + let mut vowel_count = 0; + for c in word.chars() { + match c { + 'a' | 'e' | 'i' | 'o' | 'u' => { + vowel_count += 1; + if vowel_count >= 3 { + return true + } + } + _ => vowel_count = 0 + } + } + false +} + +fn main() { + let ferris = "Ferris".to_string(); + let curious = "Curious".to_string(); + println!("{}: {}", ferris, three_vowels(&ferris)); + println!("{}: {}", curious, three_vowels(&curious)); + + // This works fine, but the following two lines would fail: + // println!("Ferris: {}", three_vowels("Ferris")); + // println!("Curious: {}", three_vowels("Curious")); + +} +``` + +This works fine because we are passing a `&String` type as a parameter. +If we remove the comments on the last two lines, the example will fail. This +is because a `&str` type will not coerce to a `&String` type. We can fix this +by simply modifying the type for our argument. + +For instance, if we change our function declaration to: + +```rust, ignore +fn three_vowels(word: &str) -> bool { +``` + +then both versions will compile and print the same output. + +```bash +Ferris: false +Curious: true +``` + +But wait, that's not all! There is more to this story. +It's likely that you may say to yourself: that doesn't matter, I will never be +using a `&'static str` as an input anyways (as we did when we used `"Ferris"`). +Even ignoring this special example, you may still find that using `&str` will +give you more flexibility than using a `&String`. + +Let's now take an example where someone gives us a sentence, and we want to +determine if any of the words in the sentence contain three consecutive vowels. +We probably should make use of the function we have already defined and simply +feed in each word from the sentence. + +An example of this could look like this: + +```rust +fn three_vowels(word: &str) -> bool { + let mut vowel_count = 0; + for c in word.chars() { + match c { + 'a' | 'e' | 'i' | 'o' | 'u' => { + vowel_count += 1; + if vowel_count >= 3 { + return true + } + } + _ => vowel_count = 0 + } + } + false +} + +fn main() { + let sentence_string = + "Once upon a time, there was a friendly curious crab named Ferris".to_string(); + for word in sentence_string.split(' ') { + if three_vowels(word) { + println!("{} has three consecutive vowels!", word); + } + } +} +``` + +Running this example using our function declared with an argument type `&str` +will yield + +```bash +curious has three consecutive vowels! +``` + +However, this example will not run when our function is declared with an +argument type `&String`. This is because string slices are a `&str` and not a +`&String` which would require an allocation to be converted to `&String` which +is not implicit, whereas converting from `String` to `&str` is cheap and implicit. + +## See also + +- [Rust Language Reference on Type Coercions](https://doc.rust-lang.org/reference/type-coercions.html) +- For more discussion on how to handle `String` and `&str` see + [this blog series (2015)](https://web.archive.org/web/20201112023149/https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html) + by Herman J. Radtke III diff --git a/src/idioms/concat-format.md b/src/idioms/concat-format.md new file mode 100644 index 00000000..372d86e5 --- /dev/null +++ b/src/idioms/concat-format.md @@ -0,0 +1,33 @@ +# Concatenating strings with `format!` + +## Description + +It is possible to build up strings using the `push` and `push_str` methods on a +mutable `String`, or using its `+` operator. However, it is often more +convenient to use `format!`, especially where there is a mix of literal and +non-literal strings. + +## Example + +```rust +fn say_hello(name: &str) -> String { + // We could construct the result string manually. + // let mut result = "Hello ".to_owned(); + // result.push_str(name); + // result.push('!'); + // result + + // But using format! is better. + format!("Hello {}!", name) +} +``` + +## Advantages + +Using `format!` is usually the most succinct and readable way to combine strings. + +## Disadvantages + +It is usually not the most efficient way to combine strings - a series of `push` +operations on a mutable string is usually the most efficient (especially if the +string has been pre-allocated to the expected size). diff --git a/src/idioms/ctor.md b/src/idioms/ctor.md new file mode 100644 index 00000000..6927bf4d --- /dev/null +++ b/src/idioms/ctor.md @@ -0,0 +1,115 @@ +# Constructors + +## Description + +Rust does not have constructors as a language construct. Instead, the +convention is to use an [associated function][] `new` to create an object: + +```rust +/// Time in seconds. +/// +/// # Example +/// +/// ``` +/// let s = Second::new(42); +/// assert_eq!(42, s.value()); +/// ``` +pub struct Second { + value: u64 +} + +impl Second { + // Constructs a new instance of [`Second`]. + // Note this is an associated function - no self. + pub fn new(value: u64) -> Self { + Self { value } + } + + /// Returns the value in seconds. + pub fn value(&self) -> u64 { + self.value + } +} +``` + +## Default Constructors + +Rust supports default constructors with the [`Default`][std-default] trait: + +```rust +/// Time in seconds. +/// +/// # Example +/// +/// ``` +/// let s = Second::default(); +/// assert_eq!(0, s.value()); +/// ``` +pub struct Second { + value: u64 +} + +impl Second { + /// Returns the value in seconds. + pub fn value(&self) -> u64 { + self.value + } +} + +impl Default for Second { + fn default() -> Self { + Self { value: 0 } + } +} +``` + +`Default` can also be derived if all types of all fields implement `Default`, +like they do with `Second`: + +```rust +/// Time in seconds. +/// +/// # Example +/// +/// ``` +/// let s = Second::default(); +/// assert_eq!(0, s.value()); +/// ``` +#[derive(Default)] +pub struct Second { + value: u64 +} + +impl Second { + /// Returns the value in seconds. + pub fn value(&self) -> u64 { + self.value + } +} +``` + +**Note:** It is common and expected for types to implement both +`Default` and an empty `new` constructor. `new` is the constructor +convention in Rust, and users expect it to exist, so if it is +reasonable for the basic constructor to take no arguments, then it +should, even if it is functionally identical to default. + +**Hint:** The advantage of implementing or deriving `Default` is that your type +can now be used where a `Default` implementation is required, most prominently, +any of the [`*or_default` functions in the standard library][std-or-default]. + +## See also + +- The [default idiom](default.md) for a more in-depth description of the + `Default` trait. + +- The [builder pattern](../patterns/creational/builder.md) for constructing + objects where there are multiple configurations. + +- [API Guidelines/C-COMMON-TRAITS][API Guidelines/C-COMMON-TRAITS] for + implementing both, `Default` and `new`. + +[associated function]: https://doc.rust-lang.org/stable/book/ch05-03-method-syntax.html#associated-functions +[std-default]: https://doc.rust-lang.org/stable/std/default/trait.Default.html +[std-or-default]: https://doc.rust-lang.org/stable/std/?search=or_default +[API Guidelines/C-COMMON-TRAITS]: https://rust-lang.github.io/api-guidelines/interoperability.html#types-eagerly-implement-common-traits-c-common-traits diff --git a/src/idioms/default.md b/src/idioms/default.md new file mode 100644 index 00000000..1baf58cd --- /dev/null +++ b/src/idioms/default.md @@ -0,0 +1,69 @@ +# The `Default` Trait + +## Description + +Many types in Rust have a [constructor]. However, this is *specific* to the +type; Rust cannot abstract over "everything that has a `new()` method". To +allow this, the [`Default`] trait was conceived, which can be used with +containers and other generic types (e.g. see [`Option::unwrap_or_default()`]). +Notably, some containers already implement it where applicable. + +Not only do one-element containers like `Cow`, `Box` or `Arc` implement +`Default` for contained `Default` types, one can automatically +`#[derive(Default)]` for structs whose fields all implement it, so the more +types implement `Default`, the more useful it becomes. + +On the other hand, constructors can take multiple arguments, while the +`default()` method does not. There can even be multiple constructors with +different names, but there can only be one `Default` implementation per type. + +## Example + +```rust +use std::{path::PathBuf, time::Duration}; + +// note that we can simply auto-derive Default here. +#[derive(Default, Debug, PartialEq)] +struct MyConfiguration { + // Option defaults to None + output: Option, + // Vecs default to empty vector + search_path: Vec, + // Duration defaults to zero time + timeout: Duration, + // bool defaults to false + check: bool, +} + +impl MyConfiguration { + // add setters here +} + +fn main() { + // construct a new instance with default values + let mut conf = MyConfiguration::default(); + // do something with conf here + conf.check = true; + println!("conf = {:#?}", conf); + + // partial initialization with default values, creates the same instance + let conf1 = MyConfiguration { + check: true, + ..Default::default() + }; + assert_eq!(conf, conf1); +} +``` + +## See also + +- The [constructor] idiom is another way to generate instances that may or may +not be "default" +- The [`Default`] documentation (scroll down for the list of implementors) +- [`Option::unwrap_or_default()`] +- [`derive(new)`] + +[constructor]: ctor.md +[`Default`]: https://doc.rust-lang.org/stable/std/default/trait.Default.html +[`Option::unwrap_or_default()`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.unwrap_or_default +[`derive(new)`]: https://crates.io/crates/derive-new/ diff --git a/src/idioms/deref.md b/src/idioms/deref.md new file mode 100644 index 00000000..0f6d9aeb --- /dev/null +++ b/src/idioms/deref.md @@ -0,0 +1,79 @@ +# Collections are smart pointers + +## Description + +Use the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) +trait to treat collections like smart pointers, offering owning +and borrowed views of data. + +## Example + +```rust,ignore +use std::ops::Deref; + +struct Vec { + data: RawVec, + //.. +} + +impl Deref for Vec { + type Target = [T]; + + fn deref(&self) -> &[T] { + //.. + } +} +``` + +A `Vec` is an owning collection of `T`s, while a slice (`&[T]`) is a borrowed +collection of `T`s. Implementing `Deref` for `Vec` allows implicit dereferencing +from `&Vec` to `&[T]` and includes the relationship in auto-derefencing +searches. Most methods you might expect to be implemented for `Vec`s are instead +implemented for slices. + +Also `String` and `&str` have a similar relation. + +## Motivation + +Ownership and borrowing are key aspects of the Rust language. Data structures +must account for these semantics properly to give a good user +experience. When implementing a data structure that owns its data, offering a +borrowed view of that data allows for more flexible APIs. + +## Advantages + +Most methods can be implemented only for the borrowed view, they are then +implicitly available for the owning view. + +Gives clients a choice between borrowing or taking ownership of data. + +## Disadvantages + +Methods and traits only available via dereferencing are not taken into account +when bounds checking, so generic programming with data structures using this +pattern can get complex (see the `Borrow` and `AsRef` traits, etc.). + +## Discussion + +Smart pointers and collections are analogous: a smart pointer points to a single +object, whereas a collection points to many objects. From the point of view of +the type system, there is little difference between the two. A collection owns +its data if the only way to access each datum is via the collection and the +collection is responsible for deleting the data (even in cases of shared +ownership, some kind of borrowed view may be appropriate). If a collection owns +its data, it is usually useful to provide a view of the data as borrowed so that +it can be referenced multiple times. + +Most smart pointers (e.g., `Foo`) implement `Deref`. However, +collections will usually dereference to a custom type. `[T]` and `str` have some +language support, but in the general case, this is not necessary. `Foo` can +implement `Deref>` where `Bar` is a dynamically sized type and +`&Bar` is a borrowed view of the data in `Foo`. + +Commonly, ordered collections will implement `Index` for `Range`s to provide +slicing syntax. The target will be the borrowed view. + +## See also + +- [Deref polymorphism anti-pattern](../anti_patterns/deref.md). +- [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html). diff --git a/src/idioms/dtor-finally.md b/src/idioms/dtor-finally.md new file mode 100644 index 00000000..45152905 --- /dev/null +++ b/src/idioms/dtor-finally.md @@ -0,0 +1,90 @@ +# Finalisation in destructors + +## Description + +Rust does not provide the equivalent to `finally` blocks - code that will be +executed no matter how a function is exited. Instead, an object's destructor can +be used to run code that must be run before exit. + +## Example + +```rust,ignore +fn bar() -> Result<(), ()> { + // These don't need to be defined inside the function. + struct Foo; + + // Implement a destructor for Foo. + impl Drop for Foo { + fn drop(&mut self) { + println!("exit"); + } + } + + // The dtor of _exit will run however the function `bar` is exited. + let _exit = Foo; + // Implicit return with `?` operator. + baz()?; + // Normal return. + Ok(()) +} +``` + +## Motivation + +If a function has multiple return points, then executing code on exit becomes +difficult and repetitive (and thus bug-prone). This is especially the case where +return is implicit due to a macro. A common case is the `?` operator which +returns if the result is an `Err`, but continues if it is `Ok`. `?` is used as +an exception handling mechanism, but unlike Java (which has `finally`), there is +no way to schedule code to run in both the normal and exceptional cases. +Panicking will also exit a function early. + +## Advantages + +Code in destructors will (nearly) always be run - copes with panics, early +returns, etc. + +## Disadvantages + +It is not guaranteed that destructors will run. For example, if there is an +infinite loop in a function or if running a function crashes before exit. +Destructors are also not run in the case of a panic in an already panicking +thread. Therefore, destructors cannot be relied on as finalizers where it is +absolutely essential that finalisation happens. + +This pattern introduces some hard to notice, implicit code. Reading a function +gives no clear indication of destructors to be run on exit. This can make +debugging tricky. + +Requiring an object and `Drop` impl just for finalisation is heavy on boilerplate. + +## Discussion + +There is some subtlety about how exactly to store the object used as a +finalizer. It must be kept alive until the end of the function and must then be +destroyed. The object must always be a value or uniquely owned pointer (e.g., +`Box`). If a shared pointer (such as `Rc`) is used, then the finalizer can +be kept alive beyond the lifetime of the function. For similar reasons, the +finalizer should not be moved or returned. + +The finalizer must be assigned into a variable, otherwise it will be destroyed +immediately, rather than when it goes out of scope. The variable name must start +with `_` if the variable is only used as a finalizer, otherwise the compiler +will warn that the finalizer is never used. However, do not call the variable +`_` with no suffix - in that case it will be destroyed immediately. + +In Rust, destructors are run when an object goes out of scope. This happens +whether we reach the end of block, there is an early return, or the program +panics. When panicking, Rust unwinds the stack running destructors for each +object in each stack frame. So, destructors get called even if the panic happens +in a function being called. + +If a destructor panics while unwinding, there is no good action to take, so Rust +aborts the thread immediately, without running further destructors. This means +that destructors are not absolutely guaranteed to run. It also means that you +must take extra care in your destructors not to panic, since it could leave +resources in an unexpected state. + +## See also + +[RAII guards](../patterns/behavioural/RAII.md). diff --git a/src/idioms/ffi/accepting-strings.md b/src/idioms/ffi/accepting-strings.md new file mode 100644 index 00000000..c4b88e60 --- /dev/null +++ b/src/idioms/ffi/accepting-strings.md @@ -0,0 +1,143 @@ +# Accepting Strings + +## Description + +When accepting strings via FFI through pointers, there are two principles that +should be followed: + +1. Keep foreign strings "borrowed", rather than copying them directly. +2. Minimize the amount of complexity and `unsafe` code involved in converting + from a C-style string to native Rust strings. + +## Motivation + +The strings used in C have different behaviours to those used in Rust, namely: + +- C strings are null-terminated while Rust strings store their length +- C strings can contain any arbitrary non-zero byte while Rust strings must be + UTF-8 +- C strings are accessed and manipulated using `unsafe` pointer operations + while interactions with Rust strings go through safe methods + +The Rust standard library comes with C equivalents of Rust's `String` and `&str` +called `CString` and `&CStr`, that allow us to avoid a lot of the complexity +and `unsafe` code involved in converting between C strings and Rust strings. + +The `&CStr` type also allows us to work with borrowed data, meaning passing +strings between Rust and C is a zero-cost operation. + +## Code Example + +```rust,ignore +pub mod unsafe_module { + + // other module content + + /// Log a message at the specified level. + /// + /// # Safety + /// + /// It is the caller's guarantee to ensure `msg`: + /// + /// - is not a null pointer + /// - points to valid, initialized data + /// - points to memory ending in a null byte + /// - won't be mutated for the duration of this function call + #[no_mangle] + pub unsafe extern "C" fn mylib_log( + msg: *const libc::c_char, + level: libc::c_int + ) { + let level: crate::LogLevel = match level { /* ... */ }; + + // SAFETY: The caller has already guaranteed this is okay (see the + // `# Safety` section of the doc-comment). + let msg_str: &str = match std::ffi::CStr::from_ptr(msg).to_str() { + Ok(s) => s, + Err(e) => { + crate::log_error("FFI string conversion failed"); + return; + } + }; + + crate::log(msg_str, level); + } +} +``` + +## Advantages + +The example is is written to ensure that: + +1. The `unsafe` block is as small as possible. +2. The pointer with an "untracked" lifetime becomes a "tracked" shared + reference + +Consider an alternative, where the string is actually copied: + +```rust,ignore +pub mod unsafe_module { + + // other module content + + pub extern "C" fn mylib_log(msg: *const libc::c_char, level: libc::c_int) { + // DO NOT USE THIS CODE. + // IT IS UGLY, VERBOSE, AND CONTAINS A SUBTLE BUG. + + let level: crate::LogLevel = match level { /* ... */ }; + + let msg_len = unsafe { /* SAFETY: strlen is what it is, I guess? */ + libc::strlen(msg) + }; + + let mut msg_data = Vec::with_capacity(msg_len + 1); + + let msg_cstr: std::ffi::CString = unsafe { + // SAFETY: copying from a foreign pointer expected to live + // for the entire stack frame into owned memory + std::ptr::copy_nonoverlapping(msg, msg_data.as_mut(), msg_len); + + msg_data.set_len(msg_len + 1); + + std::ffi::CString::from_vec_with_nul(msg_data).unwrap() + } + + let msg_str: String = unsafe { + match msg_cstr.into_string() { + Ok(s) => s, + Err(e) => { + crate::log_error("FFI string conversion failed"); + return; + } + } + }; + + crate::log(&msg_str, level); + } +} +``` + +This code in inferior to the original in two respects: + +1. There is much more `unsafe` code, and more importantly, more invariants it + must uphold. +2. Due to the extensive arithmetic required, there is a bug in this version + that cases Rust `undefined behaviour`. + +The bug here is a simple mistake in pointer arithmetic: the string was copied, +all `msg_len` bytes of it. However, the `NUL` terminator at the end was not. + +The Vector then had its size *set* to the length of the *zero padded string* -- +rather than *resized* to it, which could have added a zero at the end. +As a result, the last byte in the Vector is uninitialized memory. +When the `CString` is created at the bottom of the block, its read of the +Vector will cause `undefined behaviour`! + +Like many such issues, this would be difficult issue to track down. +Sometimes it would panic because the string was not `UTF-8`, sometimes it would +put a weird character at the end of the string, sometimes it would just +completely crash. + +## Disadvantages + +None? diff --git a/src/idioms/ffi/errors.md b/src/idioms/ffi/errors.md new file mode 100644 index 00000000..99fc77f6 --- /dev/null +++ b/src/idioms/ffi/errors.md @@ -0,0 +1,139 @@ +# Error Handling in FFI + +## Description + +In foreign languages like C, errors are represented by return codes. +However, Rust's type system allows much more rich error information to be +captured and propogated through a full type. + +This best practice shows different kinds of error codes, and how to expose them +in a usable way: + +1. Flat Enums should be converted to integers and returned as codes. +2. Structured Enums should be converted to an integer code with a string error + message for detail. +3. Custom Error Types should become "transparent", with a C representation. + +## Code Example + +### Flat Enums + +```rust,ignore +enum DatabaseError { + IsReadOnly = 1, // user attempted a write operation + IOError = 2, // user should read the C errno() for what it was + FileCorrupted = 3, // user should run a repair tool to recover it +} + +impl From for libc::c_int { + fn from(e: DatabaseError) -> libc::c_int { + (e as i8).into() + } +} +``` + +### Structured Enums + +```rust,ignore +pub mod errors { + enum DatabaseError { + IsReadOnly, + IOError(std::io::Error), + FileCorrupted(String), // message describing the issue + } + + impl From for libc::c_int { + fn from(e: DatabaseError) -> libc::c_int { + match e { + DatabaseError::IsReadOnly => 1, + DatabaseError::IOError(_) => 2, + DatabaseError::FileCorrupted(_) => 3, + } + } + } +} + +pub mod c_api { + use super::errors::DatabaseError; + + #[no_mangle] + pub extern "C" fn db_error_description( + e: *const DatabaseError + ) -> *mut libc::c_char { + + let error: &DatabaseError = unsafe { + // SAFETY: pointer lifetime is greater than the current stack frame + &*e + }; + + let error_str: String = match error { + DatabaseError::IsReadOnly => { + format!("cannot write to read-only database"); + } + DatabaseError::IOError(e) => { + format!("I/O Error: {}", e); + } + DatabaseError::FileCorrupted(s) => { + format!("File corrupted, run repair: {}", &s); + } + }; + + let c_error = unsafe { + // SAFETY: copying error_str to an allocated buffer with a NUL + // character at the end + let mut malloc: *mut u8 = libc::malloc(error_str.len() + 1) as *mut _; + + if malloc.is_null() { + return std::ptr::null_mut(); + } + + let src = error_str.as_bytes().as_ptr(); + + std::ptr::copy_nonoverlapping(src, malloc, error_str.len()); + + std::ptr::write(malloc.add(error_str.len()), 0); + + malloc as *mut libc::c_char + }; + + c_error + } +} +``` + +### Custom Error Types + +```rust,ignore +struct ParseError { + expected: char, + line: u32, + ch: u16 +} + +impl ParseError { /* ... */ } + +/* Create a second version which is exposed as a C structure */ +#[repr(C)] +pub struct parse_error { + pub expected: libc::c_char, + pub line: u32, + pub ch: u16 +} + +impl From for parse_error { + fn from(e: ParseError) -> parse_error { + let ParseError { expected, line, ch } = e; + parse_error { expected, line, ch } + } +} +``` + +## Advantages + +This ensures that the foreign language has clear access to error information +while not compromising the Rust code's API at all. + +## Disadvantages + +It's a lot of typing, and some types may not be able to be converted easily +to C. diff --git a/src/idioms/ffi/intro.md b/src/idioms/ffi/intro.md new file mode 100644 index 00000000..d1f14971 --- /dev/null +++ b/src/idioms/ffi/intro.md @@ -0,0 +1,14 @@ +# FFI Idioms + +Writing FFI code is an entire course in itself. +However, there are several idioms here that can act as pointers, and avoid +traps for inexperienced users of `unsafe` Rust. + +This section contains idioms that may be useful when doing FFI. + +1. [Idiomatic Errors](./errors.md) - Error handling with integer codes and + sentinel return values (such as `NULL` pointers) + +2. [Accepting Strings](./accepting-strings.md) with minimal unsafe code + +3. [Passing Strings](./passing-strings.md) to FFI functions diff --git a/src/idioms/ffi/passing-strings.md b/src/idioms/ffi/passing-strings.md new file mode 100644 index 00000000..18de5c83 --- /dev/null +++ b/src/idioms/ffi/passing-strings.md @@ -0,0 +1,105 @@ +# Passing Strings + +## Description + +When passing strings to FFI functions, there are four principles that should be +followed: + +1. Make the lifetime of owned strings as long as possible. +2. Minimize `unsafe` code during the conversion. +3. If the C code can modify the string data, use `Vec` instead of `CString`. +4. Unless the Foreign Function API requires it, the ownership of the string + should not transfer to the callee. + +## Motivation + +Rust has built-in support for C-style strings with its `CString` and `CStr` +types. However, there are different approaches one can take with strings that +are being sent to a foreign function call from a Rust function. + +The best practice is simple: use `CString` in such a way as to minimize +`unsafe` code. However, a secondary caveat is that +*the object must live long enough*, meaning the lifetime should be maximized. +In addition, the documentation explains that "round-tripping" a `CString` after +modification is UB, so additional work is necessary in that case. + +## Code Example + +```rust,ignore +pub mod unsafe_module { + + // other module content + + extern "C" { + fn seterr(message: *const libc::c_char); + fn geterr(buffer: *mut libc::c_char, size: libc::c_int) -> libc::c_int; + } + + fn report_error_to_ffi>( + err: S + ) -> Result<(), std::ffi::NulError>{ + let c_err = std::ffi::CString::new(err.into())?; + + unsafe { + // SAFETY: calling an FFI whose documentation says the pointer is + // const, so no modification should occur + seterr(c_err.as_ptr()); + } + + Ok(()) + // The lifetime of c_err continues until here + } + + fn get_error_from_ffi() -> Result { + let mut buffer = vec![0u8; 1024]; + unsafe { + // SAFETY: calling an FFI whose documentation implies + // that the input need only live as long as the call + let written: usize = geterr(buffer.as_mut_ptr(), 1023).into(); + + buffer.truncate(written + 1); + } + + std::ffi::CString::new(buffer).unwrap().into_string() + } +} +``` + +## Advantages + +The example is written in a way to ensure that: + +1. The `unsafe` block is as small as possible. +2. The `CString` lives long enough. +3. Errors with typecasts are always propagated when possible. + +A common mistake (so common it's in the documentation) is to not use the +variable in the first block: + +```rust,ignore +pub mod unsafe_module { + + // other module content + + fn report_error>(err: S) -> Result<(), std::ffi::NulError> { + unsafe { + // SAFETY: whoops, this contains a dangling pointer! + seterr(std::ffi::CString::new(err.into())?.as_ptr()); + } + Ok(()) + } +} +``` + +This code will result in a dangling pointer, because the lifetime of the +`CString` is not extended by the pointer creation, unlike if a reference were +created. + +Another issue frequently raised is that the initialization of a 1k vector of +zeroes is "slow". However, recent versions of Rust actually optimize that +particular macro to a call to `zmalloc`, meaning it is as fast as the operating +system's ability to return zeroed memory (which is quite fast). + +## Disadvantages + +None? diff --git a/src/idioms/index.md b/src/idioms/index.md new file mode 100644 index 00000000..22ebed6a --- /dev/null +++ b/src/idioms/index.md @@ -0,0 +1,18 @@ +# Idioms + +[Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly used +styles, guidelines and patterns largely agreed upon by a community. +Writing idiomatic code allows other developers to understand better what is +happening. + +After all, the computer only cares about the machine code that is generated +by the compiler. +Instead, the source code is mainly beneficial to the developer. +So, since we have this abstraction layer, why not make it more readable? + +Remember the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle): +"Keep It Simple, Stupid". It claims that "most systems work best if they are +kept simple rather than made complicated; therefore, simplicity should be a key +goal in design, and unnecessary complexity should be avoided". + +> Code is there for humans, not computers, to understand. diff --git a/src/idioms/mem-replace.md b/src/idioms/mem-replace.md new file mode 100644 index 00000000..b1ea07f1 --- /dev/null +++ b/src/idioms/mem-replace.md @@ -0,0 +1,113 @@ +# `mem::{take(_), replace(_)}` to keep owned values in changed enums + +## Description + +Say we have a `&mut MyEnum` which has (at least) two variants, +`A { name: String, x: u8 }` and `B { name: String }`. Now we want to change +`MyEnum::A` to a `B` if `x` is zero, while keeping `MyEnum::B` intact. + +We can do this without cloning the `name`. + +## Example + +```rust +use std::mem; + +enum MyEnum { + A { name: String, x: u8 }, + B { name: String } +} + +fn a_to_b(e: &mut MyEnum) { + if let MyEnum::A { name, x: 0 } = e { + // this takes out our `name` and put in an empty String instead + // (note that empty strings don't allocate). + // Then, construct the new enum variant (which will + // be assigned to `*e`). + *e = MyEnum::B { name: mem::take(name) } + } +} +``` + +This also works with more variants: + +```rust +use std::mem; + +enum MultiVariateEnum { + A { name: String }, + B { name: String }, + C, + D +} + +fn swizzle(e: &mut MultiVariateEnum) { + use MultiVariateEnum::*; + *e = match e { + // Ownership rules do not allow taking `name` by value, but we cannot + // take the value out of a mutable reference, unless we replace it: + A { name } => B { name: mem::take(name) }, + B { name } => A { name: mem::take(name) }, + C => D, + D => C + } +} +``` + +## Motivation + +When working with enums, we may want to change an enum value in place, perhaps +to another variant. This is usually done in two phases to keep the borrow +checker happy. In the first phase, we observe the existing value and look at +its parts to decide what to do next. In the second phase we may conditionally +change the value (as in the example above). + +The borrow checker won't allow us to take out `name` of the enum (because +*something* must be there.) We could of course `.clone()` name and put the clone +into our `MyEnum::B`, but that would be an instance of the [Clone to satisfy +the borrow checker](../anti_patterns/borrow_clone.md) anti-pattern. Anyway, we +can avoid the extra allocation by changing `e` with only a mutable borrow. + +`mem::take` lets us swap out the value, replacing it with it's default value, +and returning the previous value. For `String`, the default value is an empty +`String`, which does not need to allocate. As a result, we get the original +`name` *as an owned value*. We can then wrap this in another enum. + +__NOTE:__ `mem::replace` is very similar, but allows us to specify what to +replace the value with. An equivalent to our `mem::take` line would be +`mem::replace(name, String::new())`. + +Note, however, that if we are using an `Option` and want to replace its +value with a `None`, `Option`’s `take()` method provides a shorter and +more idiomatic alternative. + +## Advantages + +Look ma, no allocation! Also you may feel like Indiana Jones while doing it. + +## Disadvantages + +This gets a bit wordy. Getting it wrong repeatedly will make you hate the +borrow checker. The compiler may fail to optimize away the double store, +resulting in reduced performance as opposed to what you'd do in unsafe +languages. + +Furthermore, the type you are taking needs to implement the [`Default` +trait](./default.md). However, if the type you're working with doesn't +implement this, you can instead use `mem::replace`. + +## Discussion + +This pattern is only of interest in Rust. In GC'd languages, you'd take the +reference to the value by default (and the GC would keep track of refs), and in +other low-level languages like C you'd simply alias the pointer and fix things +later. + +However, in Rust, we have to do a little more work to do this. An owned value +may only have one owner, so to take it out, we need to put something back in – +like Indiana Jones, replacing the artifact with a bag of sand. + +## See also + +This gets rid of the [Clone to satisfy the borrow checker](../anti_patterns/borrow_clone.md) +anti-pattern in a specific case. diff --git a/src/idioms/on-stack-dyn-dispatch.md b/src/idioms/on-stack-dyn-dispatch.md new file mode 100644 index 00000000..d45f3ca9 --- /dev/null +++ b/src/idioms/on-stack-dyn-dispatch.md @@ -0,0 +1,93 @@ +# On-Stack Dynamic Dispatch + +## Description + +We can dynamically dispatch over multiple values, however, to do so, we need +to declare multiple variables to bind differently-typed objects. To extend the +lifetime as necessary, we can use deferred conditional initialization, as seen +below: + +## Example + +```rust +use std::io; +use std::fs; + +# fn main() -> Result<(), Box> { +# let arg = "-"; + +// These must live longer than `readable`, and thus are declared first: +let (mut stdin_read, mut file_read); + +// We need to ascribe the type to get dynamic dispatch. +let readable: &mut dyn io::Read = if arg == "-" { + stdin_read = io::stdin(); + &mut stdin_read +} else { + file_read = fs::File::open(arg)?; + &mut file_read +}; + +// Read from `readable` here. + +# Ok(()) +# } +``` + +## Motivation + +Rust monomorphises code by default. This means a copy of the code will be +generated for each type it is used with and optimized independently. While this +allows for very fast code on the hot path, it also bloats the code in places +where performance is not of the essence, thus costing compile time and cache +usage. + +Luckily, Rust allows us to use dynamic dispatch, but we have to explicitly ask +for it. + +## Advantages + +We do not need to allocate anything on the heap. Neither do we need to +initialize something we won't use later, nor do we need to monomorphize the +whole code that follows to work with both `File` or `Stdin`. + +## Disadvantages + +The code needs more moving parts than the `Box`-based version: + +```rust,ignore +// We still need to ascribe the type for dynamic dispatch. +let readable: Box = if arg == "-" { + Box::new(io::stdin()) +} else { + Box::new(fs::File::open(arg)?) +}; +// Read from `readable` here. +``` + +## Discussion + +Rust newcomers will usually learn that Rust requires all variables to be +initialized *before use*, so it's easy to overlook the fact that *unused* +variables may well be uninitialized. Rust works quite hard to ensure that this +works out fine and only the initialized values are dropped at the end of their +scope. + +The example meets all the constraints Rust places on us: + +* All variables are initialized before using (in this case borrowing) them +* Each variable only holds values of a single type. In our example, `stdin` is +of type `Stdin`, `file` is of type `File` and `readable` is of type `&mut dyn +Read` +* Each borrowed value outlives all the references borrowed from it + +## See also + +* [Finalisation in destructors](dtor-finally.md) and +[RAII guards](../patterns/behavioural/RAII.md) can benefit from tight control over +lifetimes. +* For conditionally filled `Option<&T>`s of (mutable) references, one can +initialize an `Option` directly and use its [`.as_ref()`] method to get an +optional reference. + +[`.as_ref()`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.as_ref diff --git a/src/idioms/option-iter.md b/src/idioms/option-iter.md new file mode 100644 index 00000000..a3e8adda --- /dev/null +++ b/src/idioms/option-iter.md @@ -0,0 +1,59 @@ +# Iterating over an `Option` + +## Description + +`Option` can be viewed as a container that contains either zero or one +element. In particular, it implements the `IntoIterator` trait, and as such +can be used with generic code that needs such a type. + +## Examples + +Since `Option` implements `IntoIterator`, it can be used as an argument to +[`.extend()`](https://doc.rust-lang.org/std/iter/trait.Extend.html#tymethod.extend): + +```rust +let turing = Some("Turing"); +let mut logicians = vec!["Curry", "Kleene", "Markov"]; + +logicians.extend(turing); + +// equivalent to +if let Some(turing_inner) = turing { + logicians.push(turing_inner); +} +``` + +If you need to tack an `Option` to the end of an existing iterator, you can +pass it to [`.chain()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain): + +```rust +let turing = Some("Turing"); +let logicians = vec!["Curry", "Kleene", "Markov"]; + +for logician in logicians.iter().chain(turing.iter()) { + println!("{} is a logician", logician); +} +``` + +Note that if the `Option` is always `Some`, then it is more idiomatic to use +[`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) on the +element instead. + +Also, since `Option` implements `IntoIterator`, it's possible to iterate over +it using a `for` loop. This is equivalent to matching it with `if let Some(..)`, +and in most cases you should prefer the latter. + +## See also + +* [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is an +iterator which yields exactly one element. It's a more readable alternative to +`Some(foo).into_iter()`. + +* [`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) + is a version of [`Iterator::map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map), + specialized to mapping functions which return `Option`. + +* The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions + for converting an `Option` to a zero- or one-element slice. + +* [Documentation for `Option`](https://doc.rust-lang.org/std/option/enum.Option.html) diff --git a/src/idioms/pass-var-to-closure.md b/src/idioms/pass-var-to-closure.md new file mode 100644 index 00000000..b6a351dd --- /dev/null +++ b/src/idioms/pass-var-to-closure.md @@ -0,0 +1,59 @@ +# Pass variables to closure + +## Description + +By default, closures capture their environment by borrowing. Or you can use +`move`-closure to move whole environment. However, often you want to move just +some variables to closure, give it copy of some data, pass it by reference, or +perform some other transformation. + +Use variable rebinding in separate scope for that. + +## Example + +Use + +```rust +use std::rc::Rc; + +let num1 = Rc::new(1); +let num2 = Rc::new(2); +let num3 = Rc::new(3); +let closure = { + // `num1` is moved + let num2 = num2.clone(); // `num2` is cloned + let num3 = num3.as_ref(); // `num3` is borrowed + move || { + *num1 + *num2 + *num3; + } +}; +``` + +instead of + +```rust +use std::rc::Rc; + +let num1 = Rc::new(1); +let num2 = Rc::new(2); +let num3 = Rc::new(3); + +let num2_cloned = num2.clone(); +let num3_borrowed = num3.as_ref(); +let closure = move || { + *num1 + *num2_cloned + *num3_borrowed; +}; +``` + +## Advantages + +Copied data are grouped together with closure definition, so their purpose is +more clear, and they will be dropped immediately even if they are not consumed +by closure. + +Closure uses same variable names as surrounding code whether data are copied or +moved. + +## Disadvantages + +Additional indentation of closure body. diff --git a/src/idioms/priv-extend.md b/src/idioms/priv-extend.md new file mode 100644 index 00000000..2a14fdb4 --- /dev/null +++ b/src/idioms/priv-extend.md @@ -0,0 +1,122 @@ +# `#[non_exhaustive]` and private fields for extensibility + +## Description + +A small set of scenarios exist where a library author may want to add public +fields to a public struct or new variants to an enum without breaking backwards +compatibility. + +Rust offers two solutions to this problem: + +- Use `#[non_exhaustive]` on `struct`s, `enum`s, and `enum` variants. + For extensive documentation on all the places where `#[non_exhaustive]` can be + used, see [the docs](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute). + +- You may add a private field to a struct to prevent it from being directly + instantiated or matched against (see Alternative) + +## Example + +```rust +mod a { + // Public struct. + #[non_exhaustive] + pub struct S { + pub foo: i32, + } + + #[non_exhaustive] + pub enum AdmitMoreVariants { + VariantA, + VariantB, + #[non_exhaustive] + VariantC { a: String } + } +} + +fn print_matched_variants(s: a::S) { + // Because S is `#[non_exhaustive]`, it cannot be named here and + // we must use `..` in the pattern. + let a::S { foo: _, ..} = s; + + let some_enum = a::AdmitMoreVariants::VariantA; + match some_enum { + a::AdmitMoreVariants::VariantA => println!("it's an A"), + a::AdmitMoreVariants::VariantB => println!("it's a b"), + + // .. required because this variant is non-exhaustive as well + a::AdmitMoreVariants::VariantC { a, .. } => println!("it's a c"), + + // The wildcard match is required because more variants may be + // added in the future + _ => println!("it's a new variant") + } +} +``` + +## Alternative: `Private fields` for structs + +`#[non_exhaustive]` only works across crate boundaries. +Within a crate, the private field method may be used. + +Adding a field to a struct is a mostly backwards compatible change. +However, if a client uses a pattern to deconstruct a struct instance, they +might name all the fields in the struct and adding a new one would break that +pattern. +The client could name some fields and use `..` in the pattern, in which case adding +another field is backwards compatible. +Making at least one of the struct's fields private forces clients to use the latter +form of patterns, ensuring that the struct is future-proof. + +The downside of this approach is that you might need to add an otherwise unneeded +field to the struct. +You can use the `()` type so that there is no runtime overhead and prepend `_` to +the field name to avoid the unused field warning. + +```rust +pub struct S { + pub a: i32, + // Because `b` is private, you cannot match on `S` without using `..` and `S` + // cannot be directly instantiated or matched against + _b: () +} +``` + +## Discussion + +On `struct`s, `#[non_exhaustive]` allows adding additional fields in a backwards +compatible way. +It will also prevent clients from using the struct constructor, even if all the +fields are public. +This may be helpful, but it's worth considering if you _want_ an additional field +to be found by clients as a compiler error rather than something that may be silently +undiscovered. + +`#[non_exhaustive]` can be applied to enum variants as well. +A `#[non_exhaustive]` variant behaves in the same way as a `#[non_exhaustive]` struct. + +Use this deliberately and with caution: incrementing the major version when adding +fields or variants is often a better option. +`#[non_exhaustive]` may be appropriate in scenarios where you're modeling an external +resource that may change out-of-sync with your library, but is not a general purpose +tool. + +### Disadvantages + +`#[non_exhaustive]` can make your code much less ergonomic to use, especially when +forced to handle unknown enum variants. +It should only be used when these sorts of evolutions are required **without** +incrementing the major version. + +When `#[non_exhaustive]` is applied to `enum`s, it forces clients to handle a +wildcard variant. +If there is no sensible action to take in this case, this may lead to awkward +code and code paths that are only executed in extremely rare circumstances. +If a client decides to `panic!()` in this scenario, it may have been better to +expose this error at compile time. +In fact, `#[non_exhaustive]` forces clients to handle the "Something else" case; +there is rarely a sensible action to take in this scenario. + +## See also + +- [RFC introducing #[non_exhaustive] attribute for enums and structs](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md) diff --git a/src/idioms/return-consumed-arg-on-error.md b/src/idioms/return-consumed-arg-on-error.md new file mode 100644 index 00000000..6a0c471f --- /dev/null +++ b/src/idioms/return-consumed-arg-on-error.md @@ -0,0 +1,61 @@ +# Return consumed argument on error + +## Description + +If a fallible function consumes (moves) an argument, return that argument back inside +an error. + +## Example + +```rust +pub fn send(value: String) -> Result<(), SendError> { + println!("using {value} in a meaningful way"); + // Simulate non-deterministic fallible action. + use std::time::SystemTime; + let period = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); + if period.subsec_nanos() % 2 == 1 { + Ok(()) + } else { + Err(SendError(value)) + } +} + +pub struct SendError(String); + +fn main() { + let mut value = "imagine this is very long string".to_string(); + + let success = 's: { + // Try to send value two times. + for _ in 0..2 { + value = match send(value) { + Ok(()) => break 's true, + Err(SendError(value)) => value, + } + } + false + }; + + println!("success: {}", success); +} +``` + +## Motivation + +In case of error you may want to try some alternative way or to +retry action in case of non-deterministic function. But if the argument +is always consumed, you are forced to clone it on every call, which +is not very efficient. + +The standard library uses this approach in e.g. `String::from_utf8` method. +When given a vector that doesn't contain valid UTF-8, a `FromUtf8Error` +is returned. +You can get original vector back using `FromUtf8Error::into_bytes` method. + +## Advantages + +Better performance because of moving arguments whenever possible. + +## Disadvantages + +Slightly more complex error types. diff --git a/src/idioms/rustdoc-init.md b/src/idioms/rustdoc-init.md new file mode 100644 index 00000000..64c1e5d4 --- /dev/null +++ b/src/idioms/rustdoc-init.md @@ -0,0 +1,94 @@ +# Easy doc initialization + +## Description + +If a struct takes significant effort to initialize when writing docs, it can be +quicker to wrap your example with a helper function which takes the struct as an +argument. + +## Motivation + +Sometimes there is a struct with multiple or complicated parameters and several +methods. Each of these methods should have examples. + +For example: + +```rust,ignore +struct Connection { + name: String, + stream: TcpStream, +} + +impl Connection { + /// Sends a request over the connection. + /// + /// # Example + /// ```no_run + /// # // Boilerplate are required to get an example working. + /// # let stream = TcpStream::connect("127.0.0.1:34254"); + /// # let connection = Connection { name: "foo".to_owned(), stream }; + /// # let request = Request::new("RequestId", RequestType::Get, "payload"); + /// let response = connection.send_request(request); + /// assert!(response.is_ok()); + /// ``` + fn send_request(&self, request: Request) -> Result { + // ... + } + + /// Oh no, all that boilerplate needs to be repeated here! + fn check_status(&self) -> Status { + // ... + } +} +``` + +## Example + +Instead of typing all of this boilerplate to create a `Connection` and +`Request`, it is easier to just create a wrapping helper function which takes +them as arguments: + +```rust,ignore +struct Connection { + name: String, + stream: TcpStream, +} + +impl Connection { + /// Sends a request over the connection. + /// + /// # Example + /// ``` + /// # fn call_send(connection: Connection, request: Request) { + /// let response = connection.send_request(request); + /// assert!(response.is_ok()); + /// # } + /// ``` + fn send_request(&self, request: Request) { + // ... + } +} +``` + +**Note** in the above example the line `assert!(response.is_ok());` will not +actually run while testing because it is inside a function which is never +invoked. + +## Advantages + +This is much more concise and avoids repetitive code in examples. + +## Disadvantages + +As example is in a function, the code will not be tested. Though it will still be +checked to make sure it compiles when running a `cargo test`. So this pattern is +most useful when you need `no_run`. With this, you do not need to add `no_run`. + +## Discussion + +If assertions are not required this pattern works well. + +If they are, an alternative can be to create a public method to create a helper +instance which is annotated with `#[doc(hidden)]` (so that users won't see it). +Then this method can be called inside of rustdoc because it is part of the +crate's public API. diff --git a/src/idioms/temporary-mutability.md b/src/idioms/temporary-mutability.md new file mode 100644 index 00000000..e630e102 --- /dev/null +++ b/src/idioms/temporary-mutability.md @@ -0,0 +1,45 @@ +# Temporary mutability + +## Description + +Often it is necessary to prepare and process some data, but after that data are +only inspected and never modified. The intention can be made explicit by redefining +the mutable variable as immutable. + +It can be done either by processing data within a nested block or by redefining +the variable. + +## Example + +Say, vector must be sorted before usage. + +Using nested block: + +```rust,ignore +let data = { + let mut data = get_vec(); + data.sort(); + data +}; + +// Here `data` is immutable. +``` + +Using variable rebinding: + +```rust,ignore +let mut data = get_vec(); +data.sort(); +let data = data; + +// Here `data` is immutable. +``` + +## Advantages + +Compiler ensures that you don't accidentally mutate data after some point. + +## Disadvantages + +Nested block requires additional indentation of block body. +One more line to return data from block or redefine variable. diff --git a/src/intro.md b/src/intro.md new file mode 100644 index 00000000..71c8f88b --- /dev/null +++ b/src/intro.md @@ -0,0 +1,40 @@ +# Introduction + +## Participation + +If you are interested in contributing to this book, check out the +[contribution guidelines](https://github.com/rust-unofficial/patterns/blob/master/CONTRIBUTING.md). + +## Design patterns + +In software development, we often come across problems that share +similarities regardless of the environment they appear in. Although the +implementation details are crucial to solve the task at hand, we may +abstract from these particularities to find the common practices that +are generically applicable. + +Design patterns are a collection of reusable and tested solutions to +recurring problems in engineering. They make our software more modular, +maintainable, and extensible. Moreover, these patterns provide a common +language for developers, making them an excellent tool for effective +communication when problem-solving in teams. + +## Design patterns in Rust + +Rust is not object-oriented, and the combination of all its characteristics, +such as functional elements, a strong type system, and the borrow checker, +makes it unique. +Because of this, Rust design patterns vary with respect to other +traditional object-oriented programming languages. +That's why we decided to write this book. We hope you enjoy reading it! +The book is divided in three main chapters: + +- [Idioms](./idioms/index.md): guidelines to follow when coding. + They are the social norms of the community. + You should break them only if you have a good reason for it. +- [Design patterns](./patterns/index.md): methods to solve common problems + when coding. +- [Anti-patterns](./anti_patterns/index.md): methods to solve common problems + when coding. + However, while design patterns give us benefits, + anti-patterns create more problems. diff --git a/src/patterns/behavioural/RAII.md b/src/patterns/behavioural/RAII.md new file mode 100644 index 00000000..4b3f4409 --- /dev/null +++ b/src/patterns/behavioural/RAII.md @@ -0,0 +1,121 @@ +# RAII with guards + +## Description + +[RAII][wikipedia] stands for "Resource Acquisition is Initialisation" which is a +terrible name. The essence of the pattern is that resource initialisation is done +in the constructor of an object and finalisation in the destructor. This pattern +is extended in Rust by using a RAII object as a guard of some resource and relying +on the type system to ensure that access is always mediated by the guard object. + +## Example + +Mutex guards are the classic example of this pattern from the std library (this +is a simplified version of the real implementation): + +```rust,ignore +use std::ops::Deref; + +struct Foo {} + +struct Mutex { + // We keep a reference to our data: T here. + //.. +} + +struct MutexGuard<'a, T: 'a> { + data: &'a T, + //.. +} + +// Locking the mutex is explicit. +impl Mutex { + fn lock(&self) -> MutexGuard { + // Lock the underlying OS mutex. + //.. + + // MutexGuard keeps a reference to self + MutexGuard { + data: self, + //.. + } + } +} + +// Destructor for unlocking the mutex. +impl<'a, T> Drop for MutexGuard<'a, T> { + fn drop(&mut self) { + // Unlock the underlying OS mutex. + //.. + } +} + +// Implementing Deref means we can treat MutexGuard like a pointer to T. +impl<'a, T> Deref for MutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &T { + self.data + } +} + +fn baz(x: Mutex) { + let xx = x.lock(); + xx.foo(); // foo is a method on Foo. + // The borrow checker ensures we can't store a reference to the underlying + // Foo which will outlive the guard xx. + + // x is unlocked when we exit this function and xx's destructor is executed. +} +``` + +## Motivation + +Where a resource must be finalised after use, RAII can be used to do this +finalisation. If it is an error to access that resource after finalisation, then +this pattern can be used to prevent such errors. + +## Advantages + +Prevents errors where a resource is not finalised and where a resource is used +after finalisation. + +## Discussion + +RAII is a useful pattern for ensuring resources are properly deallocated or +finalised. We can make use of the borrow checker in Rust to statically prevent +errors stemming from using resources after finalisation takes place. + +The core aim of the borrow checker is to ensure that references to data do not +outlive that data. The RAII guard pattern works because the guard object +contains a reference to the underlying resource and only exposes such +references. Rust ensures that the guard cannot outlive the underlying resource +and that references to the resource mediated by the guard cannot outlive the +guard. To see how this works it is helpful to examine the signature of `deref` +without lifetime elision: + +```rust,ignore +fn deref<'a>(&'a self) -> &'a T { + //.. +} +``` + +The returned reference to the resource has the same lifetime as `self` (`'a`). +The borrow checker therefore ensures that the lifetime of the reference to `T` +is shorter than the lifetime of `self`. + +Note that implementing `Deref` is not a core part of this pattern, it only makes +using the guard object more ergonomic. Implementing a `get` method on the guard +works just as well. + +## See also + +[Finalisation in destructors idiom](../../idioms/dtor-finally.md) + +RAII is a common pattern in C++: [cppreference.com](http://en.cppreference.com/w/cpp/language/raii), +[wikipedia][wikipedia]. + +[wikipedia]: https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization + +[Style guide entry](https://doc.rust-lang.org/1.0.0/style/ownership/raii.html) +(currently just a placeholder). diff --git a/src/patterns/behavioural/command.md b/src/patterns/behavioural/command.md new file mode 100644 index 00000000..1e0ae263 --- /dev/null +++ b/src/patterns/behavioural/command.md @@ -0,0 +1,222 @@ +# Command + +## Description + +The basic idea of the Command pattern is to separate out actions into its own +objects and pass them as parameters. + +## Motivation + +Suppose we have a sequence of actions or transactions encapsulated as objects. +We want these actions or commands to be executed or invoked in some order later +at different time. These commands may also be triggered as a result of some event. +For example, when a user pushes a button, or on arrival of a data packet. +In addition, these commands might be undoable. This may come in useful for +operations of an editor. We might want to store logs of executed commands so that +we could reapply the changes later if the system crashes. + +## Example + +Define two database operations `create table` and `add field`. Each of these +operations is a command which knows how to undo the command, e.g., `drop table` +and `remove field`. When a user invokes a database migration operation then each +command is executed in the defined order, and when the user invokes the rollback +operation then the whole set of commands is invoked in reverse order. + +## Approach: Using trait objects + +We define a common trait which encapsulates our command with two operations +`execute` and `rollback`. All command `structs` must implement this trait. + +```rust +pub trait Migration { + fn execute(&self) -> &str; + fn rollback(&self) -> &str; +} + +pub struct CreateTable; +impl Migration for CreateTable { + fn execute(&self) -> &str { + "create table" + } + fn rollback(&self) -> &str { + "drop table" + } +} + +pub struct AddField; +impl Migration for AddField { + fn execute(&self) -> &str { + "add field" + } + fn rollback(&self) -> &str { + "remove field" + } +} + +struct Schema { + commands: Vec>, +} + +impl Schema { + fn new() -> Self { + Self { commands: vec![] } + } + + fn add_migration(&mut self, cmd: Box) { + self.commands.push(cmd); + } + + fn execute(&self) -> Vec<&str> { + self.commands.iter().map(|cmd| cmd.execute()).collect() + } + fn rollback(&self) -> Vec<&str> { + self.commands + .iter() + .rev() // reverse iterator's direction + .map(|cmd| cmd.rollback()) + .collect() + } +} + +fn main() { + let mut schema = Schema::new(); + + let cmd = Box::new(CreateTable); + schema.add_migration(cmd); + let cmd = Box::new(AddField); + schema.add_migration(cmd); + + assert_eq!(vec!["create table", "add field"], schema.execute()); + assert_eq!(vec!["remove field", "drop table"], schema.rollback()); +} +``` + +## Approach: Using function pointers + +We could follow another approach by creating each individual command as +a different function and store function pointers to invoke these functions later +at a different time. Since function pointers implement all three traits `Fn`, +`FnMut`, and `FnOnce` we could as well pass and store closures instead of +function pointers. + +```rust +type FnPtr = fn() -> String; +struct Command { + execute: FnPtr, + rollback: FnPtr, +} + +struct Schema { + commands: Vec, +} + +impl Schema { + fn new() -> Self { + Self { commands: vec![] } + } + fn add_migration(&mut self, execute: FnPtr, rollback: FnPtr) { + self.commands.push(Command { execute, rollback }); + } + fn execute(&self) -> Vec { + self.commands.iter().map(|cmd| (cmd.execute)()).collect() + } + fn rollback(&self) -> Vec { + self.commands + .iter() + .rev() + .map(|cmd| (cmd.rollback)()) + .collect() + } +} + +fn add_field() -> String { + "add field".to_string() +} + +fn remove_field() -> String { + "remove field".to_string() +} + +fn main() { + let mut schema = Schema::new(); + schema.add_migration(|| "create table".to_string(), || "drop table".to_string()); + schema.add_migration(add_field, remove_field); + assert_eq!(vec!["create table", "add field"], schema.execute()); + assert_eq!(vec!["remove field", "drop table"], schema.rollback()); +} +``` + +## Approach: Using `Fn` trait objects + +Finally, instead of defining a common command trait we could store +each command implementing the `Fn` trait separately in vectors. + +```rust +type Migration<'a> = Box &'a str>; + +struct Schema<'a> { + executes: Vec>, + rollbacks: Vec>, +} + +impl<'a> Schema<'a> { + fn new() -> Self { + Self { + executes: vec![], + rollbacks: vec![], + } + } + fn add_migration(&mut self, execute: E, rollback: R) + where + E: Fn() -> &'a str + 'static, + R: Fn() -> &'a str + 'static, + { + self.executes.push(Box::new(execute)); + self.rollbacks.push(Box::new(rollback)); + } + fn execute(&self) -> Vec<&str> { + self.executes.iter().map(|cmd| cmd()).collect() + } + fn rollback(&self) -> Vec<&str> { + self.rollbacks.iter().rev().map(|cmd| cmd()).collect() + } +} + +fn add_field() -> &'static str { + "add field" +} + +fn remove_field() -> &'static str { + "remove field" +} + +fn main() { + let mut schema = Schema::new(); + schema.add_migration(|| "create table", || "drop table"); + schema.add_migration(add_field, remove_field); + assert_eq!(vec!["create table", "add field"], schema.execute()); + assert_eq!(vec!["remove field", "drop table"], schema.rollback()); +} +``` + +## Discussion + +If our commands are small and may be defined as functions or passed as a closure +then using function pointers might be preferable since it does not exploit +dynamic dispatch. But if our command is a whole struct with a bunch of functions +and variables defined as seperated module then using trait objects would be +more suitable. A case of application can be found in [`actix`](https://actix.rs/), +which uses trait objects when it registers a handler function for routes. +In case of using `Fn` trait objects we can create and use commands in the same +way as we used in case of function pointers. + +As performance, there is always a trade-off between performance and code +simplicity and organisation. Static dispatch gives faster performance, while +dynamic dispatch provides flexibility when we structure our application. + +## See also + +- [Command pattern](https://en.wikipedia.org/wiki/Command_pattern) + +- [Another example for the `command` pattern](https://web.archive.org/web/20210223131236/https://chercher.tech/rust/command-design-pattern-rust) diff --git a/src/patterns/behavioural/interpreter.md b/src/patterns/behavioural/interpreter.md new file mode 100644 index 00000000..21cf4457 --- /dev/null +++ b/src/patterns/behavioural/interpreter.md @@ -0,0 +1,147 @@ +# Interpreter + +## Description + +If a problem occurs very often and requires long and repetitive steps to solve +it, then the problem instances might be expressed in a simple language and an +interpreter object could solve it by interpreting the sentences written in this +simple language. + +Basically, for any kind of problems we define: + +- A [domain specific language](https://en.wikipedia.org/wiki/Domain-specific_language), +- A grammar for this language, +- An interpreter that solves the problem instances. + +## Motivation + +Our goal is to translate simple mathematical expressions into postfix expressions +(or [Reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation)) +For simplicity, our expressions consist of ten digits `0`, ..., `9` and two +operations `+`, `-`. For example, the expression `2 + 4` is translated into +`2 4 +`. + +## Context Free Grammar for our problem + +Our task is translating infix expressions into postfix ones. Let's define a context +free grammar for a set of infix expressions over `0`, ..., `9`, `+`, and `-`, +where: + +- Terminal symbols: `0`, `...`, `9`, `+`, `-` +- Non-terminal symbols: `exp`, `term` +- Start symbol is `exp` +- And the following are production rules + +```ignore +exp -> exp + term +exp -> exp - term +exp -> term +term -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 +``` + +__NOTE:__ This grammar should be further transformed depending on what we are going +to do with it. For example, we might need to remove left recursion. For more +details please see [Compilers: Principles,Techniques, and Tools +](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools) +(aka Dragon Book). + +## Solution + +We simply implement a recursive descent parser. For simplicity's sake, the code +panics when an expression is syntactically wrong (for example `2-34` or `2+5-` +are wrong according to the grammar definition). + +```rust +pub struct Interpreter<'a> { + it: std::str::Chars<'a>, +} + +impl<'a> Interpreter<'a> { + + pub fn new(infix: &'a str) -> Self { + Self { it: infix.chars() } + } + + fn next_char(&mut self) -> Option { + self.it.next() + } + + pub fn interpret(&mut self, out: &mut String) { + self.term(out); + + while let Some(op) = self.next_char() { + if op == '+' || op == '-' { + self.term(out); + out.push(op); + } else { + panic!("Unexpected symbol '{}'", op); + } + } + } + + fn term(&mut self, out: &mut String) { + match self.next_char() { + Some(ch) if ch.is_digit(10) => out.push(ch), + Some(ch) => panic!("Unexpected symbol '{}'", ch), + None => panic!("Unexpected end of string"), + } + } +} + +pub fn main() { + let mut intr = Interpreter::new("2+3"); + let mut postfix = String::new(); + intr.interpret(&mut postfix); + assert_eq!(postfix, "23+"); + + intr = Interpreter::new("1-2+3-4"); + postfix.clear(); + intr.interpret(&mut postfix); + assert_eq!(postfix, "12-3+4-"); +} +``` + +## Discussion + +There may be a wrong perception that the Interpreter design pattern is about design +grammars for formal languages and implementation of parsers for these grammars. +In fact, this pattern is about expressing problem instances in a more specific +way and implementing functions/classes/structs that solve these problem instances. +Rust language has `macro_rules!` that allow us to define special syntax and rules +on how to expand this syntax into source code. + +In the following example we create a simple `macro_rules!` that computes +[Euclidean length](https://en.wikipedia.org/wiki/Euclidean_distance) of `n` +dimensional vectors. Writing `norm!(x,1,2)` might be easier to express and more +efficient than packing `x,1,2` into a `Vec` and calling a function computing +the length. + +```rust +macro_rules! norm { + ($($element:expr),*) => { + { + let mut n = 0.0; + $( + n += ($element as f64)*($element as f64); + )* + n.sqrt() + } + }; +} + +fn main() { + let x = -3f64; + let y = 4f64; + + assert_eq!(3f64, norm!(x)); + assert_eq!(5f64, norm!(x, y)); + assert_eq!(0f64, norm!(0, 0, 0)); + assert_eq!(1f64, norm!(0.5, -0.5, 0.5, -0.5)); +} +``` + +## See also + +- [Interpreter pattern](https://en.wikipedia.org/wiki/Interpreter_pattern) +- [Context free grammar](https://en.wikipedia.org/wiki/Context-free_grammar) +- [macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html) diff --git a/src/patterns/behavioural/intro.md b/src/patterns/behavioural/intro.md new file mode 100644 index 00000000..8ca43a04 --- /dev/null +++ b/src/patterns/behavioural/intro.md @@ -0,0 +1,6 @@ +# Behavioural Patterns + +From [Wikipedia](https://en.wikipedia.org/wiki/Behavioral_pattern): + +> Design patterns that identify common communication patterns among objects. +> By doing so, these patterns increase flexibility in carrying out communication. diff --git a/src/patterns/behavioural/newtype.md b/src/patterns/behavioural/newtype.md new file mode 100644 index 00000000..8df944f3 --- /dev/null +++ b/src/patterns/behavioural/newtype.md @@ -0,0 +1,111 @@ +# Newtype + +What if in some cases we want a type to behave similar to another type or +enforce some behaviour at compile time when using only type aliases would +not be enough? + +For example, if we want to create a custom `Display` implementation for `String` +due to security considerations (e.g. passwords). + +For such cases we could use the `Newtype` pattern to provide __type safety__ +and __encapsulation__. + +## Description + +Use a tuple struct with a single field to make an opaque wrapper for a type. +This creates a new type, rather than an alias to a type (`type` items). + +## Example + +```rust,ignore +// Some type, not necessarily in the same module or even crate. +struct Foo { + //.. +} + +impl Foo { + // These functions are not present on Bar. + //.. +} + +// The newtype. +pub struct Bar(Foo); + +impl Bar { + // Constructor. + pub fn new( + //.. + ) -> Self { + + //.. + + } + + //.. +} + +fn main() { + let b = Bar::new(...); + + // Foo and Bar are type incompatible, the following do not type check. + // let f: Foo = b; + // let b: Bar = Foo { ... }; +} +``` + +## Motivation + +The primary motivation for newtypes is abstraction. It allows you to share +implementation details between types while precisely controlling the interface. +By using a newtype rather than exposing the implementation type as part of an +API, it allows you to change implementation backwards compatibly. + +Newtypes can be used for distinguishing units, e.g., wrapping `f64` to give +distinguishable `Miles` and `Kilometres`. + +## Advantages + +The wrapped and wrapper types are not type compatible (as opposed to using +`type`), so users of the newtype will never 'confuse' the wrapped and wrapper +types. + +Newtypes are a zero-cost abstraction - there is no runtime overhead. + +The privacy system ensures that users cannot access the wrapped type (if the +field is private, which it is by default). + +## Disadvantages + +The downside of newtypes (especially compared with type aliases), is that there +is no special language support. This means there can be *a lot* of boilerplate. +You need a 'pass through' method for every method you want to expose on the +wrapped type, and an impl for every trait you want to also be implemented for +the wrapper type. + +## Discussion + +Newtypes are very common in Rust code. Abstraction or representing units are the +most common uses, but they can be used for other reasons: + +- restricting functionality (reduce the functions exposed or traits implemented), +- making a type with copy semantics have move semantics, +- abstraction by providing a more concrete type and thus hiding internal types, + e.g., + +```rust,ignore +pub struct Foo(Bar); +``` + +Here, `Bar` might be some public, generic type and `T1` and `T2` are some internal +types. Users of our module shouldn't know that we implement `Foo` by using a `Bar`, +but what we're really hiding here is the types `T1` and `T2`, and how they are used +with `Bar`. + +## See also + +- [Advanced Types in the book](https://doc.rust-lang.org/book/ch19-04-advanced-types.html?highlight=newtype#using-the-newtype-pattern-for-type-safety-and-abstraction) +- [Newtypes in Haskell](https://wiki.haskell.org/Newtype) +- [Type aliases](https://doc.rust-lang.org/stable/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases) +- [derive_more](https://crates.io/crates/derive_more), a crate for deriving many + builtin traits on newtypes. +- [The Newtype Pattern In Rust](https://www.worthe-it.co.za/blog/2020-10-31-newtype-pattern-in-rust.html) diff --git a/src/patterns/behavioural/strategy.md b/src/patterns/behavioural/strategy.md new file mode 100644 index 00000000..751178b3 --- /dev/null +++ b/src/patterns/behavioural/strategy.md @@ -0,0 +1,179 @@ +# Strategy (aka Policy) + +## Description + +The [Strategy design pattern](https://en.wikipedia.org/wiki/Strategy_pattern) +is a technique that enables separation of concerns. +It also allows to decouple software modules through [Dependency Inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle). + +The basic idea behind the Strategy pattern is that, given an algorithm solving +a particular problem, we define only the skeleton of the algorithm at an abstract +level, and we separate the specific algorithm’s implementation into different parts. + +In this way, a client using the algorithm may choose a specific implementation, +while the general algorithm workflow remains the same. In other words, the abstract +specification of the class does not depend on the specific implementation of the +derived class, but specific implementation must adhere to the abstract specification. +This is why we call it "Dependency Inversion". + +## Motivation + +Imagine we are working on a project that generates reports every month. +We need the reports to be generated in different formats (strategies), e.g., +in `JSON` or `Plain Text` formats. +But things vary over time, and we don't know what kind of requirement we may get +in the future. For example, we may need to generate our report in a completely new +format, or just modify one of the existing formats. + +## Example + +In this example our invariants (or abstractions) are `Context`, `Formatter`, +and `Report`, while `Text` and `Json` are our strategy structs. These strategies +have to implement the `Formatter` trait. + +```rust +use std::collections::HashMap; + +type Data = HashMap; + +trait Formatter { + fn format(&self, data: &Data, buf: &mut String); +} + +struct Report; + +impl Report { + // Write should be used but we kept it as String to ignore error handling + fn generate(g: T, s: &mut String) { + // backend operations... + let mut data = HashMap::new(); + data.insert("one".to_string(), 1); + data.insert("two".to_string(), 2); + // generate report + g.format(&data, s); + } +} + +struct Text; +impl Formatter for Text { + fn format(&self, data: &Data, buf: &mut String) { + for (k, v) in data { + let entry = format!("{} {}\n", k, v); + buf.push_str(&entry); + } + } +} + +struct Json; +impl Formatter for Json { + fn format(&self, data: &Data, buf: &mut String) { + buf.push('['); + for (k, v) in data.into_iter() { + let entry = format!(r#"{{"{}":"{}"}}"#, k, v); + buf.push_str(&entry); + buf.push(','); + } + if !data.is_empty() { + buf.pop(); // remove extra , at the end + } + buf.push(']'); + } +} + +fn main() { + let mut s = String::from(""); + Report::generate(Text, &mut s); + assert!(s.contains("one 1")); + assert!(s.contains("two 2")); + + s.clear(); // reuse the same buffer + Report::generate(Json, &mut s); + assert!(s.contains(r#"{"one":"1"}"#)); + assert!(s.contains(r#"{"two":"2"}"#)); +} +``` + +## Advantages + +The main advantage is separation of concerns. For example, in this case `Report` +does not know anything about specific implementations of `Json` and `Text`, +whereas the output implementations does not care about how data is preprocessed, +stored, and fetched. The only thing they have to know is context and a specific +trait and method to implement, i.e,`Formatter` and `run`. + +## Disadvantages + +For each strategy there must be implemented at least one module, so number of modules +increases with number of strategies. If there are many strategies to choose from +then users have to know how strategies differ from one another. + +## Discussion + +In the previous example all strategies are implemented in a single file. +Ways of providing different strategies includes: + +- All in one file (as shown in this example, similar to being separated as modules) +- Separated as modules, E.g. `formatter::json` module, `formatter::text` module +- Use compiler feature flags, E.g. `json` feature, `text` feature +- Separated as crates, E.g. `json` crate, `text` crate + +Serde crate is a good example of the `Strategy` pattern in action. Serde allows +[full customization](https://serde.rs/custom-serialization.html) of the serialization +behavior by manually implementing `Serialize` and `Deserialize` traits for our +type. For example, we could easily swap `serde_json` with `serde_cbor` since they +expose similar methods. Having this makes the helper crate `serde_transcode` much +more useful and ergonomic. + +However, we don't need to use traits in order to design this pattern in Rust. + +The following toy example demonstrates the idea of the Strategy pattern using Rust +`closures`: + +```rust +struct Adder; +impl Adder { + pub fn add(x: u8, y: u8, f: F) -> u8 + where + F: Fn(u8, u8) -> u8, + { + f(x, y) + } +} + +fn main() { + let arith_adder = |x, y| x + y; + let bool_adder = |x, y| { + if x == 1 || y == 1 { + 1 + } else { + 0 + } + }; + let custom_adder = |x, y| 2 * x + y; + + assert_eq!(9, Adder::add(4, 5, arith_adder)); + assert_eq!(0, Adder::add(0, 0, bool_adder)); + assert_eq!(5, Adder::add(1, 3, custom_adder)); +} + +``` + +In fact, Rust already uses this idea for `Options`'s `map` method: + +```rust +fn main() { + let val = Some("Rust"); + + let len_strategy = |s: &str| s.len(); + assert_eq!(4, val.map(len_strategy).unwrap()); + + let first_byte_strategy = |s: &str| s.bytes().next().unwrap(); + assert_eq!(82, val.map(first_byte_strategy).unwrap()); +} +``` + +## See also + +- [Strategy Pattern](https://en.wikipedia.org/wiki/Strategy_pattern) +- [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) +- [Policy Based Design](https://en.wikipedia.org/wiki/Modern_C++_Design#Policy-based_design) diff --git a/src/patterns/behavioural/visitor.md b/src/patterns/behavioural/visitor.md new file mode 100644 index 00000000..55873147 --- /dev/null +++ b/src/patterns/behavioural/visitor.md @@ -0,0 +1,113 @@ +# Visitor + +## Description + +A visitor encapsulates an algorithm that operates over a heterogeneous +collection of objects. It allows multiple different algorithms to be written +over the same data without having to modify the data (or their primary +behaviour). + +Furthermore, the visitor pattern allows separating the traversal of +a collection of objects from the operations performed on each object. + +## Example + +```rust,ignore +// The data we will visit +mod ast { + pub enum Stmt { + Expr(Expr), + Let(Name, Expr), + } + + pub struct Name { + value: String, + } + + pub enum Expr { + IntLit(i64), + Add(Box, Box), + Sub(Box, Box), + } +} + +// The abstract visitor +mod visit { + use ast::*; + + pub trait Visitor { + fn visit_name(&mut self, n: &Name) -> T; + fn visit_stmt(&mut self, s: &Stmt) -> T; + fn visit_expr(&mut self, e: &Expr) -> T; + } +} + +use visit::*; +use ast::*; + +// An example concrete implementation - walks the AST interpreting it as code. +struct Interpreter; +impl Visitor for Interpreter { + fn visit_name(&mut self, n: &Name) -> i64 { panic!() } + fn visit_stmt(&mut self, s: &Stmt) -> i64 { + match *s { + Stmt::Expr(ref e) => self.visit_expr(e), + Stmt::Let(..) => unimplemented!(), + } + } + + fn visit_expr(&mut self, e: &Expr) -> i64 { + match *e { + Expr::IntLit(n) => n, + Expr::Add(ref lhs, ref rhs) => self.visit_expr(lhs) + self.visit_expr(rhs), + Expr::Sub(ref lhs, ref rhs) => self.visit_expr(lhs) - self.visit_expr(rhs), + } + } +} +``` + +One could implement further visitors, for example a type checker, without having +to modify the AST data. + +## Motivation + +The visitor pattern is useful anywhere that you want to apply an algorithm to +heterogeneous data. If data is homogeneous, you can use an iterator-like pattern. +Using a visitor object (rather than a functional approach) allows the visitor to +be stateful and thus communicate information between nodes. + +## Discussion + +It is common for the `visit_*` methods to return void (as opposed to in the +example). In that case it is possible to factor out the traversal code and share +it between algorithms (and also to provide noop default methods). In Rust, the +common way to do this is to provide `walk_*` functions for each datum. For +example, + +```rust,ignore +pub fn walk_expr(visitor: &mut Visitor, e: &Expr) { + match *e { + Expr::IntLit(_) => {}, + Expr::Add(ref lhs, ref rhs) => { + visitor.visit_expr(lhs); + visitor.visit_expr(rhs); + } + Expr::Sub(ref lhs, ref rhs) => { + visitor.visit_expr(lhs); + visitor.visit_expr(rhs); + } + } +} +``` + +In other languages (e.g., Java) it is common for data to have an `accept` method +which performs the same duty. + +## See also + +The visitor pattern is a common pattern in most OO languages. + +[Wikipedia article](https://en.wikipedia.org/wiki/Visitor_pattern) + +The [fold](../creational/fold.md) pattern is similar to visitor but produces +a new version of the visited data structure. diff --git a/src/patterns/creational/builder.md b/src/patterns/creational/builder.md new file mode 100644 index 00000000..211ef376 --- /dev/null +++ b/src/patterns/creational/builder.md @@ -0,0 +1,115 @@ +# Builder + +## Description + +Construct an object with calls to a builder helper. + +## Example + +```rust +#[derive(Debug, PartialEq)] +pub struct Foo { + // Lots of complicated fields. + bar: String, +} + +impl Foo { + // This method will help users to discover the builder + pub fn builder() -> FooBuilder { + FooBuilder::default() + } +} + +#[derive(Default)] +pub struct FooBuilder { + // Probably lots of optional fields. + bar: String, +} + +impl FooBuilder { + pub fn new(/* ... */) -> FooBuilder { + // Set the minimally required fields of Foo. + FooBuilder { + bar: String::from("X"), + } + } + + pub fn name(mut self, bar: String) -> FooBuilder { + // Set the name on the builder itself, and return the builder by value. + self.bar = bar; + self + } + + // If we can get away with not consuming the Builder here, that is an + // advantage. It means we can use the FooBuilder as a template for constructing + // many Foos. + pub fn build(self) -> Foo { + // Create a Foo from the FooBuilder, applying all settings in FooBuilder + // to Foo. + Foo { bar: self.bar } + } +} + +#[test] +fn builder_test() { + let foo = Foo { + bar: String::from("Y"), + }; + let foo_from_builder: Foo = FooBuilder::new().name(String::from("Y")).build(); + assert_eq!(foo, foo_from_builder); +} +``` + +## Motivation + +Useful when you would otherwise require many constructors or where +construction has side effects. + +## Advantages + +Separates methods for building from other methods. + +Prevents proliferation of constructors. + +Can be used for one-liner initialisation as well as more complex construction. + +## Disadvantages + +More complex than creating a struct object directly, or a simple constructor +function. + +## Discussion + +This pattern is seen more frequently in Rust (and for simpler objects) than in +many other languages because Rust lacks overloading. Since you can only have a +single method with a given name, having multiple constructors is less nice in +Rust than in C++, Java, or others. + +This pattern is often used where the builder object is useful in its own right, +rather than being just a builder. For example, see +[`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html) +is a builder for [`Child`](https://doc.rust-lang.org/std/process/struct.Child.html) +(a process). In these cases, the `T` and `TBuilder` naming pattern is not used. + +The example takes and returns the builder by value. It is often more ergonomic +(and more efficient) to take and return the builder as a mutable reference. The +borrow checker makes this work naturally. This approach has the advantage that +one can write code like + +```rust,ignore +let mut fb = FooBuilder::new(); +fb.a(); +fb.b(); +let f = fb.build(); +``` + +as well as the `FooBuilder::new().a().b().build()` style. + +## See also + +- [Description in the style guide](https://web.archive.org/web/20210104103100/https://doc.rust-lang.org/1.12.0/style/ownership/builders.html) +- [derive_builder](https://crates.io/crates/derive_builder), a crate for automatically + implementing this pattern while avoiding the boilerplate. +- [Constructor pattern](../../idioms/ctor.md) for when construction is simpler. +- [Builder pattern (wikipedia)](https://en.wikipedia.org/wiki/Builder_pattern) +- [Construction of complex values](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder) diff --git a/src/patterns/creational/fold.md b/src/patterns/creational/fold.md new file mode 100644 index 00000000..13eab838 --- /dev/null +++ b/src/patterns/creational/fold.md @@ -0,0 +1,122 @@ +# Fold + +## Description + +Run an algorithm over each item in a collection of data to create a new item, +thus creating a whole new collection. + +The etymology here is unclear to me. The terms 'fold' and 'folder' are used +in the Rust compiler, although it appears to me to be more like a map than a +fold in the usual sense. See the discussion below for more details. + +## Example + +```rust,ignore +// The data we will fold, a simple AST. +mod ast { + pub enum Stmt { + Expr(Box), + Let(Box, Box), + } + + pub struct Name { + value: String, + } + + pub enum Expr { + IntLit(i64), + Add(Box, Box), + Sub(Box, Box), + } +} + +// The abstract folder +mod fold { + use ast::*; + + pub trait Folder { + // A leaf node just returns the node itself. In some cases, we can do this + // to inner nodes too. + fn fold_name(&mut self, n: Box) -> Box { n } + // Create a new inner node by folding its children. + fn fold_stmt(&mut self, s: Box) -> Box { + match *s { + Stmt::Expr(e) => Box::new(Stmt::Expr(self.fold_expr(e))), + Stmt::Let(n, e) => Box::new(Stmt::Let(self.fold_name(n), self.fold_expr(e))), + } + } + fn fold_expr(&mut self, e: Box) -> Box { ... } + } +} + +use fold::*; +use ast::*; + +// An example concrete implementation - renames every name to 'foo'. +struct Renamer; +impl Folder for Renamer { + fn fold_name(&mut self, n: Box) -> Box { + Box::new(Name { value: "foo".to_owned() }) + } + // Use the default methods for the other nodes. +} +``` + +The result of running the `Renamer` on an AST is a new AST identical to the old +one, but with every name changed to `foo`. A real life folder might have some +state preserved between nodes in the struct itself. + +A folder can also be defined to map one data structure to a different (but +usually similar) data structure. For example, we could fold an AST into a HIR +tree (HIR stands for high-level intermediate representation). + +## Motivation + +It is common to want to map a data structure by performing some operation on +each node in the structure. For simple operations on simple data structures, +this can be done using `Iterator::map`. For more complex operations, perhaps +where earlier nodes can affect the operation on later nodes, or where iteration +over the data structure is non-trivial, using the fold pattern is more +appropriate. + +Like the visitor pattern, the fold pattern allows us to separate traversal of a +data structure from the operations performed to each node. + +## Discussion + +Mapping data structures in this fashion is common in functional languages. In OO +languages, it would be more common to mutate the data structure in place. The +'functional' approach is common in Rust, mostly due to the preference for +immutability. Using fresh data structures, rather than mutating old ones, makes +reasoning about the code easier in most circumstances. + +The trade-off between efficiency and reusability can be tweaked by changing how +nodes are accepted by the `fold_*` methods. + +In the above example we operate on `Box` pointers. Since these own their data +exclusively, the original copy of the data structure cannot be re-used. On the +other hand if a node is not changed, reusing it is very efficient. + +If we were to operate on borrowed references, the original data structure can be +reused; however, a node must be cloned even if unchanged, which can be +expensive. + +Using a reference counted pointer gives the best of both worlds - we can reuse +the original data structure, and we don't need to clone unchanged nodes. However, +they are less ergonomic to use and mean that the data structures cannot be +mutable. + +## See also + +Iterators have a `fold` method, however this folds a data structure into a +value, rather than into a new data structure. An iterator's `map` is more like +this fold pattern. + +In other languages, fold is usually used in the sense of Rust's iterators, +rather than this pattern. Some functional languages have powerful constructs for +performing flexible maps over data structures. + +The [visitor](../behavioural/visitor.md) pattern is closely related to fold. +They share the concept of walking a data structure performing an operation on +each node. However, the visitor does not create a new data structure nor consume +the old one. diff --git a/src/patterns/creational/intro.md b/src/patterns/creational/intro.md new file mode 100644 index 00000000..782fdc80 --- /dev/null +++ b/src/patterns/creational/intro.md @@ -0,0 +1,8 @@ +# Creational Patterns + +From [Wikipedia](https://en.wikipedia.org/wiki/Creational_pattern): + +> Design patterns that deal with object creation mechanisms, trying to create objects +> in a manner suitable to the situation. The basic form of object creation could +> result in design problems or in added complexity to the design. Creational design +> patterns solve this problem by somehow controlling this object creation. diff --git a/src/patterns/ffi/export.md b/src/patterns/ffi/export.md new file mode 100644 index 00000000..8b5a4f06 --- /dev/null +++ b/src/patterns/ffi/export.md @@ -0,0 +1,260 @@ +# Object-Based APIs + +## Description + +When designing APIs in Rust which are exposed to other languages, there are some +important design principles which are contrary to normal Rust API design: + +1. All Encapsulated types should be *owned* by Rust, *managed* by the user, + and *opaque*. +2. All Transactional data types should be *owned* by the user, and *transparent*. +3. All library behavior should be functions acting upon Encapsulated types. +4. All library behavior should be encapsulated into types not based on structure, + but *provenance/lifetime*. + +## Motivation + +Rust has built-in FFI support to other languages. +It does this by providing a way for crate authors to provide C-compatible APIs +through different ABIs (though that is unimportant to this practice). + +Well-designed Rust FFI follows C API design principles, while compromising the +design in Rust as little as possible. There are three goals with any foreign API: + +1. Make it easy to use in the target language. +2. Avoid the API dictating internal unsafety on the Rust side as much as possible. +3. Keep the potential for memory unsafety and Rust `undefined behaviour` as small + as possible. + +Rust code must trust the memory safety of the foreign language beyond a certain +point. However, every bit of `unsafe` code on the Rust side is an opportunity for +bugs, or to exacerbate `undefined behaviour`. + +For example, if a pointer provenance is wrong, that may be a segfault due to +invalid memory access. But if it is manipulated by unsafe code, it could become +full-blown heap corruption. + +The Object-Based API design allows for writing shims that have good memory safety +characteristics, and a clean boundary of what is safe and what is `unsafe`. + +## Code Example + +The POSIX standard defines the API to access an on-file database, known as [DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/ndbm.h). +It is an excellent example of an "object-based" API. + +Here is the definition in C, which hopefully should be easy to read for those +involved in FFI. The commentary below should help explain it for those who +miss the subtleties. + +```C +struct DBM; +typedef struct { void *dptr, size_t dsize } datum; + +int dbm_clearerr(DBM *); +void dbm_close(DBM *); +int dbm_delete(DBM *, datum); +int dbm_error(DBM *); +datum dbm_fetch(DBM *, datum); +datum dbm_firstkey(DBM *); +datum dbm_nextkey(DBM *); +DBM *dbm_open(const char *, int, mode_t); +int dbm_store(DBM *, datum, datum, int); +``` + +This API defines two types: `DBM` and `datum`. + +The `DBM` type was called an "encapsulated" type above. +It is designed to contain internal state, and acts as an entry point for the +library's behavior. + +It is completely opaque to the user, who cannot create a `DBM` themselves since +they don't know its size or layout. Instead, they must call `dbm_open`, and that +only gives them *a pointer to one*. + +This means all `DBM`s are "owned" by the library in a Rust sense. +The internal state of unknown size is kept in memory controlled by the library, +not the user. The user can only manage its life cycle with `open` and `close`, +and perform operations on it with the other functions. + +The `datum` type was called a "transactional" type above. +It is designed to facilitate the exchange of information between the library and +its user. + +The database is designed to store "unstructured data", with no pre-defined length +or meaning. As a result, the `datum` is the C equivalent of a Rust slice: a bunch +of bytes, and a count of how many there are. The main difference is that there is +no type information, which is what `void` indicates. + +Keep in mind that this header is written from the library's point of view. +The user likely has some type they are using, which has a known size. +But the library does not care, and by the rules of C casting, any type behind a +pointer can be cast to `void`. + +As noted earlier, this type is *transparent* to the user. But also, this type is +*owned* by the user. +This has subtle ramifications, due to that pointer inside it. +The question is, who owns the memory that pointer points to? + +The answer for best memory safety is, "the user". +But in cases such as retrieving a value, the user does not know how to allocate +it correctly (since they don't know how long the value is). In this case, the library +code is expected to use the heap that the user has access to -- such as the C library +`malloc` and `free` -- and then *transfer ownership* in the Rust sense. + +This may all seem speculative, but this is what a pointer means in C. +It means the same thing as Rust: "user defined lifetime." +The user of the library needs to read the documentation in order to use it correctly. +That said, there are some decisions that have fewer or greater consequences if users +do it wrong. Minimizing those are what this best practice is about, and the key +is to *transfer ownership of everything that is transparent*. + +## Advantages + +This minimizes the number of memory safety guarantees the user must uphold to a +relatively small number: + +1. Do not call any function with a pointer not returned by `dbm_open` (invalid + access or corruption). +2. Do not call any function on a pointer after close (use after free). +3. The `dptr` on any `datum` must be `NULL`, or point to a valid slice of memory + at the advertised length. + +In addition, it avoids a lot of pointer provenance issues. +To understand why, let us consider an alternative in some depth: key iteration. + +Rust is well known for its iterators. +When implementing one, the programmer makes a separate type with a bounded lifetime +to its owner, and implements the `Iterator` trait. + +Here is how iteration would be done in Rust for `DBM`: + +```rust,ignore +struct Dbm { ... } + +impl Dbm { + /* ... */ + pub fn keys<'it>(&'it self) -> DbmKeysIter<'it> { ... } + /* ... */ +} + +struct DbmKeysIter<'it> { + owner: &'it Dbm, +} + +impl<'it> Iterator for DbmKeysIter<'it> { ... } +``` + +This is clean, idiomatic, and safe. thanks to Rust's guarantees. +However, consider what a straightforward API translation would look like: + +```rust,ignore +#[no_mangle] +pub extern "C" fn dbm_iter_new(owner: *const Dbm) -> *mut DbmKeysIter { + // THIS API IS A BAD IDEA! For real applications, use object-based design instead. +} +#[no_mangle] +pub extern "C" fn dbm_iter_next( + iter: *mut DbmKeysIter, + key_out: *const datum +) -> libc::c_int { + // THIS API IS A BAD IDEA! For real applications, use object-based design instead. +} +#[no_mangle] +pub extern "C" fn dbm_iter_del(*mut DbmKeysIter) { + // THIS API IS A BAD IDEA! For real applications, use object-based design instead. +} +``` + +This API loses a key piece of information: the lifetime of the iterator must not +exceed the lifetime of the `Dbm` object that owns it. A user of the library could +use it in a way which causes the iterator to outlive the data it is iterating on, +resulting in reading uninitialized memory. + +This example written in C contains a bug that will be explained afterwards: + +```C +int count_key_sizes(DBM *db) { + // DO NOT USE THIS FUNCTION. IT HAS A SUBTLE BUT SERIOUS BUG! + datum key; + int len = 0; + + if (!dbm_iter_new(db)) { + dbm_close(db); + return -1; + } + + int l; + while ((l = dbm_iter_next(owner, &key)) >= 0) { // an error is indicated by -1 + free(key.dptr); + len += key.dsize; + if (l == 0) { // end of the iterator + dbm_close(owner); + } + } + if l >= 0 { + return -1; + } else { + return len; + } +} +``` + +This bug is a classic. Here's what happens when the iterator returns the +end-of-iteration marker: + +1. The loop condition sets `l` to zero, and enters the loop because `0 >= 0`. +2. The length is incremented, in this case by zero. +3. The if statement is true, so the database is closed. There should be a break + statement here. +4. The loop condition executes again, causing a `next` call on the closed object. + +The worst part about this bug? +If the Rust implementation was careful, this code will work most of the time! +If the memory for the `Dbm` object is not immediately reused, an internal check +will almost certainly fail, resulting in the iterator returning a `-1` indicating +an error. But occasionally, it will cause a segmentation fault, or even worse, +nonsensical memory corruption! + +None of this can be avoided by Rust. +From its perspective, it put those objects on its heap, returned pointers to them, +and gave up control of their lifetimes. The C code simply must "play nice". + +The programmer must read and understand the API documentation. +While some consider that par for the course in C, a good API design can mitigate +this risk. The POSIX API for `DBM` did this by *consolidating the ownership* of +the iterator with its parent: + +```C +datum dbm_firstkey(DBM *); +datum dbm_nextkey(DBM *); +``` + +Thus, all the lifetimes were bound together, and such unsafety was prevented. + +## Disadvantages + +However, this design choice also has a number of drawbacks, which should be +considered as well. + +First, the API itself becomes less expressive. +With POSIX DBM, there is only one iterator per object, and every call changes +its state. This is much more restrictive than iterators in almost any language, +even though it is safe. Perhaps with other related objects, whose lifetimes are +less hierarchical, this limitation is more of a cost than the safety. + +Second, depending on the relationships of the API's parts, significant design effort +may be involved. Many of the easier design points have other patterns associated +with them: + +- [Wrapper Type Consolidation](./wrappers.md) groups multiple Rust types together + into an opaque "object" + +- [FFI Error Passing](../../idioms/ffi/errors.md) explains error handling with integer + codes and sentinel return values (such as `NULL` pointers) + +- [Accepting Foreign Strings](../../idioms/ffi/accepting-strings.md) allows accepting + strings with minimal unsafe code, and is easier to get right than + [Passing Strings to FFI](../../idioms/ffi/passing-strings.md) + +However, not every API can be done this way. +It is up to the best judgement of the programmer as to who their audience is. diff --git a/src/patterns/ffi/intro.md b/src/patterns/ffi/intro.md new file mode 100644 index 00000000..f65a0d28 --- /dev/null +++ b/src/patterns/ffi/intro.md @@ -0,0 +1,13 @@ +# FFI Patterns + +Writing FFI code is an entire course in itself. +However, there are several idioms here that can act as pointers, and avoid traps +for inexperienced users of unsafe Rust. + +This section contains design patterns that may be useful when doing FFI. + +1. [Object-Based API](./export.md) design that has good memory safety characteristics, + and a clean boundary of what is safe and what is unsafe + +2. [Type Consolidation into Wrappers](./wrappers.md) - group multiple Rust types + together into an opaque "object" diff --git a/src/patterns/ffi/wrappers.md b/src/patterns/ffi/wrappers.md new file mode 100644 index 00000000..77312596 --- /dev/null +++ b/src/patterns/ffi/wrappers.md @@ -0,0 +1,162 @@ +# Type Consolidation into Wrappers + +## Description + +This pattern is designed to allow gracefully handling multiple related types, +while minimizing the surface area for memory unsafety. + +One of the cornerstones of Rust's aliasing rules is lifetimes. +This ensures that many patterns of access between types can be memory safe, +data race safety included. + +However, when Rust types are exported to other languages, they are usually transformed +into pointers. In Rust, a pointer means "the user manages the lifetime of the pointee." +It is their responsibility to avoid memory unsafety. + +Some level of trust in the user code is thus required, notably around use-after-free +which Rust can do nothing about. However, some API designs place higher burdens +than others on the code written in the other language. + +The lowest risk API is the "consolidated wrapper", where all possible interactions +with an object are folded into a "wrapper type", while keeping the Rust API clean. + +## Code Example + +To understand this, let us look at a classic example of an API to export: iteration +through a collection. + +That API looks like this: + +1. The iterator is initialized with `first_key`. +2. Each call to `next_key` will advance the iterator. +3. Calls to `next_key` if the iterator is at the end will do nothing. +4. As noted above, the iterator is "wrapped into" the collection (unlike the native + Rust API). + +If the iterator implements `nth()` efficiently, then it is possible to make it +ephemeral to each function call: + +```rust,ignore +struct MySetWrapper { + myset: MySet, + iter_next: usize, +} + +impl MySetWrapper { + pub fn first_key(&mut self) -> Option<&Key> { + self.iter_next = 0; + self.next_key() + } + pub fn next_key(&mut self) -> Option<&Key> { + if let Some(next) = self.myset.keys().nth(self.iter_next) { + self.iter_next += 1; + Some(next) + } else { + None + } + } +} +``` + +As a result, the wrapper is simple and contains no `unsafe` code. + +## Advantages + +This makes APIs safer to use, avoiding issues with lifetimes between types. +See [Object-Based APIs](./export.md) for more on the advantages and pitfalls +this avoids. + +## Disadvantages + +Often, wrapping types is quite difficult, and sometimes a Rust API compromise +would make things easier. + +As an example, consider an iterator which does not efficiently implement `nth()`. +It would definitely be worth putting in special logic to make the object handle +iteration internally, or to support a different access pattern efficiently that +only the Foreign Function API will use. + +### Trying to Wrap Iterators (and Failing) + +To wrap any type of iterator into the API correctly, the wrapper would need to +do what a C version of the code would do: erase the lifetime of the iterator, +and manage it manually. + +Suffice it to say, this is *incredibly* difficult. + +Here is an illustration of just *one* pitfall. + +A first version of `MySetWrapper` would look like this: + +```rust,ignore +struct MySetWrapper { + myset: MySet, + iter_next: usize, + // created from a transmuted Box + iterator: Option>>, +} +``` + +With `transmute` being used to extend a lifetime, and a pointer to hide it, +it's ugly already. But it gets even worse: *any other operation can cause +Rust `undefined behaviour`*. + +Consider that the `MySet` in the wrapper could be manipulated by other +functions during iteration, such as storing a new value to the key it was +iterating over. The API doesn't discourage this, and in fact some similar C +libraries expect it. + +A simple implementation of `myset_store` would be: + +```rust,ignore +pub mod unsafe_module { + + // other module content + + pub fn myset_store( + myset: *mut MySetWrapper, + key: datum, + value: datum) -> libc::c_int { + + // DO NOT USE THIS CODE. IT IS UNSAFE TO DEMONSTRATE A PROLBEM. + + let myset: &mut MySet = unsafe { // SAFETY: whoops, UB occurs in here! + &mut (*myset).myset + }; + + /* ...check and cast key and value data... */ + + match myset.store(casted_key, casted_value) { + Ok(_) => 0, + Err(e) => e.into() + } + } +} +``` + +If the iterator exists when this function is called, we have violated one of Rust's +aliasing rules. According to Rust, the mutable reference in this block must have +*exclusive* access to the object. If the iterator simply exists, it's not exclusive, +so we have `undefined behaviour`! [^1] + +To avoid this, we must have a way of ensuring that mutable reference really is exclusive. +That basically means clearing out the iterator's shared reference while it exists, +and then reconstructing it. In most cases, that will still be less efficient than +the C version. + +Some may ask: how can C do this more efficiently? +The answer is, it cheats. Rust's aliasing rules are the problem, and C simply ignores +them for its pointers. In exchange, it is common to see code that is declared +in the manual as "not thread safe" under some or all circumstances. In fact, +the [GNU C library](https://manpages.debian.org/buster/manpages/attributes.7.en.html) +has an entire lexicon dedicated to concurrent behavior! + +Rust would rather make everything memory safe all the time, for both safety and +optimizations that C code cannot attain. Being denied access to certain shortcuts +is the price Rust programmers need to pay. + +[^1]: For the C programmers out there scratching their heads, the iterator need + not be read *during* this code cause the UB. The exclusivity rule also enables + compiler optimizations which may cause inconsistent observations by the iterator's + shared reference (e.g. stack spills or reordering instructions for efficiency). + These observations may happen *any time after* the mutable reference is created. diff --git a/src/patterns/index.md b/src/patterns/index.md new file mode 100644 index 00000000..1718174c --- /dev/null +++ b/src/patterns/index.md @@ -0,0 +1,28 @@ +# Design Patterns + +[Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) are +"general reusable solutions to a commonly occurring problem within a given +context in software design". Design patterns are a great way to describe the +culture of a programming language. Design patterns are very language-specific - +what is a pattern in one language may be unnecessary in another due to a +language feature, or impossible to express due to a missing feature. + +If overused, design patterns can add unnecessary complexity to programs. +However, they are a great way to share intermediate and advanced level knowledge +about a programming language. + +## Design patterns in Rust + +Rust has many unique features. These features give us great benefit by removing +whole classes of problems. Some of them are also patterns that are _unique_ to Rust. + +## YAGNI + +YAGNI is an acronym that stands for `You Aren't Going to Need It`. +It's a vital software design principle to apply as you write code. + +> The best code I ever wrote was code I never wrote. + +If we apply YAGNI to design patterns, we see that the features of Rust allow us to +throw out many patterns. For instance, there is no need for the [strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern) +in Rust because we can just use [traits](https://doc.rust-lang.org/book/traits.html). diff --git a/src/patterns/structural/compose-structs.md b/src/patterns/structural/compose-structs.md new file mode 100644 index 00000000..013f6cb7 --- /dev/null +++ b/src/patterns/structural/compose-structs.md @@ -0,0 +1,99 @@ +# Compose structs together for better borrowing + +TODO - this is not a very snappy name + +## Description + +Sometimes a large struct will cause issues with the borrow checker - although +fields can be borrowed independently, sometimes the whole struct ends up being +used at once, preventing other uses. A solution might be to decompose the struct +into several smaller structs. Then compose these together into the original +struct. Then each struct can be borrowed separately and have more flexible +behaviour. + +This will often lead to a better design in other ways: applying this design +pattern often reveals smaller units of functionality. + +## Example + +Here is a contrived example of where the borrow checker foils us in our plan to +use a struct: + +```rust +struct A { + f1: u32, + f2: u32, + f3: u32, +} + +fn foo(a: &mut A) -> &u32 { &a.f2 } +fn bar(a: &mut A) -> u32 { a.f1 + a.f3 } + +fn baz(a: &mut A) { + // The later usage of x causes a to be borrowed for the rest of the function. + let x = foo(a); + // Borrow checker error: + // let y = bar(a); // ~ ERROR: cannot borrow `*a` as mutable more than once + // at a time + println!("{}", x); +} +``` + +We can apply this design pattern and refactor `A` into two smaller structs, thus +solving the borrow checking issue: + +```rust +// A is now composed of two structs - B and C. +struct A { + b: B, + c: C, +} +struct B { + f2: u32, +} +struct C { + f1: u32, + f3: u32, +} + +// These functions take a B or C, rather than A. +fn foo(b: &mut B) -> &u32 { &b.f2 } +fn bar(c: &mut C) -> u32 { c.f1 + c.f3 } + +fn baz(a: &mut A) { + let x = foo(&mut a.b); + // Now it's OK! + let y = bar(&mut a.c); + println!("{}", x); +} +``` + +## Motivation + +TODO Why and where you should use the pattern + +## Advantages + +Lets you work around limitations in the borrow checker. + +Often produces a better design. + +## Disadvantages + +Leads to more verbose code. + +Sometimes, the smaller structs are not good abstractions, and so we end up with +a worse design. That is probably a 'code smell', indicating that the program +should be refactored in some way. + +## Discussion + +This pattern is not required in languages that don't have a borrow checker, so +in that sense is unique to Rust. However, making smaller units of functionality +often leads to cleaner code: a widely acknowledged principle of software +engineering, independent of the language. + +This pattern relies on Rust's borrow checker to be able to borrow fields +independently of each other. In the example, the borrow checker knows that `a.b` +and `a.c` are distinct and can be borrowed independently, it does not try to +borrow all of `a`, which would make this pattern useless. diff --git a/src/patterns/structural/intro.md b/src/patterns/structural/intro.md new file mode 100644 index 00000000..34d9cc9b --- /dev/null +++ b/src/patterns/structural/intro.md @@ -0,0 +1,6 @@ +# Structural Patterns + +From [Wikipedia](https://en.wikipedia.org/wiki/Structural_pattern): + +> Design patterns that ease the design by identifying a simple way to realize relationships +> among entities. diff --git a/src/patterns/structural/small-crates.md b/src/patterns/structural/small-crates.md new file mode 100644 index 00000000..01060e31 --- /dev/null +++ b/src/patterns/structural/small-crates.md @@ -0,0 +1,47 @@ +# Prefer small crates + +## Description + +Prefer small crates that do one thing well. + +Cargo and crates.io make it easy to add third-party libraries, much more so than +in say C or C++. Moreover, since packages on crates.io cannot be edited or removed +after publication, any build that works now should continue to work in the future. +We should take advantage of this tooling, and use smaller, more fine-grained dependencies. + +## Advantages + +* Small crates are easier to understand, and encourage more modular code. +* Crates allow for re-using code between projects. + For example, the `url` crate was developed as part of the Servo browser engine, + but has since found wide use outside the project. +* Since the compilation unit + of Rust is the crate, splitting a project into multiple crates can allow more of + the code to be built in parallel. + +## Disadvantages + +* This can lead to "dependency hell", when a project depends on multiple conflicting + versions of a crate at the same time. For example, the `url` crate has both versions + 1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url` from `url:0.5` are + different types, an HTTP client that uses `url:0.5` would not accept `Url` values + from a web scraper that uses `url:1.0`. +* Packages on crates.io are not curated. A crate may be poorly written, have + unhelpful documentation, or be outright malicious. +* Two small crates may be less optimized than one large one, since the compiler + does not perform link-time optimization (LTO) by default. + +## Examples + +The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions +for converting `&T` to `&[T]`. + +The [`url`](https://crates.io/crates/url) crate provides tools for working with +URLs. + +The [`num_cpus`](https://crates.io/crates/num_cpus) crate provides a function to +query the number of CPUs on a machine. + +## See also + +* [crates.io: The Rust community crate host](https://crates.io/) diff --git a/src/patterns/structural/unsafe-mods.md b/src/patterns/structural/unsafe-mods.md new file mode 100644 index 00000000..41737023 --- /dev/null +++ b/src/patterns/structural/unsafe-mods.md @@ -0,0 +1,34 @@ +# Contain unsafety in small modules + +## Description + +If you have `unsafe` code, create the smallest possible module that can uphold +the needed invariants to build a minimal safe interface upon the unsafety. Embed +this into a larger module that contains only safe code and presents an ergonomic +interface. Note that the outer module can contain unsafe functions and methods +that call directly into the unsafe code. Users may use this to gain speed benefits. + +## Advantages + +* This restricts the unsafe code that must be audited +* Writing the outer module is much easier, since you can count on the guarantees +of the inner module + +## Disadvantages + +* Sometimes, it may be hard to find a suitable interface. +* The abstraction may introduce inefficiencies. + +## Examples + +* The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe operations + in submodules, presenting a safe interface to users. +* `std`'s `String` class is a wrapper over `Vec` with the added invariant +that the contents must be valid UTF-8. The operations on `String` ensure this +behavior. +However, users have the option of using an `unsafe` method to create a `String`, +in which case the onus is on them to guarantee the validity of the contents. + +## See also + +* [Ralf Jung's Blog about invariants in unsafe code](https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html) diff --git a/src/refactoring/index.md b/src/refactoring/index.md new file mode 100644 index 00000000..178a53b0 --- /dev/null +++ b/src/refactoring/index.md @@ -0,0 +1,22 @@ +# Refactoring + +Refactoring is very important in relation to these topics. +Just as important as the other topics covered here, is how to take good code and +turn it into great code. + +We can use [design patterns](../patterns/index.md) to [DRY] up code and generalize +abstractions. We must avoid [anti-patterns](../anti_patterns/index.md) while we +do this. While they may be tempting to employ, their costs outweigh their benefits. + +> Shortcuts make for long days. + +We can also use [idioms](../idioms/index.md) to structure our code in a way that +is understandable. + +## Tests + +Tests are of vital importance during refactoring. + +## Small changes + +[DRY]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself diff --git a/src/translations.md b/src/translations.md new file mode 100644 index 00000000..8c89dea2 --- /dev/null +++ b/src/translations.md @@ -0,0 +1,6 @@ +# Translations + +- [简体中文](https://fomalhauthmj.github.io/patterns/) + +If you want to add a translation, please open an issue in the +[main repository](https://github.com/rust-unofficial/patterns). diff --git a/theme/book.js b/theme/book.js new file mode 100644 index 00000000..43fb708d --- /dev/null +++ b/theme/book.js @@ -0,0 +1 @@ +../third_party/mdbook/book.js \ No newline at end of file diff --git a/theme/index.hbs b/theme/index.hbs new file mode 100644 index 00000000..1381b492 --- /dev/null +++ b/theme/index.hbs @@ -0,0 +1,376 @@ + + + + + + {{ title }} + {{#if is_print }} + + {{/if}} + {{#if base_url}} + + {{/if}} + + + + + + + + {{> head}} + + + + + + {{#if favicon_svg}} + + {{/if}} + {{#if favicon_png}} + + {{/if}} + + + + {{#if print_enable}} + + {{/if}} + + + + {{#if copy_fonts}} + + {{/if}} + + + + + + + + {{#each additional_css}} + + {{/each}} + + {{#if mathjax_support}} + + + {{/if}} + + + + + + + + + + + + + + +

+ +
+ +
+ {{> header}} + + + + {{#if search_enabled}} + + {{/if}} + + + + +
+
+ {{{ content }}} +
+ + +
+
+ + + +
+ + {{#if live_reload_endpoint}} + + + {{/if}} + + {{#if google_analytics}} + + + {{/if}} + + {{#if playground_line_numbers}} + + {{/if}} + + {{#if playground_copyable}} + + {{/if}} + + {{#if playground_js}} + + + + + + {{/if}} + + {{#if search_js}} + + + + {{/if}} + + + + + + + {{#each additional_js}} + + {{/each}} + + {{#if is_print}} + {{#if mathjax_support}} + + {{else}} + + {{/if}} + {{/if}} + + + From 57e231697010d80a25a6b8b84f3de8918adea7e2 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:15:20 +0200 Subject: [PATCH 02/29] Begin of refactoring for translations --- SUMMARY.md | 55 ---- additional_resources/design-principles.md | 95 ------ additional_resources/index.md | 16 - anti_patterns/borrow_clone.md | 73 ----- anti_patterns/deny-warnings.md | 106 ------- anti_patterns/deref.md | 128 -------- anti_patterns/index.md | 8 - functional/generics-type-classes.md | 286 ----------------- functional/index.md | 10 - functional/lenses.md | 368 ---------------------- functional/paradigms.md | 69 ---- idioms/coercion-arguments.md | 138 -------- idioms/concat-format.md | 33 -- idioms/ctor.md | 115 ------- idioms/default.md | 69 ---- idioms/deref.md | 79 ----- idioms/dtor-finally.md | 90 ------ idioms/ffi/accepting-strings.md | 143 --------- idioms/ffi/errors.md | 139 -------- idioms/ffi/intro.md | 14 - idioms/ffi/passing-strings.md | 105 ------ idioms/index.md | 18 -- idioms/mem-replace.md | 113 ------- idioms/on-stack-dyn-dispatch.md | 93 ------ idioms/option-iter.md | 59 ---- idioms/pass-var-to-closure.md | 59 ---- idioms/priv-extend.md | 122 ------- idioms/return-consumed-arg-on-error.md | 61 ---- idioms/rustdoc-init.md | 94 ------ idioms/temporary-mutability.md | 45 --- intro.md | 40 --- patterns/behavioural/RAII.md | 121 ------- patterns/behavioural/command.md | 222 ------------- patterns/behavioural/interpreter.md | 147 --------- patterns/behavioural/intro.md | 6 - patterns/behavioural/newtype.md | 111 ------- patterns/behavioural/strategy.md | 179 ----------- patterns/behavioural/visitor.md | 113 ------- patterns/creational/builder.md | 115 ------- patterns/creational/fold.md | 122 ------- patterns/creational/intro.md | 8 - patterns/ffi/export.md | 260 --------------- patterns/ffi/intro.md | 13 - patterns/ffi/wrappers.md | 162 ---------- patterns/index.md | 28 -- patterns/structural/compose-structs.md | 99 ------ patterns/structural/intro.md | 6 - patterns/structural/small-crates.md | 47 --- patterns/structural/unsafe-mods.md | 34 -- refactoring/index.md | 22 -- translations.md | 6 - 51 files changed, 4664 deletions(-) delete mode 100644 SUMMARY.md delete mode 100644 additional_resources/design-principles.md delete mode 100644 additional_resources/index.md delete mode 100644 anti_patterns/borrow_clone.md delete mode 100644 anti_patterns/deny-warnings.md delete mode 100644 anti_patterns/deref.md delete mode 100644 anti_patterns/index.md delete mode 100644 functional/generics-type-classes.md delete mode 100644 functional/index.md delete mode 100644 functional/lenses.md delete mode 100644 functional/paradigms.md delete mode 100644 idioms/coercion-arguments.md delete mode 100644 idioms/concat-format.md delete mode 100644 idioms/ctor.md delete mode 100644 idioms/default.md delete mode 100644 idioms/deref.md delete mode 100644 idioms/dtor-finally.md delete mode 100644 idioms/ffi/accepting-strings.md delete mode 100644 idioms/ffi/errors.md delete mode 100644 idioms/ffi/intro.md delete mode 100644 idioms/ffi/passing-strings.md delete mode 100644 idioms/index.md delete mode 100644 idioms/mem-replace.md delete mode 100644 idioms/on-stack-dyn-dispatch.md delete mode 100644 idioms/option-iter.md delete mode 100644 idioms/pass-var-to-closure.md delete mode 100644 idioms/priv-extend.md delete mode 100644 idioms/return-consumed-arg-on-error.md delete mode 100644 idioms/rustdoc-init.md delete mode 100644 idioms/temporary-mutability.md delete mode 100644 intro.md delete mode 100644 patterns/behavioural/RAII.md delete mode 100644 patterns/behavioural/command.md delete mode 100644 patterns/behavioural/interpreter.md delete mode 100644 patterns/behavioural/intro.md delete mode 100644 patterns/behavioural/newtype.md delete mode 100644 patterns/behavioural/strategy.md delete mode 100644 patterns/behavioural/visitor.md delete mode 100644 patterns/creational/builder.md delete mode 100644 patterns/creational/fold.md delete mode 100644 patterns/creational/intro.md delete mode 100644 patterns/ffi/export.md delete mode 100644 patterns/ffi/intro.md delete mode 100644 patterns/ffi/wrappers.md delete mode 100644 patterns/index.md delete mode 100644 patterns/structural/compose-structs.md delete mode 100644 patterns/structural/intro.md delete mode 100644 patterns/structural/small-crates.md delete mode 100644 patterns/structural/unsafe-mods.md delete mode 100644 refactoring/index.md delete mode 100644 translations.md diff --git a/SUMMARY.md b/SUMMARY.md deleted file mode 100644 index 2c73804f..00000000 --- a/SUMMARY.md +++ /dev/null @@ -1,55 +0,0 @@ -# Summary - -- [Introduction](./intro.md) - - [Translations](./translations.md) -- [Idioms](./idioms/index.md) - - [Use borrowed types for arguments](./idioms/coercion-arguments.md) - - [Concatenating Strings with `format!`](./idioms/concat-format.md) - - [Constructor](./idioms/ctor.md) - - [The `Default` Trait](./idioms/default.md) - - [Collections Are Smart Pointers](./idioms/deref.md) - - [Finalisation in Destructors](./idioms/dtor-finally.md) - - [`mem::{take(_), replace(_)}`](./idioms/mem-replace.md) - - [On-Stack Dynamic Dispatch](./idioms/on-stack-dyn-dispatch.md) - - [Foreign function interface (FFI)](./idioms/ffi/intro.md) - - [Idiomatic Errors](./idioms/ffi/errors.md) - - [Accepting Strings](./idioms/ffi/accepting-strings.md) - - [Passing Strings](./idioms/ffi/passing-strings.md) - - [Iterating over an `Option`](./idioms/option-iter.md) - - [Pass Variables to Closure](./idioms/pass-var-to-closure.md) - - [Privacy For Extensibility](./idioms/priv-extend.md) - - [Easy doc initialization](./idioms/rustdoc-init.md) - - [Temporary mutability](./idioms/temporary-mutability.md) - - [Return consumed arg on error](./idioms/return-consumed-arg-on-error.md) - -- [Design Patterns](./patterns/index.md) - - [Behavioural](./patterns/behavioural/intro.md) - - [Command](./patterns/behavioural/command.md) - - [Interpreter](./patterns/behavioural/interpreter.md) - - [Newtype](./patterns/behavioural/newtype.md) - - [RAII Guards](./patterns/behavioural/RAII.md) - - [Strategy](./patterns/behavioural/strategy.md) - - [Visitor](./patterns/behavioural/visitor.md) - - [Creational](./patterns/creational/intro.md) - - [Builder](./patterns/creational/builder.md) - - [Fold](./patterns/creational/fold.md) - - [Structural](./patterns/structural/intro.md) - - [Compose Structs](./patterns/structural/compose-structs.md) - - [Prefer Small Crates](./patterns/structural/small-crates.md) - - [Contain unsafety in small modules](./patterns/structural/unsafe-mods.md) - - [Foreign function interface (FFI)](./patterns/ffi/intro.md) - - [Object-Based APIs](./patterns/ffi/export.md) - - [Type Consolidation into Wrappers](./patterns/ffi/wrappers.md) - -- [Anti-patterns](./anti_patterns/index.md) - - [Clone to satisfy the borrow checker](./anti_patterns/borrow_clone.md) - - [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md) - - [Deref Polymorphism](./anti_patterns/deref.md) - -- [Functional Programming](./functional/index.md) - - [Programming paradigms](./functional/paradigms.md) - - [Generics as Type Classes](./functional/generics-type-classes.md) - - [Lenses and Prisms](./functional/lenses.md) - -- [Additional Resources](./additional_resources/index.md) - - [Design principles](./additional_resources/design-principles.md) diff --git a/additional_resources/design-principles.md b/additional_resources/design-principles.md deleted file mode 100644 index d20ff914..00000000 --- a/additional_resources/design-principles.md +++ /dev/null @@ -1,95 +0,0 @@ -# Design principles - -## A brief overview over common design principles - ---- - -## [SOLID](https://en.wikipedia.org/wiki/SOLID) - -- [Single Responsibility Principle (SRP)](https://en.wikipedia.org/wiki/Single-responsibility_principle): - A class should only have a single responsibility, that is, only changes to - one part of the software's specification should be able to affect the - specification of the class. -- [Open/Closed Principle (OCP)](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle): - "Software entities ... should be open for extension, but closed for - modification." -- [Liskov Substitution Principle (LSP)](https://en.wikipedia.org/wiki/Liskov_substitution_principle): - "Objects in a program should be replaceable with instances of their subtypes - without altering the correctness of that program." -- [Interface Segregation Principle (ISP)](https://en.wikipedia.org/wiki/Interface_segregation_principle): - "Many client-specific interfaces are better than one general-purpose - interface." -- [Dependency Inversion Principle (DIP)](https://en.wikipedia.org/wiki/Dependency_inversion_principle): - One should "depend upon abstractions, [not] concretions." - -## [DRY (Don’t Repeat Yourself)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) - -"Every piece of knowledge must have a single, unambiguous, authoritative -representation within a system" - -## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle) - -most systems work best if they are kept simple rather than made complicated; -therefore, simplicity should be a key goal in design, and unnecessary -complexity should be avoided - -## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter) - -a given object should assume as little as possible about the structure or -properties of anything else (including its subcomponents), in accordance with -the principle of "information hiding" - -## [Design by contract (DbC)](https://en.wikipedia.org/wiki/Design_by_contract) - -software designers should define formal, precise and verifiable interface -specifications for software components, which extend the ordinary definition of -abstract data types with preconditions, postconditions and invariants - -## [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)) - -bundling of data with the methods that operate on that data, or the restricting -of direct access to some of an object's components. Encapsulation is used to -hide the values or state of a structured data object inside a class, preventing -unauthorized parties' direct access to them. - -## [Command-Query-Separation(CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation) - -“Functions should not produce abstract side effects...only commands -(procedures) will be permitted to produce side effects.” - Bertrand Meyer: -Object-Oriented Software Construction - -## [Principle of least astonishment (POLA)](https://en.wikipedia.org/wiki/Principle_of_least_astonishment) - -a component of a system should behave in a way that most users will expect it -to behave. The behavior should not astonish or surprise users - -## Linguistic-Modular-Units - -“Modules must correspond to syntactic units in the language used.” - Bertrand -Meyer: Object-Oriented Software Construction - -## Self-Documentation - -“The designer of a module should strive to make all information about the -module part of the module itself.” - Bertrand Meyer: Object-Oriented Software -Construction - -## Uniform-Access - -“All services offered by a module should be available through a uniform -notation, which does not betray whether they are implemented through storage or -through computation.” - Bertrand Meyer: Object-Oriented Software Construction - -## Single-Choice - -“Whenever a software system must support a set of alternatives, one and only -one module in the system should know their exhaustive list.” - Bertrand Meyer: -Object-Oriented Software Construction - -## Persistence-Closure - -“Whenever a storage mechanism stores an object, it must store with it the -dependents of that object. Whenever a retrieval mechanism retrieves a -previously stored object, it must also retrieve any dependent of that object -that has not yet been retrieved.” - Bertrand Meyer: Object-Oriented Software -Construction diff --git a/additional_resources/index.md b/additional_resources/index.md deleted file mode 100644 index 29022937..00000000 --- a/additional_resources/index.md +++ /dev/null @@ -1,16 +0,0 @@ -# Additional resources - -A collection of complementary helpful content - -## Talks - -- [Design Patterns in Rust](https://www.youtube.com/watch?v=Pm_oO0N5B9k) by - Nicholas Cameron at the PDRust (2016) -- [Writing Idiomatic Libraries in Rust](https://www.youtube.com/watch?v=0zOg8_B71gE) - by Pascal Hertleif at RustFest (2017) -- [Rust Programming Techniques](https://www.youtube.com/watch?v=vqavdUGKeb4) by - Nicholas Cameron at LinuxConfAu (2018) - -## Books (Online) - -- [The Rust API Guidelines](https://rust-lang.github.io/api-guidelines) diff --git a/anti_patterns/borrow_clone.md b/anti_patterns/borrow_clone.md deleted file mode 100644 index f5806aa2..00000000 --- a/anti_patterns/borrow_clone.md +++ /dev/null @@ -1,73 +0,0 @@ -# Clone to satisfy the borrow checker - -## Description - -The borrow checker prevents Rust users from developing otherwise unsafe code by -ensuring that either: only one mutable reference exists, or potentially many but -all immutable references exist. If the code written does not hold true to these -conditions, this anti-pattern arises when the developer resolves the compiler -error by cloning the variable. - -## Example - -```rust -// define any variable -let mut x = 5; - -// Borrow `x` -- but clone it first -let y = &mut (x.clone()); - -// without the x.clone() two lines prior, this line would fail on compile as -// x has been borrowed -// thanks to x.clone(), x was never borrowed, and this line will run. -println!("{}", x); - -// perform some action on the borrow to prevent rust from optimizing this -//out of existence -*y += 1; -``` - -## Motivation - -It is tempting, particularly for beginners, to use this pattern to resolve -confusing issues with the borrow checker. However, there are serious -consequences. Using `.clone()` causes a copy of the data to be made. Any changes -between the two are not synchronized -- as if two completely separate variables -exist. - -There are special cases -- `Rc` is designed to handle clones intelligently. -It internally manages exactly one copy of the data, and cloning it will only -clone the reference. - -There is also `Arc` which provides shared ownership of a value of type T -that is allocated in the heap. Invoking `.clone()` on `Arc` produces a new `Arc` -instance, which points to the same allocation on the heap as the source `Arc`, -while increasing a reference count. - -In general, clones should be deliberate, with full understanding of the -consequences. If a clone is used to make a borrow checker error disappear, -that's a good indication this anti-pattern may be in use. - -Even though `.clone()` is an indication of a bad pattern, sometimes -**it is fine to write inefficient code**, in cases such as when: - -- the developer is still new to ownership -- the code doesn't have great speed or memory constraints - (like hackathon projects or prototypes) -- satisfying the borrow checker is really complicated, and you prefer to - optimize readability over performance - -If an unnecessary clone is suspected, The [Rust Book's chapter on Ownership](https://doc.rust-lang.org/book/ownership.html) -should be understood fully before assessing whether the clone is required or not. - -Also be sure to always run `cargo clippy` in your project, which will detect some -cases in which `.clone()` is not necessary, like [1](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone), -[2](https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy), -[3](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) or [4](https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref). - -## See also - -- [`mem::{take(_), replace(_)}` to keep owned values in changed enums](../idioms/mem-replace.md) -- [`Rc` documentation, which handles .clone() intelligently](http://doc.rust-lang.org/std/rc/) -- [`Arc` documentation, a thread-safe reference-counting pointer](https://doc.rust-lang.org/std/sync/struct.Arc.html) -- [Tricks with ownership in Rust](https://web.archive.org/web/20210120233744/https://xion.io/post/code/rust-borrowchk-tricks.html) diff --git a/anti_patterns/deny-warnings.md b/anti_patterns/deny-warnings.md deleted file mode 100644 index 1dae9d29..00000000 --- a/anti_patterns/deny-warnings.md +++ /dev/null @@ -1,106 +0,0 @@ -# `#![deny(warnings)]` - -## Description - -A well-intentioned crate author wants to ensure their code builds without -warnings. So they annotate their crate root with the following: - -## Example - -```rust -#![deny(warnings)] - -// All is well. -``` - -## Advantages - -It is short and will stop the build if anything is amiss. - -## Drawbacks - -By disallowing the compiler to build with warnings, a crate author opts out of -Rust's famed stability. Sometimes new features or old misfeatures need a change -in how things are done, thus lints are written that `warn` for a certain grace -period before being turned to `deny`. - -For example, it was discovered that a type could have two `impl`s with the same -method. This was deemed a bad idea, but in order to make the transition smooth, -the `overlapping-inherent-impls` lint was introduced to give a warning to those -stumbling on this fact, before it becomes a hard error in a future release. - -Also sometimes APIs get deprecated, so their use will emit a warning where -before there was none. - -All this conspires to potentially break the build whenever something changes. - -Furthermore, crates that supply additional lints (e.g. [rust-clippy]) can no -longer be used unless the annotation is removed. This is mitigated with -[--cap-lints]. The `--cap-lints=warn` command line argument, turns all `deny` -lint errors into warnings. - -## Alternatives - -There are two ways of tackling this problem: First, we can decouple the build -setting from the code, and second, we can name the lints we want to deny -explicitly. - -The following command line will build with all warnings set to `deny`: - -```RUSTFLAGS="-D warnings" cargo build``` - -This can be done by any individual developer (or be set in a CI tool like -Travis, but remember that this may break the build when something changes) -without requiring a change to the code. - -Alternatively, we can specify the lints that we want to `deny` in the code. -Here is a list of warning lints that is (hopefully) safe to deny (as of Rustc 1.48.0): - -```rust,ignore -#![deny(bad_style, - const_err, - dead_code, - improper_ctypes, - non_shorthand_field_patterns, - no_mangle_generic_items, - overflowing_literals, - path_statements, - patterns_in_fns_without_body, - private_in_public, - unconditional_recursion, - unused, - unused_allocation, - unused_comparisons, - unused_parens, - while_true)] -``` - -In addition, the following `allow`ed lints may be a good idea to `deny`: - -```rust,ignore -#![deny(missing_debug_implementations, - missing_docs, - trivial_casts, - trivial_numeric_casts, - unused_extern_crates, - unused_import_braces, - unused_qualifications, - unused_results)] -``` - -Some may also want to add `missing-copy-implementations` to their list. - -Note that we explicitly did not add the `deprecated` lint, as it is fairly -certain that there will be more deprecated APIs in the future. - -## See also - -- [A collection of all clippy lints](https://rust-lang.github.io/rust-clippy/master) -- [deprecate attribute] documentation -- Type `rustc -W help` for a list of lints on your system. Also type -`rustc --help` for a general list of options -- [rust-clippy] is a collection of lints for better Rust code - -[rust-clippy]: https://github.com/Manishearth/rust-clippy -[deprecate attribute]: https://doc.rust-lang.org/reference/attributes.html#deprecation -[--cap-lints]: https://doc.rust-lang.org/rustc/lints/levels.html#capping-lints diff --git a/anti_patterns/deref.md b/anti_patterns/deref.md deleted file mode 100644 index b6f8f789..00000000 --- a/anti_patterns/deref.md +++ /dev/null @@ -1,128 +0,0 @@ -# `Deref` polymorphism - -## Description - -Misuse the `Deref` trait to emulate inheritance between structs, and thus reuse -methods. - -## Example - -Sometimes we want to emulate the following common pattern from OO languages such -as Java: - -```java -class Foo { - void m() { ... } -} - -class Bar extends Foo {} - -public static void main(String[] args) { - Bar b = new Bar(); - b.m(); -} -``` - -We can use the deref polymorphism anti-pattern to do so: - -```rust -use std::ops::Deref; - -struct Foo {} - -impl Foo { - fn m(&self) { - //.. - } -} - -struct Bar { - f: Foo, -} - -impl Deref for Bar { - type Target = Foo; - fn deref(&self) -> &Foo { - &self.f - } -} - -fn main() { - let b = Bar { f: Foo {} }; - b.m(); -} -``` - -There is no struct inheritance in Rust. Instead we use composition and include -an instance of `Foo` in `Bar` (since the field is a value, it is stored inline, -so if there were fields, they would have the same layout in memory as the Java -version (probably, you should use `#[repr(C)]` if you want to be sure)). - -In order to make the method call work we implement `Deref` for `Bar` with `Foo` -as the target (returning the embedded `Foo` field). That means that when we -dereference a `Bar` (for example, using `*`) then we will get a `Foo`. That is -pretty weird. Dereferencing usually gives a `T` from a reference to `T`, here we -have two unrelated types. However, since the dot operator does implicit -dereferencing, it means that the method call will search for methods on `Foo` as -well as `Bar`. - -## Advantages - -You save a little boilerplate, e.g., - -```rust,ignore -impl Bar { - fn m(&self) { - self.f.m() - } -} -``` - -## Disadvantages - -Most importantly this is a surprising idiom - future programmers reading this in -code will not expect this to happen. That's because we are misusing the `Deref` -trait rather than using it as intended (and documented, etc.). It's also because -the mechanism here is completely implicit. - -This pattern does not introduce subtyping between `Foo` and `Bar` like -inheritance in Java or C++ does. Furthermore, traits implemented by `Foo` are -not automatically implemented for `Bar`, so this pattern interacts badly with -bounds checking and thus generic programming. - -Using this pattern gives subtly different semantics from most OO languages with -regards to `self`. Usually it remains a reference to the sub-class, with this -pattern it will be the 'class' where the method is defined. - -Finally, this pattern only supports single inheritance, and has no notion of -interfaces, class-based privacy, or other inheritance-related features. So, it -gives an experience that will be subtly surprising to programmers used to Java -inheritance, etc. - -## Discussion - -There is no one good alternative. Depending on the exact circumstances it might -be better to re-implement using traits or to write out the facade methods to -dispatch to `Foo` manually. We do intend to add a mechanism for inheritance -similar to this to Rust, but it is likely to be some time before it reaches -stable Rust. See these [blog](http://aturon.github.io/blog/2015/09/18/reuse/) -[posts](http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-structs-part-4-extended-enums-and-thin-traits/) -and this [RFC issue](https://github.com/rust-lang/rfcs/issues/349) for more details. - -The `Deref` trait is designed for the implementation of custom pointer types. -The intention is that it will take a pointer-to-`T` to a `T`, not convert -between different types. It is a shame that this isn't (probably cannot be) -enforced by the trait definition. - -Rust tries to strike a careful balance between explicit and implicit mechanisms, -favouring explicit conversions between types. Automatic dereferencing in the dot -operator is a case where the ergonomics strongly favour an implicit mechanism, -but the intention is that this is limited to degrees of indirection, not -conversion between arbitrary types. - -## See also - -- [Collections are smart pointers idiom](../idioms/deref.md). -- Delegation crates for less boilerplate like [delegate](https://crates.io/crates/delegate) - or [ambassador](https://crates.io/crates/ambassador) -- [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html). diff --git a/anti_patterns/index.md b/anti_patterns/index.md deleted file mode 100644 index 84107e04..00000000 --- a/anti_patterns/index.md +++ /dev/null @@ -1,8 +0,0 @@ -# Anti-patterns - -An [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) is a solution to -a "recurring problem that is usually ineffective and risks being highly -counterproductive". Just as valuable as knowing how to solve a problem, is -knowing how _not_ to solve it. Anti-patterns give us great counter-examples to -consider relative to design patterns. Anti-patterns are not confined to code. -For example, a process can be an anti-pattern, too. diff --git a/functional/generics-type-classes.md b/functional/generics-type-classes.md deleted file mode 100644 index df80a08b..00000000 --- a/functional/generics-type-classes.md +++ /dev/null @@ -1,286 +0,0 @@ -# Generics as Type Classes - -## Description - -Rust's type system is designed more like functional languages (like Haskell) -rather than imperative languages (like Java and C++). As a result, Rust can turn -many kinds of programming problems into "static typing" problems. This is one -of the biggest wins of choosing a functional language, and is critical to many -of Rust's compile time guarantees. - -A key part of this idea is the way generic types work. In C++ and Java, for -example, generic types are a meta-programming construct for the compiler. -`vector` and `vector` in C++ are just two different copies of the -same boilerplate code for a `vector` type (known as a `template`) with two -different types filled in. - -In Rust, a generic type parameter creates what is known in functional languages -as a "type class constraint", and each different parameter filled in by an end -user *actually changes the type*. In other words, `Vec` and `Vec` -*are two different types*, which are recognized as distinct by all parts of the -type system. - -This is called **monomorphization**, where different types are created from -**polymorphic** code. This special behavior requires `impl` blocks to specify -generic parameters. Different values for the generic type cause different types, -and different types can have different `impl` blocks. - -In object-oriented languages, classes can inherit behavior from their parents. -However, this allows the attachment of not only additional behavior to -particular members of a type class, but extra behavior as well. - -The nearest equivalent is the runtime polymorphism in Javascript and Python, -where new members can be added to objects willy-nilly by any constructor. -However, unlike those languages, all of Rust's additional methods can be type -checked when they are used, because their generics are statically defined. That -makes them more usable while remaining safe. - -## Example - -Suppose you are designing a storage server for a series of lab machines. -Because of the software involved, there are two different protocols you need -to support: BOOTP (for PXE network boot), and NFS (for remote mount storage). - -Your goal is to have one program, written in Rust, which can handle both of -them. It will have protocol handlers and listen for both kinds of requests. The -main application logic will then allow a lab administrator to configure storage -and security controls for the actual files. - -The requests from machines in the lab for files contain the same basic -information, no matter what protocol they came from: an authentication method, -and a file name to retrieve. A straightforward implementation would look -something like this: - -```rust,ignore - -enum AuthInfo { - Nfs(crate::nfs::AuthInfo), - Bootp(crate::bootp::AuthInfo), -} - -struct FileDownloadRequest { - file_name: PathBuf, - authentication: AuthInfo, -} -``` - -This design might work well enough. But now suppose you needed to support -adding metadata that was *protocol specific*. For example, with NFS, you -wanted to determine what their mount point was in order to enforce additional -security rules. - -The way the current struct is designed leaves the protocol decision until -runtime. That means any method that applies to one protocol and not the other -requires the programmer to do a runtime check. - -Here is how getting an NFS mount point would look: - -```rust,ignore -struct FileDownloadRequest { - file_name: PathBuf, - authentication: AuthInfo, - mount_point: Option, -} - -impl FileDownloadRequest { - // ... other methods ... - - /// Gets an NFS mount point if this is an NFS request. Otherwise, - /// return None. - pub fn mount_point(&self) -> Option<&Path> { - self.mount_point.as_ref() - } -} -``` - -Every caller of `mount_point()` must check for `None` and write code to handle -it. This is true even if they know only NFS requests are ever used in a given -code path! - -It would be far more optimal to cause a compile-time error if the different -request types were confused. After all, the entire path of the user's code, -including what functions from the library they use, will know whether a request -is an NFS request or a BOOTP request. - -In Rust, this is actually possible! The solution is to *add a generic type* in -order to split the API. - -Here is what that looks like: - -```rust -use std::path::{Path, PathBuf}; - -mod nfs { - #[derive(Clone)] - pub(crate) struct AuthInfo(String); // NFS session management omitted -} - -mod bootp { - pub(crate) struct AuthInfo(); // no authentication in bootp -} - -// private module, lest outside users invent their own protocol kinds! -mod proto_trait { - use std::path::{Path, PathBuf}; - use super::{bootp, nfs}; - - pub(crate) trait ProtoKind { - type AuthInfo; - fn auth_info(&self) -> Self::AuthInfo; - } - - pub struct Nfs { - auth: nfs::AuthInfo, - mount_point: PathBuf, - } - - impl Nfs { - pub(crate) fn mount_point(&self) -> &Path { - &self.mount_point - } - } - - impl ProtoKind for Nfs { - type AuthInfo = nfs::AuthInfo; - fn auth_info(&self) -> Self::AuthInfo { - self.auth.clone() - } - } - - pub struct Bootp(); // no additional metadata - - impl ProtoKind for Bootp { - type AuthInfo = bootp::AuthInfo; - fn auth_info(&self) -> Self::AuthInfo { - bootp::AuthInfo() - } - } -} - -use proto_trait::ProtoKind; // keep internal to prevent impls -pub use proto_trait::{Nfs, Bootp}; // re-export so callers can see them - -struct FileDownloadRequest { - file_name: PathBuf, - protocol: P, -} - -// all common API parts go into a generic impl block -impl FileDownloadRequest

{ - fn file_path(&self) -> &Path { - &self.file_name - } - - fn auth_info(&self) -> P::AuthInfo { - self.protocol.auth_info() - } -} - -// all protocol-specific impls go into their own block -impl FileDownloadRequest { - fn mount_point(&self) -> &Path { - self.protocol.mount_point() - } -} - -fn main() { - // your code here -} -``` - -With this approach, if the user were to make a mistake and use the wrong -type; - -```rust,ignore -fn main() { - let mut socket = crate::bootp::listen()?; - while let Some(request) = socket.next_request()? { - match request.mount_point().as_ref() - "/secure" => socket.send("Access denied"), - _ => {} // continue on... - } - // Rest of the code here - } -} -``` - -They would get a syntax error. The type `FileDownloadRequest` does not -implement `mount_point()`, only the type `FileDownloadRequest` does. And -that is created by the NFS module, not the BOOTP module of course! - -## Advantages - -First, it allows fields that are common to multiple states to be de-duplicated. -By making the non-shared fields generic, they are implemented once. - -Second, it makes the `impl` blocks easier to read, because they are broken down -by state. Methods common to all states are typed once in one block, and methods -unique to one state are in a separate block. - -Both of these mean there are fewer lines of code, and they are better organized. - -## Disadvantages - -This currently increases the size of the binary, due to the way monomorphization -is implemented in the compiler. Hopefully the implementation will be able to -improve in the future. - -## Alternatives - -* If a type seems to need a "split API" due to construction or partial -initialization, consider the -[Builder Pattern](../patterns/creational/builder.md) instead. - -* If the API between types does not change -- only the behavior does -- then -the [Strategy Pattern](../patterns/behavioural/strategy.md) is better used -instead. - -## See also - -This pattern is used throughout the standard library: - -* `Vec` can be cast from a String, unlike every other type of `Vec`.[^1] -* They can also be cast into a binary heap, but only if they contain a type - that implements the `Ord` trait.[^2] -* The `to_string` method was specialized for `Cow` only of type `str`.[^3] - -It is also used by several popular crates to allow API flexibility: - -* The `embedded-hal` ecosystem used for embedded devices makes extensive use of - this pattern. For example, it allows statically verifying the configuration of - device registers used to control embedded pins. When a pin is put into a mode, - it returns a `Pin` struct, whose generic determines the functions - usable in that mode, which are not on the `Pin` itself. [^4] - -* The `hyper` HTTP client library uses this to expose rich APIs for different - pluggable requests. Clients with different connectors have different methods - on them as well as different trait implementations, while a core set of - methods apply to any connector. [^5] - -* The "type state" pattern -- where an object gains and loses API based on an - internal state or invariant -- is implemented in Rust using the same basic - concept, and a slightly different technique. [^6] - -[^1]: See: [impl From\ for Vec\]( -https://doc.rust-lang.org/1.59.0/src/std/ffi/c_str.rs.html#803-811) - -[^2]: See: [impl\ From\\> for BinaryHeap\]( -https://doc.rust-lang.org/stable/src/alloc/collections/binary_heap.rs.html#1345-1354) - -[^3]: See: [impl\<'\_\> ToString for Cow\<'\_, str>]( -https://doc.rust-lang.org/stable/src/alloc/string.rs.html#2235-2240) - -[^4]: Example: -[https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html]( -https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html) - -[^5]: See: -[https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html]( -https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html) - -[^6]: See: -[The Case for the Type State Pattern]( -https://web.archive.org/web/20210325065112/https://www.novatec-gmbh.de/en/blog/the-case-for-the-typestate-pattern-the-typestate-pattern-itself/) -and -[Rusty Typestate Series (an extensive thesis)]( -https://web.archive.org/web/20210328164854/https://rustype.github.io/notes/notes/rust-typestate-series/rust-typestate-index) diff --git a/functional/index.md b/functional/index.md deleted file mode 100644 index b29e9caf..00000000 --- a/functional/index.md +++ /dev/null @@ -1,10 +0,0 @@ -# Functional Usage of Rust - -Rust is an imperative language, but it follows many -[functional programming](https://en.wikipedia.org/wiki/Functional_programming) paradigms. - -> In computer science, *functional programming* is a programming paradigm where -> programs are constructed by applying and composing functions. -> It is a declarative programming paradigm in which function definitions are -> trees of expressions that each return a value, rather than a sequence of -> imperative statements which change the state of the program. diff --git a/functional/lenses.md b/functional/lenses.md deleted file mode 100644 index c5f1ab59..00000000 --- a/functional/lenses.md +++ /dev/null @@ -1,368 +0,0 @@ -# Lenses and Prisms - -This is a pure functional concept that is not frequently used in Rust. -Nevertheless, exploring the concept may be helpful to understand other -patterns in Rust APIs, such as [visitors](../patterns/behavioural/visitor.md). -They also have niche use cases. - -## Lenses: Uniform Access Across Types - -A lens is a concept from functional programming languages that allows -accessing parts of a data type in an abstract, unified way.[^1] -In basic concept, it is similar to the way Rust traits work with type erasure, -but it has a bit more power and flexibility. - -For example, suppose a bank contains several JSON formats for customer -data. -This is because they come from different databases or legacy systems. -One database contains the data needed to perform credit checks: - -```json -{ "name": "Jane Doe", - "dob": "2002-02-24", - [...] - "customer_id": 1048576332, -} -``` - -Another one contains the account information: - -```json -{ "customer_id": 1048576332, - "accounts": [ - { "account_id": 2121, - "account_type: "savings", - "joint_customer_ids": [], - [...] - }, - { "account_id": 2122, - "account_type: "checking", - "joint_customer_ids": [1048576333], - [...] - }, - ] -} -``` - -Notice that both types have a customer ID number which corresponds to a person. -How would a single function handle both records of different types? - -In Rust, a `struct` could represent each of these types, and a trait would have -a `get_customer_id` function they would implement: - -```rust -use std::collections::HashSet; - -pub struct Account { - account_id: u32, - account_type: String, - // other fields omitted -} - -pub trait CustomerId { - fn get_customer_id(&self) -> u64; -} - -pub struct CreditRecord { - customer_id: u64, - name: String, - dob: String, - // other fields omitted -} - -impl CustomerId for CreditRecord { - fn get_customer_id(&self) -> u64 { - self.customer_id - } -} - -pub struct AccountRecord { - customer_id: u64, - accounts: Vec, -} - -impl CustomerId for AccountRecord { - fn get_customer_id(&self) -> u64 { - self.customer_id - } -} - -// static polymorphism: only one type, but each function call can choose it -fn unique_ids_set(records: &[R]) -> HashSet { - records.iter().map(|r| r.get_customer_id()).collect() -} - -// dynamic dispatch: iterates over any type with a customer ID, collecting all -// values together -fn unique_ids_iter(iterator: I) -> HashSet - where I: Iterator> -{ - iterator.map(|r| r.as_ref().get_customer_id()).collect() -} -``` - -Lenses, however, allow the code supporting customer ID to be moved from the -*type* to the *accessor function*. -Rather than implementing a trait on each type, all matching structures can -simply be accessed the same way. - -While the Rust language itself does not support this (type erasure is the -preferred solution to this problem), the [lens-rs -crate](https://github.com/TOETOE55/lens-rs/blob/master/guide.md) allows code -that feels like this to be written with macros: - -```rust,ignore -use std::collections::HashSet; - -use lens_rs::{optics, Lens, LensRef, Optics}; - -#[derive(Clone, Debug, Lens /* derive to allow lenses to work */)] -pub struct CreditRecord { - #[optic(ref)] // macro attribute to allow viewing this field - customer_id: u64, - name: String, - dob: String, - // other fields omitted -} - -#[derive(Clone, Debug)] -pub struct Account { - account_id: u32, - account_type: String, - // other fields omitted -} - -#[derive(Clone, Debug, Lens)] -pub struct AccountRecord { - #[optic(ref)] - customer_id: u64, - accounts: Vec, -} - -fn unique_ids_lens(iter: impl Iterator) -> HashSet -where - T: LensRef, // any type with this field -{ - iter.map(|r| *r.view_ref(optics!(customer_id))).collect() -} -``` - -The version of `unique_ids_lens` shown here allows any type to be in the iterator, -so long as it has an attribute called `customer_id` which can be accessed by -the function. -This is how most functional programming languages operate on lenses. - -Rather than macros, they achieve this with a technique known as "currying". -That is, they "partially construct" the function, leaving the type of the -final parameter (the value being operated on) unfilled until the function is -called. -Thus it can be called with different types dynamically even from one place in -the code. -That is what the `optics!` and `view_ref` in the example above simulates. - -The functional approach need not be restricted to accessing members. -More powerful lenses can be created which both *set* and *get* data in a -structure. -But the concept really becomes interesting when used as a building block for -composition. -That is where the concept appears more clearly in Rust. - -## Prisms: A Higher-Order form of "Optics" - -A simple function such as `unique_ids_lens` above operates on a single lens. -A *prism* is a function that operates on a *family* of lenses. -It is one conceptual level higher, using lenses as a building block, and -continuing the metaphor, is part of a family of "optics". -It is the main one that is useful in understanding Rust APIs, so will be the -focus here. - -The same way that traits allow "lens-like" design with static polymorphism and -dynamic dispatch, prism-like designs appear in Rust APIs which split problems -into multiple associated types to be composed. -A good example of this is the traits in the parsing crate *Serde*. - -Trying to understand the way *Serde* works by only reading the API is a -challenge, especially the first time. -Consider the `Deserializer` trait, implemented by some type in any library -which parses a new format: - -```rust,ignore -pub trait Deserializer<'de>: Sized { - type Error: Error; - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>; - - fn deserialize_bool(self, visitor: V) -> Result - where - V: Visitor<'de>; - - // remainder ommitted -} -``` - -For a trait that is just supposed to parse data from a format and return a -value, this looks odd. - -Why are all the return types type erased? - -To understand that, we need to keep the lens concept in mind and look at -the definition of the `Visitor` type that is passed in generically: - -```rust,ignore -pub trait Visitor<'de>: Sized { - type Value; - - fn visit_bool(self, v: bool) -> Result - where - E: Error; - - fn visit_u64(self, v: u64) -> Result - where - E: Error; - - fn visit_str(self, v: &str) -> Result - where - E: Error; - - // remainder omitted -} -``` - -The job of the `Visitor` type is to construct values in the *Serde* data model, -which are represented by its associated `Value` type. - -These values represent parts of the Rust value being deserialized. -If this fails, it returns an `Error` type - an error type determined by the -`Deserializer` when its methods were called. - -This highlights that `Deserializer` is similar to `CustomerId` from earlier, -allowing any format parser which implements it to create `Value`s based on what -it parsed. -The `Value` trait is acting like a lens in functional programming languages. - -But unlike the `CustomerId` trait, the return types of `Visitor` methods are -*generic*, and the concrete `Value` type is *determined by the Visitor itself*. - -Instead of acting as one lens, it effectively acts as a family of -lenses, one for each concrete type of `Visitor`. - -The `Deserializer` API is based on having a generic set of "lenses" work across -a set of other generic types for "observation". -It is a *prism*. - -For example, consider the identity record from earlier but simplified: - -```json -{ "name": "Jane Doe", - "customer_id": 1048576332, -} -``` - -How would the *Serde* library deserialize this JSON into `struct CreditRecord`? - -1. The user would call a library function to deserialize the data. This would - create a `Deserializer` based on the JSON format. -1. Based on the fields in the struct, a `Visitor` would be created (more on - that in a moment) which knows how to create each type in a generic data - model that was needed to represent it: `u64` and `String`. -1. The deserializer would make calls to the `Visitor` as it parsed items. -1. The `Visitor` would indicate if the items found were expected, and if not, - raise an error to indicate deserialization has failed. - -For our very simple structure above, the expected pattern would be: - -1. Visit a map (*Serde*'s equvialent to `HashMap` or JSON's dictionary). -1. Visit a string key called "name". -1. Visit a string value, which will go into the `name` field. -1. Visit a string key called "customer_id". -1. Visit a string value, which will go into the `customer_id` field. -1. Visit the end of the map. - -But what determines which "observation" pattern is expected? - -A functional programming language would be able to use currying to create -reflection of each type based on the type itself. -Rust does not support that, so every single type would need to have its own -code written based on its fields and their properties. - -*Serde* solves this usability challenge with a derive macro: - -```rust,ignore -use serde::Deserialize; - -#[derive(Deserialize)] -struct IdRecord { - name: String, - customer_id: String, -} -``` - -That macro simply generates an impl block causing the struct to implement a -trait called `Deserialize`. - -It is defined this way: - -```rust,ignore -pub trait Deserialize<'de>: Sized { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>; -} -``` - -This is the function that determines how to create the struct itself. -Code is generated based on the struct's fields. -When the parsing library is called - in our example, a JSON parsing library - -it creates a `Deserializer` and calls `Type::deserialize` with it as a -parameter. - -The `deserialize` code will then create a `Visitor` which will have its calls -"refracted" by the `Deserializer`. -If everything goes well, eventually that `Visitor` will construct a value -corresponding to the type being parsed and return it. - -For a complete example, see the [*Serde* -documentation](https://serde.rs/deserialize-struct.html). - -To wrap up, this is the power of *Serde*: - -1. The structure being parsed is represented by an `impl` block for `Deserialize` -1. The input data format (e.g. JSON) is represented by a `Deserializer` called - by `Deserialize` -1. The `Deserializer` acts like a prism which "refracts" lens-like `Visitor` - calls which actually build the data value - -The result is that types to be deserialized only implement the "top layer" of -the API, and file formats only need to implement the "bottom layer". -Each piece can then "just work" with the rest of the ecosystem, since generic -types will bridge them. - -To emphasize, the only reason this model works on any format and any type is -because the `Deserializer` trait's output type **is specified by the -implementor of `Visitor` it is passed**, rather than being tied to one specific -type. -This was not true in the account example earlier. - -Rust's generic-inspired type system can bring it close to these concepts and -use their power, as shown in this API design. -But it may also need procedural macros to create bridges for its generics. - -## See Also - -- [lens-rs crate](https://crates.io/crates/lens-rs) for a pre-built lenses - implementation, with a cleaner interface than these examples -- [serde](https://serde.rs) itself, which makes these concepts intuitive for - end users (i.e. defining the structs) without needing to undestand the - details -- [luminance](https://github.com/phaazon/luminance-rs) is a crate for drawing - computer graphics that uses lens API design, including proceducal macros to - create full prisms for buffers of different pixel types that remain generic -- [An Article about Lenses in - Scala](https://web.archive.org/web/20221128185849/https://medium.com/zyseme-technology/functional-references-lens-and-other-optics-in-scala-e5f7e2fdafe) - that is very readable even without Scala expertise. -- [Paper: Profunctor Optics: Modular Data - Accessors](https://web.archive.org/web/20220701102832/https://arxiv.org/ftp/arxiv/papers/1703/1703.10857.pdf) - -[^1]: [School of Haskell: A Little Lens Starter Tutorial](https://web.archive.org/web/20221128190041/https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/a-little-lens-starter-tutorial) diff --git a/functional/paradigms.md b/functional/paradigms.md deleted file mode 100644 index 9fc3b99e..00000000 --- a/functional/paradigms.md +++ /dev/null @@ -1,69 +0,0 @@ -# Programming paradigms - -One of the biggest hurdles to understanding functional programs when coming -from an imperative background is the shift in thinking. Imperative programs -describe __how__ to do something, whereas declarative programs describe -__what__ to do. Let's sum the numbers from 1 to 10 to show this. - -## Imperative - -```rust -let mut sum = 0; -for i in 1..11 { - sum += i; -} -println!("{}", sum); -``` - -With imperative programs, we have to play compiler to see what is happening. -Here, we start with a `sum` of `0`. -Next, we iterate through the range from 1 to 10. -Each time through the loop, we add the corresponding value in the range. -Then we print it out. - -| `i` | `sum` | -|:---:|:-----:| -| 1 | 1 | -| 2 | 3 | -| 3 | 6 | -| 4 | 10 | -| 5 | 15 | -| 6 | 21 | -| 7 | 28 | -| 8 | 36 | -| 9 | 45 | -| 10 | 55 | - -This is how most of us start out programming. We learn that a program is a set -of steps. - -## Declarative - -```rust -println!("{}", (1..11).fold(0, |a, b| a + b)); -``` - -Whoa! This is really different! What's going on here? -Remember that with declarative programs we are describing __what__ to do, -rather than __how__ to do it. `fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition) -functions. The name is a convention from Haskell. - -Here, we are composing functions of addition (this closure: `|a, b| a + b`) -with a range from 1 to 10. The `0` is the starting point, so `a` is `0` at -first. `b` is the first element of the range, `1`. `0 + 1 = 1` is the result. -So now we `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the next -result. This process continues until we get to the last element in the range, -`10`. - -| `a` | `b` | result | -|:---:|:---:|:------:| -| 0 | 1 | 1 | -| 1 | 2 | 3 | -| 3 | 3 | 6 | -| 6 | 4 | 10 | -| 10 | 5 | 15 | -| 15 | 6 | 21 | -| 21 | 7 | 28 | -| 28 | 8 | 36 | -| 36 | 9 | 45 | -| 45 | 10 | 55 | diff --git a/idioms/coercion-arguments.md b/idioms/coercion-arguments.md deleted file mode 100644 index 2ca63ab5..00000000 --- a/idioms/coercion-arguments.md +++ /dev/null @@ -1,138 +0,0 @@ -# Use borrowed types for arguments - -## Description - -Using a target of a deref coercion can increase the flexibility of your code -when you are deciding which argument type to use for a function argument. -In this way, the function will accept more input types. - -This is not limited to slice-able or fat pointer types. -In fact, you should always prefer using the __borrowed type__ over -__borrowing the owned type__. -Such as `&str` over `&String`, `&[T]` over `&Vec`, or `&T` over `&Box`. - -Using borrowed types you can avoid layers of indirection for those instances -where the owned type already provides a layer of indirection. For instance, a -`String` has a layer of indirection, so a `&String` will have two layers of -indirection. We can avoid this by using `&str` instead, and letting `&String` -coerce to a `&str` whenever the function is invoked. - -## Example - -For this example, we will illustrate some differences for using `&String` as a -function argument versus using a `&str`, but the ideas apply as well to using -`&Vec` versus using a `&[T]` or using a `&Box` versus a `&T`. - -Consider an example where we wish to determine if a word contains three -consecutive vowels. We don't need to own the string to determine this, so we -will take a reference. - -The code might look something like this: - -```rust -fn three_vowels(word: &String) -> bool { - let mut vowel_count = 0; - for c in word.chars() { - match c { - 'a' | 'e' | 'i' | 'o' | 'u' => { - vowel_count += 1; - if vowel_count >= 3 { - return true - } - } - _ => vowel_count = 0 - } - } - false -} - -fn main() { - let ferris = "Ferris".to_string(); - let curious = "Curious".to_string(); - println!("{}: {}", ferris, three_vowels(&ferris)); - println!("{}: {}", curious, three_vowels(&curious)); - - // This works fine, but the following two lines would fail: - // println!("Ferris: {}", three_vowels("Ferris")); - // println!("Curious: {}", three_vowels("Curious")); - -} -``` - -This works fine because we are passing a `&String` type as a parameter. -If we remove the comments on the last two lines, the example will fail. This -is because a `&str` type will not coerce to a `&String` type. We can fix this -by simply modifying the type for our argument. - -For instance, if we change our function declaration to: - -```rust, ignore -fn three_vowels(word: &str) -> bool { -``` - -then both versions will compile and print the same output. - -```bash -Ferris: false -Curious: true -``` - -But wait, that's not all! There is more to this story. -It's likely that you may say to yourself: that doesn't matter, I will never be -using a `&'static str` as an input anyways (as we did when we used `"Ferris"`). -Even ignoring this special example, you may still find that using `&str` will -give you more flexibility than using a `&String`. - -Let's now take an example where someone gives us a sentence, and we want to -determine if any of the words in the sentence contain three consecutive vowels. -We probably should make use of the function we have already defined and simply -feed in each word from the sentence. - -An example of this could look like this: - -```rust -fn three_vowels(word: &str) -> bool { - let mut vowel_count = 0; - for c in word.chars() { - match c { - 'a' | 'e' | 'i' | 'o' | 'u' => { - vowel_count += 1; - if vowel_count >= 3 { - return true - } - } - _ => vowel_count = 0 - } - } - false -} - -fn main() { - let sentence_string = - "Once upon a time, there was a friendly curious crab named Ferris".to_string(); - for word in sentence_string.split(' ') { - if three_vowels(word) { - println!("{} has three consecutive vowels!", word); - } - } -} -``` - -Running this example using our function declared with an argument type `&str` -will yield - -```bash -curious has three consecutive vowels! -``` - -However, this example will not run when our function is declared with an -argument type `&String`. This is because string slices are a `&str` and not a -`&String` which would require an allocation to be converted to `&String` which -is not implicit, whereas converting from `String` to `&str` is cheap and implicit. - -## See also - -- [Rust Language Reference on Type Coercions](https://doc.rust-lang.org/reference/type-coercions.html) -- For more discussion on how to handle `String` and `&str` see - [this blog series (2015)](https://web.archive.org/web/20201112023149/https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html) - by Herman J. Radtke III diff --git a/idioms/concat-format.md b/idioms/concat-format.md deleted file mode 100644 index 372d86e5..00000000 --- a/idioms/concat-format.md +++ /dev/null @@ -1,33 +0,0 @@ -# Concatenating strings with `format!` - -## Description - -It is possible to build up strings using the `push` and `push_str` methods on a -mutable `String`, or using its `+` operator. However, it is often more -convenient to use `format!`, especially where there is a mix of literal and -non-literal strings. - -## Example - -```rust -fn say_hello(name: &str) -> String { - // We could construct the result string manually. - // let mut result = "Hello ".to_owned(); - // result.push_str(name); - // result.push('!'); - // result - - // But using format! is better. - format!("Hello {}!", name) -} -``` - -## Advantages - -Using `format!` is usually the most succinct and readable way to combine strings. - -## Disadvantages - -It is usually not the most efficient way to combine strings - a series of `push` -operations on a mutable string is usually the most efficient (especially if the -string has been pre-allocated to the expected size). diff --git a/idioms/ctor.md b/idioms/ctor.md deleted file mode 100644 index 6927bf4d..00000000 --- a/idioms/ctor.md +++ /dev/null @@ -1,115 +0,0 @@ -# Constructors - -## Description - -Rust does not have constructors as a language construct. Instead, the -convention is to use an [associated function][] `new` to create an object: - -```rust -/// Time in seconds. -/// -/// # Example -/// -/// ``` -/// let s = Second::new(42); -/// assert_eq!(42, s.value()); -/// ``` -pub struct Second { - value: u64 -} - -impl Second { - // Constructs a new instance of [`Second`]. - // Note this is an associated function - no self. - pub fn new(value: u64) -> Self { - Self { value } - } - - /// Returns the value in seconds. - pub fn value(&self) -> u64 { - self.value - } -} -``` - -## Default Constructors - -Rust supports default constructors with the [`Default`][std-default] trait: - -```rust -/// Time in seconds. -/// -/// # Example -/// -/// ``` -/// let s = Second::default(); -/// assert_eq!(0, s.value()); -/// ``` -pub struct Second { - value: u64 -} - -impl Second { - /// Returns the value in seconds. - pub fn value(&self) -> u64 { - self.value - } -} - -impl Default for Second { - fn default() -> Self { - Self { value: 0 } - } -} -``` - -`Default` can also be derived if all types of all fields implement `Default`, -like they do with `Second`: - -```rust -/// Time in seconds. -/// -/// # Example -/// -/// ``` -/// let s = Second::default(); -/// assert_eq!(0, s.value()); -/// ``` -#[derive(Default)] -pub struct Second { - value: u64 -} - -impl Second { - /// Returns the value in seconds. - pub fn value(&self) -> u64 { - self.value - } -} -``` - -**Note:** It is common and expected for types to implement both -`Default` and an empty `new` constructor. `new` is the constructor -convention in Rust, and users expect it to exist, so if it is -reasonable for the basic constructor to take no arguments, then it -should, even if it is functionally identical to default. - -**Hint:** The advantage of implementing or deriving `Default` is that your type -can now be used where a `Default` implementation is required, most prominently, -any of the [`*or_default` functions in the standard library][std-or-default]. - -## See also - -- The [default idiom](default.md) for a more in-depth description of the - `Default` trait. - -- The [builder pattern](../patterns/creational/builder.md) for constructing - objects where there are multiple configurations. - -- [API Guidelines/C-COMMON-TRAITS][API Guidelines/C-COMMON-TRAITS] for - implementing both, `Default` and `new`. - -[associated function]: https://doc.rust-lang.org/stable/book/ch05-03-method-syntax.html#associated-functions -[std-default]: https://doc.rust-lang.org/stable/std/default/trait.Default.html -[std-or-default]: https://doc.rust-lang.org/stable/std/?search=or_default -[API Guidelines/C-COMMON-TRAITS]: https://rust-lang.github.io/api-guidelines/interoperability.html#types-eagerly-implement-common-traits-c-common-traits diff --git a/idioms/default.md b/idioms/default.md deleted file mode 100644 index 1baf58cd..00000000 --- a/idioms/default.md +++ /dev/null @@ -1,69 +0,0 @@ -# The `Default` Trait - -## Description - -Many types in Rust have a [constructor]. However, this is *specific* to the -type; Rust cannot abstract over "everything that has a `new()` method". To -allow this, the [`Default`] trait was conceived, which can be used with -containers and other generic types (e.g. see [`Option::unwrap_or_default()`]). -Notably, some containers already implement it where applicable. - -Not only do one-element containers like `Cow`, `Box` or `Arc` implement -`Default` for contained `Default` types, one can automatically -`#[derive(Default)]` for structs whose fields all implement it, so the more -types implement `Default`, the more useful it becomes. - -On the other hand, constructors can take multiple arguments, while the -`default()` method does not. There can even be multiple constructors with -different names, but there can only be one `Default` implementation per type. - -## Example - -```rust -use std::{path::PathBuf, time::Duration}; - -// note that we can simply auto-derive Default here. -#[derive(Default, Debug, PartialEq)] -struct MyConfiguration { - // Option defaults to None - output: Option, - // Vecs default to empty vector - search_path: Vec, - // Duration defaults to zero time - timeout: Duration, - // bool defaults to false - check: bool, -} - -impl MyConfiguration { - // add setters here -} - -fn main() { - // construct a new instance with default values - let mut conf = MyConfiguration::default(); - // do something with conf here - conf.check = true; - println!("conf = {:#?}", conf); - - // partial initialization with default values, creates the same instance - let conf1 = MyConfiguration { - check: true, - ..Default::default() - }; - assert_eq!(conf, conf1); -} -``` - -## See also - -- The [constructor] idiom is another way to generate instances that may or may -not be "default" -- The [`Default`] documentation (scroll down for the list of implementors) -- [`Option::unwrap_or_default()`] -- [`derive(new)`] - -[constructor]: ctor.md -[`Default`]: https://doc.rust-lang.org/stable/std/default/trait.Default.html -[`Option::unwrap_or_default()`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.unwrap_or_default -[`derive(new)`]: https://crates.io/crates/derive-new/ diff --git a/idioms/deref.md b/idioms/deref.md deleted file mode 100644 index 0f6d9aeb..00000000 --- a/idioms/deref.md +++ /dev/null @@ -1,79 +0,0 @@ -# Collections are smart pointers - -## Description - -Use the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) -trait to treat collections like smart pointers, offering owning -and borrowed views of data. - -## Example - -```rust,ignore -use std::ops::Deref; - -struct Vec { - data: RawVec, - //.. -} - -impl Deref for Vec { - type Target = [T]; - - fn deref(&self) -> &[T] { - //.. - } -} -``` - -A `Vec` is an owning collection of `T`s, while a slice (`&[T]`) is a borrowed -collection of `T`s. Implementing `Deref` for `Vec` allows implicit dereferencing -from `&Vec` to `&[T]` and includes the relationship in auto-derefencing -searches. Most methods you might expect to be implemented for `Vec`s are instead -implemented for slices. - -Also `String` and `&str` have a similar relation. - -## Motivation - -Ownership and borrowing are key aspects of the Rust language. Data structures -must account for these semantics properly to give a good user -experience. When implementing a data structure that owns its data, offering a -borrowed view of that data allows for more flexible APIs. - -## Advantages - -Most methods can be implemented only for the borrowed view, they are then -implicitly available for the owning view. - -Gives clients a choice between borrowing or taking ownership of data. - -## Disadvantages - -Methods and traits only available via dereferencing are not taken into account -when bounds checking, so generic programming with data structures using this -pattern can get complex (see the `Borrow` and `AsRef` traits, etc.). - -## Discussion - -Smart pointers and collections are analogous: a smart pointer points to a single -object, whereas a collection points to many objects. From the point of view of -the type system, there is little difference between the two. A collection owns -its data if the only way to access each datum is via the collection and the -collection is responsible for deleting the data (even in cases of shared -ownership, some kind of borrowed view may be appropriate). If a collection owns -its data, it is usually useful to provide a view of the data as borrowed so that -it can be referenced multiple times. - -Most smart pointers (e.g., `Foo`) implement `Deref`. However, -collections will usually dereference to a custom type. `[T]` and `str` have some -language support, but in the general case, this is not necessary. `Foo` can -implement `Deref>` where `Bar` is a dynamically sized type and -`&Bar` is a borrowed view of the data in `Foo`. - -Commonly, ordered collections will implement `Index` for `Range`s to provide -slicing syntax. The target will be the borrowed view. - -## See also - -- [Deref polymorphism anti-pattern](../anti_patterns/deref.md). -- [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html). diff --git a/idioms/dtor-finally.md b/idioms/dtor-finally.md deleted file mode 100644 index 45152905..00000000 --- a/idioms/dtor-finally.md +++ /dev/null @@ -1,90 +0,0 @@ -# Finalisation in destructors - -## Description - -Rust does not provide the equivalent to `finally` blocks - code that will be -executed no matter how a function is exited. Instead, an object's destructor can -be used to run code that must be run before exit. - -## Example - -```rust,ignore -fn bar() -> Result<(), ()> { - // These don't need to be defined inside the function. - struct Foo; - - // Implement a destructor for Foo. - impl Drop for Foo { - fn drop(&mut self) { - println!("exit"); - } - } - - // The dtor of _exit will run however the function `bar` is exited. - let _exit = Foo; - // Implicit return with `?` operator. - baz()?; - // Normal return. - Ok(()) -} -``` - -## Motivation - -If a function has multiple return points, then executing code on exit becomes -difficult and repetitive (and thus bug-prone). This is especially the case where -return is implicit due to a macro. A common case is the `?` operator which -returns if the result is an `Err`, but continues if it is `Ok`. `?` is used as -an exception handling mechanism, but unlike Java (which has `finally`), there is -no way to schedule code to run in both the normal and exceptional cases. -Panicking will also exit a function early. - -## Advantages - -Code in destructors will (nearly) always be run - copes with panics, early -returns, etc. - -## Disadvantages - -It is not guaranteed that destructors will run. For example, if there is an -infinite loop in a function or if running a function crashes before exit. -Destructors are also not run in the case of a panic in an already panicking -thread. Therefore, destructors cannot be relied on as finalizers where it is -absolutely essential that finalisation happens. - -This pattern introduces some hard to notice, implicit code. Reading a function -gives no clear indication of destructors to be run on exit. This can make -debugging tricky. - -Requiring an object and `Drop` impl just for finalisation is heavy on boilerplate. - -## Discussion - -There is some subtlety about how exactly to store the object used as a -finalizer. It must be kept alive until the end of the function and must then be -destroyed. The object must always be a value or uniquely owned pointer (e.g., -`Box`). If a shared pointer (such as `Rc`) is used, then the finalizer can -be kept alive beyond the lifetime of the function. For similar reasons, the -finalizer should not be moved or returned. - -The finalizer must be assigned into a variable, otherwise it will be destroyed -immediately, rather than when it goes out of scope. The variable name must start -with `_` if the variable is only used as a finalizer, otherwise the compiler -will warn that the finalizer is never used. However, do not call the variable -`_` with no suffix - in that case it will be destroyed immediately. - -In Rust, destructors are run when an object goes out of scope. This happens -whether we reach the end of block, there is an early return, or the program -panics. When panicking, Rust unwinds the stack running destructors for each -object in each stack frame. So, destructors get called even if the panic happens -in a function being called. - -If a destructor panics while unwinding, there is no good action to take, so Rust -aborts the thread immediately, without running further destructors. This means -that destructors are not absolutely guaranteed to run. It also means that you -must take extra care in your destructors not to panic, since it could leave -resources in an unexpected state. - -## See also - -[RAII guards](../patterns/behavioural/RAII.md). diff --git a/idioms/ffi/accepting-strings.md b/idioms/ffi/accepting-strings.md deleted file mode 100644 index c4b88e60..00000000 --- a/idioms/ffi/accepting-strings.md +++ /dev/null @@ -1,143 +0,0 @@ -# Accepting Strings - -## Description - -When accepting strings via FFI through pointers, there are two principles that -should be followed: - -1. Keep foreign strings "borrowed", rather than copying them directly. -2. Minimize the amount of complexity and `unsafe` code involved in converting - from a C-style string to native Rust strings. - -## Motivation - -The strings used in C have different behaviours to those used in Rust, namely: - -- C strings are null-terminated while Rust strings store their length -- C strings can contain any arbitrary non-zero byte while Rust strings must be - UTF-8 -- C strings are accessed and manipulated using `unsafe` pointer operations - while interactions with Rust strings go through safe methods - -The Rust standard library comes with C equivalents of Rust's `String` and `&str` -called `CString` and `&CStr`, that allow us to avoid a lot of the complexity -and `unsafe` code involved in converting between C strings and Rust strings. - -The `&CStr` type also allows us to work with borrowed data, meaning passing -strings between Rust and C is a zero-cost operation. - -## Code Example - -```rust,ignore -pub mod unsafe_module { - - // other module content - - /// Log a message at the specified level. - /// - /// # Safety - /// - /// It is the caller's guarantee to ensure `msg`: - /// - /// - is not a null pointer - /// - points to valid, initialized data - /// - points to memory ending in a null byte - /// - won't be mutated for the duration of this function call - #[no_mangle] - pub unsafe extern "C" fn mylib_log( - msg: *const libc::c_char, - level: libc::c_int - ) { - let level: crate::LogLevel = match level { /* ... */ }; - - // SAFETY: The caller has already guaranteed this is okay (see the - // `# Safety` section of the doc-comment). - let msg_str: &str = match std::ffi::CStr::from_ptr(msg).to_str() { - Ok(s) => s, - Err(e) => { - crate::log_error("FFI string conversion failed"); - return; - } - }; - - crate::log(msg_str, level); - } -} -``` - -## Advantages - -The example is is written to ensure that: - -1. The `unsafe` block is as small as possible. -2. The pointer with an "untracked" lifetime becomes a "tracked" shared - reference - -Consider an alternative, where the string is actually copied: - -```rust,ignore -pub mod unsafe_module { - - // other module content - - pub extern "C" fn mylib_log(msg: *const libc::c_char, level: libc::c_int) { - // DO NOT USE THIS CODE. - // IT IS UGLY, VERBOSE, AND CONTAINS A SUBTLE BUG. - - let level: crate::LogLevel = match level { /* ... */ }; - - let msg_len = unsafe { /* SAFETY: strlen is what it is, I guess? */ - libc::strlen(msg) - }; - - let mut msg_data = Vec::with_capacity(msg_len + 1); - - let msg_cstr: std::ffi::CString = unsafe { - // SAFETY: copying from a foreign pointer expected to live - // for the entire stack frame into owned memory - std::ptr::copy_nonoverlapping(msg, msg_data.as_mut(), msg_len); - - msg_data.set_len(msg_len + 1); - - std::ffi::CString::from_vec_with_nul(msg_data).unwrap() - } - - let msg_str: String = unsafe { - match msg_cstr.into_string() { - Ok(s) => s, - Err(e) => { - crate::log_error("FFI string conversion failed"); - return; - } - } - }; - - crate::log(&msg_str, level); - } -} -``` - -This code in inferior to the original in two respects: - -1. There is much more `unsafe` code, and more importantly, more invariants it - must uphold. -2. Due to the extensive arithmetic required, there is a bug in this version - that cases Rust `undefined behaviour`. - -The bug here is a simple mistake in pointer arithmetic: the string was copied, -all `msg_len` bytes of it. However, the `NUL` terminator at the end was not. - -The Vector then had its size *set* to the length of the *zero padded string* -- -rather than *resized* to it, which could have added a zero at the end. -As a result, the last byte in the Vector is uninitialized memory. -When the `CString` is created at the bottom of the block, its read of the -Vector will cause `undefined behaviour`! - -Like many such issues, this would be difficult issue to track down. -Sometimes it would panic because the string was not `UTF-8`, sometimes it would -put a weird character at the end of the string, sometimes it would just -completely crash. - -## Disadvantages - -None? diff --git a/idioms/ffi/errors.md b/idioms/ffi/errors.md deleted file mode 100644 index 99fc77f6..00000000 --- a/idioms/ffi/errors.md +++ /dev/null @@ -1,139 +0,0 @@ -# Error Handling in FFI - -## Description - -In foreign languages like C, errors are represented by return codes. -However, Rust's type system allows much more rich error information to be -captured and propogated through a full type. - -This best practice shows different kinds of error codes, and how to expose them -in a usable way: - -1. Flat Enums should be converted to integers and returned as codes. -2. Structured Enums should be converted to an integer code with a string error - message for detail. -3. Custom Error Types should become "transparent", with a C representation. - -## Code Example - -### Flat Enums - -```rust,ignore -enum DatabaseError { - IsReadOnly = 1, // user attempted a write operation - IOError = 2, // user should read the C errno() for what it was - FileCorrupted = 3, // user should run a repair tool to recover it -} - -impl From for libc::c_int { - fn from(e: DatabaseError) -> libc::c_int { - (e as i8).into() - } -} -``` - -### Structured Enums - -```rust,ignore -pub mod errors { - enum DatabaseError { - IsReadOnly, - IOError(std::io::Error), - FileCorrupted(String), // message describing the issue - } - - impl From for libc::c_int { - fn from(e: DatabaseError) -> libc::c_int { - match e { - DatabaseError::IsReadOnly => 1, - DatabaseError::IOError(_) => 2, - DatabaseError::FileCorrupted(_) => 3, - } - } - } -} - -pub mod c_api { - use super::errors::DatabaseError; - - #[no_mangle] - pub extern "C" fn db_error_description( - e: *const DatabaseError - ) -> *mut libc::c_char { - - let error: &DatabaseError = unsafe { - // SAFETY: pointer lifetime is greater than the current stack frame - &*e - }; - - let error_str: String = match error { - DatabaseError::IsReadOnly => { - format!("cannot write to read-only database"); - } - DatabaseError::IOError(e) => { - format!("I/O Error: {}", e); - } - DatabaseError::FileCorrupted(s) => { - format!("File corrupted, run repair: {}", &s); - } - }; - - let c_error = unsafe { - // SAFETY: copying error_str to an allocated buffer with a NUL - // character at the end - let mut malloc: *mut u8 = libc::malloc(error_str.len() + 1) as *mut _; - - if malloc.is_null() { - return std::ptr::null_mut(); - } - - let src = error_str.as_bytes().as_ptr(); - - std::ptr::copy_nonoverlapping(src, malloc, error_str.len()); - - std::ptr::write(malloc.add(error_str.len()), 0); - - malloc as *mut libc::c_char - }; - - c_error - } -} -``` - -### Custom Error Types - -```rust,ignore -struct ParseError { - expected: char, - line: u32, - ch: u16 -} - -impl ParseError { /* ... */ } - -/* Create a second version which is exposed as a C structure */ -#[repr(C)] -pub struct parse_error { - pub expected: libc::c_char, - pub line: u32, - pub ch: u16 -} - -impl From for parse_error { - fn from(e: ParseError) -> parse_error { - let ParseError { expected, line, ch } = e; - parse_error { expected, line, ch } - } -} -``` - -## Advantages - -This ensures that the foreign language has clear access to error information -while not compromising the Rust code's API at all. - -## Disadvantages - -It's a lot of typing, and some types may not be able to be converted easily -to C. diff --git a/idioms/ffi/intro.md b/idioms/ffi/intro.md deleted file mode 100644 index d1f14971..00000000 --- a/idioms/ffi/intro.md +++ /dev/null @@ -1,14 +0,0 @@ -# FFI Idioms - -Writing FFI code is an entire course in itself. -However, there are several idioms here that can act as pointers, and avoid -traps for inexperienced users of `unsafe` Rust. - -This section contains idioms that may be useful when doing FFI. - -1. [Idiomatic Errors](./errors.md) - Error handling with integer codes and - sentinel return values (such as `NULL` pointers) - -2. [Accepting Strings](./accepting-strings.md) with minimal unsafe code - -3. [Passing Strings](./passing-strings.md) to FFI functions diff --git a/idioms/ffi/passing-strings.md b/idioms/ffi/passing-strings.md deleted file mode 100644 index 18de5c83..00000000 --- a/idioms/ffi/passing-strings.md +++ /dev/null @@ -1,105 +0,0 @@ -# Passing Strings - -## Description - -When passing strings to FFI functions, there are four principles that should be -followed: - -1. Make the lifetime of owned strings as long as possible. -2. Minimize `unsafe` code during the conversion. -3. If the C code can modify the string data, use `Vec` instead of `CString`. -4. Unless the Foreign Function API requires it, the ownership of the string - should not transfer to the callee. - -## Motivation - -Rust has built-in support for C-style strings with its `CString` and `CStr` -types. However, there are different approaches one can take with strings that -are being sent to a foreign function call from a Rust function. - -The best practice is simple: use `CString` in such a way as to minimize -`unsafe` code. However, a secondary caveat is that -*the object must live long enough*, meaning the lifetime should be maximized. -In addition, the documentation explains that "round-tripping" a `CString` after -modification is UB, so additional work is necessary in that case. - -## Code Example - -```rust,ignore -pub mod unsafe_module { - - // other module content - - extern "C" { - fn seterr(message: *const libc::c_char); - fn geterr(buffer: *mut libc::c_char, size: libc::c_int) -> libc::c_int; - } - - fn report_error_to_ffi>( - err: S - ) -> Result<(), std::ffi::NulError>{ - let c_err = std::ffi::CString::new(err.into())?; - - unsafe { - // SAFETY: calling an FFI whose documentation says the pointer is - // const, so no modification should occur - seterr(c_err.as_ptr()); - } - - Ok(()) - // The lifetime of c_err continues until here - } - - fn get_error_from_ffi() -> Result { - let mut buffer = vec![0u8; 1024]; - unsafe { - // SAFETY: calling an FFI whose documentation implies - // that the input need only live as long as the call - let written: usize = geterr(buffer.as_mut_ptr(), 1023).into(); - - buffer.truncate(written + 1); - } - - std::ffi::CString::new(buffer).unwrap().into_string() - } -} -``` - -## Advantages - -The example is written in a way to ensure that: - -1. The `unsafe` block is as small as possible. -2. The `CString` lives long enough. -3. Errors with typecasts are always propagated when possible. - -A common mistake (so common it's in the documentation) is to not use the -variable in the first block: - -```rust,ignore -pub mod unsafe_module { - - // other module content - - fn report_error>(err: S) -> Result<(), std::ffi::NulError> { - unsafe { - // SAFETY: whoops, this contains a dangling pointer! - seterr(std::ffi::CString::new(err.into())?.as_ptr()); - } - Ok(()) - } -} -``` - -This code will result in a dangling pointer, because the lifetime of the -`CString` is not extended by the pointer creation, unlike if a reference were -created. - -Another issue frequently raised is that the initialization of a 1k vector of -zeroes is "slow". However, recent versions of Rust actually optimize that -particular macro to a call to `zmalloc`, meaning it is as fast as the operating -system's ability to return zeroed memory (which is quite fast). - -## Disadvantages - -None? diff --git a/idioms/index.md b/idioms/index.md deleted file mode 100644 index 22ebed6a..00000000 --- a/idioms/index.md +++ /dev/null @@ -1,18 +0,0 @@ -# Idioms - -[Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly used -styles, guidelines and patterns largely agreed upon by a community. -Writing idiomatic code allows other developers to understand better what is -happening. - -After all, the computer only cares about the machine code that is generated -by the compiler. -Instead, the source code is mainly beneficial to the developer. -So, since we have this abstraction layer, why not make it more readable? - -Remember the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle): -"Keep It Simple, Stupid". It claims that "most systems work best if they are -kept simple rather than made complicated; therefore, simplicity should be a key -goal in design, and unnecessary complexity should be avoided". - -> Code is there for humans, not computers, to understand. diff --git a/idioms/mem-replace.md b/idioms/mem-replace.md deleted file mode 100644 index b1ea07f1..00000000 --- a/idioms/mem-replace.md +++ /dev/null @@ -1,113 +0,0 @@ -# `mem::{take(_), replace(_)}` to keep owned values in changed enums - -## Description - -Say we have a `&mut MyEnum` which has (at least) two variants, -`A { name: String, x: u8 }` and `B { name: String }`. Now we want to change -`MyEnum::A` to a `B` if `x` is zero, while keeping `MyEnum::B` intact. - -We can do this without cloning the `name`. - -## Example - -```rust -use std::mem; - -enum MyEnum { - A { name: String, x: u8 }, - B { name: String } -} - -fn a_to_b(e: &mut MyEnum) { - if let MyEnum::A { name, x: 0 } = e { - // this takes out our `name` and put in an empty String instead - // (note that empty strings don't allocate). - // Then, construct the new enum variant (which will - // be assigned to `*e`). - *e = MyEnum::B { name: mem::take(name) } - } -} -``` - -This also works with more variants: - -```rust -use std::mem; - -enum MultiVariateEnum { - A { name: String }, - B { name: String }, - C, - D -} - -fn swizzle(e: &mut MultiVariateEnum) { - use MultiVariateEnum::*; - *e = match e { - // Ownership rules do not allow taking `name` by value, but we cannot - // take the value out of a mutable reference, unless we replace it: - A { name } => B { name: mem::take(name) }, - B { name } => A { name: mem::take(name) }, - C => D, - D => C - } -} -``` - -## Motivation - -When working with enums, we may want to change an enum value in place, perhaps -to another variant. This is usually done in two phases to keep the borrow -checker happy. In the first phase, we observe the existing value and look at -its parts to decide what to do next. In the second phase we may conditionally -change the value (as in the example above). - -The borrow checker won't allow us to take out `name` of the enum (because -*something* must be there.) We could of course `.clone()` name and put the clone -into our `MyEnum::B`, but that would be an instance of the [Clone to satisfy -the borrow checker](../anti_patterns/borrow_clone.md) anti-pattern. Anyway, we -can avoid the extra allocation by changing `e` with only a mutable borrow. - -`mem::take` lets us swap out the value, replacing it with it's default value, -and returning the previous value. For `String`, the default value is an empty -`String`, which does not need to allocate. As a result, we get the original -`name` *as an owned value*. We can then wrap this in another enum. - -__NOTE:__ `mem::replace` is very similar, but allows us to specify what to -replace the value with. An equivalent to our `mem::take` line would be -`mem::replace(name, String::new())`. - -Note, however, that if we are using an `Option` and want to replace its -value with a `None`, `Option`’s `take()` method provides a shorter and -more idiomatic alternative. - -## Advantages - -Look ma, no allocation! Also you may feel like Indiana Jones while doing it. - -## Disadvantages - -This gets a bit wordy. Getting it wrong repeatedly will make you hate the -borrow checker. The compiler may fail to optimize away the double store, -resulting in reduced performance as opposed to what you'd do in unsafe -languages. - -Furthermore, the type you are taking needs to implement the [`Default` -trait](./default.md). However, if the type you're working with doesn't -implement this, you can instead use `mem::replace`. - -## Discussion - -This pattern is only of interest in Rust. In GC'd languages, you'd take the -reference to the value by default (and the GC would keep track of refs), and in -other low-level languages like C you'd simply alias the pointer and fix things -later. - -However, in Rust, we have to do a little more work to do this. An owned value -may only have one owner, so to take it out, we need to put something back in – -like Indiana Jones, replacing the artifact with a bag of sand. - -## See also - -This gets rid of the [Clone to satisfy the borrow checker](../anti_patterns/borrow_clone.md) -anti-pattern in a specific case. diff --git a/idioms/on-stack-dyn-dispatch.md b/idioms/on-stack-dyn-dispatch.md deleted file mode 100644 index d45f3ca9..00000000 --- a/idioms/on-stack-dyn-dispatch.md +++ /dev/null @@ -1,93 +0,0 @@ -# On-Stack Dynamic Dispatch - -## Description - -We can dynamically dispatch over multiple values, however, to do so, we need -to declare multiple variables to bind differently-typed objects. To extend the -lifetime as necessary, we can use deferred conditional initialization, as seen -below: - -## Example - -```rust -use std::io; -use std::fs; - -# fn main() -> Result<(), Box> { -# let arg = "-"; - -// These must live longer than `readable`, and thus are declared first: -let (mut stdin_read, mut file_read); - -// We need to ascribe the type to get dynamic dispatch. -let readable: &mut dyn io::Read = if arg == "-" { - stdin_read = io::stdin(); - &mut stdin_read -} else { - file_read = fs::File::open(arg)?; - &mut file_read -}; - -// Read from `readable` here. - -# Ok(()) -# } -``` - -## Motivation - -Rust monomorphises code by default. This means a copy of the code will be -generated for each type it is used with and optimized independently. While this -allows for very fast code on the hot path, it also bloats the code in places -where performance is not of the essence, thus costing compile time and cache -usage. - -Luckily, Rust allows us to use dynamic dispatch, but we have to explicitly ask -for it. - -## Advantages - -We do not need to allocate anything on the heap. Neither do we need to -initialize something we won't use later, nor do we need to monomorphize the -whole code that follows to work with both `File` or `Stdin`. - -## Disadvantages - -The code needs more moving parts than the `Box`-based version: - -```rust,ignore -// We still need to ascribe the type for dynamic dispatch. -let readable: Box = if arg == "-" { - Box::new(io::stdin()) -} else { - Box::new(fs::File::open(arg)?) -}; -// Read from `readable` here. -``` - -## Discussion - -Rust newcomers will usually learn that Rust requires all variables to be -initialized *before use*, so it's easy to overlook the fact that *unused* -variables may well be uninitialized. Rust works quite hard to ensure that this -works out fine and only the initialized values are dropped at the end of their -scope. - -The example meets all the constraints Rust places on us: - -* All variables are initialized before using (in this case borrowing) them -* Each variable only holds values of a single type. In our example, `stdin` is -of type `Stdin`, `file` is of type `File` and `readable` is of type `&mut dyn -Read` -* Each borrowed value outlives all the references borrowed from it - -## See also - -* [Finalisation in destructors](dtor-finally.md) and -[RAII guards](../patterns/behavioural/RAII.md) can benefit from tight control over -lifetimes. -* For conditionally filled `Option<&T>`s of (mutable) references, one can -initialize an `Option` directly and use its [`.as_ref()`] method to get an -optional reference. - -[`.as_ref()`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.as_ref diff --git a/idioms/option-iter.md b/idioms/option-iter.md deleted file mode 100644 index a3e8adda..00000000 --- a/idioms/option-iter.md +++ /dev/null @@ -1,59 +0,0 @@ -# Iterating over an `Option` - -## Description - -`Option` can be viewed as a container that contains either zero or one -element. In particular, it implements the `IntoIterator` trait, and as such -can be used with generic code that needs such a type. - -## Examples - -Since `Option` implements `IntoIterator`, it can be used as an argument to -[`.extend()`](https://doc.rust-lang.org/std/iter/trait.Extend.html#tymethod.extend): - -```rust -let turing = Some("Turing"); -let mut logicians = vec!["Curry", "Kleene", "Markov"]; - -logicians.extend(turing); - -// equivalent to -if let Some(turing_inner) = turing { - logicians.push(turing_inner); -} -``` - -If you need to tack an `Option` to the end of an existing iterator, you can -pass it to [`.chain()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain): - -```rust -let turing = Some("Turing"); -let logicians = vec!["Curry", "Kleene", "Markov"]; - -for logician in logicians.iter().chain(turing.iter()) { - println!("{} is a logician", logician); -} -``` - -Note that if the `Option` is always `Some`, then it is more idiomatic to use -[`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) on the -element instead. - -Also, since `Option` implements `IntoIterator`, it's possible to iterate over -it using a `for` loop. This is equivalent to matching it with `if let Some(..)`, -and in most cases you should prefer the latter. - -## See also - -* [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is an -iterator which yields exactly one element. It's a more readable alternative to -`Some(foo).into_iter()`. - -* [`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) - is a version of [`Iterator::map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map), - specialized to mapping functions which return `Option`. - -* The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions - for converting an `Option` to a zero- or one-element slice. - -* [Documentation for `Option`](https://doc.rust-lang.org/std/option/enum.Option.html) diff --git a/idioms/pass-var-to-closure.md b/idioms/pass-var-to-closure.md deleted file mode 100644 index b6a351dd..00000000 --- a/idioms/pass-var-to-closure.md +++ /dev/null @@ -1,59 +0,0 @@ -# Pass variables to closure - -## Description - -By default, closures capture their environment by borrowing. Or you can use -`move`-closure to move whole environment. However, often you want to move just -some variables to closure, give it copy of some data, pass it by reference, or -perform some other transformation. - -Use variable rebinding in separate scope for that. - -## Example - -Use - -```rust -use std::rc::Rc; - -let num1 = Rc::new(1); -let num2 = Rc::new(2); -let num3 = Rc::new(3); -let closure = { - // `num1` is moved - let num2 = num2.clone(); // `num2` is cloned - let num3 = num3.as_ref(); // `num3` is borrowed - move || { - *num1 + *num2 + *num3; - } -}; -``` - -instead of - -```rust -use std::rc::Rc; - -let num1 = Rc::new(1); -let num2 = Rc::new(2); -let num3 = Rc::new(3); - -let num2_cloned = num2.clone(); -let num3_borrowed = num3.as_ref(); -let closure = move || { - *num1 + *num2_cloned + *num3_borrowed; -}; -``` - -## Advantages - -Copied data are grouped together with closure definition, so their purpose is -more clear, and they will be dropped immediately even if they are not consumed -by closure. - -Closure uses same variable names as surrounding code whether data are copied or -moved. - -## Disadvantages - -Additional indentation of closure body. diff --git a/idioms/priv-extend.md b/idioms/priv-extend.md deleted file mode 100644 index 2a14fdb4..00000000 --- a/idioms/priv-extend.md +++ /dev/null @@ -1,122 +0,0 @@ -# `#[non_exhaustive]` and private fields for extensibility - -## Description - -A small set of scenarios exist where a library author may want to add public -fields to a public struct or new variants to an enum without breaking backwards -compatibility. - -Rust offers two solutions to this problem: - -- Use `#[non_exhaustive]` on `struct`s, `enum`s, and `enum` variants. - For extensive documentation on all the places where `#[non_exhaustive]` can be - used, see [the docs](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute). - -- You may add a private field to a struct to prevent it from being directly - instantiated or matched against (see Alternative) - -## Example - -```rust -mod a { - // Public struct. - #[non_exhaustive] - pub struct S { - pub foo: i32, - } - - #[non_exhaustive] - pub enum AdmitMoreVariants { - VariantA, - VariantB, - #[non_exhaustive] - VariantC { a: String } - } -} - -fn print_matched_variants(s: a::S) { - // Because S is `#[non_exhaustive]`, it cannot be named here and - // we must use `..` in the pattern. - let a::S { foo: _, ..} = s; - - let some_enum = a::AdmitMoreVariants::VariantA; - match some_enum { - a::AdmitMoreVariants::VariantA => println!("it's an A"), - a::AdmitMoreVariants::VariantB => println!("it's a b"), - - // .. required because this variant is non-exhaustive as well - a::AdmitMoreVariants::VariantC { a, .. } => println!("it's a c"), - - // The wildcard match is required because more variants may be - // added in the future - _ => println!("it's a new variant") - } -} -``` - -## Alternative: `Private fields` for structs - -`#[non_exhaustive]` only works across crate boundaries. -Within a crate, the private field method may be used. - -Adding a field to a struct is a mostly backwards compatible change. -However, if a client uses a pattern to deconstruct a struct instance, they -might name all the fields in the struct and adding a new one would break that -pattern. -The client could name some fields and use `..` in the pattern, in which case adding -another field is backwards compatible. -Making at least one of the struct's fields private forces clients to use the latter -form of patterns, ensuring that the struct is future-proof. - -The downside of this approach is that you might need to add an otherwise unneeded -field to the struct. -You can use the `()` type so that there is no runtime overhead and prepend `_` to -the field name to avoid the unused field warning. - -```rust -pub struct S { - pub a: i32, - // Because `b` is private, you cannot match on `S` without using `..` and `S` - // cannot be directly instantiated or matched against - _b: () -} -``` - -## Discussion - -On `struct`s, `#[non_exhaustive]` allows adding additional fields in a backwards -compatible way. -It will also prevent clients from using the struct constructor, even if all the -fields are public. -This may be helpful, but it's worth considering if you _want_ an additional field -to be found by clients as a compiler error rather than something that may be silently -undiscovered. - -`#[non_exhaustive]` can be applied to enum variants as well. -A `#[non_exhaustive]` variant behaves in the same way as a `#[non_exhaustive]` struct. - -Use this deliberately and with caution: incrementing the major version when adding -fields or variants is often a better option. -`#[non_exhaustive]` may be appropriate in scenarios where you're modeling an external -resource that may change out-of-sync with your library, but is not a general purpose -tool. - -### Disadvantages - -`#[non_exhaustive]` can make your code much less ergonomic to use, especially when -forced to handle unknown enum variants. -It should only be used when these sorts of evolutions are required **without** -incrementing the major version. - -When `#[non_exhaustive]` is applied to `enum`s, it forces clients to handle a -wildcard variant. -If there is no sensible action to take in this case, this may lead to awkward -code and code paths that are only executed in extremely rare circumstances. -If a client decides to `panic!()` in this scenario, it may have been better to -expose this error at compile time. -In fact, `#[non_exhaustive]` forces clients to handle the "Something else" case; -there is rarely a sensible action to take in this scenario. - -## See also - -- [RFC introducing #[non_exhaustive] attribute for enums and structs](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md) diff --git a/idioms/return-consumed-arg-on-error.md b/idioms/return-consumed-arg-on-error.md deleted file mode 100644 index 6a0c471f..00000000 --- a/idioms/return-consumed-arg-on-error.md +++ /dev/null @@ -1,61 +0,0 @@ -# Return consumed argument on error - -## Description - -If a fallible function consumes (moves) an argument, return that argument back inside -an error. - -## Example - -```rust -pub fn send(value: String) -> Result<(), SendError> { - println!("using {value} in a meaningful way"); - // Simulate non-deterministic fallible action. - use std::time::SystemTime; - let period = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); - if period.subsec_nanos() % 2 == 1 { - Ok(()) - } else { - Err(SendError(value)) - } -} - -pub struct SendError(String); - -fn main() { - let mut value = "imagine this is very long string".to_string(); - - let success = 's: { - // Try to send value two times. - for _ in 0..2 { - value = match send(value) { - Ok(()) => break 's true, - Err(SendError(value)) => value, - } - } - false - }; - - println!("success: {}", success); -} -``` - -## Motivation - -In case of error you may want to try some alternative way or to -retry action in case of non-deterministic function. But if the argument -is always consumed, you are forced to clone it on every call, which -is not very efficient. - -The standard library uses this approach in e.g. `String::from_utf8` method. -When given a vector that doesn't contain valid UTF-8, a `FromUtf8Error` -is returned. -You can get original vector back using `FromUtf8Error::into_bytes` method. - -## Advantages - -Better performance because of moving arguments whenever possible. - -## Disadvantages - -Slightly more complex error types. diff --git a/idioms/rustdoc-init.md b/idioms/rustdoc-init.md deleted file mode 100644 index 64c1e5d4..00000000 --- a/idioms/rustdoc-init.md +++ /dev/null @@ -1,94 +0,0 @@ -# Easy doc initialization - -## Description - -If a struct takes significant effort to initialize when writing docs, it can be -quicker to wrap your example with a helper function which takes the struct as an -argument. - -## Motivation - -Sometimes there is a struct with multiple or complicated parameters and several -methods. Each of these methods should have examples. - -For example: - -```rust,ignore -struct Connection { - name: String, - stream: TcpStream, -} - -impl Connection { - /// Sends a request over the connection. - /// - /// # Example - /// ```no_run - /// # // Boilerplate are required to get an example working. - /// # let stream = TcpStream::connect("127.0.0.1:34254"); - /// # let connection = Connection { name: "foo".to_owned(), stream }; - /// # let request = Request::new("RequestId", RequestType::Get, "payload"); - /// let response = connection.send_request(request); - /// assert!(response.is_ok()); - /// ``` - fn send_request(&self, request: Request) -> Result { - // ... - } - - /// Oh no, all that boilerplate needs to be repeated here! - fn check_status(&self) -> Status { - // ... - } -} -``` - -## Example - -Instead of typing all of this boilerplate to create a `Connection` and -`Request`, it is easier to just create a wrapping helper function which takes -them as arguments: - -```rust,ignore -struct Connection { - name: String, - stream: TcpStream, -} - -impl Connection { - /// Sends a request over the connection. - /// - /// # Example - /// ``` - /// # fn call_send(connection: Connection, request: Request) { - /// let response = connection.send_request(request); - /// assert!(response.is_ok()); - /// # } - /// ``` - fn send_request(&self, request: Request) { - // ... - } -} -``` - -**Note** in the above example the line `assert!(response.is_ok());` will not -actually run while testing because it is inside a function which is never -invoked. - -## Advantages - -This is much more concise and avoids repetitive code in examples. - -## Disadvantages - -As example is in a function, the code will not be tested. Though it will still be -checked to make sure it compiles when running a `cargo test`. So this pattern is -most useful when you need `no_run`. With this, you do not need to add `no_run`. - -## Discussion - -If assertions are not required this pattern works well. - -If they are, an alternative can be to create a public method to create a helper -instance which is annotated with `#[doc(hidden)]` (so that users won't see it). -Then this method can be called inside of rustdoc because it is part of the -crate's public API. diff --git a/idioms/temporary-mutability.md b/idioms/temporary-mutability.md deleted file mode 100644 index e630e102..00000000 --- a/idioms/temporary-mutability.md +++ /dev/null @@ -1,45 +0,0 @@ -# Temporary mutability - -## Description - -Often it is necessary to prepare and process some data, but after that data are -only inspected and never modified. The intention can be made explicit by redefining -the mutable variable as immutable. - -It can be done either by processing data within a nested block or by redefining -the variable. - -## Example - -Say, vector must be sorted before usage. - -Using nested block: - -```rust,ignore -let data = { - let mut data = get_vec(); - data.sort(); - data -}; - -// Here `data` is immutable. -``` - -Using variable rebinding: - -```rust,ignore -let mut data = get_vec(); -data.sort(); -let data = data; - -// Here `data` is immutable. -``` - -## Advantages - -Compiler ensures that you don't accidentally mutate data after some point. - -## Disadvantages - -Nested block requires additional indentation of block body. -One more line to return data from block or redefine variable. diff --git a/intro.md b/intro.md deleted file mode 100644 index 71c8f88b..00000000 --- a/intro.md +++ /dev/null @@ -1,40 +0,0 @@ -# Introduction - -## Participation - -If you are interested in contributing to this book, check out the -[contribution guidelines](https://github.com/rust-unofficial/patterns/blob/master/CONTRIBUTING.md). - -## Design patterns - -In software development, we often come across problems that share -similarities regardless of the environment they appear in. Although the -implementation details are crucial to solve the task at hand, we may -abstract from these particularities to find the common practices that -are generically applicable. - -Design patterns are a collection of reusable and tested solutions to -recurring problems in engineering. They make our software more modular, -maintainable, and extensible. Moreover, these patterns provide a common -language for developers, making them an excellent tool for effective -communication when problem-solving in teams. - -## Design patterns in Rust - -Rust is not object-oriented, and the combination of all its characteristics, -such as functional elements, a strong type system, and the borrow checker, -makes it unique. -Because of this, Rust design patterns vary with respect to other -traditional object-oriented programming languages. -That's why we decided to write this book. We hope you enjoy reading it! -The book is divided in three main chapters: - -- [Idioms](./idioms/index.md): guidelines to follow when coding. - They are the social norms of the community. - You should break them only if you have a good reason for it. -- [Design patterns](./patterns/index.md): methods to solve common problems - when coding. -- [Anti-patterns](./anti_patterns/index.md): methods to solve common problems - when coding. - However, while design patterns give us benefits, - anti-patterns create more problems. diff --git a/patterns/behavioural/RAII.md b/patterns/behavioural/RAII.md deleted file mode 100644 index 4b3f4409..00000000 --- a/patterns/behavioural/RAII.md +++ /dev/null @@ -1,121 +0,0 @@ -# RAII with guards - -## Description - -[RAII][wikipedia] stands for "Resource Acquisition is Initialisation" which is a -terrible name. The essence of the pattern is that resource initialisation is done -in the constructor of an object and finalisation in the destructor. This pattern -is extended in Rust by using a RAII object as a guard of some resource and relying -on the type system to ensure that access is always mediated by the guard object. - -## Example - -Mutex guards are the classic example of this pattern from the std library (this -is a simplified version of the real implementation): - -```rust,ignore -use std::ops::Deref; - -struct Foo {} - -struct Mutex { - // We keep a reference to our data: T here. - //.. -} - -struct MutexGuard<'a, T: 'a> { - data: &'a T, - //.. -} - -// Locking the mutex is explicit. -impl Mutex { - fn lock(&self) -> MutexGuard { - // Lock the underlying OS mutex. - //.. - - // MutexGuard keeps a reference to self - MutexGuard { - data: self, - //.. - } - } -} - -// Destructor for unlocking the mutex. -impl<'a, T> Drop for MutexGuard<'a, T> { - fn drop(&mut self) { - // Unlock the underlying OS mutex. - //.. - } -} - -// Implementing Deref means we can treat MutexGuard like a pointer to T. -impl<'a, T> Deref for MutexGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - self.data - } -} - -fn baz(x: Mutex) { - let xx = x.lock(); - xx.foo(); // foo is a method on Foo. - // The borrow checker ensures we can't store a reference to the underlying - // Foo which will outlive the guard xx. - - // x is unlocked when we exit this function and xx's destructor is executed. -} -``` - -## Motivation - -Where a resource must be finalised after use, RAII can be used to do this -finalisation. If it is an error to access that resource after finalisation, then -this pattern can be used to prevent such errors. - -## Advantages - -Prevents errors where a resource is not finalised and where a resource is used -after finalisation. - -## Discussion - -RAII is a useful pattern for ensuring resources are properly deallocated or -finalised. We can make use of the borrow checker in Rust to statically prevent -errors stemming from using resources after finalisation takes place. - -The core aim of the borrow checker is to ensure that references to data do not -outlive that data. The RAII guard pattern works because the guard object -contains a reference to the underlying resource and only exposes such -references. Rust ensures that the guard cannot outlive the underlying resource -and that references to the resource mediated by the guard cannot outlive the -guard. To see how this works it is helpful to examine the signature of `deref` -without lifetime elision: - -```rust,ignore -fn deref<'a>(&'a self) -> &'a T { - //.. -} -``` - -The returned reference to the resource has the same lifetime as `self` (`'a`). -The borrow checker therefore ensures that the lifetime of the reference to `T` -is shorter than the lifetime of `self`. - -Note that implementing `Deref` is not a core part of this pattern, it only makes -using the guard object more ergonomic. Implementing a `get` method on the guard -works just as well. - -## See also - -[Finalisation in destructors idiom](../../idioms/dtor-finally.md) - -RAII is a common pattern in C++: [cppreference.com](http://en.cppreference.com/w/cpp/language/raii), -[wikipedia][wikipedia]. - -[wikipedia]: https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization - -[Style guide entry](https://doc.rust-lang.org/1.0.0/style/ownership/raii.html) -(currently just a placeholder). diff --git a/patterns/behavioural/command.md b/patterns/behavioural/command.md deleted file mode 100644 index 1e0ae263..00000000 --- a/patterns/behavioural/command.md +++ /dev/null @@ -1,222 +0,0 @@ -# Command - -## Description - -The basic idea of the Command pattern is to separate out actions into its own -objects and pass them as parameters. - -## Motivation - -Suppose we have a sequence of actions or transactions encapsulated as objects. -We want these actions or commands to be executed or invoked in some order later -at different time. These commands may also be triggered as a result of some event. -For example, when a user pushes a button, or on arrival of a data packet. -In addition, these commands might be undoable. This may come in useful for -operations of an editor. We might want to store logs of executed commands so that -we could reapply the changes later if the system crashes. - -## Example - -Define two database operations `create table` and `add field`. Each of these -operations is a command which knows how to undo the command, e.g., `drop table` -and `remove field`. When a user invokes a database migration operation then each -command is executed in the defined order, and when the user invokes the rollback -operation then the whole set of commands is invoked in reverse order. - -## Approach: Using trait objects - -We define a common trait which encapsulates our command with two operations -`execute` and `rollback`. All command `structs` must implement this trait. - -```rust -pub trait Migration { - fn execute(&self) -> &str; - fn rollback(&self) -> &str; -} - -pub struct CreateTable; -impl Migration for CreateTable { - fn execute(&self) -> &str { - "create table" - } - fn rollback(&self) -> &str { - "drop table" - } -} - -pub struct AddField; -impl Migration for AddField { - fn execute(&self) -> &str { - "add field" - } - fn rollback(&self) -> &str { - "remove field" - } -} - -struct Schema { - commands: Vec>, -} - -impl Schema { - fn new() -> Self { - Self { commands: vec![] } - } - - fn add_migration(&mut self, cmd: Box) { - self.commands.push(cmd); - } - - fn execute(&self) -> Vec<&str> { - self.commands.iter().map(|cmd| cmd.execute()).collect() - } - fn rollback(&self) -> Vec<&str> { - self.commands - .iter() - .rev() // reverse iterator's direction - .map(|cmd| cmd.rollback()) - .collect() - } -} - -fn main() { - let mut schema = Schema::new(); - - let cmd = Box::new(CreateTable); - schema.add_migration(cmd); - let cmd = Box::new(AddField); - schema.add_migration(cmd); - - assert_eq!(vec!["create table", "add field"], schema.execute()); - assert_eq!(vec!["remove field", "drop table"], schema.rollback()); -} -``` - -## Approach: Using function pointers - -We could follow another approach by creating each individual command as -a different function and store function pointers to invoke these functions later -at a different time. Since function pointers implement all three traits `Fn`, -`FnMut`, and `FnOnce` we could as well pass and store closures instead of -function pointers. - -```rust -type FnPtr = fn() -> String; -struct Command { - execute: FnPtr, - rollback: FnPtr, -} - -struct Schema { - commands: Vec, -} - -impl Schema { - fn new() -> Self { - Self { commands: vec![] } - } - fn add_migration(&mut self, execute: FnPtr, rollback: FnPtr) { - self.commands.push(Command { execute, rollback }); - } - fn execute(&self) -> Vec { - self.commands.iter().map(|cmd| (cmd.execute)()).collect() - } - fn rollback(&self) -> Vec { - self.commands - .iter() - .rev() - .map(|cmd| (cmd.rollback)()) - .collect() - } -} - -fn add_field() -> String { - "add field".to_string() -} - -fn remove_field() -> String { - "remove field".to_string() -} - -fn main() { - let mut schema = Schema::new(); - schema.add_migration(|| "create table".to_string(), || "drop table".to_string()); - schema.add_migration(add_field, remove_field); - assert_eq!(vec!["create table", "add field"], schema.execute()); - assert_eq!(vec!["remove field", "drop table"], schema.rollback()); -} -``` - -## Approach: Using `Fn` trait objects - -Finally, instead of defining a common command trait we could store -each command implementing the `Fn` trait separately in vectors. - -```rust -type Migration<'a> = Box &'a str>; - -struct Schema<'a> { - executes: Vec>, - rollbacks: Vec>, -} - -impl<'a> Schema<'a> { - fn new() -> Self { - Self { - executes: vec![], - rollbacks: vec![], - } - } - fn add_migration(&mut self, execute: E, rollback: R) - where - E: Fn() -> &'a str + 'static, - R: Fn() -> &'a str + 'static, - { - self.executes.push(Box::new(execute)); - self.rollbacks.push(Box::new(rollback)); - } - fn execute(&self) -> Vec<&str> { - self.executes.iter().map(|cmd| cmd()).collect() - } - fn rollback(&self) -> Vec<&str> { - self.rollbacks.iter().rev().map(|cmd| cmd()).collect() - } -} - -fn add_field() -> &'static str { - "add field" -} - -fn remove_field() -> &'static str { - "remove field" -} - -fn main() { - let mut schema = Schema::new(); - schema.add_migration(|| "create table", || "drop table"); - schema.add_migration(add_field, remove_field); - assert_eq!(vec!["create table", "add field"], schema.execute()); - assert_eq!(vec!["remove field", "drop table"], schema.rollback()); -} -``` - -## Discussion - -If our commands are small and may be defined as functions or passed as a closure -then using function pointers might be preferable since it does not exploit -dynamic dispatch. But if our command is a whole struct with a bunch of functions -and variables defined as seperated module then using trait objects would be -more suitable. A case of application can be found in [`actix`](https://actix.rs/), -which uses trait objects when it registers a handler function for routes. -In case of using `Fn` trait objects we can create and use commands in the same -way as we used in case of function pointers. - -As performance, there is always a trade-off between performance and code -simplicity and organisation. Static dispatch gives faster performance, while -dynamic dispatch provides flexibility when we structure our application. - -## See also - -- [Command pattern](https://en.wikipedia.org/wiki/Command_pattern) - -- [Another example for the `command` pattern](https://web.archive.org/web/20210223131236/https://chercher.tech/rust/command-design-pattern-rust) diff --git a/patterns/behavioural/interpreter.md b/patterns/behavioural/interpreter.md deleted file mode 100644 index 21cf4457..00000000 --- a/patterns/behavioural/interpreter.md +++ /dev/null @@ -1,147 +0,0 @@ -# Interpreter - -## Description - -If a problem occurs very often and requires long and repetitive steps to solve -it, then the problem instances might be expressed in a simple language and an -interpreter object could solve it by interpreting the sentences written in this -simple language. - -Basically, for any kind of problems we define: - -- A [domain specific language](https://en.wikipedia.org/wiki/Domain-specific_language), -- A grammar for this language, -- An interpreter that solves the problem instances. - -## Motivation - -Our goal is to translate simple mathematical expressions into postfix expressions -(or [Reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation)) -For simplicity, our expressions consist of ten digits `0`, ..., `9` and two -operations `+`, `-`. For example, the expression `2 + 4` is translated into -`2 4 +`. - -## Context Free Grammar for our problem - -Our task is translating infix expressions into postfix ones. Let's define a context -free grammar for a set of infix expressions over `0`, ..., `9`, `+`, and `-`, -where: - -- Terminal symbols: `0`, `...`, `9`, `+`, `-` -- Non-terminal symbols: `exp`, `term` -- Start symbol is `exp` -- And the following are production rules - -```ignore -exp -> exp + term -exp -> exp - term -exp -> term -term -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 -``` - -__NOTE:__ This grammar should be further transformed depending on what we are going -to do with it. For example, we might need to remove left recursion. For more -details please see [Compilers: Principles,Techniques, and Tools -](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools) -(aka Dragon Book). - -## Solution - -We simply implement a recursive descent parser. For simplicity's sake, the code -panics when an expression is syntactically wrong (for example `2-34` or `2+5-` -are wrong according to the grammar definition). - -```rust -pub struct Interpreter<'a> { - it: std::str::Chars<'a>, -} - -impl<'a> Interpreter<'a> { - - pub fn new(infix: &'a str) -> Self { - Self { it: infix.chars() } - } - - fn next_char(&mut self) -> Option { - self.it.next() - } - - pub fn interpret(&mut self, out: &mut String) { - self.term(out); - - while let Some(op) = self.next_char() { - if op == '+' || op == '-' { - self.term(out); - out.push(op); - } else { - panic!("Unexpected symbol '{}'", op); - } - } - } - - fn term(&mut self, out: &mut String) { - match self.next_char() { - Some(ch) if ch.is_digit(10) => out.push(ch), - Some(ch) => panic!("Unexpected symbol '{}'", ch), - None => panic!("Unexpected end of string"), - } - } -} - -pub fn main() { - let mut intr = Interpreter::new("2+3"); - let mut postfix = String::new(); - intr.interpret(&mut postfix); - assert_eq!(postfix, "23+"); - - intr = Interpreter::new("1-2+3-4"); - postfix.clear(); - intr.interpret(&mut postfix); - assert_eq!(postfix, "12-3+4-"); -} -``` - -## Discussion - -There may be a wrong perception that the Interpreter design pattern is about design -grammars for formal languages and implementation of parsers for these grammars. -In fact, this pattern is about expressing problem instances in a more specific -way and implementing functions/classes/structs that solve these problem instances. -Rust language has `macro_rules!` that allow us to define special syntax and rules -on how to expand this syntax into source code. - -In the following example we create a simple `macro_rules!` that computes -[Euclidean length](https://en.wikipedia.org/wiki/Euclidean_distance) of `n` -dimensional vectors. Writing `norm!(x,1,2)` might be easier to express and more -efficient than packing `x,1,2` into a `Vec` and calling a function computing -the length. - -```rust -macro_rules! norm { - ($($element:expr),*) => { - { - let mut n = 0.0; - $( - n += ($element as f64)*($element as f64); - )* - n.sqrt() - } - }; -} - -fn main() { - let x = -3f64; - let y = 4f64; - - assert_eq!(3f64, norm!(x)); - assert_eq!(5f64, norm!(x, y)); - assert_eq!(0f64, norm!(0, 0, 0)); - assert_eq!(1f64, norm!(0.5, -0.5, 0.5, -0.5)); -} -``` - -## See also - -- [Interpreter pattern](https://en.wikipedia.org/wiki/Interpreter_pattern) -- [Context free grammar](https://en.wikipedia.org/wiki/Context-free_grammar) -- [macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html) diff --git a/patterns/behavioural/intro.md b/patterns/behavioural/intro.md deleted file mode 100644 index 8ca43a04..00000000 --- a/patterns/behavioural/intro.md +++ /dev/null @@ -1,6 +0,0 @@ -# Behavioural Patterns - -From [Wikipedia](https://en.wikipedia.org/wiki/Behavioral_pattern): - -> Design patterns that identify common communication patterns among objects. -> By doing so, these patterns increase flexibility in carrying out communication. diff --git a/patterns/behavioural/newtype.md b/patterns/behavioural/newtype.md deleted file mode 100644 index 8df944f3..00000000 --- a/patterns/behavioural/newtype.md +++ /dev/null @@ -1,111 +0,0 @@ -# Newtype - -What if in some cases we want a type to behave similar to another type or -enforce some behaviour at compile time when using only type aliases would -not be enough? - -For example, if we want to create a custom `Display` implementation for `String` -due to security considerations (e.g. passwords). - -For such cases we could use the `Newtype` pattern to provide __type safety__ -and __encapsulation__. - -## Description - -Use a tuple struct with a single field to make an opaque wrapper for a type. -This creates a new type, rather than an alias to a type (`type` items). - -## Example - -```rust,ignore -// Some type, not necessarily in the same module or even crate. -struct Foo { - //.. -} - -impl Foo { - // These functions are not present on Bar. - //.. -} - -// The newtype. -pub struct Bar(Foo); - -impl Bar { - // Constructor. - pub fn new( - //.. - ) -> Self { - - //.. - - } - - //.. -} - -fn main() { - let b = Bar::new(...); - - // Foo and Bar are type incompatible, the following do not type check. - // let f: Foo = b; - // let b: Bar = Foo { ... }; -} -``` - -## Motivation - -The primary motivation for newtypes is abstraction. It allows you to share -implementation details between types while precisely controlling the interface. -By using a newtype rather than exposing the implementation type as part of an -API, it allows you to change implementation backwards compatibly. - -Newtypes can be used for distinguishing units, e.g., wrapping `f64` to give -distinguishable `Miles` and `Kilometres`. - -## Advantages - -The wrapped and wrapper types are not type compatible (as opposed to using -`type`), so users of the newtype will never 'confuse' the wrapped and wrapper -types. - -Newtypes are a zero-cost abstraction - there is no runtime overhead. - -The privacy system ensures that users cannot access the wrapped type (if the -field is private, which it is by default). - -## Disadvantages - -The downside of newtypes (especially compared with type aliases), is that there -is no special language support. This means there can be *a lot* of boilerplate. -You need a 'pass through' method for every method you want to expose on the -wrapped type, and an impl for every trait you want to also be implemented for -the wrapper type. - -## Discussion - -Newtypes are very common in Rust code. Abstraction or representing units are the -most common uses, but they can be used for other reasons: - -- restricting functionality (reduce the functions exposed or traits implemented), -- making a type with copy semantics have move semantics, -- abstraction by providing a more concrete type and thus hiding internal types, - e.g., - -```rust,ignore -pub struct Foo(Bar); -``` - -Here, `Bar` might be some public, generic type and `T1` and `T2` are some internal -types. Users of our module shouldn't know that we implement `Foo` by using a `Bar`, -but what we're really hiding here is the types `T1` and `T2`, and how they are used -with `Bar`. - -## See also - -- [Advanced Types in the book](https://doc.rust-lang.org/book/ch19-04-advanced-types.html?highlight=newtype#using-the-newtype-pattern-for-type-safety-and-abstraction) -- [Newtypes in Haskell](https://wiki.haskell.org/Newtype) -- [Type aliases](https://doc.rust-lang.org/stable/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases) -- [derive_more](https://crates.io/crates/derive_more), a crate for deriving many - builtin traits on newtypes. -- [The Newtype Pattern In Rust](https://www.worthe-it.co.za/blog/2020-10-31-newtype-pattern-in-rust.html) diff --git a/patterns/behavioural/strategy.md b/patterns/behavioural/strategy.md deleted file mode 100644 index 751178b3..00000000 --- a/patterns/behavioural/strategy.md +++ /dev/null @@ -1,179 +0,0 @@ -# Strategy (aka Policy) - -## Description - -The [Strategy design pattern](https://en.wikipedia.org/wiki/Strategy_pattern) -is a technique that enables separation of concerns. -It also allows to decouple software modules through [Dependency Inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle). - -The basic idea behind the Strategy pattern is that, given an algorithm solving -a particular problem, we define only the skeleton of the algorithm at an abstract -level, and we separate the specific algorithm’s implementation into different parts. - -In this way, a client using the algorithm may choose a specific implementation, -while the general algorithm workflow remains the same. In other words, the abstract -specification of the class does not depend on the specific implementation of the -derived class, but specific implementation must adhere to the abstract specification. -This is why we call it "Dependency Inversion". - -## Motivation - -Imagine we are working on a project that generates reports every month. -We need the reports to be generated in different formats (strategies), e.g., -in `JSON` or `Plain Text` formats. -But things vary over time, and we don't know what kind of requirement we may get -in the future. For example, we may need to generate our report in a completely new -format, or just modify one of the existing formats. - -## Example - -In this example our invariants (or abstractions) are `Context`, `Formatter`, -and `Report`, while `Text` and `Json` are our strategy structs. These strategies -have to implement the `Formatter` trait. - -```rust -use std::collections::HashMap; - -type Data = HashMap; - -trait Formatter { - fn format(&self, data: &Data, buf: &mut String); -} - -struct Report; - -impl Report { - // Write should be used but we kept it as String to ignore error handling - fn generate(g: T, s: &mut String) { - // backend operations... - let mut data = HashMap::new(); - data.insert("one".to_string(), 1); - data.insert("two".to_string(), 2); - // generate report - g.format(&data, s); - } -} - -struct Text; -impl Formatter for Text { - fn format(&self, data: &Data, buf: &mut String) { - for (k, v) in data { - let entry = format!("{} {}\n", k, v); - buf.push_str(&entry); - } - } -} - -struct Json; -impl Formatter for Json { - fn format(&self, data: &Data, buf: &mut String) { - buf.push('['); - for (k, v) in data.into_iter() { - let entry = format!(r#"{{"{}":"{}"}}"#, k, v); - buf.push_str(&entry); - buf.push(','); - } - if !data.is_empty() { - buf.pop(); // remove extra , at the end - } - buf.push(']'); - } -} - -fn main() { - let mut s = String::from(""); - Report::generate(Text, &mut s); - assert!(s.contains("one 1")); - assert!(s.contains("two 2")); - - s.clear(); // reuse the same buffer - Report::generate(Json, &mut s); - assert!(s.contains(r#"{"one":"1"}"#)); - assert!(s.contains(r#"{"two":"2"}"#)); -} -``` - -## Advantages - -The main advantage is separation of concerns. For example, in this case `Report` -does not know anything about specific implementations of `Json` and `Text`, -whereas the output implementations does not care about how data is preprocessed, -stored, and fetched. The only thing they have to know is context and a specific -trait and method to implement, i.e,`Formatter` and `run`. - -## Disadvantages - -For each strategy there must be implemented at least one module, so number of modules -increases with number of strategies. If there are many strategies to choose from -then users have to know how strategies differ from one another. - -## Discussion - -In the previous example all strategies are implemented in a single file. -Ways of providing different strategies includes: - -- All in one file (as shown in this example, similar to being separated as modules) -- Separated as modules, E.g. `formatter::json` module, `formatter::text` module -- Use compiler feature flags, E.g. `json` feature, `text` feature -- Separated as crates, E.g. `json` crate, `text` crate - -Serde crate is a good example of the `Strategy` pattern in action. Serde allows -[full customization](https://serde.rs/custom-serialization.html) of the serialization -behavior by manually implementing `Serialize` and `Deserialize` traits for our -type. For example, we could easily swap `serde_json` with `serde_cbor` since they -expose similar methods. Having this makes the helper crate `serde_transcode` much -more useful and ergonomic. - -However, we don't need to use traits in order to design this pattern in Rust. - -The following toy example demonstrates the idea of the Strategy pattern using Rust -`closures`: - -```rust -struct Adder; -impl Adder { - pub fn add(x: u8, y: u8, f: F) -> u8 - where - F: Fn(u8, u8) -> u8, - { - f(x, y) - } -} - -fn main() { - let arith_adder = |x, y| x + y; - let bool_adder = |x, y| { - if x == 1 || y == 1 { - 1 - } else { - 0 - } - }; - let custom_adder = |x, y| 2 * x + y; - - assert_eq!(9, Adder::add(4, 5, arith_adder)); - assert_eq!(0, Adder::add(0, 0, bool_adder)); - assert_eq!(5, Adder::add(1, 3, custom_adder)); -} - -``` - -In fact, Rust already uses this idea for `Options`'s `map` method: - -```rust -fn main() { - let val = Some("Rust"); - - let len_strategy = |s: &str| s.len(); - assert_eq!(4, val.map(len_strategy).unwrap()); - - let first_byte_strategy = |s: &str| s.bytes().next().unwrap(); - assert_eq!(82, val.map(first_byte_strategy).unwrap()); -} -``` - -## See also - -- [Strategy Pattern](https://en.wikipedia.org/wiki/Strategy_pattern) -- [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) -- [Policy Based Design](https://en.wikipedia.org/wiki/Modern_C++_Design#Policy-based_design) diff --git a/patterns/behavioural/visitor.md b/patterns/behavioural/visitor.md deleted file mode 100644 index 55873147..00000000 --- a/patterns/behavioural/visitor.md +++ /dev/null @@ -1,113 +0,0 @@ -# Visitor - -## Description - -A visitor encapsulates an algorithm that operates over a heterogeneous -collection of objects. It allows multiple different algorithms to be written -over the same data without having to modify the data (or their primary -behaviour). - -Furthermore, the visitor pattern allows separating the traversal of -a collection of objects from the operations performed on each object. - -## Example - -```rust,ignore -// The data we will visit -mod ast { - pub enum Stmt { - Expr(Expr), - Let(Name, Expr), - } - - pub struct Name { - value: String, - } - - pub enum Expr { - IntLit(i64), - Add(Box, Box), - Sub(Box, Box), - } -} - -// The abstract visitor -mod visit { - use ast::*; - - pub trait Visitor { - fn visit_name(&mut self, n: &Name) -> T; - fn visit_stmt(&mut self, s: &Stmt) -> T; - fn visit_expr(&mut self, e: &Expr) -> T; - } -} - -use visit::*; -use ast::*; - -// An example concrete implementation - walks the AST interpreting it as code. -struct Interpreter; -impl Visitor for Interpreter { - fn visit_name(&mut self, n: &Name) -> i64 { panic!() } - fn visit_stmt(&mut self, s: &Stmt) -> i64 { - match *s { - Stmt::Expr(ref e) => self.visit_expr(e), - Stmt::Let(..) => unimplemented!(), - } - } - - fn visit_expr(&mut self, e: &Expr) -> i64 { - match *e { - Expr::IntLit(n) => n, - Expr::Add(ref lhs, ref rhs) => self.visit_expr(lhs) + self.visit_expr(rhs), - Expr::Sub(ref lhs, ref rhs) => self.visit_expr(lhs) - self.visit_expr(rhs), - } - } -} -``` - -One could implement further visitors, for example a type checker, without having -to modify the AST data. - -## Motivation - -The visitor pattern is useful anywhere that you want to apply an algorithm to -heterogeneous data. If data is homogeneous, you can use an iterator-like pattern. -Using a visitor object (rather than a functional approach) allows the visitor to -be stateful and thus communicate information between nodes. - -## Discussion - -It is common for the `visit_*` methods to return void (as opposed to in the -example). In that case it is possible to factor out the traversal code and share -it between algorithms (and also to provide noop default methods). In Rust, the -common way to do this is to provide `walk_*` functions for each datum. For -example, - -```rust,ignore -pub fn walk_expr(visitor: &mut Visitor, e: &Expr) { - match *e { - Expr::IntLit(_) => {}, - Expr::Add(ref lhs, ref rhs) => { - visitor.visit_expr(lhs); - visitor.visit_expr(rhs); - } - Expr::Sub(ref lhs, ref rhs) => { - visitor.visit_expr(lhs); - visitor.visit_expr(rhs); - } - } -} -``` - -In other languages (e.g., Java) it is common for data to have an `accept` method -which performs the same duty. - -## See also - -The visitor pattern is a common pattern in most OO languages. - -[Wikipedia article](https://en.wikipedia.org/wiki/Visitor_pattern) - -The [fold](../creational/fold.md) pattern is similar to visitor but produces -a new version of the visited data structure. diff --git a/patterns/creational/builder.md b/patterns/creational/builder.md deleted file mode 100644 index 211ef376..00000000 --- a/patterns/creational/builder.md +++ /dev/null @@ -1,115 +0,0 @@ -# Builder - -## Description - -Construct an object with calls to a builder helper. - -## Example - -```rust -#[derive(Debug, PartialEq)] -pub struct Foo { - // Lots of complicated fields. - bar: String, -} - -impl Foo { - // This method will help users to discover the builder - pub fn builder() -> FooBuilder { - FooBuilder::default() - } -} - -#[derive(Default)] -pub struct FooBuilder { - // Probably lots of optional fields. - bar: String, -} - -impl FooBuilder { - pub fn new(/* ... */) -> FooBuilder { - // Set the minimally required fields of Foo. - FooBuilder { - bar: String::from("X"), - } - } - - pub fn name(mut self, bar: String) -> FooBuilder { - // Set the name on the builder itself, and return the builder by value. - self.bar = bar; - self - } - - // If we can get away with not consuming the Builder here, that is an - // advantage. It means we can use the FooBuilder as a template for constructing - // many Foos. - pub fn build(self) -> Foo { - // Create a Foo from the FooBuilder, applying all settings in FooBuilder - // to Foo. - Foo { bar: self.bar } - } -} - -#[test] -fn builder_test() { - let foo = Foo { - bar: String::from("Y"), - }; - let foo_from_builder: Foo = FooBuilder::new().name(String::from("Y")).build(); - assert_eq!(foo, foo_from_builder); -} -``` - -## Motivation - -Useful when you would otherwise require many constructors or where -construction has side effects. - -## Advantages - -Separates methods for building from other methods. - -Prevents proliferation of constructors. - -Can be used for one-liner initialisation as well as more complex construction. - -## Disadvantages - -More complex than creating a struct object directly, or a simple constructor -function. - -## Discussion - -This pattern is seen more frequently in Rust (and for simpler objects) than in -many other languages because Rust lacks overloading. Since you can only have a -single method with a given name, having multiple constructors is less nice in -Rust than in C++, Java, or others. - -This pattern is often used where the builder object is useful in its own right, -rather than being just a builder. For example, see -[`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html) -is a builder for [`Child`](https://doc.rust-lang.org/std/process/struct.Child.html) -(a process). In these cases, the `T` and `TBuilder` naming pattern is not used. - -The example takes and returns the builder by value. It is often more ergonomic -(and more efficient) to take and return the builder as a mutable reference. The -borrow checker makes this work naturally. This approach has the advantage that -one can write code like - -```rust,ignore -let mut fb = FooBuilder::new(); -fb.a(); -fb.b(); -let f = fb.build(); -``` - -as well as the `FooBuilder::new().a().b().build()` style. - -## See also - -- [Description in the style guide](https://web.archive.org/web/20210104103100/https://doc.rust-lang.org/1.12.0/style/ownership/builders.html) -- [derive_builder](https://crates.io/crates/derive_builder), a crate for automatically - implementing this pattern while avoiding the boilerplate. -- [Constructor pattern](../../idioms/ctor.md) for when construction is simpler. -- [Builder pattern (wikipedia)](https://en.wikipedia.org/wiki/Builder_pattern) -- [Construction of complex values](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder) diff --git a/patterns/creational/fold.md b/patterns/creational/fold.md deleted file mode 100644 index 13eab838..00000000 --- a/patterns/creational/fold.md +++ /dev/null @@ -1,122 +0,0 @@ -# Fold - -## Description - -Run an algorithm over each item in a collection of data to create a new item, -thus creating a whole new collection. - -The etymology here is unclear to me. The terms 'fold' and 'folder' are used -in the Rust compiler, although it appears to me to be more like a map than a -fold in the usual sense. See the discussion below for more details. - -## Example - -```rust,ignore -// The data we will fold, a simple AST. -mod ast { - pub enum Stmt { - Expr(Box), - Let(Box, Box), - } - - pub struct Name { - value: String, - } - - pub enum Expr { - IntLit(i64), - Add(Box, Box), - Sub(Box, Box), - } -} - -// The abstract folder -mod fold { - use ast::*; - - pub trait Folder { - // A leaf node just returns the node itself. In some cases, we can do this - // to inner nodes too. - fn fold_name(&mut self, n: Box) -> Box { n } - // Create a new inner node by folding its children. - fn fold_stmt(&mut self, s: Box) -> Box { - match *s { - Stmt::Expr(e) => Box::new(Stmt::Expr(self.fold_expr(e))), - Stmt::Let(n, e) => Box::new(Stmt::Let(self.fold_name(n), self.fold_expr(e))), - } - } - fn fold_expr(&mut self, e: Box) -> Box { ... } - } -} - -use fold::*; -use ast::*; - -// An example concrete implementation - renames every name to 'foo'. -struct Renamer; -impl Folder for Renamer { - fn fold_name(&mut self, n: Box) -> Box { - Box::new(Name { value: "foo".to_owned() }) - } - // Use the default methods for the other nodes. -} -``` - -The result of running the `Renamer` on an AST is a new AST identical to the old -one, but with every name changed to `foo`. A real life folder might have some -state preserved between nodes in the struct itself. - -A folder can also be defined to map one data structure to a different (but -usually similar) data structure. For example, we could fold an AST into a HIR -tree (HIR stands for high-level intermediate representation). - -## Motivation - -It is common to want to map a data structure by performing some operation on -each node in the structure. For simple operations on simple data structures, -this can be done using `Iterator::map`. For more complex operations, perhaps -where earlier nodes can affect the operation on later nodes, or where iteration -over the data structure is non-trivial, using the fold pattern is more -appropriate. - -Like the visitor pattern, the fold pattern allows us to separate traversal of a -data structure from the operations performed to each node. - -## Discussion - -Mapping data structures in this fashion is common in functional languages. In OO -languages, it would be more common to mutate the data structure in place. The -'functional' approach is common in Rust, mostly due to the preference for -immutability. Using fresh data structures, rather than mutating old ones, makes -reasoning about the code easier in most circumstances. - -The trade-off between efficiency and reusability can be tweaked by changing how -nodes are accepted by the `fold_*` methods. - -In the above example we operate on `Box` pointers. Since these own their data -exclusively, the original copy of the data structure cannot be re-used. On the -other hand if a node is not changed, reusing it is very efficient. - -If we were to operate on borrowed references, the original data structure can be -reused; however, a node must be cloned even if unchanged, which can be -expensive. - -Using a reference counted pointer gives the best of both worlds - we can reuse -the original data structure, and we don't need to clone unchanged nodes. However, -they are less ergonomic to use and mean that the data structures cannot be -mutable. - -## See also - -Iterators have a `fold` method, however this folds a data structure into a -value, rather than into a new data structure. An iterator's `map` is more like -this fold pattern. - -In other languages, fold is usually used in the sense of Rust's iterators, -rather than this pattern. Some functional languages have powerful constructs for -performing flexible maps over data structures. - -The [visitor](../behavioural/visitor.md) pattern is closely related to fold. -They share the concept of walking a data structure performing an operation on -each node. However, the visitor does not create a new data structure nor consume -the old one. diff --git a/patterns/creational/intro.md b/patterns/creational/intro.md deleted file mode 100644 index 782fdc80..00000000 --- a/patterns/creational/intro.md +++ /dev/null @@ -1,8 +0,0 @@ -# Creational Patterns - -From [Wikipedia](https://en.wikipedia.org/wiki/Creational_pattern): - -> Design patterns that deal with object creation mechanisms, trying to create objects -> in a manner suitable to the situation. The basic form of object creation could -> result in design problems or in added complexity to the design. Creational design -> patterns solve this problem by somehow controlling this object creation. diff --git a/patterns/ffi/export.md b/patterns/ffi/export.md deleted file mode 100644 index 8b5a4f06..00000000 --- a/patterns/ffi/export.md +++ /dev/null @@ -1,260 +0,0 @@ -# Object-Based APIs - -## Description - -When designing APIs in Rust which are exposed to other languages, there are some -important design principles which are contrary to normal Rust API design: - -1. All Encapsulated types should be *owned* by Rust, *managed* by the user, - and *opaque*. -2. All Transactional data types should be *owned* by the user, and *transparent*. -3. All library behavior should be functions acting upon Encapsulated types. -4. All library behavior should be encapsulated into types not based on structure, - but *provenance/lifetime*. - -## Motivation - -Rust has built-in FFI support to other languages. -It does this by providing a way for crate authors to provide C-compatible APIs -through different ABIs (though that is unimportant to this practice). - -Well-designed Rust FFI follows C API design principles, while compromising the -design in Rust as little as possible. There are three goals with any foreign API: - -1. Make it easy to use in the target language. -2. Avoid the API dictating internal unsafety on the Rust side as much as possible. -3. Keep the potential for memory unsafety and Rust `undefined behaviour` as small - as possible. - -Rust code must trust the memory safety of the foreign language beyond a certain -point. However, every bit of `unsafe` code on the Rust side is an opportunity for -bugs, or to exacerbate `undefined behaviour`. - -For example, if a pointer provenance is wrong, that may be a segfault due to -invalid memory access. But if it is manipulated by unsafe code, it could become -full-blown heap corruption. - -The Object-Based API design allows for writing shims that have good memory safety -characteristics, and a clean boundary of what is safe and what is `unsafe`. - -## Code Example - -The POSIX standard defines the API to access an on-file database, known as [DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/ndbm.h). -It is an excellent example of an "object-based" API. - -Here is the definition in C, which hopefully should be easy to read for those -involved in FFI. The commentary below should help explain it for those who -miss the subtleties. - -```C -struct DBM; -typedef struct { void *dptr, size_t dsize } datum; - -int dbm_clearerr(DBM *); -void dbm_close(DBM *); -int dbm_delete(DBM *, datum); -int dbm_error(DBM *); -datum dbm_fetch(DBM *, datum); -datum dbm_firstkey(DBM *); -datum dbm_nextkey(DBM *); -DBM *dbm_open(const char *, int, mode_t); -int dbm_store(DBM *, datum, datum, int); -``` - -This API defines two types: `DBM` and `datum`. - -The `DBM` type was called an "encapsulated" type above. -It is designed to contain internal state, and acts as an entry point for the -library's behavior. - -It is completely opaque to the user, who cannot create a `DBM` themselves since -they don't know its size or layout. Instead, they must call `dbm_open`, and that -only gives them *a pointer to one*. - -This means all `DBM`s are "owned" by the library in a Rust sense. -The internal state of unknown size is kept in memory controlled by the library, -not the user. The user can only manage its life cycle with `open` and `close`, -and perform operations on it with the other functions. - -The `datum` type was called a "transactional" type above. -It is designed to facilitate the exchange of information between the library and -its user. - -The database is designed to store "unstructured data", with no pre-defined length -or meaning. As a result, the `datum` is the C equivalent of a Rust slice: a bunch -of bytes, and a count of how many there are. The main difference is that there is -no type information, which is what `void` indicates. - -Keep in mind that this header is written from the library's point of view. -The user likely has some type they are using, which has a known size. -But the library does not care, and by the rules of C casting, any type behind a -pointer can be cast to `void`. - -As noted earlier, this type is *transparent* to the user. But also, this type is -*owned* by the user. -This has subtle ramifications, due to that pointer inside it. -The question is, who owns the memory that pointer points to? - -The answer for best memory safety is, "the user". -But in cases such as retrieving a value, the user does not know how to allocate -it correctly (since they don't know how long the value is). In this case, the library -code is expected to use the heap that the user has access to -- such as the C library -`malloc` and `free` -- and then *transfer ownership* in the Rust sense. - -This may all seem speculative, but this is what a pointer means in C. -It means the same thing as Rust: "user defined lifetime." -The user of the library needs to read the documentation in order to use it correctly. -That said, there are some decisions that have fewer or greater consequences if users -do it wrong. Minimizing those are what this best practice is about, and the key -is to *transfer ownership of everything that is transparent*. - -## Advantages - -This minimizes the number of memory safety guarantees the user must uphold to a -relatively small number: - -1. Do not call any function with a pointer not returned by `dbm_open` (invalid - access or corruption). -2. Do not call any function on a pointer after close (use after free). -3. The `dptr` on any `datum` must be `NULL`, or point to a valid slice of memory - at the advertised length. - -In addition, it avoids a lot of pointer provenance issues. -To understand why, let us consider an alternative in some depth: key iteration. - -Rust is well known for its iterators. -When implementing one, the programmer makes a separate type with a bounded lifetime -to its owner, and implements the `Iterator` trait. - -Here is how iteration would be done in Rust for `DBM`: - -```rust,ignore -struct Dbm { ... } - -impl Dbm { - /* ... */ - pub fn keys<'it>(&'it self) -> DbmKeysIter<'it> { ... } - /* ... */ -} - -struct DbmKeysIter<'it> { - owner: &'it Dbm, -} - -impl<'it> Iterator for DbmKeysIter<'it> { ... } -``` - -This is clean, idiomatic, and safe. thanks to Rust's guarantees. -However, consider what a straightforward API translation would look like: - -```rust,ignore -#[no_mangle] -pub extern "C" fn dbm_iter_new(owner: *const Dbm) -> *mut DbmKeysIter { - // THIS API IS A BAD IDEA! For real applications, use object-based design instead. -} -#[no_mangle] -pub extern "C" fn dbm_iter_next( - iter: *mut DbmKeysIter, - key_out: *const datum -) -> libc::c_int { - // THIS API IS A BAD IDEA! For real applications, use object-based design instead. -} -#[no_mangle] -pub extern "C" fn dbm_iter_del(*mut DbmKeysIter) { - // THIS API IS A BAD IDEA! For real applications, use object-based design instead. -} -``` - -This API loses a key piece of information: the lifetime of the iterator must not -exceed the lifetime of the `Dbm` object that owns it. A user of the library could -use it in a way which causes the iterator to outlive the data it is iterating on, -resulting in reading uninitialized memory. - -This example written in C contains a bug that will be explained afterwards: - -```C -int count_key_sizes(DBM *db) { - // DO NOT USE THIS FUNCTION. IT HAS A SUBTLE BUT SERIOUS BUG! - datum key; - int len = 0; - - if (!dbm_iter_new(db)) { - dbm_close(db); - return -1; - } - - int l; - while ((l = dbm_iter_next(owner, &key)) >= 0) { // an error is indicated by -1 - free(key.dptr); - len += key.dsize; - if (l == 0) { // end of the iterator - dbm_close(owner); - } - } - if l >= 0 { - return -1; - } else { - return len; - } -} -``` - -This bug is a classic. Here's what happens when the iterator returns the -end-of-iteration marker: - -1. The loop condition sets `l` to zero, and enters the loop because `0 >= 0`. -2. The length is incremented, in this case by zero. -3. The if statement is true, so the database is closed. There should be a break - statement here. -4. The loop condition executes again, causing a `next` call on the closed object. - -The worst part about this bug? -If the Rust implementation was careful, this code will work most of the time! -If the memory for the `Dbm` object is not immediately reused, an internal check -will almost certainly fail, resulting in the iterator returning a `-1` indicating -an error. But occasionally, it will cause a segmentation fault, or even worse, -nonsensical memory corruption! - -None of this can be avoided by Rust. -From its perspective, it put those objects on its heap, returned pointers to them, -and gave up control of their lifetimes. The C code simply must "play nice". - -The programmer must read and understand the API documentation. -While some consider that par for the course in C, a good API design can mitigate -this risk. The POSIX API for `DBM` did this by *consolidating the ownership* of -the iterator with its parent: - -```C -datum dbm_firstkey(DBM *); -datum dbm_nextkey(DBM *); -``` - -Thus, all the lifetimes were bound together, and such unsafety was prevented. - -## Disadvantages - -However, this design choice also has a number of drawbacks, which should be -considered as well. - -First, the API itself becomes less expressive. -With POSIX DBM, there is only one iterator per object, and every call changes -its state. This is much more restrictive than iterators in almost any language, -even though it is safe. Perhaps with other related objects, whose lifetimes are -less hierarchical, this limitation is more of a cost than the safety. - -Second, depending on the relationships of the API's parts, significant design effort -may be involved. Many of the easier design points have other patterns associated -with them: - -- [Wrapper Type Consolidation](./wrappers.md) groups multiple Rust types together - into an opaque "object" - -- [FFI Error Passing](../../idioms/ffi/errors.md) explains error handling with integer - codes and sentinel return values (such as `NULL` pointers) - -- [Accepting Foreign Strings](../../idioms/ffi/accepting-strings.md) allows accepting - strings with minimal unsafe code, and is easier to get right than - [Passing Strings to FFI](../../idioms/ffi/passing-strings.md) - -However, not every API can be done this way. -It is up to the best judgement of the programmer as to who their audience is. diff --git a/patterns/ffi/intro.md b/patterns/ffi/intro.md deleted file mode 100644 index f65a0d28..00000000 --- a/patterns/ffi/intro.md +++ /dev/null @@ -1,13 +0,0 @@ -# FFI Patterns - -Writing FFI code is an entire course in itself. -However, there are several idioms here that can act as pointers, and avoid traps -for inexperienced users of unsafe Rust. - -This section contains design patterns that may be useful when doing FFI. - -1. [Object-Based API](./export.md) design that has good memory safety characteristics, - and a clean boundary of what is safe and what is unsafe - -2. [Type Consolidation into Wrappers](./wrappers.md) - group multiple Rust types - together into an opaque "object" diff --git a/patterns/ffi/wrappers.md b/patterns/ffi/wrappers.md deleted file mode 100644 index 77312596..00000000 --- a/patterns/ffi/wrappers.md +++ /dev/null @@ -1,162 +0,0 @@ -# Type Consolidation into Wrappers - -## Description - -This pattern is designed to allow gracefully handling multiple related types, -while minimizing the surface area for memory unsafety. - -One of the cornerstones of Rust's aliasing rules is lifetimes. -This ensures that many patterns of access between types can be memory safe, -data race safety included. - -However, when Rust types are exported to other languages, they are usually transformed -into pointers. In Rust, a pointer means "the user manages the lifetime of the pointee." -It is their responsibility to avoid memory unsafety. - -Some level of trust in the user code is thus required, notably around use-after-free -which Rust can do nothing about. However, some API designs place higher burdens -than others on the code written in the other language. - -The lowest risk API is the "consolidated wrapper", where all possible interactions -with an object are folded into a "wrapper type", while keeping the Rust API clean. - -## Code Example - -To understand this, let us look at a classic example of an API to export: iteration -through a collection. - -That API looks like this: - -1. The iterator is initialized with `first_key`. -2. Each call to `next_key` will advance the iterator. -3. Calls to `next_key` if the iterator is at the end will do nothing. -4. As noted above, the iterator is "wrapped into" the collection (unlike the native - Rust API). - -If the iterator implements `nth()` efficiently, then it is possible to make it -ephemeral to each function call: - -```rust,ignore -struct MySetWrapper { - myset: MySet, - iter_next: usize, -} - -impl MySetWrapper { - pub fn first_key(&mut self) -> Option<&Key> { - self.iter_next = 0; - self.next_key() - } - pub fn next_key(&mut self) -> Option<&Key> { - if let Some(next) = self.myset.keys().nth(self.iter_next) { - self.iter_next += 1; - Some(next) - } else { - None - } - } -} -``` - -As a result, the wrapper is simple and contains no `unsafe` code. - -## Advantages - -This makes APIs safer to use, avoiding issues with lifetimes between types. -See [Object-Based APIs](./export.md) for more on the advantages and pitfalls -this avoids. - -## Disadvantages - -Often, wrapping types is quite difficult, and sometimes a Rust API compromise -would make things easier. - -As an example, consider an iterator which does not efficiently implement `nth()`. -It would definitely be worth putting in special logic to make the object handle -iteration internally, or to support a different access pattern efficiently that -only the Foreign Function API will use. - -### Trying to Wrap Iterators (and Failing) - -To wrap any type of iterator into the API correctly, the wrapper would need to -do what a C version of the code would do: erase the lifetime of the iterator, -and manage it manually. - -Suffice it to say, this is *incredibly* difficult. - -Here is an illustration of just *one* pitfall. - -A first version of `MySetWrapper` would look like this: - -```rust,ignore -struct MySetWrapper { - myset: MySet, - iter_next: usize, - // created from a transmuted Box - iterator: Option>>, -} -``` - -With `transmute` being used to extend a lifetime, and a pointer to hide it, -it's ugly already. But it gets even worse: *any other operation can cause -Rust `undefined behaviour`*. - -Consider that the `MySet` in the wrapper could be manipulated by other -functions during iteration, such as storing a new value to the key it was -iterating over. The API doesn't discourage this, and in fact some similar C -libraries expect it. - -A simple implementation of `myset_store` would be: - -```rust,ignore -pub mod unsafe_module { - - // other module content - - pub fn myset_store( - myset: *mut MySetWrapper, - key: datum, - value: datum) -> libc::c_int { - - // DO NOT USE THIS CODE. IT IS UNSAFE TO DEMONSTRATE A PROLBEM. - - let myset: &mut MySet = unsafe { // SAFETY: whoops, UB occurs in here! - &mut (*myset).myset - }; - - /* ...check and cast key and value data... */ - - match myset.store(casted_key, casted_value) { - Ok(_) => 0, - Err(e) => e.into() - } - } -} -``` - -If the iterator exists when this function is called, we have violated one of Rust's -aliasing rules. According to Rust, the mutable reference in this block must have -*exclusive* access to the object. If the iterator simply exists, it's not exclusive, -so we have `undefined behaviour`! [^1] - -To avoid this, we must have a way of ensuring that mutable reference really is exclusive. -That basically means clearing out the iterator's shared reference while it exists, -and then reconstructing it. In most cases, that will still be less efficient than -the C version. - -Some may ask: how can C do this more efficiently? -The answer is, it cheats. Rust's aliasing rules are the problem, and C simply ignores -them for its pointers. In exchange, it is common to see code that is declared -in the manual as "not thread safe" under some or all circumstances. In fact, -the [GNU C library](https://manpages.debian.org/buster/manpages/attributes.7.en.html) -has an entire lexicon dedicated to concurrent behavior! - -Rust would rather make everything memory safe all the time, for both safety and -optimizations that C code cannot attain. Being denied access to certain shortcuts -is the price Rust programmers need to pay. - -[^1]: For the C programmers out there scratching their heads, the iterator need - not be read *during* this code cause the UB. The exclusivity rule also enables - compiler optimizations which may cause inconsistent observations by the iterator's - shared reference (e.g. stack spills or reordering instructions for efficiency). - These observations may happen *any time after* the mutable reference is created. diff --git a/patterns/index.md b/patterns/index.md deleted file mode 100644 index 1718174c..00000000 --- a/patterns/index.md +++ /dev/null @@ -1,28 +0,0 @@ -# Design Patterns - -[Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) are -"general reusable solutions to a commonly occurring problem within a given -context in software design". Design patterns are a great way to describe the -culture of a programming language. Design patterns are very language-specific - -what is a pattern in one language may be unnecessary in another due to a -language feature, or impossible to express due to a missing feature. - -If overused, design patterns can add unnecessary complexity to programs. -However, they are a great way to share intermediate and advanced level knowledge -about a programming language. - -## Design patterns in Rust - -Rust has many unique features. These features give us great benefit by removing -whole classes of problems. Some of them are also patterns that are _unique_ to Rust. - -## YAGNI - -YAGNI is an acronym that stands for `You Aren't Going to Need It`. -It's a vital software design principle to apply as you write code. - -> The best code I ever wrote was code I never wrote. - -If we apply YAGNI to design patterns, we see that the features of Rust allow us to -throw out many patterns. For instance, there is no need for the [strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern) -in Rust because we can just use [traits](https://doc.rust-lang.org/book/traits.html). diff --git a/patterns/structural/compose-structs.md b/patterns/structural/compose-structs.md deleted file mode 100644 index 013f6cb7..00000000 --- a/patterns/structural/compose-structs.md +++ /dev/null @@ -1,99 +0,0 @@ -# Compose structs together for better borrowing - -TODO - this is not a very snappy name - -## Description - -Sometimes a large struct will cause issues with the borrow checker - although -fields can be borrowed independently, sometimes the whole struct ends up being -used at once, preventing other uses. A solution might be to decompose the struct -into several smaller structs. Then compose these together into the original -struct. Then each struct can be borrowed separately and have more flexible -behaviour. - -This will often lead to a better design in other ways: applying this design -pattern often reveals smaller units of functionality. - -## Example - -Here is a contrived example of where the borrow checker foils us in our plan to -use a struct: - -```rust -struct A { - f1: u32, - f2: u32, - f3: u32, -} - -fn foo(a: &mut A) -> &u32 { &a.f2 } -fn bar(a: &mut A) -> u32 { a.f1 + a.f3 } - -fn baz(a: &mut A) { - // The later usage of x causes a to be borrowed for the rest of the function. - let x = foo(a); - // Borrow checker error: - // let y = bar(a); // ~ ERROR: cannot borrow `*a` as mutable more than once - // at a time - println!("{}", x); -} -``` - -We can apply this design pattern and refactor `A` into two smaller structs, thus -solving the borrow checking issue: - -```rust -// A is now composed of two structs - B and C. -struct A { - b: B, - c: C, -} -struct B { - f2: u32, -} -struct C { - f1: u32, - f3: u32, -} - -// These functions take a B or C, rather than A. -fn foo(b: &mut B) -> &u32 { &b.f2 } -fn bar(c: &mut C) -> u32 { c.f1 + c.f3 } - -fn baz(a: &mut A) { - let x = foo(&mut a.b); - // Now it's OK! - let y = bar(&mut a.c); - println!("{}", x); -} -``` - -## Motivation - -TODO Why and where you should use the pattern - -## Advantages - -Lets you work around limitations in the borrow checker. - -Often produces a better design. - -## Disadvantages - -Leads to more verbose code. - -Sometimes, the smaller structs are not good abstractions, and so we end up with -a worse design. That is probably a 'code smell', indicating that the program -should be refactored in some way. - -## Discussion - -This pattern is not required in languages that don't have a borrow checker, so -in that sense is unique to Rust. However, making smaller units of functionality -often leads to cleaner code: a widely acknowledged principle of software -engineering, independent of the language. - -This pattern relies on Rust's borrow checker to be able to borrow fields -independently of each other. In the example, the borrow checker knows that `a.b` -and `a.c` are distinct and can be borrowed independently, it does not try to -borrow all of `a`, which would make this pattern useless. diff --git a/patterns/structural/intro.md b/patterns/structural/intro.md deleted file mode 100644 index 34d9cc9b..00000000 --- a/patterns/structural/intro.md +++ /dev/null @@ -1,6 +0,0 @@ -# Structural Patterns - -From [Wikipedia](https://en.wikipedia.org/wiki/Structural_pattern): - -> Design patterns that ease the design by identifying a simple way to realize relationships -> among entities. diff --git a/patterns/structural/small-crates.md b/patterns/structural/small-crates.md deleted file mode 100644 index 01060e31..00000000 --- a/patterns/structural/small-crates.md +++ /dev/null @@ -1,47 +0,0 @@ -# Prefer small crates - -## Description - -Prefer small crates that do one thing well. - -Cargo and crates.io make it easy to add third-party libraries, much more so than -in say C or C++. Moreover, since packages on crates.io cannot be edited or removed -after publication, any build that works now should continue to work in the future. -We should take advantage of this tooling, and use smaller, more fine-grained dependencies. - -## Advantages - -* Small crates are easier to understand, and encourage more modular code. -* Crates allow for re-using code between projects. - For example, the `url` crate was developed as part of the Servo browser engine, - but has since found wide use outside the project. -* Since the compilation unit - of Rust is the crate, splitting a project into multiple crates can allow more of - the code to be built in parallel. - -## Disadvantages - -* This can lead to "dependency hell", when a project depends on multiple conflicting - versions of a crate at the same time. For example, the `url` crate has both versions - 1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url` from `url:0.5` are - different types, an HTTP client that uses `url:0.5` would not accept `Url` values - from a web scraper that uses `url:1.0`. -* Packages on crates.io are not curated. A crate may be poorly written, have - unhelpful documentation, or be outright malicious. -* Two small crates may be less optimized than one large one, since the compiler - does not perform link-time optimization (LTO) by default. - -## Examples - -The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions -for converting `&T` to `&[T]`. - -The [`url`](https://crates.io/crates/url) crate provides tools for working with -URLs. - -The [`num_cpus`](https://crates.io/crates/num_cpus) crate provides a function to -query the number of CPUs on a machine. - -## See also - -* [crates.io: The Rust community crate host](https://crates.io/) diff --git a/patterns/structural/unsafe-mods.md b/patterns/structural/unsafe-mods.md deleted file mode 100644 index 41737023..00000000 --- a/patterns/structural/unsafe-mods.md +++ /dev/null @@ -1,34 +0,0 @@ -# Contain unsafety in small modules - -## Description - -If you have `unsafe` code, create the smallest possible module that can uphold -the needed invariants to build a minimal safe interface upon the unsafety. Embed -this into a larger module that contains only safe code and presents an ergonomic -interface. Note that the outer module can contain unsafe functions and methods -that call directly into the unsafe code. Users may use this to gain speed benefits. - -## Advantages - -* This restricts the unsafe code that must be audited -* Writing the outer module is much easier, since you can count on the guarantees -of the inner module - -## Disadvantages - -* Sometimes, it may be hard to find a suitable interface. -* The abstraction may introduce inefficiencies. - -## Examples - -* The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe operations - in submodules, presenting a safe interface to users. -* `std`'s `String` class is a wrapper over `Vec` with the added invariant -that the contents must be valid UTF-8. The operations on `String` ensure this -behavior. -However, users have the option of using an `unsafe` method to create a `String`, -in which case the onus is on them to guarantee the validity of the contents. - -## See also - -* [Ralf Jung's Blog about invariants in unsafe code](https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html) diff --git a/refactoring/index.md b/refactoring/index.md deleted file mode 100644 index 178a53b0..00000000 --- a/refactoring/index.md +++ /dev/null @@ -1,22 +0,0 @@ -# Refactoring - -Refactoring is very important in relation to these topics. -Just as important as the other topics covered here, is how to take good code and -turn it into great code. - -We can use [design patterns](../patterns/index.md) to [DRY] up code and generalize -abstractions. We must avoid [anti-patterns](../anti_patterns/index.md) while we -do this. While they may be tempting to employ, their costs outweigh their benefits. - -> Shortcuts make for long days. - -We can also use [idioms](../idioms/index.md) to structure our code in a way that -is understandable. - -## Tests - -Tests are of vital importance during refactoring. - -## Small changes - -[DRY]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself diff --git a/translations.md b/translations.md deleted file mode 100644 index 8c89dea2..00000000 --- a/translations.md +++ /dev/null @@ -1,6 +0,0 @@ -# Translations - -- [简体中文](https://fomalhauthmj.github.io/patterns/) - -If you want to add a translation, please open an issue in the -[main repository](https://github.com/rust-unofficial/patterns). From a9ef0117d11f7f703358efc2f9268b36164851ea Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:47:58 +0200 Subject: [PATCH 03/29] Add third_party directory --- book.toml | 2 +- third_party/mdbook/LICENSE | 362 ++++++++++++++++++ third_party/mdbook/README.md | 8 + third_party/mdbook/book.js | 715 +++++++++++++++++++++++++++++++++++ 4 files changed, 1086 insertions(+), 1 deletion(-) create mode 100644 third_party/mdbook/LICENSE create mode 100644 third_party/mdbook/README.md create mode 100644 third_party/mdbook/book.js diff --git a/book.toml b/book.toml index 51a6580b..da4b9401 100644 --- a/book.toml +++ b/book.toml @@ -8,7 +8,7 @@ src = "src" [build] create-missing = false -extra-watch-dirs = ["po"] +extra-watch-dirs = ["po", "third_party"] [preprocessor.gettext] after = ["links"] diff --git a/third_party/mdbook/LICENSE b/third_party/mdbook/LICENSE new file mode 100644 index 00000000..be2cc4df --- /dev/null +++ b/third_party/mdbook/LICENSE @@ -0,0 +1,362 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. diff --git a/third_party/mdbook/README.md b/third_party/mdbook/README.md new file mode 100644 index 00000000..fe7e9a39 --- /dev/null +++ b/third_party/mdbook/README.md @@ -0,0 +1,8 @@ +# mdBook + +This directory contains files copied from mdBook. Please see + for the full project. + +## License + +mdBook is licensed under the Mozilla Public License v2.0 ([LICENSE](LICENSE)). diff --git a/third_party/mdbook/book.js b/third_party/mdbook/book.js new file mode 100644 index 00000000..a3384d98 --- /dev/null +++ b/third_party/mdbook/book.js @@ -0,0 +1,715 @@ +"use strict"; + +// Fix back button cache problem +window.onunload = function () { }; + +function isPlaygroundModified(playground) { + let code_block = playground.querySelector("code"); + if (window.ace && code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + return editor.getValue() != editor.originalCode; + } else { + return false; + } +} + +// Global variable, shared between modules +function playground_text(playground, hidden = true) { + let code_block = playground.querySelector("code"); + + if (window.ace && code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + return editor.getValue(); + } else if (hidden) { + return code_block.textContent; + } else { + return code_block.innerText; + } +} + +(function codeSnippets() { + function fetch_with_timeout(url, options, timeout = 15000) { + return Promise.race([ + fetch(url, options), + new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout)) + ]); + } + + var playgrounds = Array.from(document.querySelectorAll(".playground")); + if (playgrounds.length > 0) { + fetch_with_timeout("https://play.rust-lang.org/meta/crates", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + }) + .then(response => response.json()) + .then(response => { + // get list of crates available in the rust playground + let playground_crates = response.crates.map(item => item["id"]); + playgrounds.forEach(block => handle_crate_list_update(block, playground_crates)); + }); + } + + function handle_crate_list_update(playground_block, playground_crates) { + // update the play buttons after receiving the response + update_play_button(playground_block, playground_crates); + + // and install on change listener to dynamically update ACE editors + if (window.ace) { + let code_block = playground_block.querySelector("code"); + if (code_block.classList.contains("editable")) { + let editor = window.ace.edit(code_block); + editor.addEventListener("change", function (e) { + update_play_button(playground_block, playground_crates); + }); + // add Ctrl-Enter command to execute rust code + editor.commands.addCommand({ + name: "run", + bindKey: { + win: "Ctrl-Enter", + mac: "Ctrl-Enter" + }, + exec: _editor => run_rust_code(playground_block) + }); + } + } + } + + // updates the visibility of play button based on `no_run` class and + // used crates vs ones available on http://play.rust-lang.org + function update_play_button(pre_block, playground_crates) { + var play_button = pre_block.querySelector(".play-button"); + + // skip if code is `no_run` + if (pre_block.querySelector('code').classList.contains("no_run")) { + play_button.classList.add("hidden"); + return; + } + + // get list of `extern crate`'s from snippet + var txt = playground_text(pre_block); + var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g; + var snippet_crates = []; + var item; + while (item = re.exec(txt)) { + snippet_crates.push(item[1]); + } + + // check if all used crates are available on play.rust-lang.org + var all_available = snippet_crates.every(function (elem) { + return playground_crates.indexOf(elem) > -1; + }); + + if (all_available) { + play_button.classList.remove("hidden"); + } else { + play_button.classList.add("hidden"); + } + } + + function run_rust_code(code_block) { + var result_block = code_block.querySelector(".result"); + if (!result_block) { + result_block = document.createElement('code'); + result_block.className = 'result hljs language-bash'; + + code_block.append(result_block); + } + + let text = playground_text(code_block); + let classes = code_block.querySelector('code').classList; + let edition = "2015"; + if(classes.contains("edition2018")) { + edition = "2018"; + } else if(classes.contains("edition2021")) { + edition = "2021"; + } + var params = { + version: "stable", + optimize: "0", + code: text, + edition: edition + }; + + if (text.indexOf("#![feature") !== -1) { + params.version = "nightly"; + } + + result_block.innerText = "Running..."; + + const playgroundModified = isPlaygroundModified(code_block); + const startTime = window.performance.now(); + fetch_with_timeout("https://play.rust-lang.org/evaluate.json", { + headers: { + 'Content-Type': "application/json", + }, + method: 'POST', + mode: 'cors', + body: JSON.stringify(params) + }) + .then(response => response.json()) + .then(response => { + const endTime = window.performance.now(); + gtag("event", "playground", { + "modified": playgroundModified, + "error": (response.error == null) ? null : 'compilation_error', + "latency": (endTime - startTime) / 1000, + }); + + if (response.result.trim() === '') { + result_block.innerText = "No output"; + result_block.classList.add("result-no-output"); + } else { + result_block.innerText = response.result; + result_block.classList.remove("result-no-output"); + } + }) + .catch(error => { + const endTime = window.performance.now(); + gtag("event", "playground", { + "modified": playgroundModified, + "error": error.message, + "latency": (endTime - startTime) / 1000, + }); + result_block.innerText = "Playground Communication: " + error.message + }); + } + + // Syntax highlighting Configuration + hljs.configure({ + tabReplace: ' ', // 4 spaces + languages: [], // Languages used for auto-detection + }); + + let code_nodes = Array + .from(document.querySelectorAll('code')) + // Don't highlight `inline code` blocks in headers. + .filter(function (node) {return !node.parentElement.classList.contains("header"); }); + + if (window.ace) { + // language-rust class needs to be removed for editable + // blocks or highlightjs will capture events + code_nodes + .filter(function (node) {return node.classList.contains("editable"); }) + .forEach(function (block) { block.classList.remove('language-rust'); }); + + code_nodes + .filter(function (node) {return !node.classList.contains("editable"); }) + .forEach(function (block) { hljs.highlightBlock(block); }); + } else { + code_nodes.forEach(function (block) { hljs.highlightBlock(block); }); + } + + // Adding the hljs class gives code blocks the color css + // even if highlighting doesn't apply + code_nodes.forEach(function (block) { block.classList.add('hljs'); }); + + Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) { + + var lines = Array.from(block.querySelectorAll('.boring')); + // If no lines were hidden, return + if (!lines.length) { return; } + block.classList.add("hide-boring"); + + var buttons = document.createElement('div'); + buttons.className = 'buttons'; + buttons.innerHTML = ""; + + // add expand button + var pre_block = block.parentNode; + pre_block.insertBefore(buttons, pre_block.firstChild); + + pre_block.querySelector('.buttons').addEventListener('click', function (e) { + if (e.target.classList.contains('fa-eye')) { + e.target.classList.remove('fa-eye'); + e.target.classList.add('fa-eye-slash'); + e.target.title = 'Hide lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.remove('hide-boring'); + } else if (e.target.classList.contains('fa-eye-slash')) { + e.target.classList.remove('fa-eye-slash'); + e.target.classList.add('fa-eye'); + e.target.title = 'Show hidden lines'; + e.target.setAttribute('aria-label', e.target.title); + + block.classList.add('hide-boring'); + } + }); + }); + + if (window.playground_copyable) { + Array.from(document.querySelectorAll('pre code')).forEach(function (block) { + var pre_block = block.parentNode; + if (!pre_block.classList.contains('playground')) { + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var clipButton = document.createElement('button'); + clipButton.className = 'fa fa-copy clip-button'; + clipButton.title = 'Copy to clipboard'; + clipButton.setAttribute('aria-label', clipButton.title); + clipButton.innerHTML = ''; + + buttons.insertBefore(clipButton, buttons.firstChild); + } + }); + } + + // Process playground code blocks + Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) { + // Add play button + var buttons = pre_block.querySelector(".buttons"); + if (!buttons) { + buttons = document.createElement('div'); + buttons.className = 'buttons'; + pre_block.insertBefore(buttons, pre_block.firstChild); + } + + var runCodeButton = document.createElement('button'); + runCodeButton.className = 'fa fa-play play-button'; + runCodeButton.hidden = true; + runCodeButton.title = 'Run this code'; + runCodeButton.setAttribute('aria-label', runCodeButton.title); + + buttons.insertBefore(runCodeButton, buttons.firstChild); + runCodeButton.addEventListener('click', function (e) { + run_rust_code(pre_block); + }); + + if (window.playground_copyable) { + var copyCodeClipboardButton = document.createElement('button'); + copyCodeClipboardButton.className = 'fa fa-copy clip-button'; + copyCodeClipboardButton.innerHTML = ''; + copyCodeClipboardButton.title = 'Copy to clipboard'; + copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title); + + buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild); + } + + let code_block = pre_block.querySelector("code"); + if (window.ace && code_block.classList.contains("editable")) { + var undoChangesButton = document.createElement('button'); + undoChangesButton.className = 'fa fa-history reset-button'; + undoChangesButton.title = 'Undo changes'; + undoChangesButton.setAttribute('aria-label', undoChangesButton.title); + + buttons.insertBefore(undoChangesButton, buttons.firstChild); + + undoChangesButton.addEventListener('click', function () { + let editor = window.ace.edit(code_block); + editor.setValue(editor.originalCode); + editor.clearSelection(); + }); + } + }); +})(); + +(function themes() { + var html = document.querySelector('html'); + var themeToggleButton = document.getElementById('theme-toggle'); + var themePopup = document.getElementById('theme-list'); + var themeColorMetaTag = document.querySelector('meta[name="theme-color"]'); + var stylesheets = { + ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"), + tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"), + highlight: document.querySelector("[href$='highlight.css']"), + }; + + function showThemes() { + themePopup.style.display = 'block'; + themeToggleButton.setAttribute('aria-expanded', true); + themePopup.querySelector("button#" + get_theme()).focus(); + } + + function updateThemeSelected() { + themePopup.querySelectorAll('.theme-selected').forEach(function (el) { + el.classList.remove('theme-selected'); + }); + themePopup.querySelector("button#" + get_theme()).classList.add('theme-selected'); + } + + function hideThemes() { + themePopup.style.display = 'none'; + themeToggleButton.setAttribute('aria-expanded', false); + themeToggleButton.focus(); + } + + function get_theme() { + var theme; + try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { } + if (theme === null || theme === undefined) { + return default_theme; + } else { + return theme; + } + } + + function set_theme(theme, store = true) { + let ace_theme; + + if (theme == 'coal' || theme == 'navy') { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = false; + stylesheets.highlight.disabled = true; + + ace_theme = "ace/theme/tomorrow_night"; + } else if (theme == 'ayu') { + stylesheets.ayuHighlight.disabled = false; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = true; + ace_theme = "ace/theme/tomorrow_night"; + } else { + stylesheets.ayuHighlight.disabled = true; + stylesheets.tomorrowNight.disabled = true; + stylesheets.highlight.disabled = false; + ace_theme = "ace/theme/dawn"; + } + + setTimeout(function () { + themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor; + }, 1); + + if (window.ace && window.editors) { + window.editors.forEach(function (editor) { + editor.setTheme(ace_theme); + }); + } + + var previousTheme = get_theme(); + + if (store) { + try { localStorage.setItem('mdbook-theme', theme); } catch (e) { } + } + + html.classList.remove(previousTheme); + html.classList.add(theme); + updateThemeSelected(); + } + + // Set theme + var theme = get_theme(); + + set_theme(theme, false); + + themeToggleButton.addEventListener('click', function () { + if (themePopup.style.display === 'block') { + hideThemes(); + } else { + showThemes(); + } + }); + + themePopup.addEventListener('click', function (e) { + var theme; + if (e.target.className === "theme") { + theme = e.target.id; + } else if (e.target.parentElement.className === "theme") { + theme = e.target.parentElement.id; + } else { + return; + } + set_theme(theme); + }); + + themePopup.addEventListener('focusout', function(e) { + // e.relatedTarget is null in Safari and Firefox on macOS (see workaround below) + if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) { + hideThemes(); + } + }); + + // Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628 + document.addEventListener('click', function(e) { + if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) { + hideThemes(); + } + }); + + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (!themePopup.contains(e.target)) { return; } + + switch (e.key) { + case 'Escape': + e.preventDefault(); + hideThemes(); + break; + case 'ArrowUp': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.previousElementSibling) { + li.previousElementSibling.querySelector('button').focus(); + } + break; + case 'ArrowDown': + e.preventDefault(); + var li = document.activeElement.parentElement; + if (li && li.nextElementSibling) { + li.nextElementSibling.querySelector('button').focus(); + } + break; + case 'Home': + e.preventDefault(); + themePopup.querySelector('li:first-child button').focus(); + break; + case 'End': + e.preventDefault(); + themePopup.querySelector('li:last-child button').focus(); + break; + } + }); +})(); + +(function sidebar() { + var html = document.querySelector("html"); + var sidebar = document.getElementById("sidebar"); + var sidebarLinks = document.querySelectorAll('#sidebar a'); + var sidebarToggleButton = document.getElementById("sidebar-toggle"); + var sidebarResizeHandle = document.getElementById("sidebar-resize-handle"); + var firstContact = null; + + function showSidebar() { + html.classList.remove('sidebar-hidden') + html.classList.add('sidebar-visible'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', 0); + }); + sidebarToggleButton.setAttribute('aria-expanded', true); + sidebar.setAttribute('aria-hidden', false); + try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { } + } + + + var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle'); + + function toggleSection(ev) { + ev.currentTarget.parentElement.classList.toggle('expanded'); + } + + Array.from(sidebarAnchorToggles).forEach(function (el) { + el.addEventListener('click', toggleSection); + }); + + function hideSidebar() { + html.classList.remove('sidebar-visible') + html.classList.add('sidebar-hidden'); + Array.from(sidebarLinks).forEach(function (link) { + link.setAttribute('tabIndex', -1); + }); + sidebarToggleButton.setAttribute('aria-expanded', false); + sidebar.setAttribute('aria-hidden', true); + try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { } + } + + // Toggle sidebar + sidebarToggleButton.addEventListener('click', function sidebarToggle() { + if (html.classList.contains("sidebar-hidden")) { + var current_width = parseInt( + document.documentElement.style.getPropertyValue('--sidebar-width'), 10); + if (current_width < 150) { + document.documentElement.style.setProperty('--sidebar-width', '150px'); + } + showSidebar(); + } else if (html.classList.contains("sidebar-visible")) { + hideSidebar(); + } else { + if (getComputedStyle(sidebar)['transform'] === 'none') { + hideSidebar(); + } else { + showSidebar(); + } + } + }); + + sidebarResizeHandle.addEventListener('mousedown', initResize, false); + + function initResize(e) { + window.addEventListener('mousemove', resize, false); + window.addEventListener('mouseup', stopResize, false); + html.classList.add('sidebar-resizing'); + } + function resize(e) { + var pos = (e.clientX - sidebar.offsetLeft); + if (pos < 20) { + hideSidebar(); + } else { + if (html.classList.contains("sidebar-hidden")) { + showSidebar(); + } + pos = Math.min(pos, window.innerWidth - 100); + document.documentElement.style.setProperty('--sidebar-width', pos + 'px'); + } + } + //on mouseup remove windows functions mousemove & mouseup + function stopResize(e) { + html.classList.remove('sidebar-resizing'); + window.removeEventListener('mousemove', resize, false); + window.removeEventListener('mouseup', stopResize, false); + } + + document.addEventListener('touchstart', function (e) { + firstContact = { + x: e.touches[0].clientX, + time: Date.now() + }; + }, { passive: true }); + + document.addEventListener('touchmove', function (e) { + if (!firstContact) + return; + + var curX = e.touches[0].clientX; + var xDiff = curX - firstContact.x, + tDiff = Date.now() - firstContact.time; + + if (tDiff < 250 && Math.abs(xDiff) >= 150) { + if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300)) + showSidebar(); + else if (xDiff < 0 && curX < 300) + hideSidebar(); + + firstContact = null; + } + }, { passive: true }); + + // Scroll sidebar to current active section + var activeSection = document.getElementById("sidebar").querySelector(".active"); + if (activeSection) { + // https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView + activeSection.scrollIntoView({ block: 'center' }); + } +})(); + +(function chapterNavigation() { + document.addEventListener('keydown', function (e) { + if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; } + if (window.search && window.search.hasFocus()) { return; } + + switch (e.key) { + case 'ArrowRight': + e.preventDefault(); + var nextButton = document.querySelector('.nav-chapters.next'); + if (nextButton) { + window.location.href = nextButton.href; + } + break; + case 'ArrowLeft': + e.preventDefault(); + var previousButton = document.querySelector('.nav-chapters.previous'); + if (previousButton) { + window.location.href = previousButton.href; + } + break; + } + }); +})(); + +(function clipboard() { + var clipButtons = document.querySelectorAll('.clip-button'); + + function hideTooltip(elem) { + elem.firstChild.innerText = ""; + elem.className = 'fa fa-copy clip-button'; + } + + function showTooltip(elem, msg) { + elem.firstChild.innerText = msg; + elem.className = 'fa fa-copy tooltipped'; + } + + var clipboardSnippets = new ClipboardJS('.clip-button', { + text: function (trigger) { + hideTooltip(trigger); + let playground = trigger.closest("pre"); + return playground_text(playground, false); + } + }); + + Array.from(clipButtons).forEach(function (clipButton) { + clipButton.addEventListener('mouseout', function (e) { + hideTooltip(e.currentTarget); + }); + }); + + clipboardSnippets.on('success', function (e) { + e.clearSelection(); + showTooltip(e.trigger, "Copied!"); + }); + + clipboardSnippets.on('error', function (e) { + showTooltip(e.trigger, "Clipboard error!"); + }); +})(); + +(function scrollToTop () { + var menuTitle = document.querySelector('.menu-title'); + + menuTitle.addEventListener('click', function () { + document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' }); + }); +})(); + +(function controllMenu() { + var menu = document.getElementById('menu-bar'); + + (function controllPosition() { + var scrollTop = document.scrollingElement.scrollTop; + var prevScrollTop = scrollTop; + var minMenuY = -menu.clientHeight - 50; + // When the script loads, the page can be at any scroll (e.g. if you reforesh it). + menu.style.top = scrollTop + 'px'; + // Same as parseInt(menu.style.top.slice(0, -2), but faster + var topCache = menu.style.top.slice(0, -2); + menu.classList.remove('sticky'); + var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster + document.addEventListener('scroll', function () { + scrollTop = Math.max(document.scrollingElement.scrollTop, 0); + // `null` means that it doesn't need to be updated + var nextSticky = null; + var nextTop = null; + var scrollDown = scrollTop > prevScrollTop; + var menuPosAbsoluteY = topCache - scrollTop; + if (scrollDown) { + nextSticky = false; + if (menuPosAbsoluteY > 0) { + nextTop = prevScrollTop; + } + } else { + if (menuPosAbsoluteY > 0) { + nextSticky = true; + } else if (menuPosAbsoluteY < minMenuY) { + nextTop = prevScrollTop + minMenuY; + } + } + if (nextSticky === true && stickyCache === false) { + menu.classList.add('sticky'); + stickyCache = true; + } else if (nextSticky === false && stickyCache === true) { + menu.classList.remove('sticky'); + stickyCache = false; + } + if (nextTop !== null) { + menu.style.top = nextTop + 'px'; + topCache = nextTop; + } + prevScrollTop = scrollTop; + }, { passive: true }); + })(); + (function controllBorder() { + menu.classList.remove('bordered'); + document.addEventListener('scroll', function () { + if (menu.offsetTop === 0) { + menu.classList.remove('bordered'); + } else { + menu.classList.add('bordered'); + } + }, { passive: true }); + })(); +})(); From eb062d4147b7d16cfffdf013e8cc45d059f67227 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:21:14 +0200 Subject: [PATCH 04/29] Fold menu items on lvl 1 --- book.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/book.toml b/book.toml index da4b9401..331593a7 100644 --- a/book.toml +++ b/book.toml @@ -27,7 +27,7 @@ curly-quotes = true [output.html.fold] enable = true -level = 0 +level = 1 [output.html.playground] editable = true From 849e856328e72e63d737e6a63146ca189815f14e Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:21:25 +0200 Subject: [PATCH 05/29] Remove hardcoded non-existing translations --- theme/index.hbs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/theme/index.hbs b/theme/index.hbs index 1381b492..78315adc 100644 --- a/theme/index.hbs +++ b/theme/index.hbs @@ -11,17 +11,6 @@ {{/if}} - - - - - {{> head}} @@ -157,12 +146,12 @@

  • -
  • + --}}