├── .dprint.json ├── .env ├── .github ├── dependabot.yml └── workflows │ ├── build.yml │ ├── install-mdbook │ └── action.yml │ ├── lint.yml │ ├── publish.yml │ ├── setup-rust-cache │ └── action.yml │ ├── url-check-config.json │ ├── url-check-on-change.yml │ └── url-check-periodic.yml ├── .gitignore ├── .markdownlint.yaml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── book.toml ├── po ├── es.po ├── it.po ├── messages.pot └── pt-BR.po ├── src ├── SUMMARY.md ├── additional_resources │ ├── design-principles.md │ └── index.md ├── anti_patterns │ ├── borrow_clone.md │ ├── deny-warnings.md │ ├── deref.md │ └── index.md ├── functional │ ├── generics-type-classes.md │ ├── index.md │ ├── optics.md │ └── paradigms.md ├── idioms │ ├── coercion-arguments.md │ ├── concat-format.md │ ├── ctor.md │ ├── default.md │ ├── deref.md │ ├── dtor-finally.md │ ├── ffi │ │ ├── accepting-strings.md │ │ ├── errors.md │ │ ├── intro.md │ │ └── passing-strings.md │ ├── index.md │ ├── mem-replace.md │ ├── on-stack-dyn-dispatch.md │ ├── option-iter.md │ ├── pass-var-to-closure.md │ ├── priv-extend.md │ ├── return-consumed-arg-on-error.md │ ├── rustdoc-init.md │ └── temporary-mutability.md ├── intro.md ├── patterns │ ├── behavioural │ │ ├── RAII.md │ │ ├── command.md │ │ ├── interpreter.md │ │ ├── intro.md │ │ ├── newtype.md │ │ ├── strategy.md │ │ └── visitor.md │ ├── creational │ │ ├── builder.md │ │ ├── fold.md │ │ └── intro.md │ ├── ffi │ │ ├── export.md │ │ ├── intro.md │ │ └── wrappers.md │ ├── index.md │ └── structural │ │ ├── compose-structs.md │ │ ├── intro.md │ │ ├── small-crates.md │ │ └── unsafe-mods.md ├── refactoring │ └── index.md └── translations.md ├── styles └── last-changed.css ├── template.md ├── theme ├── book.js ├── css │ └── language-picker.css ├── head.hbs └── index.hbs └── third_party └── mdbook ├── LICENSE ├── README.md └── book.js /.dprint.json: -------------------------------------------------------------------------------- 1 | { 2 | "lineWidth": 80, 3 | "markdown": { 4 | "lineWidth": 80, 5 | "emphasisKind": "asterisks", 6 | "strongKind": "asterisks", 7 | "textWrap": "always" 8 | }, 9 | "toml": { 10 | "lineWidth": 80 11 | }, 12 | "json": { 13 | "lineWidth": 80, 14 | "indentWidth": 4 15 | }, 16 | "includes": [ 17 | "**/*.{md}", 18 | "**/*.{toml}", 19 | "**/*.{json}", 20 | "**/*.{js,ts,tsx,jsx}" 21 | ], 22 | "excludes": [ 23 | "book/**/book.js", 24 | "theme/book.js", 25 | "target/**/*" 26 | ], 27 | "exec": { 28 | "commands": [{ 29 | "command": "rustfmt --edition 2021", 30 | "exts": ["rs"] 31 | }] 32 | }, 33 | "plugins": [ 34 | "https://plugins.dprint.dev/markdown-0.16.4.wasm", 35 | "https://plugins.dprint.dev/toml-0.6.1.wasm", 36 | "https://plugins.dprint.dev/json-0.19.2.wasm", 37 | "https://plugins.dprint.dev/typescript-0.89.3.wasm", 38 | "https://plugins.dprint.dev/exec-0.4.4.json@c207bf9b9a4ee1f0ecb75c594f774924baf62e8e53a2ce9d873816a408cecbf7" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | MDBOOK_VERSION=0.4.37 2 | MDBOOK_I18N_HELPERS_VERSION=0.3.2 3 | MDBOOK_PANDOC_VERSION=0.6.0 4 | MDBOOK_LAST_CHANGED_VERSION=0.1.4 5 | PANDOC_VERSION=3.1.12.2 -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | # Check for updates every Monday 6 | schedule: 7 | interval: "weekly" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Test mdbook chapters 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | mdbook-test: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | 19 | - name: Setup Rust cache 20 | uses: ./.github/workflows/setup-rust-cache 21 | 22 | - name: Install mdbook 23 | uses: ./.github/workflows/install-mdbook 24 | 25 | - name: Test code snippets 26 | run: mdbook test 27 | 28 | # TODO: Activate when first translation is available 29 | # i18n-helpers: 30 | # runs-on: ubuntu-latest 31 | # steps: 32 | # - name: Checkout 33 | # uses: actions/checkout@v4 34 | 35 | # - name: Install Gettext 36 | # run: sudo apt install gettext 37 | 38 | # - name: Setup Rust cache 39 | # uses: ./.github/workflows/setup-rust-cache 40 | 41 | # - name: Install mdbook 42 | # uses: ./.github/workflows/install-mdbook 43 | 44 | # - name: Generate po/messages.pot 45 | # run: mdbook build -d po 46 | # env: 47 | # MDBOOK_OUTPUT: '{"xgettext": {"pot-file": "messages.pot"}}' 48 | 49 | # - name: Test messages.pot 50 | # run: msgfmt --statistics -o /dev/null po/messages.pot 51 | 52 | # - name: Expand includes without translation 53 | # run: mdbook build -d expanded 54 | # env: 55 | # MDBOOK_OUTPUT: '{"markdown": {}}' 56 | 57 | # - name: Expand includes with no-op translation 58 | # run: mdbook build -d no-op 59 | # env: 60 | # MDBOOK_OUTPUT: '{"markdown": {}}' 61 | # MDBOOK_PREPROCESSOR__GETTEXT__PO_FILE: po/messages.pot 62 | 63 | # - name: Compare no translation to no-op translation 64 | # run: diff --color=always --unified --recursive expanded no-op 65 | 66 | # find-translations: 67 | # runs-on: ubuntu-latest 68 | # outputs: 69 | # languages: ${{ steps.find-translations.outputs.languages }} 70 | # steps: 71 | # - name: Checkout 72 | # uses: actions/checkout@v4 73 | 74 | # - name: Find translations 75 | # id: find-translations 76 | # shell: python 77 | # run: | 78 | # import os, json, pathlib 79 | # languages = [p.stem for p in pathlib.Path("po").iterdir() if p.suffix == ".po"] 80 | # github_output = open(os.environ["GITHUB_OUTPUT"], "a") 81 | # github_output.write("languages=") 82 | # json.dump(sorted(languages), github_output) 83 | 84 | # translations: 85 | # runs-on: ubuntu-latest 86 | # needs: 87 | # - find-translations 88 | # strategy: 89 | # matrix: 90 | # language: ${{ fromJSON(needs.find-translations.outputs.languages) }} 91 | # env: 92 | # MDBOOK_BOOK__LANGUAGE: ${{ matrix.language }} 93 | # steps: 94 | # - name: Checkout 95 | # uses: actions/checkout@v4 96 | 97 | # - name: Install Gettext 98 | # run: sudo apt install gettext 99 | 100 | # - name: Setup Rust cache 101 | # uses: ./.github/workflows/setup-rust-cache 102 | 103 | # - name: Install mdbook 104 | # uses: ./.github/workflows/install-mdbook 105 | 106 | # - name: Test ${{ matrix.language }} translation 107 | # run: msgfmt --statistics -o /dev/null po/${{ matrix.language }}.po 108 | 109 | # - name: Build book with ${{ matrix.language }} translation 110 | # run: mdbook build 111 | 112 | # - name: Upload ${{ matrix.language }} translation 113 | # uses: actions/upload-artifact@v3 114 | # with: 115 | # name: rust-design-patterns-${{ matrix.language }} 116 | # path: book/ 117 | 118 | # - name: Test code snippets with ${{ matrix.language }} translation 119 | # run: mdbook test 120 | -------------------------------------------------------------------------------- /.github/workflows/install-mdbook/action.yml: -------------------------------------------------------------------------------- 1 | name: Install mdbook and dependencies 2 | 3 | description: Install the mdbook with the dependencies we need. 4 | 5 | runs: 6 | using: composite 7 | steps: 8 | - name: Read mdbook version from .env 9 | id: mdbook-version 10 | run: | 11 | . ./.env 12 | echo "version=$MDBOOK_VERSION" >> $GITHUB_OUTPUT 13 | shell: bash 14 | 15 | - name: Read mdbook-i18n-helpers version from .env 16 | id: mdbook-i18n-helpers-version 17 | run: | 18 | . ./.env 19 | echo "version=$MDBOOK_I18N_HELPERS_VERSION" >> $GITHUB_OUTPUT 20 | shell: bash 21 | 22 | - name: Read mdbook-pandoc version from .env 23 | id: mdbook-pandoc-version 24 | run: | 25 | . ./.env 26 | echo "version=$MDBOOK_PANDOC_VERSION" >> $GITHUB_OUTPUT 27 | shell: bash 28 | 29 | - name: Read mdbook-last-changed version from .env 30 | id: mdbook-last-changed-version 31 | run: | 32 | . ./.env 33 | echo "version=$MDBOOK_LAST_CHANGED_VERSION" >> $GITHUB_OUTPUT 34 | shell: bash 35 | 36 | - name: Install dependencies 37 | uses: taiki-e/install-action@v2 38 | with: 39 | tool: mdbook@${{ steps.mdbook-version.outputs.version }},mdbook-pandoc@${{ steps.mdbook-pandoc-version.outputs.version }},mdbook-i18n-helpers@${{ steps.mdbook-i18n-helpers-version.outputs.version }},mdbook-last-changed@${{ steps.mdbook-last-changed-version.outputs.version }} 40 | 41 | - name: Install mdbook-pandoc and related dependencies 42 | run: | 43 | . ./.env 44 | sudo apt-get update 45 | sudo apt-get install -y texlive texlive-latex-extra texlive-luatex texlive-lang-cjk librsvg2-bin fonts-noto 46 | curl -LsSf https://github.com/jgm/pandoc/releases/download/$PANDOC_VERSION/pandoc-$PANDOC_VERSION-linux-amd64.tar.gz | tar zxf - 47 | echo "$PWD/pandoc-$PANDOC_VERSION/bin" >> $GITHUB_PATH 48 | shell: bash 49 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint with dprint 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | jobs: 9 | style: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | 14 | - name: Install Rust toolchain 15 | uses: dtolnay/rust-toolchain@888c2e1ea69ab0d4330cbf0af1ecc7b68f368cc1 # v1 16 | with: 17 | toolchain: stable 18 | components: rustfmt 19 | 20 | - uses: Swatinem/rust-cache@f0deed1e0edfc6a9be95417288c0e1099b1eeec3 # v2 21 | 22 | - uses: dprint/check@v2.2 -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Deploy mdBook sites to GH Pages 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: read 10 | pages: write 11 | id-token: write 12 | 13 | # Allow one concurrent deployment 14 | concurrency: 15 | group: "pages" 16 | cancel-in-progress: true 17 | 18 | env: 19 | CARGO_TERM_COLOR: always 20 | # TODO: Update the language picker in index.hbs to link new languages. 21 | # TODO: As long as https://github.com/google/mdbook-i18n-helpers/issues/12 is not implemented, yet. 22 | # TODO: Activate when first translation is available 23 | # These are the languages in addition to 'en', which is the main language 24 | # LANGUAGES: xx 25 | 26 | jobs: 27 | publish: 28 | environment: 29 | name: github-pages 30 | url: ${{ steps.deployment.outputs.page_url }} 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v4 35 | 36 | - name: Setup Rust cache 37 | uses: ./.github/workflows/setup-rust-cache 38 | 39 | - name: Install mdbook 40 | uses: ./.github/workflows/install-mdbook 41 | 42 | - name: Build course in English 43 | run: | 44 | mdbook build -d book 45 | mv book/html/* book/pandoc/pdf/rust-design-patterns.pdf book/ 46 | rm -r book/html book/pandoc 47 | 48 | # TODO: Activate when first translation is available 49 | # - name: Build all translations 50 | # run: | 51 | # for po_lang in ${{ env.LANGUAGES }}; do 52 | # echo "::group::Building $po_lang translation" 53 | # MDBOOK_BOOK__LANGUAGE=$po_lang \ 54 | # MDBOOK_OUTPUT__HTML__SITE_URL=/patterns/$po_lang/ \ 55 | # mdbook build -d book/$po_lang 56 | # echo "::endgroup::" 57 | # done 58 | 59 | - name: Setup Pages 60 | id: pages 61 | uses: actions/configure-pages@v5 62 | 63 | - name: Upload artifact 64 | uses: actions/upload-pages-artifact@v3 65 | with: 66 | path: ./book 67 | 68 | - name: Deploy to GitHub Pages 69 | id: deployment 70 | uses: actions/deploy-pages@v4 71 | -------------------------------------------------------------------------------- /.github/workflows/setup-rust-cache/action.yml: -------------------------------------------------------------------------------- 1 | name: Setup Rust cache 2 | 3 | description: Configure the rust-cache workflow. 4 | 5 | runs: 6 | using: composite 7 | steps: 8 | - name: Setup Rust cache 9 | uses: Swatinem/rust-cache@v2 10 | with: 11 | prefix-key: v1 12 | -------------------------------------------------------------------------------- /.github/workflows/url-check-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ 3 | { 4 | "pattern": "^(http|https)://crates.io/" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.github/workflows/url-check-on-change.yml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | jobs: 9 | markdown-link-check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 14 | with: 15 | use-verbose-mode: 'yes' 16 | use-quiet-mode: 'yes' 17 | config-file: '.github/workflows/url-check-config.json' 18 | check-modified-files-only: 'yes' 19 | base-branch: 'main' 20 | -------------------------------------------------------------------------------- /.github/workflows/url-check-periodic.yml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links Periodically 2 | 3 | on: 4 | schedule: 5 | # Run everyday at 0:00 AM 6 | - cron: "0 0 * * *" 7 | 8 | jobs: 9 | markdown-link-check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: gaurav-nelson/github-action-markdown-link-check@v1 14 | with: 15 | use-verbose-mode: 'yes' 16 | use-quiet-mode: 'yes' 17 | config-file: '.github/workflows/url-check-config.json' 18 | base-branch: 'main' 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated output of mdbook 2 | /book 3 | .DS_Store 4 | .vscode/settings.json 5 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Use `#` for headers 3 | MD003: 4 | style: atx 5 | 6 | # Set maximum line length 7 | MD013: 8 | line_length: 80 9 | 10 | # Use `---` for horizontal rule 11 | MD035: 12 | style: --- 13 | 14 | # Use ``` for code blocks 15 | MD046: 16 | style: fenced 17 | MD048: 18 | style: backtick 19 | 20 | # See https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md for 21 | # additional info 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Introduction 4 | 5 | This book is a catalogue of Rust programming techniques, (anti-)patterns, idioms 6 | and other explanations. It is a compilation of collective (sometimes implicit) 7 | knowledge as well as experiences that have emerged through collaborative work. 8 | 9 | The patterns described here are **not rules**, but should be taken as guidelines 10 | for writing idiomatic code in Rust. We are collecting Rust patterns in this book 11 | so people can learn the tradeoffs between Rust idioms and use them properly in 12 | their own code. 13 | 14 | If you want to be part of this effort here are some ways you can participate: 15 | 16 | ## Discussion board 17 | 18 | If you have a question or an idea regarding certain content, but you want to 19 | have feedback of fellow community members, and you think it may not be 20 | appropriate to file an issue open a discussion in our 21 | [discussion board](https://github.com/rust-unofficial/patterns/discussions). 22 | 23 | ## Writing a new article 24 | 25 | Before writing a new article please check in one of the following resources if 26 | there is an existing discussion or if someone is already working on that topic: 27 | 28 | - [Umbrella issue](https://github.com/rust-unofficial/patterns/issues/116), 29 | - [All issues](https://github.com/rust-unofficial/patterns/issues), 30 | - [Pull Requests](https://github.com/rust-unofficial/patterns/pulls) 31 | 32 | If you don't find an issue regarding your topic, and you are sure it is not more 33 | feasible to open a thread in the 34 | [discussion board](https://github.com/rust-unofficial/patterns/discussions) 35 | please open a new issue, so we can discuss the ideas and future content of the 36 | article together and maybe give some feedback/input on it. 37 | 38 | When writing a new article it's recommended to copy the 39 | [pattern template](https://github.com/rust-unofficial/patterns/blob/master/template.md) 40 | into the appropriate directory and start editing it. You may not want to fill 41 | out every section and remove it, or you might want to add extra sections. 42 | 43 | Consider writing your article in a way that has a low barrier of entry so also 44 | [Rustlings](https://github.com/rust-lang/rustlings) can follow and understand 45 | the thought process behind it. So we can encourage people to use these patterns 46 | early on. 47 | 48 | We encourage you to write idiomatic Rust code that builds in the 49 | [playground](https://play.rust-lang.org/). 50 | 51 | If you use links to blogposts or in general content that is not to be sure 52 | existing in a few years (e.g. pdfs) please take a snapshot with the 53 | [Wayback Machine](https://web.archive.org/) and use the link to that snapshot in 54 | your article. 55 | 56 | Don't forget to add your new article to the `SUMMARY.md` to let it be rendered 57 | to the book. 58 | 59 | Please make `Draft Pull requests` early, so we can follow your progress and can 60 | give early feedback (see the following section). 61 | 62 | ## Style guide 63 | 64 | In order to have a consistent style across the book, we suggest to: 65 | 66 | - Follow the official Rust book's 67 | [style guide](https://github.com/rust-lang/book/blob/master/style-guide.md). 68 | - Follow 69 | [RFC 1574](https://github.com/rust-lang/rfcs/blob/master/text/1574-more-api-documentation-conventions.md#appendix-a-full-conventions-text). 70 | Tl;dr: 71 | - Prefer full types name. For example `Option` instead of `Option`. 72 | - Prefer line comments (`//`) over block comments (`/* */`) where applicable. 73 | 74 | ## Check the article locally 75 | 76 | Before submitting the PR launch the commands `mdbook build` to make sure that 77 | the book builds and `mdbook test` to make sure that code examples are correct. 78 | 79 | ### Markdown lint 80 | 81 | To make sure the files comply with our Markdown style we use 82 | [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli). To spare 83 | you some manual work to get through the CI test you can use the following 84 | commands to automatically fix most of the emerging problems when writing 85 | Markdown files. 86 | 87 | - Install: 88 | 89 | ```sh 90 | npm install -g markdownlint-cli 91 | ``` 92 | 93 | - Check all markdown files: 94 | - unix: `markdownlint '**/*.md'` 95 | - windows: `markdownlint **/*.md` 96 | 97 | - Automatically fix basic errors: 98 | - unix: `markdownlint -f '**/*.md'` 99 | - windows: `markdownlint -f **/*.md` 100 | 101 | ## Creating a Pull Request 102 | 103 | "Release early and often!" also applies to pull requests! 104 | 105 | Once your article has some visible work, create a `[WIP]` draft pull request and 106 | give it a description of what you did or want to do. Early reviews of the 107 | community are not meant as an offense but to give feedback. 108 | 109 | A good principle: "Work together, share ideas, teach others." 110 | 111 | ### Important Note 112 | 113 | Please **don't force push** commits in your branch, in order to keep commit 114 | history and make it easier for us to see changes between reviews. 115 | 116 | Make sure to `Allow edits of maintainers` (under the text box) in the PR so 117 | people can actually collaborate on things or fix smaller issues themselves. 118 | 119 | ## Maintainers 120 | 121 | This repository is maintained by the following people: 122 | 123 | - [simonsan](https://github.com/simonsan). 124 | - [marcoieni](https://github.com/marcoieni). Marco only works on the book CI. He 125 | doesn't review the book content and he doesn't receive notifications on new 126 | issues and pull requests. If you want him to have a look at a CI issue, ping 127 | him directly. 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | 375 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Design Patterns 2 | 3 | An open source book about design patterns and idioms in the Rust programming 4 | language that you can read [here](https://rust-unofficial.github.io/patterns/). 5 | 6 | You can also download the book in PDF format from 7 | [this link](https://rust-unofficial.github.io/patterns/rust-design-patterns.pdf). 8 | 9 | ## Contributing 10 | 11 | You are missing content in this repository that can be helpful for others, and 12 | you are eager to explain it? Awesome! We are always happy about new 13 | contributions (e.g. elaboration or corrections on certain topics) to this 14 | project. 15 | 16 | You can check the 17 | [Umbrella issue](https://github.com/rust-unofficial/patterns/issues/116) for all 18 | the patterns, anti-patterns, and idioms that could be added. 19 | 20 | We suggest reading our [Contribution guide](./CONTRIBUTING.md) to get more 21 | information on how contributing to this repository works. 22 | 23 | ## Building with mdbook 24 | 25 | This book is built with [mdbook](https://rust-lang.github.io/mdBook/). You can 26 | install it by running `cargo install mdbook`. 27 | 28 | ### Additional dependencies 29 | 30 | - `cargo install mdbook-last-changed` for date changes in the footer 31 | 32 | - `cargo install mdbook-pandoc` for rendering the book to PDF 33 | 34 | - `cargo install mdbook-i18n-helpers` for translation and i8n support 35 | 36 | #### Texlive 37 | 38 | ```sh 39 | # Source the .env file to get the PANDOC_VERSION 40 | . ./.env 41 | 42 | sudo apt-get update 43 | 44 | sudo apt-get install -y texlive texlive-latex-extra texlive-luatex texlive-lang-cjk librsvg2-bin fonts-noto 45 | 46 | curl -LsSf https://github.com/jgm/pandoc/releases/download/$PANDOC_VERSION/pandoc-$PANDOC_VERSION-linux-amd64.tar.gz | tar zxf - 47 | ``` 48 | 49 | ### Building the book 50 | 51 | If you want to build it locally you can run one of these two commands in the 52 | root directory of the repository: 53 | 54 | - `mdbook build` 55 | 56 | Builds static html pages as output and place them in the `/book` directory by 57 | default. 58 | 59 | - `mdbook serve` 60 | 61 | Serves the book at `http://localhost:3000` (port is changeable, take a look at 62 | the terminal output to be sure) and reloads the browser when a change occurs. 63 | 64 | ## License 65 | 66 | The content of this repository is licensed under **MPL-2.0**; see 67 | [LICENSE](./LICENSE). 68 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "Rust Design Patterns" 3 | authors = ["the rust-unofficial authors"] 4 | description = "A catalogue of Rust design patterns, anti-patterns and idioms" 5 | language = "en" 6 | src = "src" 7 | 8 | [build] 9 | create-missing = false 10 | extra-watch-dirs = ["po", "third_party"] 11 | 12 | [preprocessor.gettext] 13 | after = ["links"] 14 | 15 | [rust] 16 | edition = "2021" 17 | 18 | [output.html] 19 | curly-quotes = true 20 | default-theme = "rust" 21 | site-url = "/patterns/" 22 | git-repository-url = "https://github.com/rust-unofficial/patterns" 23 | git-repository-icon = "fa-github" 24 | edit-url-template = "https://github.com/rust-unofficial/patterns/edit/main/{path}" 25 | additional-css = [ 26 | "./theme/css/language-picker.css", 27 | "./styles/last-changed.css", 28 | ] 29 | 30 | [preprocessor.last-changed] 31 | command = "mdbook-last-changed" 32 | renderer = ["html"] 33 | 34 | [output.html.fold] 35 | enable = true 36 | level = 1 37 | 38 | [output.html.playground] 39 | editable = false 40 | 41 | # [output.linkcheck] # enable the "mdbook-linkcheck" renderer, disabled due to gh-actions 42 | 43 | [output.html.redirect] 44 | # Redirects in the form of "old-path" = "new-path", where the new path 45 | # is relative to the old path. 46 | "functional/lenses.html" = "optics.html" 47 | 48 | [output.pandoc] 49 | optional = true 50 | hosted-html = "https://rust-unofficial.github.io/patterns/" 51 | 52 | [output.pandoc.profile.pdf] 53 | output-file = "rust-design-patterns.pdf" 54 | pdf-engine = "lualatex" 55 | 56 | [output.pandoc.profile.pdf.variables] 57 | mainfont = "Noto Serif" 58 | sansfont = "Noto Sans" 59 | monofont = "Noto Sans Mono" 60 | mainfontfallback = ["NotoSerifCJKSC:"] 61 | geometry = ["margin=1in"] 62 | linkcolor = "blue" 63 | urlcolor = "red" 64 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](./intro.md) 4 | - [Translations](./translations.md) 5 | - [Idioms](./idioms/index.md) 6 | - [Use borrowed types for arguments](./idioms/coercion-arguments.md) 7 | - [Concatenating Strings with format!](./idioms/concat-format.md) 8 | - [Constructor](./idioms/ctor.md) 9 | - [The Default Trait](./idioms/default.md) 10 | - [Collections Are Smart Pointers](./idioms/deref.md) 11 | - [Finalisation in Destructors](./idioms/dtor-finally.md) 12 | - [`mem::{take(_), replace(_)}`](./idioms/mem-replace.md) 13 | - [On-Stack Dynamic Dispatch](./idioms/on-stack-dyn-dispatch.md) 14 | - [Foreign function interface (FFI)](./idioms/ffi/intro.md) 15 | - [Idiomatic Errors](./idioms/ffi/errors.md) 16 | - [Accepting Strings](./idioms/ffi/accepting-strings.md) 17 | - [Passing Strings](./idioms/ffi/passing-strings.md) 18 | - [Iterating over an Option](./idioms/option-iter.md) 19 | - [Pass Variables to Closure](./idioms/pass-var-to-closure.md) 20 | - [Privacy For Extensibility](./idioms/priv-extend.md) 21 | - [Easy doc initialization](./idioms/rustdoc-init.md) 22 | - [Temporary mutability](./idioms/temporary-mutability.md) 23 | - [Return consumed arg on error](./idioms/return-consumed-arg-on-error.md) 24 | 25 | - [Design Patterns](./patterns/index.md) 26 | - [Behavioural](./patterns/behavioural/intro.md) 27 | - [Command](./patterns/behavioural/command.md) 28 | - [Interpreter](./patterns/behavioural/interpreter.md) 29 | - [Newtype](./patterns/behavioural/newtype.md) 30 | - [RAII Guards](./patterns/behavioural/RAII.md) 31 | - [Strategy](./patterns/behavioural/strategy.md) 32 | - [Visitor](./patterns/behavioural/visitor.md) 33 | - [Creational](./patterns/creational/intro.md) 34 | - [Builder](./patterns/creational/builder.md) 35 | - [Fold](./patterns/creational/fold.md) 36 | - [Structural](./patterns/structural/intro.md) 37 | - [Compose Structs](./patterns/structural/compose-structs.md) 38 | - [Prefer Small Crates](./patterns/structural/small-crates.md) 39 | - [Contain unsafety in small modules](./patterns/structural/unsafe-mods.md) 40 | - [Foreign function interface (FFI)](./patterns/ffi/intro.md) 41 | - [Object-Based APIs](./patterns/ffi/export.md) 42 | - [Type Consolidation into Wrappers](./patterns/ffi/wrappers.md) 43 | 44 | - [Anti-patterns](./anti_patterns/index.md) 45 | - [Clone to satisfy the borrow checker](./anti_patterns/borrow_clone.md) 46 | - [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md) 47 | - [Deref Polymorphism](./anti_patterns/deref.md) 48 | 49 | - [Functional Programming](./functional/index.md) 50 | - [Programming paradigms](./functional/paradigms.md) 51 | - [Generics as Type Classes](./functional/generics-type-classes.md) 52 | - [Functional Optics](./functional/optics.md) 53 | 54 | - [Additional Resources](./additional_resources/index.md) 55 | - [Design principles](./additional_resources/design-principles.md) 56 | -------------------------------------------------------------------------------- /src/additional_resources/design-principles.md: -------------------------------------------------------------------------------- 1 | # Design principles 2 | 3 | ## A brief overview over common design principles 4 | 5 | --- 6 | 7 | ## [SOLID](https://en.wikipedia.org/wiki/SOLID) 8 | 9 | - [Single Responsibility Principle (SRP)](https://en.wikipedia.org/wiki/Single-responsibility_principle): 10 | A class should only have a single responsibility, that is, only changes to one 11 | part of the software's specification should be able to affect the 12 | specification of the class. 13 | - [Open/Closed Principle (OCP)](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle): 14 | "Software entities ... should be open for extension, but closed for 15 | modification." 16 | - [Liskov Substitution Principle (LSP)](https://en.wikipedia.org/wiki/Liskov_substitution_principle): 17 | "Objects in a program should be replaceable with instances of their subtypes 18 | without altering the correctness of that program." 19 | - [Interface Segregation Principle (ISP)](https://en.wikipedia.org/wiki/Interface_segregation_principle): 20 | "Many client-specific interfaces are better than one general-purpose 21 | interface." 22 | - [Dependency Inversion Principle (DIP)](https://en.wikipedia.org/wiki/Dependency_inversion_principle): 23 | One should "depend upon abstractions, [not] concretions." 24 | 25 | ## [CRP (Composite Reuse Principle) or Composition over inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance) 26 | 27 | “a the principle that classes should favor polymorphic behavior and code reuse 28 | by their composition (by containing instances of other classes that implement 29 | the desired functionality) over inheritance from a base or parent class” - 30 | Knoernschild, Kirk (2002). Java Design - Objects, UML, and Process 31 | 32 | ## [DRY (Don’t Repeat Yourself)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 33 | 34 | "Every piece of knowledge must have a single, unambiguous, authoritative 35 | representation within a system" 36 | 37 | ## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle) 38 | 39 | most systems work best if they are kept simple rather than made complicated; 40 | therefore, simplicity should be a key goal in design, and unnecessary complexity 41 | should be avoided 42 | 43 | ## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter) 44 | 45 | a given object should assume as little as possible about the structure or 46 | properties of anything else (including its subcomponents), in accordance with 47 | the principle of "information hiding" 48 | 49 | ## [Design by contract (DbC)](https://en.wikipedia.org/wiki/Design_by_contract) 50 | 51 | software designers should define formal, precise and verifiable interface 52 | specifications for software components, which extend the ordinary definition of 53 | abstract data types with preconditions, postconditions and invariants 54 | 55 | ## [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)) 56 | 57 | bundling of data with the methods that operate on that data, or the restricting 58 | of direct access to some of an object's components. Encapsulation is used to 59 | hide the values or state of a structured data object inside a class, preventing 60 | unauthorized parties' direct access to them. 61 | 62 | ## [Command-Query-Separation (CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation) 63 | 64 | “Functions should not produce abstract side effects...only commands (procedures) 65 | will be permitted to produce side effects.” - Bertrand Meyer: Object-Oriented 66 | Software Construction 67 | 68 | ## [Principle of least astonishment (POLA)](https://en.wikipedia.org/wiki/Principle_of_least_astonishment) 69 | 70 | a component of a system should behave in a way that most users will expect it to 71 | behave. The behavior should not astonish or surprise users 72 | 73 | ## Linguistic-Modular-Units 74 | 75 | “Modules must correspond to syntactic units in the language used.” - Bertrand 76 | Meyer: Object-Oriented Software Construction 77 | 78 | ## Self-Documentation 79 | 80 | “The designer of a module should strive to make all information about the module 81 | part of the module itself.” - Bertrand Meyer: Object-Oriented Software 82 | Construction 83 | 84 | ## Uniform-Access 85 | 86 | “All services offered by a module should be available through a uniform 87 | notation, which does not betray whether they are implemented through storage or 88 | through computation.” - Bertrand Meyer: Object-Oriented Software Construction 89 | 90 | ## Single-Choice 91 | 92 | “Whenever a software system must support a set of alternatives, one and only one 93 | module in the system should know their exhaustive list.” - Bertrand Meyer: 94 | Object-Oriented Software Construction 95 | 96 | ## Persistence-Closure 97 | 98 | “Whenever a storage mechanism stores an object, it must store with it the 99 | dependents of that object. Whenever a retrieval mechanism retrieves a previously 100 | stored object, it must also retrieve any dependent of that object that has not 101 | yet been retrieved.” - Bertrand Meyer: Object-Oriented Software Construction 102 | -------------------------------------------------------------------------------- /src/additional_resources/index.md: -------------------------------------------------------------------------------- 1 | # Additional resources 2 | 3 | A collection of complementary helpful content 4 | 5 | ## Talks 6 | 7 | - [Design Patterns in Rust](https://www.youtube.com/watch?v=Pm_oO0N5B9k) by 8 | Nicholas Cameron at the PDRust (2016) 9 | - [Writing Idiomatic Libraries in Rust](https://www.youtube.com/watch?v=0zOg8_B71gE) 10 | by Pascal Hertleif at RustFest (2017) 11 | - [Rust Programming Techniques](https://www.youtube.com/watch?v=vqavdUGKeb4) by 12 | Nicholas Cameron at LinuxConfAu (2018) 13 | 14 | ## Books (Online) 15 | 16 | - [The Rust API Guidelines](https://rust-lang.github.io/api-guidelines) 17 | -------------------------------------------------------------------------------- /src/anti_patterns/borrow_clone.md: -------------------------------------------------------------------------------- 1 | # Clone to satisfy the borrow checker 2 | 3 | ## Description 4 | 5 | The borrow checker prevents Rust users from developing otherwise unsafe code by 6 | ensuring that either: only one mutable reference exists, or potentially many but 7 | all immutable references exist. If the code written does not hold true to these 8 | conditions, this anti-pattern arises when the developer resolves the compiler 9 | error by cloning the variable. 10 | 11 | ## Example 12 | 13 | ```rust 14 | // define any variable 15 | let mut x = 5; 16 | 17 | // Borrow `x` -- but clone it first 18 | let y = &mut (x.clone()); 19 | 20 | // without the x.clone() two lines prior, this line would fail on compile as 21 | // x has been borrowed 22 | // thanks to x.clone(), x was never borrowed, and this line will run. 23 | println!("{x}"); 24 | 25 | // perform some action on the borrow to prevent rust from optimizing this 26 | //out of existence 27 | *y += 1; 28 | ``` 29 | 30 | ## Motivation 31 | 32 | It is tempting, particularly for beginners, to use this pattern to resolve 33 | confusing issues with the borrow checker. However, there are serious 34 | consequences. Using `.clone()` causes a copy of the data to be made. Any changes 35 | between the two are not synchronized -- as if two completely separate variables 36 | exist. 37 | 38 | There are special cases -- `Rc` is designed to handle clones intelligently. 39 | It internally manages exactly one copy of the data. Invoking `.clone()` on `Rc` 40 | produces a new `Rc` instance, which points to the same data as the source `Rc`, 41 | while increasing a reference count. The same applies to `Arc`, the thread-safe 42 | counterpart of `Rc`. 43 | 44 | In general, clones should be deliberate, with full understanding of the 45 | consequences. If a clone is used to make a borrow checker error disappear, 46 | that's a good indication this anti-pattern may be in use. 47 | 48 | Even though `.clone()` is an indication of a bad pattern, sometimes **it is fine 49 | to write inefficient code**, in cases such as when: 50 | 51 | - the developer is still new to ownership 52 | - the code doesn't have great speed or memory constraints (like hackathon 53 | projects or prototypes) 54 | - satisfying the borrow checker is really complicated, and you prefer to 55 | optimize readability over performance 56 | 57 | If an unnecessary clone is suspected, The 58 | [Rust Book's chapter on Ownership](https://doc.rust-lang.org/book/ownership.html) 59 | should be understood fully before assessing whether the clone is required or 60 | not. 61 | 62 | Also be sure to always run `cargo clippy` in your project, which will detect 63 | some cases in which `.clone()` is not necessary. 64 | 65 | ## See also 66 | 67 | - [`mem::{take(_), replace(_)}` to keep owned values in changed enums](../idioms/mem-replace.md) 68 | - [`Rc` documentation, which handles .clone() intelligently](http://doc.rust-lang.org/std/rc/) 69 | - [`Arc` documentation, a thread-safe reference-counting pointer](https://doc.rust-lang.org/std/sync/struct.Arc.html) 70 | - [Tricks with ownership in Rust](https://web.archive.org/web/20210120233744/https://xion.io/post/code/rust-borrowchk-tricks.html) 71 | -------------------------------------------------------------------------------- /src/anti_patterns/deny-warnings.md: -------------------------------------------------------------------------------- 1 | # `#![deny(warnings)]` 2 | 3 | ## Description 4 | 5 | A well-intentioned crate author wants to ensure their code builds without 6 | warnings. So they annotate their crate root with the following: 7 | 8 | ## Example 9 | 10 | ```rust 11 | #![deny(warnings)] 12 | 13 | // All is well. 14 | ``` 15 | 16 | ## Advantages 17 | 18 | It is short and will stop the build if anything is amiss. 19 | 20 | ## Drawbacks 21 | 22 | By disallowing the compiler to build with warnings, a crate author opts out of 23 | Rust's famed stability. Sometimes new features or old misfeatures need a change 24 | in how things are done, thus lints are written that `warn` for a certain grace 25 | period before being turned to `deny`. 26 | 27 | For example, it was discovered that a type could have two `impl`s with the same 28 | method. This was deemed a bad idea, but in order to make the transition smooth, 29 | the `overlapping-inherent-impls` lint was introduced to give a warning to those 30 | stumbling on this fact, before it becomes a hard error in a future release. 31 | 32 | Also sometimes APIs get deprecated, so their use will emit a warning where 33 | before there was none. 34 | 35 | All this conspires to potentially break the build whenever something changes. 36 | 37 | Furthermore, crates that supply additional lints (e.g. [rust-clippy]) can no 38 | longer be used unless the annotation is removed. This is mitigated with 39 | [--cap-lints]. The `--cap-lints=warn` command line argument, turns all `deny` 40 | lint errors into warnings. 41 | 42 | ## Alternatives 43 | 44 | There are two ways of tackling this problem: First, we can decouple the build 45 | setting from the code, and second, we can name the lints we want to deny 46 | explicitly. 47 | 48 | The following command line will build with all warnings set to `deny`: 49 | 50 | `RUSTFLAGS="-D warnings" cargo build` 51 | 52 | This can be done by any individual developer (or be set in a CI tool like 53 | Travis, but remember that this may break the build when something changes) 54 | without requiring a change to the code. 55 | 56 | Alternatively, we can specify the lints that we want to `deny` in the code. Here 57 | is a list of warning lints that is (hopefully) safe to deny (as of rustc 58 | 1.48.0): 59 | 60 | ```rust,ignore 61 | #![deny( 62 | bad_style, 63 | const_err, 64 | dead_code, 65 | improper_ctypes, 66 | non_shorthand_field_patterns, 67 | no_mangle_generic_items, 68 | overflowing_literals, 69 | path_statements, 70 | patterns_in_fns_without_body, 71 | private_in_public, 72 | unconditional_recursion, 73 | unused, 74 | unused_allocation, 75 | unused_comparisons, 76 | unused_parens, 77 | while_true 78 | )] 79 | ``` 80 | 81 | In addition, the following `allow`ed lints may be a good idea to `deny`: 82 | 83 | ```rust,ignore 84 | #![deny( 85 | missing_debug_implementations, 86 | missing_docs, 87 | trivial_casts, 88 | trivial_numeric_casts, 89 | unused_extern_crates, 90 | unused_import_braces, 91 | unused_qualifications, 92 | unused_results 93 | )] 94 | ``` 95 | 96 | Some may also want to add `missing-copy-implementations` to their list. 97 | 98 | Note that we explicitly did not add the `deprecated` lint, as it is fairly 99 | certain that there will be more deprecated APIs in the future. 100 | 101 | ## See also 102 | 103 | - [A collection of all clippy lints](https://rust-lang.github.io/rust-clippy/master) 104 | - [deprecate attribute] documentation 105 | - Type `rustc -W help` for a list of lints on your system. Also type 106 | `rustc --help` for a general list of options 107 | - [rust-clippy] is a collection of lints for better Rust code 108 | 109 | [rust-clippy]: https://github.com/rust-lang/rust-clippy 110 | [deprecate attribute]: https://doc.rust-lang.org/reference/attributes.html#deprecation 111 | [--cap-lints]: https://doc.rust-lang.org/rustc/lints/levels.html#capping-lints 112 | -------------------------------------------------------------------------------- /src/anti_patterns/deref.md: -------------------------------------------------------------------------------- 1 | # `Deref` polymorphism 2 | 3 | ## Description 4 | 5 | Misuse the `Deref` trait to emulate inheritance between structs, and thus reuse 6 | methods. 7 | 8 | ## Example 9 | 10 | Sometimes we want to emulate the following common pattern from OO languages such 11 | as Java: 12 | 13 | ```java 14 | class Foo { 15 | void m() { ... } 16 | } 17 | 18 | class Bar extends Foo {} 19 | 20 | public static void main(String[] args) { 21 | Bar b = new Bar(); 22 | b.m(); 23 | } 24 | ``` 25 | 26 | We can use the deref polymorphism anti-pattern to do so: 27 | 28 | ```rust 29 | use std::ops::Deref; 30 | 31 | struct Foo {} 32 | 33 | impl Foo { 34 | fn m(&self) { 35 | //.. 36 | } 37 | } 38 | 39 | struct Bar { 40 | f: Foo, 41 | } 42 | 43 | impl Deref for Bar { 44 | type Target = Foo; 45 | fn deref(&self) -> &Foo { 46 | &self.f 47 | } 48 | } 49 | 50 | fn main() { 51 | let b = Bar { f: Foo {} }; 52 | b.m(); 53 | } 54 | ``` 55 | 56 | There is no struct inheritance in Rust. Instead we use composition and include 57 | an instance of `Foo` in `Bar` (since the field is a value, it is stored inline, 58 | so if there were fields, they would have the same layout in memory as the Java 59 | version (probably, you should use `#[repr(C)]` if you want to be sure)). 60 | 61 | In order to make the method call work we implement `Deref` for `Bar` with `Foo` 62 | as the target (returning the embedded `Foo` field). That means that when we 63 | dereference a `Bar` (for example, using `*`) then we will get a `Foo`. That is 64 | pretty weird. Dereferencing usually gives a `T` from a reference to `T`, here we 65 | have two unrelated types. However, since the dot operator does implicit 66 | dereferencing, it means that the method call will search for methods on `Foo` as 67 | well as `Bar`. 68 | 69 | ## Advantages 70 | 71 | You save a little boilerplate, e.g., 72 | 73 | ```rust,ignore 74 | impl Bar { 75 | fn m(&self) { 76 | self.f.m() 77 | } 78 | } 79 | ``` 80 | 81 | ## Disadvantages 82 | 83 | Most importantly this is a surprising idiom - future programmers reading this in 84 | code will not expect this to happen. That's because we are misusing the `Deref` 85 | trait rather than using it as intended (and documented, etc.). It's also because 86 | the mechanism here is completely implicit. 87 | 88 | This pattern does not introduce subtyping between `Foo` and `Bar` like 89 | inheritance in Java or C++ does. Furthermore, traits implemented by `Foo` are 90 | not automatically implemented for `Bar`, so this pattern interacts badly with 91 | bounds checking and thus generic programming. 92 | 93 | Using this pattern gives subtly different semantics from most OO languages with 94 | regards to `self`. Usually it remains a reference to the sub-class, with this 95 | pattern it will be the 'class' where the method is defined. 96 | 97 | Finally, this pattern only supports single inheritance, and has no notion of 98 | interfaces, class-based privacy, or other inheritance-related features. So, it 99 | gives an experience that will be subtly surprising to programmers used to Java 100 | inheritance, etc. 101 | 102 | ## Discussion 103 | 104 | There is no one good alternative. Depending on the exact circumstances it might 105 | be better to re-implement using traits or to write out the facade methods to 106 | dispatch to `Foo` manually. We do intend to add a mechanism for inheritance 107 | similar to this to Rust, but it is likely to be some time before it reaches 108 | stable Rust. See these [blog](http://aturon.github.io/blog/2015/09/18/reuse/) 109 | [posts](http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-structs-part-4-extended-enums-and-thin-traits/) 110 | and this [RFC issue](https://github.com/rust-lang/rfcs/issues/349) for more 111 | details. 112 | 113 | The `Deref` trait is designed for the implementation of custom pointer types. 114 | The intention is that it will take a pointer-to-`T` to a `T`, not convert 115 | between different types. It is a shame that this isn't (probably cannot be) 116 | enforced by the trait definition. 117 | 118 | Rust tries to strike a careful balance between explicit and implicit mechanisms, 119 | favouring explicit conversions between types. Automatic dereferencing in the dot 120 | operator is a case where the ergonomics strongly favour an implicit mechanism, 121 | but the intention is that this is limited to degrees of indirection, not 122 | conversion between arbitrary types. 123 | 124 | ## See also 125 | 126 | - [Collections are smart pointers idiom](../idioms/deref.md). 127 | - Delegation crates for less boilerplate like 128 | [delegate](https://crates.io/crates/delegate) or 129 | [ambassador](https://crates.io/crates/ambassador) 130 | - [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html). 131 | -------------------------------------------------------------------------------- /src/anti_patterns/index.md: -------------------------------------------------------------------------------- 1 | # Anti-patterns 2 | 3 | An [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) is a solution to a 4 | "recurring problem that is usually ineffective and risks being highly 5 | counterproductive". Just as valuable as knowing how to solve a problem, is 6 | knowing how *not* to solve it. Anti-patterns give us great counter-examples to 7 | consider relative to design patterns. Anti-patterns are not confined to code. 8 | For example, a process can be an anti-pattern, too. 9 | -------------------------------------------------------------------------------- /src/functional/generics-type-classes.md: -------------------------------------------------------------------------------- 1 | # Generics as Type Classes 2 | 3 | ## Description 4 | 5 | Rust's type system is designed more like functional languages (like Haskell) 6 | rather than imperative languages (like Java and C++). As a result, Rust can turn 7 | many kinds of programming problems into "static typing" problems. This is one of 8 | the biggest wins of choosing a functional language, and is critical to many of 9 | Rust's compile time guarantees. 10 | 11 | A key part of this idea is the way generic types work. In C++ and Java, for 12 | example, generic types are a meta-programming construct for the compiler. 13 | `vector` and `vector` in C++ are just two different copies of the 14 | same boilerplate code for a `vector` type (known as a `template`) with two 15 | different types filled in. 16 | 17 | In Rust, a generic type parameter creates what is known in functional languages 18 | as a "type class constraint", and each different parameter filled in by an end 19 | user *actually changes the type*. In other words, `Vec` and `Vec` 20 | *are two different types*, which are recognized as distinct by all parts of the 21 | type system. 22 | 23 | This is called **monomorphization**, where different types are created from 24 | **polymorphic** code. This special behavior requires `impl` blocks to specify 25 | generic parameters. Different values for the generic type cause different types, 26 | and different types can have different `impl` blocks. 27 | 28 | In object-oriented languages, classes can inherit behavior from their parents. 29 | However, this allows the attachment of not only additional behavior to 30 | particular members of a type class, but extra behavior as well. 31 | 32 | The nearest equivalent is the runtime polymorphism in Javascript and Python, 33 | where new members can be added to objects willy-nilly by any constructor. 34 | However, unlike those languages, all of Rust's additional methods can be type 35 | checked when they are used, because their generics are statically defined. That 36 | makes them more usable while remaining safe. 37 | 38 | ## Example 39 | 40 | Suppose you are designing a storage server for a series of lab machines. Because 41 | of the software involved, there are two different protocols you need to support: 42 | BOOTP (for PXE network boot), and NFS (for remote mount storage). 43 | 44 | Your goal is to have one program, written in Rust, which can handle both of 45 | them. It will have protocol handlers and listen for both kinds of requests. The 46 | main application logic will then allow a lab administrator to configure storage 47 | and security controls for the actual files. 48 | 49 | The requests from machines in the lab for files contain the same basic 50 | information, no matter what protocol they came from: an authentication method, 51 | and a file name to retrieve. A straightforward implementation would look 52 | something like this: 53 | 54 | ```rust,ignore 55 | enum AuthInfo { 56 | Nfs(crate::nfs::AuthInfo), 57 | Bootp(crate::bootp::AuthInfo), 58 | } 59 | 60 | struct FileDownloadRequest { 61 | file_name: PathBuf, 62 | authentication: AuthInfo, 63 | } 64 | ``` 65 | 66 | This design might work well enough. But now suppose you needed to support adding 67 | metadata that was *protocol specific*. For example, with NFS, you wanted to 68 | determine what their mount point was in order to enforce additional security 69 | rules. 70 | 71 | The way the current struct is designed leaves the protocol decision until 72 | runtime. That means any method that applies to one protocol and not the other 73 | requires the programmer to do a runtime check. 74 | 75 | Here is how getting an NFS mount point would look: 76 | 77 | ```rust,ignore 78 | struct FileDownloadRequest { 79 | file_name: PathBuf, 80 | authentication: AuthInfo, 81 | mount_point: Option, 82 | } 83 | 84 | impl FileDownloadRequest { 85 | // ... other methods ... 86 | 87 | /// Gets an NFS mount point if this is an NFS request. Otherwise, 88 | /// return None. 89 | pub fn mount_point(&self) -> Option<&Path> { 90 | self.mount_point.as_ref() 91 | } 92 | } 93 | ``` 94 | 95 | Every caller of `mount_point()` must check for `None` and write code to handle 96 | it. This is true even if they know only NFS requests are ever used in a given 97 | code path! 98 | 99 | It would be far more optimal to cause a compile-time error if the different 100 | request types were confused. After all, the entire path of the user's code, 101 | including what functions from the library they use, will know whether a request 102 | is an NFS request or a BOOTP request. 103 | 104 | In Rust, this is actually possible! The solution is to *add a generic type* in 105 | order to split the API. 106 | 107 | Here is what that looks like: 108 | 109 | ```rust 110 | use std::path::{Path, PathBuf}; 111 | 112 | mod nfs { 113 | #[derive(Clone)] 114 | pub(crate) struct AuthInfo(String); // NFS session management omitted 115 | } 116 | 117 | mod bootp { 118 | pub(crate) struct AuthInfo(); // no authentication in bootp 119 | } 120 | 121 | // private module, lest outside users invent their own protocol kinds! 122 | mod proto_trait { 123 | use super::{bootp, nfs}; 124 | use std::path::{Path, PathBuf}; 125 | 126 | pub(crate) trait ProtoKind { 127 | type AuthInfo; 128 | fn auth_info(&self) -> Self::AuthInfo; 129 | } 130 | 131 | pub struct Nfs { 132 | auth: nfs::AuthInfo, 133 | mount_point: PathBuf, 134 | } 135 | 136 | impl Nfs { 137 | pub(crate) fn mount_point(&self) -> &Path { 138 | &self.mount_point 139 | } 140 | } 141 | 142 | impl ProtoKind for Nfs { 143 | type AuthInfo = nfs::AuthInfo; 144 | fn auth_info(&self) -> Self::AuthInfo { 145 | self.auth.clone() 146 | } 147 | } 148 | 149 | pub struct Bootp(); // no additional metadata 150 | 151 | impl ProtoKind for Bootp { 152 | type AuthInfo = bootp::AuthInfo; 153 | fn auth_info(&self) -> Self::AuthInfo { 154 | bootp::AuthInfo() 155 | } 156 | } 157 | } 158 | 159 | use proto_trait::ProtoKind; // keep internal to prevent impls 160 | pub use proto_trait::{Bootp, Nfs}; // re-export so callers can see them 161 | 162 | struct FileDownloadRequest { 163 | file_name: PathBuf, 164 | protocol: P, 165 | } 166 | 167 | // all common API parts go into a generic impl block 168 | impl FileDownloadRequest

{ 169 | fn file_path(&self) -> &Path { 170 | &self.file_name 171 | } 172 | 173 | fn auth_info(&self) -> P::AuthInfo { 174 | self.protocol.auth_info() 175 | } 176 | } 177 | 178 | // all protocol-specific impls go into their own block 179 | impl FileDownloadRequest { 180 | fn mount_point(&self) -> &Path { 181 | self.protocol.mount_point() 182 | } 183 | } 184 | 185 | fn main() { 186 | // your code here 187 | } 188 | ``` 189 | 190 | With this approach, if the user were to make a mistake and use the wrong type; 191 | 192 | ```rust,ignore 193 | fn main() { 194 | let mut socket = crate::bootp::listen()?; 195 | while let Some(request) = socket.next_request()? { 196 | match request.mount_point().as_ref() { 197 | "/secure" => socket.send("Access denied"), 198 | _ => {} // continue on... 199 | } 200 | // Rest of the code here 201 | } 202 | } 203 | ``` 204 | 205 | They would get a syntax error. The type `FileDownloadRequest` does not 206 | implement `mount_point()`, only the type `FileDownloadRequest` does. And 207 | that is created by the NFS module, not the BOOTP module of course! 208 | 209 | ## Advantages 210 | 211 | First, it allows fields that are common to multiple states to be de-duplicated. 212 | By making the non-shared fields generic, they are implemented once. 213 | 214 | Second, it makes the `impl` blocks easier to read, because they are broken down 215 | by state. Methods common to all states are typed once in one block, and methods 216 | unique to one state are in a separate block. 217 | 218 | Both of these mean there are fewer lines of code, and they are better organized. 219 | 220 | ## Disadvantages 221 | 222 | This currently increases the size of the binary, due to the way monomorphization 223 | is implemented in the compiler. Hopefully the implementation will be able to 224 | improve in the future. 225 | 226 | ## Alternatives 227 | 228 | - If a type seems to need a "split API" due to construction or partial 229 | initialization, consider the 230 | [Builder Pattern](../patterns/creational/builder.md) instead. 231 | 232 | - If the API between types does not change -- only the behavior does -- then the 233 | [Strategy Pattern](../patterns/behavioural/strategy.md) is better used 234 | instead. 235 | 236 | ## See also 237 | 238 | This pattern is used throughout the standard library: 239 | 240 | - `Vec` can be cast from a String, unlike every other type of `Vec`.[^1] 241 | - Iterators can be cast into a binary heap, but only if they contain a type that 242 | implements the `Ord` trait.[^2] 243 | - The `to_string` method was specialized for `Cow` only of type `str`.[^3] 244 | 245 | It is also used by several popular crates to allow API flexibility: 246 | 247 | - The `embedded-hal` ecosystem used for embedded devices makes extensive use of 248 | this pattern. For example, it allows statically verifying the configuration of 249 | device registers used to control embedded pins. When a pin is put into a mode, 250 | it returns a `Pin` struct, whose generic determines the functions usable 251 | in that mode, which are not on the `Pin` itself. [^4] 252 | 253 | - The `hyper` HTTP client library uses this to expose rich APIs for different 254 | pluggable requests. Clients with different connectors have different methods 255 | on them as well as different trait implementations, while a core set of 256 | methods apply to any connector. [^5] 257 | 258 | - The "type state" pattern -- where an object gains and loses API based on an 259 | internal state or invariant -- is implemented in Rust using the same basic 260 | concept, and a slightly different technique. [^6] 261 | 262 | [^1]: See: 263 | [impl From\ for Vec\](https://doc.rust-lang.org/1.59.0/src/std/ffi/c_str.rs.html#803-811) 264 | 265 | [^2]: See: 266 | [impl\ FromIterator\ for BinaryHeap\](https://web.archive.org/web/20201030132806/https://doc.rust-lang.org/stable/src/alloc/collections/binary_heap.rs.html#1330-1335) 267 | 268 | [^3]: See: 269 | [impl\<'\_\> ToString for Cow\<'\_, str>](https://doc.rust-lang.org/stable/src/alloc/string.rs.html#2235-2240) 270 | 271 | [^4]: Example: 272 | [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) 273 | 274 | [^5]: See: 275 | [https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html](https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html) 276 | 277 | [^6]: See: 278 | [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/) 279 | and 280 | [Rusty Typestate Series (an extensive thesis)](https://web.archive.org/web/20210328164854/https://rustype.github.io/notes/notes/rust-typestate-series/rust-typestate-index) 281 | -------------------------------------------------------------------------------- /src/functional/index.md: -------------------------------------------------------------------------------- 1 | # Functional Usage of Rust 2 | 3 | Rust is an imperative language, but it follows many 4 | [functional programming](https://en.wikipedia.org/wiki/Functional_programming) 5 | paradigms. 6 | 7 | > In computer science, *functional programming* is a programming paradigm where 8 | > programs are constructed by applying and composing functions. It is a 9 | > declarative programming paradigm in which function definitions are trees of 10 | > expressions that each return a value, rather than a sequence of imperative 11 | > statements which change the state of the program. 12 | -------------------------------------------------------------------------------- /src/functional/paradigms.md: -------------------------------------------------------------------------------- 1 | # Programming paradigms 2 | 3 | One of the biggest hurdles to understanding functional programs when coming from 4 | an imperative background is the shift in thinking. Imperative programs describe 5 | **how** to do something, whereas declarative programs describe **what** to do. 6 | Let's sum the numbers from 1 to 10 to show this. 7 | 8 | ## Imperative 9 | 10 | ```rust 11 | let mut sum = 0; 12 | for i in 1..11 { 13 | sum += i; 14 | } 15 | println!("{sum}"); 16 | ``` 17 | 18 | With imperative programs, we have to play compiler to see what is happening. 19 | Here, we start with a `sum` of `0`. Next, we iterate through the range from 1 20 | to 10. Each time through the loop, we add the corresponding value in the range. 21 | Then we print it out. 22 | 23 | | `i` | `sum` | 24 | | :-: | :---: | 25 | | 1 | 1 | 26 | | 2 | 3 | 27 | | 3 | 6 | 28 | | 4 | 10 | 29 | | 5 | 15 | 30 | | 6 | 21 | 31 | | 7 | 28 | 32 | | 8 | 36 | 33 | | 9 | 45 | 34 | | 10 | 55 | 35 | 36 | This is how most of us start out programming. We learn that a program is a set 37 | of steps. 38 | 39 | ## Declarative 40 | 41 | ```rust 42 | println!("{}", (1..11).fold(0, |a, b| a + b)); 43 | ``` 44 | 45 | Whoa! This is really different! What's going on here? Remember that with 46 | declarative programs we are describing **what** to do, rather than **how** to do 47 | it. `fold` is a function that 48 | [composes](https://en.wikipedia.org/wiki/Function_composition) functions. The 49 | name is a convention from Haskell. 50 | 51 | Here, we are composing functions of addition (this closure: `|a, b| a + b`) with 52 | a range from 1 to 10. The `0` is the starting point, so `a` is `0` at first. `b` 53 | is the first element of the range, `1`. `0 + 1 = 1` is the result. So now we 54 | `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the next result. This 55 | process continues until we get to the last element in the range, `10`. 56 | 57 | | `a` | `b` | result | 58 | | :-: | :-: | :----: | 59 | | 0 | 1 | 1 | 60 | | 1 | 2 | 3 | 61 | | 3 | 3 | 6 | 62 | | 6 | 4 | 10 | 63 | | 10 | 5 | 15 | 64 | | 15 | 6 | 21 | 65 | | 21 | 7 | 28 | 66 | | 28 | 8 | 36 | 67 | | 36 | 9 | 45 | 68 | | 45 | 10 | 55 | 69 | -------------------------------------------------------------------------------- /src/idioms/coercion-arguments.md: -------------------------------------------------------------------------------- 1 | # Use borrowed types for arguments 2 | 3 | ## Description 4 | 5 | Using a target of a deref coercion can increase the flexibility of your code 6 | when you are deciding which argument type to use for a function argument. In 7 | this way, the function will accept more input types. 8 | 9 | This is not limited to slice-able or fat pointer types. In fact, you should 10 | always prefer using the **borrowed type** over **borrowing the owned type**. 11 | Such as `&str` over `&String`, `&[T]` over `&Vec`, or `&T` over `&Box`. 12 | 13 | Using borrowed types you can avoid layers of indirection for those instances 14 | where the owned type already provides a layer of indirection. For instance, a 15 | `String` has a layer of indirection, so a `&String` will have two layers of 16 | indirection. We can avoid this by using `&str` instead, and letting `&String` 17 | coerce to a `&str` whenever the function is invoked. 18 | 19 | ## Example 20 | 21 | For this example, we will illustrate some differences for using `&String` as a 22 | function argument versus using a `&str`, but the ideas apply as well to using 23 | `&Vec` versus using a `&[T]` or using a `&Box` versus a `&T`. 24 | 25 | Consider an example where we wish to determine if a word contains three 26 | consecutive vowels. We don't need to own the string to determine this, so we 27 | will take a reference. 28 | 29 | The code might look something like this: 30 | 31 | ```rust 32 | fn three_vowels(word: &String) -> bool { 33 | let mut vowel_count = 0; 34 | for c in word.chars() { 35 | match c { 36 | 'a' | 'e' | 'i' | 'o' | 'u' => { 37 | vowel_count += 1; 38 | if vowel_count >= 3 { 39 | return true; 40 | } 41 | } 42 | _ => vowel_count = 0, 43 | } 44 | } 45 | false 46 | } 47 | 48 | fn main() { 49 | let ferris = "Ferris".to_string(); 50 | let curious = "Curious".to_string(); 51 | println!("{}: {}", ferris, three_vowels(&ferris)); 52 | println!("{}: {}", curious, three_vowels(&curious)); 53 | 54 | // This works fine, but the following two lines would fail: 55 | // println!("Ferris: {}", three_vowels("Ferris")); 56 | // println!("Curious: {}", three_vowels("Curious")); 57 | } 58 | ``` 59 | 60 | This works fine because we are passing a `&String` type as a parameter. If we 61 | remove the comments on the last two lines, the example will fail. This is 62 | because a `&str` type will not coerce to a `&String` type. We can fix this by 63 | simply modifying the type for our argument. 64 | 65 | For instance, if we change our function declaration to: 66 | 67 | ```rust, ignore 68 | fn three_vowels(word: &str) -> bool { 69 | ``` 70 | 71 | then both versions will compile and print the same output. 72 | 73 | ```bash 74 | Ferris: false 75 | Curious: true 76 | ``` 77 | 78 | But wait, that's not all! There is more to this story. It's likely that you may 79 | say to yourself: that doesn't matter, I will never be using a `&'static str` as 80 | an input anyways (as we did when we used `"Ferris"`). Even ignoring this special 81 | example, you may still find that using `&str` will give you more flexibility 82 | than using a `&String`. 83 | 84 | Let's now take an example where someone gives us a sentence, and we want to 85 | determine if any of the words in the sentence contain three consecutive vowels. 86 | We probably should make use of the function we have already defined and simply 87 | feed in each word from the sentence. 88 | 89 | An example of this could look like this: 90 | 91 | ```rust 92 | fn three_vowels(word: &str) -> bool { 93 | let mut vowel_count = 0; 94 | for c in word.chars() { 95 | match c { 96 | 'a' | 'e' | 'i' | 'o' | 'u' => { 97 | vowel_count += 1; 98 | if vowel_count >= 3 { 99 | return true; 100 | } 101 | } 102 | _ => vowel_count = 0, 103 | } 104 | } 105 | false 106 | } 107 | 108 | fn main() { 109 | let sentence_string = 110 | "Once upon a time, there was a friendly curious crab named Ferris".to_string(); 111 | for word in sentence_string.split(' ') { 112 | if three_vowels(word) { 113 | println!("{word} has three consecutive vowels!"); 114 | } 115 | } 116 | } 117 | ``` 118 | 119 | Running this example using our function declared with an argument type `&str` 120 | will yield 121 | 122 | ```bash 123 | curious has three consecutive vowels! 124 | ``` 125 | 126 | However, this example will not run when our function is declared with an 127 | argument type `&String`. This is because string slices are a `&str` and not a 128 | `&String` which would require an allocation to be converted to `&String` which 129 | is not implicit, whereas converting from `String` to `&str` is cheap and 130 | implicit. 131 | 132 | ## See also 133 | 134 | - [Rust Language Reference on Type Coercions](https://doc.rust-lang.org/reference/type-coercions.html) 135 | - For more discussion on how to handle `String` and `&str` see 136 | [this blog series (2015)](https://web.archive.org/web/20201112023149/https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html) 137 | by Herman J. Radtke III 138 | - [Steve Klabnik's Blogpost on 'When should I use String vs &str?'](https://archive.ph/LBpD0) 139 | -------------------------------------------------------------------------------- /src/idioms/concat-format.md: -------------------------------------------------------------------------------- 1 | # Concatenating strings with `format!` 2 | 3 | ## Description 4 | 5 | It is possible to build up strings using the `push` and `push_str` methods on a 6 | mutable `String`, or using its `+` operator. However, it is often more 7 | convenient to use `format!`, especially where there is a mix of literal and 8 | non-literal strings. 9 | 10 | ## Example 11 | 12 | ```rust 13 | fn say_hello(name: &str) -> String { 14 | // We could construct the result string manually. 15 | // let mut result = "Hello ".to_owned(); 16 | // result.push_str(name); 17 | // result.push('!'); 18 | // result 19 | 20 | // But using format! is better. 21 | format!("Hello {name}!") 22 | } 23 | ``` 24 | 25 | ## Advantages 26 | 27 | Using `format!` is usually the most succinct and readable way to combine 28 | strings. 29 | 30 | ## Disadvantages 31 | 32 | It is usually not the most efficient way to combine strings - a series of `push` 33 | operations on a mutable string is usually the most efficient (especially if the 34 | string has been pre-allocated to the expected size). 35 | -------------------------------------------------------------------------------- /src/idioms/ctor.md: -------------------------------------------------------------------------------- 1 | # Constructors 2 | 3 | ## Description 4 | 5 | Rust does not have constructors as a language construct. Instead, the convention 6 | is to use an [associated function][associated function] `new` to create an 7 | object: 8 | 9 | ````rust 10 | /// Time in seconds. 11 | /// 12 | /// # Example 13 | /// 14 | /// ``` 15 | /// let s = Second::new(42); 16 | /// assert_eq!(42, s.value()); 17 | /// ``` 18 | pub struct Second { 19 | value: u64, 20 | } 21 | 22 | impl Second { 23 | // Constructs a new instance of [`Second`]. 24 | // Note this is an associated function - no self. 25 | pub fn new(value: u64) -> Self { 26 | Self { value } 27 | } 28 | 29 | /// Returns the value in seconds. 30 | pub fn value(&self) -> u64 { 31 | self.value 32 | } 33 | } 34 | ```` 35 | 36 | ## Default Constructors 37 | 38 | Rust supports default constructors with the [`Default`][std-default] trait: 39 | 40 | ````rust 41 | /// Time in seconds. 42 | /// 43 | /// # Example 44 | /// 45 | /// ``` 46 | /// let s = Second::default(); 47 | /// assert_eq!(0, s.value()); 48 | /// ``` 49 | pub struct Second { 50 | value: u64, 51 | } 52 | 53 | impl Second { 54 | /// Returns the value in seconds. 55 | pub fn value(&self) -> u64 { 56 | self.value 57 | } 58 | } 59 | 60 | impl Default for Second { 61 | fn default() -> Self { 62 | Self { value: 0 } 63 | } 64 | } 65 | ```` 66 | 67 | `Default` can also be derived if all types of all fields implement `Default`, 68 | like they do with `Second`: 69 | 70 | ````rust 71 | /// Time in seconds. 72 | /// 73 | /// # Example 74 | /// 75 | /// ``` 76 | /// let s = Second::default(); 77 | /// assert_eq!(0, s.value()); 78 | /// ``` 79 | #[derive(Default)] 80 | pub struct Second { 81 | value: u64, 82 | } 83 | 84 | impl Second { 85 | /// Returns the value in seconds. 86 | pub fn value(&self) -> u64 { 87 | self.value 88 | } 89 | } 90 | ```` 91 | 92 | **Note:** It is common and expected for types to implement both `Default` and an 93 | empty `new` constructor. `new` is the constructor convention in Rust, and users 94 | expect it to exist, so if it is reasonable for the basic constructor to take no 95 | arguments, then it should, even if it is functionally identical to default. 96 | 97 | **Hint:** The advantage of implementing or deriving `Default` is that your type 98 | can now be used where a `Default` implementation is required, most prominently, 99 | any of the [`*or_default` functions in the standard library][std-or-default]. 100 | 101 | ## See also 102 | 103 | - The [default idiom](default.md) for a more in-depth description of the 104 | `Default` trait. 105 | 106 | - The [builder pattern](../patterns/creational/builder.md) for constructing 107 | objects where there are multiple configurations. 108 | 109 | - [API Guidelines/C-COMMON-TRAITS][API Guidelines/C-COMMON-TRAITS] for 110 | implementing both, `Default` and `new`. 111 | 112 | [associated function]: https://doc.rust-lang.org/stable/book/ch05-03-method-syntax.html#associated-functions 113 | [std-default]: https://doc.rust-lang.org/stable/std/default/trait.Default.html 114 | [std-or-default]: https://doc.rust-lang.org/stable/std/?search=or_default 115 | [API Guidelines/C-COMMON-TRAITS]: https://rust-lang.github.io/api-guidelines/interoperability.html#types-eagerly-implement-common-traits-c-common-traits 116 | -------------------------------------------------------------------------------- /src/idioms/default.md: -------------------------------------------------------------------------------- 1 | # The `Default` Trait 2 | 3 | ## Description 4 | 5 | Many types in Rust have a [constructor]. However, this is *specific* to the 6 | type; Rust cannot abstract over "everything that has a `new()` method". To allow 7 | this, the [`Default`] trait was conceived, which can be used with containers and 8 | other generic types (e.g. see [`Option::unwrap_or_default()`]). Notably, some 9 | containers already implement it where applicable. 10 | 11 | Not only do one-element containers like `Cow`, `Box` or `Arc` implement 12 | `Default` for contained `Default` types, one can automatically 13 | `#[derive(Default)]` for structs whose fields all implement it, so the more 14 | types implement `Default`, the more useful it becomes. 15 | 16 | On the other hand, constructors can take multiple arguments, while the 17 | `default()` method does not. There can even be multiple constructors with 18 | different names, but there can only be one `Default` implementation per type. 19 | 20 | ## Example 21 | 22 | ```rust 23 | use std::{path::PathBuf, time::Duration}; 24 | 25 | // note that we can simply auto-derive Default here. 26 | #[derive(Default, Debug, PartialEq)] 27 | struct MyConfiguration { 28 | // Option defaults to None 29 | output: Option, 30 | // Vecs default to empty vector 31 | search_path: Vec, 32 | // Duration defaults to zero time 33 | timeout: Duration, 34 | // bool defaults to false 35 | check: bool, 36 | } 37 | 38 | impl MyConfiguration { 39 | // add setters here 40 | } 41 | 42 | fn main() { 43 | // construct a new instance with default values 44 | let mut conf = MyConfiguration::default(); 45 | // do something with conf here 46 | conf.check = true; 47 | println!("conf = {conf:#?}"); 48 | 49 | // partial initialization with default values, creates the same instance 50 | let conf1 = MyConfiguration { 51 | check: true, 52 | ..Default::default() 53 | }; 54 | assert_eq!(conf, conf1); 55 | } 56 | ``` 57 | 58 | ## See also 59 | 60 | - The [constructor] idiom is another way to generate instances that may or may 61 | not be "default" 62 | - The [`Default`] documentation (scroll down for the list of implementors) 63 | - [`Option::unwrap_or_default()`] 64 | - [`derive(new)`] 65 | 66 | [constructor]: ctor.md 67 | [`Default`]: https://doc.rust-lang.org/stable/std/default/trait.Default.html 68 | [`Option::unwrap_or_default()`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.unwrap_or_default 69 | [`derive(new)`]: https://crates.io/crates/derive-new/ 70 | -------------------------------------------------------------------------------- /src/idioms/deref.md: -------------------------------------------------------------------------------- 1 | # Collections are smart pointers 2 | 3 | ## Description 4 | 5 | Use the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) trait to 6 | treat collections like smart pointers, offering owning and borrowed views of 7 | data. 8 | 9 | ## Example 10 | 11 | ```rust,ignore 12 | use std::ops::Deref; 13 | 14 | struct Vec { 15 | data: RawVec, 16 | //.. 17 | } 18 | 19 | impl Deref for Vec { 20 | type Target = [T]; 21 | 22 | fn deref(&self) -> &[T] { 23 | //.. 24 | } 25 | } 26 | ``` 27 | 28 | A `Vec` is an owning collection of `T`s, while a slice (`&[T]`) is a borrowed 29 | collection of `T`s. Implementing `Deref` for `Vec` allows implicit dereferencing 30 | from `&Vec` to `&[T]` and includes the relationship in auto-dereferencing 31 | searches. Most methods you might expect to be implemented for `Vec`s are instead 32 | implemented for slices. 33 | 34 | Also `String` and `&str` have a similar relation. 35 | 36 | ## Motivation 37 | 38 | Ownership and borrowing are key aspects of the Rust language. Data structures 39 | must account for these semantics properly to give a good user experience. When 40 | implementing a data structure that owns its data, offering a borrowed view of 41 | that data allows for more flexible APIs. 42 | 43 | ## Advantages 44 | 45 | Most methods can be implemented only for the borrowed view, they are then 46 | implicitly available for the owning view. 47 | 48 | Gives clients a choice between borrowing or taking ownership of data. 49 | 50 | ## Disadvantages 51 | 52 | Methods and traits only available via dereferencing are not taken into account 53 | when bounds checking, so generic programming with data structures using this 54 | pattern can get complex (see the `Borrow` and `AsRef` traits, etc.). 55 | 56 | ## Discussion 57 | 58 | Smart pointers and collections are analogous: a smart pointer points to a single 59 | object, whereas a collection points to many objects. From the point of view of 60 | the type system, there is little difference between the two. A collection owns 61 | its data if the only way to access each datum is via the collection and the 62 | collection is responsible for deleting the data (even in cases of shared 63 | ownership, some kind of borrowed view may be appropriate). If a collection owns 64 | its data, it is usually useful to provide a view of the data as borrowed so that 65 | it can be referenced multiple times. 66 | 67 | Most smart pointers (e.g., `Foo`) implement `Deref`. However, 68 | collections will usually dereference to a custom type. `[T]` and `str` have some 69 | language support, but in the general case, this is not necessary. `Foo` can 70 | implement `Deref>` where `Bar` is a dynamically sized type and 71 | `&Bar` is a borrowed view of the data in `Foo`. 72 | 73 | Commonly, ordered collections will implement `Index` for `Range`s to provide 74 | slicing syntax. The target will be the borrowed view. 75 | 76 | ## See also 77 | 78 | - [Deref polymorphism anti-pattern](../anti_patterns/deref.md). 79 | - [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html). 80 | -------------------------------------------------------------------------------- /src/idioms/dtor-finally.md: -------------------------------------------------------------------------------- 1 | # Finalisation in destructors 2 | 3 | ## Description 4 | 5 | Rust does not provide the equivalent to `finally` blocks - code that will be 6 | executed no matter how a function is exited. Instead, an object's destructor can 7 | be used to run code that must be run before exit. 8 | 9 | ## Example 10 | 11 | ```rust,ignore 12 | fn baz() -> Result<(), ()> { 13 | // some code 14 | } 15 | 16 | fn bar() -> Result<(), ()> { 17 | // These don't need to be defined inside the function. 18 | struct Foo; 19 | 20 | // Implement a destructor for Foo. 21 | impl Drop for Foo { 22 | fn drop(&mut self) { 23 | println!("exit"); 24 | } 25 | } 26 | 27 | // The dtor of _exit will run however the function `bar` is exited. 28 | let _exit = Foo; 29 | // Implicit return with `?` operator. 30 | baz()?; 31 | // Normal return. 32 | Ok(()) 33 | } 34 | ``` 35 | 36 | ## Motivation 37 | 38 | If a function has multiple return points, then executing code on exit becomes 39 | difficult and repetitive (and thus bug-prone). This is especially the case where 40 | return is implicit due to a macro. A common case is the `?` operator which 41 | returns if the result is an `Err`, but continues if it is `Ok`. `?` is used as 42 | an exception handling mechanism, but unlike Java (which has `finally`), there is 43 | no way to schedule code to run in both the normal and exceptional cases. 44 | Panicking will also exit a function early. 45 | 46 | ## Advantages 47 | 48 | Code in destructors will (nearly) always be run - copes with panics, early 49 | returns, etc. 50 | 51 | ## Disadvantages 52 | 53 | It is not guaranteed that destructors will run. For example, if there is an 54 | infinite loop in a function or if running a function crashes before exit. 55 | Destructors are also not run in the case of a panic in an already panicking 56 | thread. Therefore, destructors cannot be relied on as finalizers where it is 57 | absolutely essential that finalisation happens. 58 | 59 | This pattern introduces some hard to notice, implicit code. Reading a function 60 | gives no clear indication of destructors to be run on exit. This can make 61 | debugging tricky. 62 | 63 | Requiring an object and `Drop` impl just for finalisation is heavy on 64 | boilerplate. 65 | 66 | ## Discussion 67 | 68 | There is some subtlety about how exactly to store the object used as a 69 | finalizer. It must be kept alive until the end of the function and must then be 70 | destroyed. The object must always be a value or uniquely owned pointer (e.g., 71 | `Box`). If a shared pointer (such as `Rc`) is used, then the finalizer can 72 | be kept alive beyond the lifetime of the function. For similar reasons, the 73 | finalizer should not be moved or returned. 74 | 75 | The finalizer must be assigned into a variable, otherwise it will be destroyed 76 | immediately, rather than when it goes out of scope. The variable name must start 77 | with `_` if the variable is only used as a finalizer, otherwise the compiler 78 | will warn that the finalizer is never used. However, do not call the variable 79 | `_` with no suffix - in that case it will be destroyed immediately. 80 | 81 | In Rust, destructors are run when an object goes out of scope. This happens 82 | whether we reach the end of block, there is an early return, or the program 83 | panics. When panicking, Rust unwinds the stack running destructors for each 84 | object in each stack frame. So, destructors get called even if the panic happens 85 | in a function being called. 86 | 87 | If a destructor panics while unwinding, there is no good action to take, so Rust 88 | aborts the thread immediately, without running further destructors. This means 89 | that destructors are not absolutely guaranteed to run. It also means that you 90 | must take extra care in your destructors not to panic, since it could leave 91 | resources in an unexpected state. 92 | 93 | ## See also 94 | 95 | [RAII guards](../patterns/behavioural/RAII.md). 96 | -------------------------------------------------------------------------------- /src/idioms/ffi/accepting-strings.md: -------------------------------------------------------------------------------- 1 | # Accepting Strings 2 | 3 | ## Description 4 | 5 | When accepting strings via FFI through pointers, there are two principles that 6 | should be followed: 7 | 8 | 1. Keep foreign strings "borrowed", rather than copying them directly. 9 | 2. Minimize the amount of complexity and `unsafe` code involved in converting 10 | from a C-style string to native Rust strings. 11 | 12 | ## Motivation 13 | 14 | The strings used in C have different behaviours to those used in Rust, namely: 15 | 16 | - C strings are null-terminated while Rust strings store their length 17 | - C strings can contain any arbitrary non-zero byte while Rust strings must be 18 | UTF-8 19 | - C strings are accessed and manipulated using `unsafe` pointer operations while 20 | interactions with Rust strings go through safe methods 21 | 22 | The Rust standard library comes with C equivalents of Rust's `String` and `&str` 23 | called `CString` and `&CStr`, that allow us to avoid a lot of the complexity and 24 | `unsafe` code involved in converting between C strings and Rust strings. 25 | 26 | The `&CStr` type also allows us to work with borrowed data, meaning passing 27 | strings between Rust and C is a zero-cost operation. 28 | 29 | ## Code Example 30 | 31 | ```rust,ignore 32 | pub mod unsafe_module { 33 | 34 | // other module content 35 | 36 | /// Log a message at the specified level. 37 | /// 38 | /// # Safety 39 | /// 40 | /// It is the caller's guarantee to ensure `msg`: 41 | /// 42 | /// - is not a null pointer 43 | /// - points to valid, initialized data 44 | /// - points to memory ending in a null byte 45 | /// - won't be mutated for the duration of this function call 46 | #[no_mangle] 47 | pub unsafe extern "C" fn mylib_log(msg: *const libc::c_char, level: libc::c_int) { 48 | let level: crate::LogLevel = match level { /* ... */ }; 49 | 50 | // SAFETY: The caller has already guaranteed this is okay (see the 51 | // `# Safety` section of the doc-comment). 52 | let msg_str: &str = match std::ffi::CStr::from_ptr(msg).to_str() { 53 | Ok(s) => s, 54 | Err(e) => { 55 | crate::log_error("FFI string conversion failed"); 56 | return; 57 | } 58 | }; 59 | 60 | crate::log(msg_str, level); 61 | } 62 | } 63 | ``` 64 | 65 | ## Advantages 66 | 67 | The example is written to ensure that: 68 | 69 | 1. The `unsafe` block is as small as possible. 70 | 2. The pointer with an "untracked" lifetime becomes a "tracked" shared reference 71 | 72 | Consider an alternative, where the string is actually copied: 73 | 74 | ```rust,ignore 75 | pub mod unsafe_module { 76 | 77 | // other module content 78 | 79 | pub extern "C" fn mylib_log(msg: *const libc::c_char, level: libc::c_int) { 80 | // DO NOT USE THIS CODE. 81 | // IT IS UGLY, VERBOSE, AND CONTAINS A SUBTLE BUG. 82 | 83 | let level: crate::LogLevel = match level { /* ... */ }; 84 | 85 | let msg_len = unsafe { /* SAFETY: strlen is what it is, I guess? */ 86 | libc::strlen(msg) 87 | }; 88 | 89 | let mut msg_data = Vec::with_capacity(msg_len + 1); 90 | 91 | let msg_cstr: std::ffi::CString = unsafe { 92 | // SAFETY: copying from a foreign pointer expected to live 93 | // for the entire stack frame into owned memory 94 | std::ptr::copy_nonoverlapping(msg, msg_data.as_mut(), msg_len); 95 | 96 | msg_data.set_len(msg_len + 1); 97 | 98 | std::ffi::CString::from_vec_with_nul(msg_data).unwrap() 99 | } 100 | 101 | let msg_str: String = unsafe { 102 | match msg_cstr.into_string() { 103 | Ok(s) => s, 104 | Err(e) => { 105 | crate::log_error("FFI string conversion failed"); 106 | return; 107 | } 108 | } 109 | }; 110 | 111 | crate::log(&msg_str, level); 112 | } 113 | } 114 | ``` 115 | 116 | This code is inferior to the original in two respects: 117 | 118 | 1. There is much more `unsafe` code, and more importantly, more invariants it 119 | must uphold. 120 | 2. Due to the extensive arithmetic required, there is a bug in this version that 121 | cases Rust `undefined behaviour`. 122 | 123 | The bug here is a simple mistake in pointer arithmetic: the string was copied, 124 | all `msg_len` bytes of it. However, the `NUL` terminator at the end was not. 125 | 126 | The Vector then had its size *set* to the length of the *zero padded string* -- 127 | rather than *resized* to it, which could have added a zero at the end. As a 128 | result, the last byte in the Vector is uninitialized memory. When the `CString` 129 | is created at the bottom of the block, its read of the Vector will cause 130 | `undefined behaviour`! 131 | 132 | Like many such issues, this would be difficult issue to track down. Sometimes it 133 | would panic because the string was not `UTF-8`, sometimes it would put a weird 134 | character at the end of the string, sometimes it would just completely crash. 135 | 136 | ## Disadvantages 137 | 138 | None? 139 | -------------------------------------------------------------------------------- /src/idioms/ffi/errors.md: -------------------------------------------------------------------------------- 1 | # Error Handling in FFI 2 | 3 | ## Description 4 | 5 | In foreign languages like C, errors are represented by return codes. However, 6 | Rust's type system allows much more rich error information to be captured and 7 | propagated through a full type. 8 | 9 | This best practice shows different kinds of error codes, and how to expose them 10 | in a usable way: 11 | 12 | 1. Flat Enums should be converted to integers and returned as codes. 13 | 2. Structured Enums should be converted to an integer code with a string error 14 | message for detail. 15 | 3. Custom Error Types should become "transparent", with a C representation. 16 | 17 | ## Code Example 18 | 19 | ### Flat Enums 20 | 21 | ```rust,ignore 22 | enum DatabaseError { 23 | IsReadOnly = 1, // user attempted a write operation 24 | IOError = 2, // user should read the C errno() for what it was 25 | FileCorrupted = 3, // user should run a repair tool to recover it 26 | } 27 | 28 | impl From for libc::c_int { 29 | fn from(e: DatabaseError) -> libc::c_int { 30 | (e as i8).into() 31 | } 32 | } 33 | ``` 34 | 35 | ### Structured Enums 36 | 37 | ```rust,ignore 38 | pub mod errors { 39 | enum DatabaseError { 40 | IsReadOnly, 41 | IOError(std::io::Error), 42 | FileCorrupted(String), // message describing the issue 43 | } 44 | 45 | impl From for libc::c_int { 46 | fn from(e: DatabaseError) -> libc::c_int { 47 | match e { 48 | DatabaseError::IsReadOnly => 1, 49 | DatabaseError::IOError(_) => 2, 50 | DatabaseError::FileCorrupted(_) => 3, 51 | } 52 | } 53 | } 54 | } 55 | 56 | pub mod c_api { 57 | use super::errors::DatabaseError; 58 | use core::ptr; 59 | 60 | #[no_mangle] 61 | pub extern "C" fn db_error_description( 62 | e: Option>, 63 | ) -> Option> { 64 | // SAFETY: we assume that the lifetime of `e` is greater than 65 | // the current stack frame. 66 | let error = unsafe { e?.as_ref() }; 67 | 68 | let error_str: String = match error { 69 | DatabaseError::IsReadOnly => { 70 | format!("cannot write to read-only database") 71 | } 72 | DatabaseError::IOError(e) => { 73 | format!("I/O Error: {e}") 74 | } 75 | DatabaseError::FileCorrupted(s) => { 76 | format!("File corrupted, run repair: {}", &s) 77 | } 78 | }; 79 | 80 | let error_bytes = error_str.as_bytes(); 81 | 82 | let c_error = unsafe { 83 | // SAFETY: copying error_bytes to an allocated buffer with a '\0' 84 | // byte at the end. 85 | let buffer = ptr::NonNull::::new(libc::malloc(error_bytes.len() + 1).cast())?; 86 | 87 | buffer 88 | .as_ptr() 89 | .copy_from_nonoverlapping(error_bytes.as_ptr(), error_bytes.len()); 90 | buffer.as_ptr().add(error_bytes.len()).write(0_u8); 91 | buffer 92 | }; 93 | 94 | Some(c_error.cast()) 95 | } 96 | } 97 | ``` 98 | 99 | ### Custom Error Types 100 | 101 | ```rust,ignore 102 | struct ParseError { 103 | expected: char, 104 | line: u32, 105 | ch: u16, 106 | } 107 | 108 | impl ParseError { 109 | /* ... */ 110 | } 111 | 112 | /* Create a second version which is exposed as a C structure */ 113 | #[repr(C)] 114 | pub struct parse_error { 115 | pub expected: libc::c_char, 116 | pub line: u32, 117 | pub ch: u16, 118 | } 119 | 120 | impl From for parse_error { 121 | fn from(e: ParseError) -> parse_error { 122 | let ParseError { expected, line, ch } = e; 123 | parse_error { expected, line, ch } 124 | } 125 | } 126 | ``` 127 | 128 | ## Advantages 129 | 130 | This ensures that the foreign language has clear access to error information 131 | while not compromising the Rust code's API at all. 132 | 133 | ## Disadvantages 134 | 135 | It's a lot of typing, and some types may not be able to be converted easily to 136 | C. 137 | -------------------------------------------------------------------------------- /src/idioms/ffi/intro.md: -------------------------------------------------------------------------------- 1 | # FFI Idioms 2 | 3 | Writing FFI code is an entire course in itself. However, there are several 4 | idioms here that can act as pointers, and avoid traps for inexperienced users of 5 | `unsafe` Rust. 6 | 7 | This section contains idioms that may be useful when doing FFI. 8 | 9 | 1. [Idiomatic Errors](./errors.md) - Error handling with integer codes and 10 | sentinel return values (such as `NULL` pointers) 11 | 12 | 2. [Accepting Strings](./accepting-strings.md) with minimal unsafe code 13 | 14 | 3. [Passing Strings](./passing-strings.md) to FFI functions 15 | -------------------------------------------------------------------------------- /src/idioms/ffi/passing-strings.md: -------------------------------------------------------------------------------- 1 | # Passing Strings 2 | 3 | ## Description 4 | 5 | When passing strings to FFI functions, there are four principles that should be 6 | followed: 7 | 8 | 1. Make the lifetime of owned strings as long as possible. 9 | 2. Minimize `unsafe` code during the conversion. 10 | 3. If the C code can modify the string data, use `Vec` instead of `CString`. 11 | 4. Unless the Foreign Function API requires it, the ownership of the string 12 | should not transfer to the callee. 13 | 14 | ## Motivation 15 | 16 | Rust has built-in support for C-style strings with its `CString` and `CStr` 17 | types. However, there are different approaches one can take with strings that 18 | are being sent to a foreign function call from a Rust function. 19 | 20 | The best practice is simple: use `CString` in such a way as to minimize `unsafe` 21 | code. However, a secondary caveat is that *the object must live long enough*, 22 | meaning the lifetime should be maximized. In addition, the documentation 23 | explains that "round-tripping" a `CString` after modification is UB, so 24 | additional work is necessary in that case. 25 | 26 | ## Code Example 27 | 28 | ```rust,ignore 29 | pub mod unsafe_module { 30 | 31 | // other module content 32 | 33 | extern "C" { 34 | fn seterr(message: *const libc::c_char); 35 | fn geterr(buffer: *mut libc::c_char, size: libc::c_int) -> libc::c_int; 36 | } 37 | 38 | fn report_error_to_ffi>(err: S) -> Result<(), std::ffi::NulError> { 39 | let c_err = std::ffi::CString::new(err.into())?; 40 | 41 | unsafe { 42 | // SAFETY: calling an FFI whose documentation says the pointer is 43 | // const, so no modification should occur 44 | seterr(c_err.as_ptr()); 45 | } 46 | 47 | Ok(()) 48 | // The lifetime of c_err continues until here 49 | } 50 | 51 | fn get_error_from_ffi() -> Result { 52 | let mut buffer = vec![0u8; 1024]; 53 | unsafe { 54 | // SAFETY: calling an FFI whose documentation implies 55 | // that the input need only live as long as the call 56 | let written: usize = geterr(buffer.as_mut_ptr(), 1023).into(); 57 | 58 | buffer.truncate(written + 1); 59 | } 60 | 61 | std::ffi::CString::new(buffer).unwrap().into_string() 62 | } 63 | } 64 | ``` 65 | 66 | ## Advantages 67 | 68 | The example is written in a way to ensure that: 69 | 70 | 1. The `unsafe` block is as small as possible. 71 | 2. The `CString` lives long enough. 72 | 3. Errors with typecasts are always propagated when possible. 73 | 74 | A common mistake (so common it's in the documentation) is to not use the 75 | variable in the first block: 76 | 77 | ```rust,ignore 78 | pub mod unsafe_module { 79 | 80 | // other module content 81 | 82 | fn report_error>(err: S) -> Result<(), std::ffi::NulError> { 83 | unsafe { 84 | // SAFETY: whoops, this contains a dangling pointer! 85 | seterr(std::ffi::CString::new(err.into())?.as_ptr()); 86 | } 87 | Ok(()) 88 | } 89 | } 90 | ``` 91 | 92 | This code will result in a dangling pointer, because the lifetime of the 93 | `CString` is not extended by the pointer creation, unlike if a reference were 94 | created. 95 | 96 | Another issue frequently raised is that the initialization of a 1k vector of 97 | zeroes is "slow". However, recent versions of Rust actually optimize that 98 | particular macro to a call to `zmalloc`, meaning it is as fast as the operating 99 | system's ability to return zeroed memory (which is quite fast). 100 | 101 | ## Disadvantages 102 | 103 | None? 104 | -------------------------------------------------------------------------------- /src/idioms/index.md: -------------------------------------------------------------------------------- 1 | # Idioms 2 | 3 | [Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly used 4 | styles, guidelines and patterns largely agreed upon by a community. Writing 5 | idiomatic code allows other developers to understand better what is happening. 6 | 7 | After all, the computer only cares about the machine code that is generated by 8 | the compiler. Instead, the source code is mainly beneficial to the developer. 9 | So, since we have this abstraction layer, why not make it more readable? 10 | 11 | Remember the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle): 12 | "Keep It Simple, Stupid". It claims that "most systems work best if they are 13 | kept simple rather than made complicated; therefore, simplicity should be a key 14 | goal in design, and unnecessary complexity should be avoided". 15 | 16 | > Code is there for humans, not computers, to understand. 17 | -------------------------------------------------------------------------------- /src/idioms/mem-replace.md: -------------------------------------------------------------------------------- 1 | # `mem::{take(_), replace(_)}` to keep owned values in changed enums 2 | 3 | ## Description 4 | 5 | Say we have a `&mut MyEnum` which has (at least) two variants, 6 | `A { name: String, x: u8 }` and `B { name: String }`. Now we want to change 7 | `MyEnum::A` to a `B` if `x` is zero, while keeping `MyEnum::B` intact. 8 | 9 | We can do this without cloning the `name`. 10 | 11 | ## Example 12 | 13 | ```rust 14 | use std::mem; 15 | 16 | enum MyEnum { 17 | A { name: String, x: u8 }, 18 | B { name: String }, 19 | } 20 | 21 | fn a_to_b(e: &mut MyEnum) { 22 | if let MyEnum::A { name, x: 0 } = e { 23 | // This takes out our `name` and puts in an empty String instead 24 | // (note that empty strings don't allocate). 25 | // Then, construct the new enum variant (which will 26 | // be assigned to `*e`). 27 | *e = MyEnum::B { 28 | name: mem::take(name), 29 | } 30 | } 31 | } 32 | ``` 33 | 34 | This also works with more variants: 35 | 36 | ```rust 37 | use std::mem; 38 | 39 | enum MultiVariateEnum { 40 | A { name: String }, 41 | B { name: String }, 42 | C, 43 | D, 44 | } 45 | 46 | fn swizzle(e: &mut MultiVariateEnum) { 47 | use MultiVariateEnum::*; 48 | *e = match e { 49 | // Ownership rules do not allow taking `name` by value, but we cannot 50 | // take the value out of a mutable reference, unless we replace it: 51 | A { name } => B { 52 | name: mem::take(name), 53 | }, 54 | B { name } => A { 55 | name: mem::take(name), 56 | }, 57 | C => D, 58 | D => C, 59 | } 60 | } 61 | ``` 62 | 63 | ## Motivation 64 | 65 | When working with enums, we may want to change an enum value in place, perhaps 66 | to another variant. This is usually done in two phases to keep the borrow 67 | checker happy. In the first phase, we observe the existing value and look at its 68 | parts to decide what to do next. In the second phase we may conditionally change 69 | the value (as in the example above). 70 | 71 | The borrow checker won't allow us to take out `name` of the enum (because 72 | *something* must be there.) We could of course `.clone()` name and put the clone 73 | into our `MyEnum::B`, but that would be an instance of the 74 | [Clone to satisfy the borrow checker](../anti_patterns/borrow_clone.md) 75 | anti-pattern. Anyway, we can avoid the extra allocation by changing `e` with 76 | only a mutable borrow. 77 | 78 | `mem::take` lets us swap out the value, replacing it with its default value, and 79 | returning the previous value. For `String`, the default value is an empty 80 | `String`, which does not need to allocate. As a result, we get the original 81 | `name` *as an owned value*. We can then wrap this in another enum. 82 | 83 | **NOTE:** `mem::replace` is very similar, but allows us to specify what to 84 | replace the value with. An equivalent to our `mem::take` line would be 85 | `mem::replace(name, String::new())`. 86 | 87 | Note, however, that if we are using an `Option` and want to replace its value 88 | with a `None`, `Option`’s `take()` method provides a shorter and more idiomatic 89 | alternative. 90 | 91 | ## Advantages 92 | 93 | Look ma, no allocation! Also you may feel like Indiana Jones while doing it. 94 | 95 | ## Disadvantages 96 | 97 | This gets a bit wordy. Getting it wrong repeatedly will make you hate the borrow 98 | checker. The compiler may fail to optimize away the double store, resulting in 99 | reduced performance as opposed to what you'd do in unsafe languages. 100 | 101 | Furthermore, the type you are taking needs to implement the 102 | [`Default` trait](./default.md). However, if the type you're working with 103 | doesn't implement this, you can instead use `mem::replace`. 104 | 105 | ## Discussion 106 | 107 | This pattern is only of interest in Rust. In GC'd languages, you'd take the 108 | reference to the value by default (and the GC would keep track of refs), and in 109 | other low-level languages like C you'd simply alias the pointer and fix things 110 | later. 111 | 112 | However, in Rust, we have to do a little more work to do this. An owned value 113 | may only have one owner, so to take it out, we need to put something back in – 114 | like Indiana Jones, replacing the artifact with a bag of sand. 115 | 116 | ## See also 117 | 118 | This gets rid of the 119 | [Clone to satisfy the borrow checker](../anti_patterns/borrow_clone.md) 120 | anti-pattern in a specific case. 121 | -------------------------------------------------------------------------------- /src/idioms/on-stack-dyn-dispatch.md: -------------------------------------------------------------------------------- 1 | # On-Stack Dynamic Dispatch 2 | 3 | ## Description 4 | 5 | We can dynamically dispatch over multiple values, however, to do so, we need to 6 | declare multiple variables to bind differently-typed objects. To extend the 7 | lifetime as necessary, we can use deferred conditional initialization, as seen 8 | below: 9 | 10 | ## Example 11 | 12 | ```rust 13 | use std::io; 14 | use std::fs; 15 | 16 | # fn main() -> Result<(), Box> { 17 | # let arg = "-"; 18 | 19 | // We need to describe the type to get dynamic dispatch. 20 | let readable: &mut dyn io::Read = if arg == "-" { 21 | &mut io::stdin() 22 | } else { 23 | &mut fs::File::open(arg)? 24 | }; 25 | 26 | // Read from `readable` here. 27 | 28 | # Ok(()) 29 | # } 30 | ``` 31 | 32 | ## Motivation 33 | 34 | Rust monomorphises code by default. This means a copy of the code will be 35 | generated for each type it is used with and optimized independently. While this 36 | allows for very fast code on the hot path, it also bloats the code in places 37 | where performance is not of the essence, thus costing compile time and cache 38 | usage. 39 | 40 | Luckily, Rust allows us to use dynamic dispatch, but we have to explicitly ask 41 | for it. 42 | 43 | ## Advantages 44 | 45 | We do not need to allocate anything on the heap. Neither do we need to 46 | initialize something we won't use later, nor do we need to monomorphize the 47 | whole code that follows to work with both `File` or `Stdin`. 48 | 49 | ## Disadvantages 50 | 51 | Before Rust 1.79.0, the code needed two `let` bindings with deferred 52 | initialization, which made up more moving parts than the `Box`-based version: 53 | 54 | ```rust,ignore 55 | // We still need to ascribe the type for dynamic dispatch. 56 | let readable: Box = if arg == "-" { 57 | Box::new(io::stdin()) 58 | } else { 59 | Box::new(fs::File::open(arg)?) 60 | }; 61 | // Read from `readable` here. 62 | ``` 63 | 64 | Luckily, this disadvantage is now gone. Yay! 65 | 66 | ## Discussion 67 | 68 | Since Rust 1.79.0, the compiler will automatically extend the lifetimes of 69 | temporary values within `&` or `&mut` as long as possible within the scope of 70 | the function. 71 | 72 | This means we can simply use a `&mut` value here without worrying about placing 73 | the contents into some `let` binding (which would have been needed for deferred 74 | initialization, which was the solution used before that change). 75 | 76 | We still have a place for each value (even if that place is temporary), the 77 | compiler knows the size of each value and each borrowed value outlives all 78 | references borrowed from it. 79 | 80 | ## See also 81 | 82 | - [Finalisation in destructors](dtor-finally.md) and 83 | [RAII guards](../patterns/behavioural/RAII.md) can benefit from tight control 84 | over lifetimes. 85 | - For conditionally filled `Option<&T>`s of (mutable) references, one can 86 | initialize an `Option` directly and use its [`.as_ref()`] method to get an 87 | optional reference. 88 | 89 | [`.as_ref()`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.as_ref 90 | -------------------------------------------------------------------------------- /src/idioms/option-iter.md: -------------------------------------------------------------------------------- 1 | # Iterating over an `Option` 2 | 3 | ## Description 4 | 5 | `Option` can be viewed as a container that contains either zero or one element. 6 | In particular, it implements the `IntoIterator` trait, and as such can be used 7 | with generic code that needs such a type. 8 | 9 | ## Examples 10 | 11 | Since `Option` implements `IntoIterator`, it can be used as an argument to 12 | [`.extend()`](https://doc.rust-lang.org/std/iter/trait.Extend.html#tymethod.extend): 13 | 14 | ```rust 15 | let turing = Some("Turing"); 16 | let mut logicians = vec!["Curry", "Kleene", "Markov"]; 17 | 18 | logicians.extend(turing); 19 | 20 | // equivalent to 21 | if let Some(turing_inner) = turing { 22 | logicians.push(turing_inner); 23 | } 24 | ``` 25 | 26 | If you need to tack an `Option` to the end of an existing iterator, you can pass 27 | it to 28 | [`.chain()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain): 29 | 30 | ```rust 31 | let turing = Some("Turing"); 32 | let logicians = vec!["Curry", "Kleene", "Markov"]; 33 | 34 | for logician in logicians.iter().chain(turing.iter()) { 35 | println!("{logician} is a logician"); 36 | } 37 | ``` 38 | 39 | Note that if the `Option` is always `Some`, then it is more idiomatic to use 40 | [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) on the 41 | element instead. 42 | 43 | Also, since `Option` implements `IntoIterator`, it's possible to iterate over it 44 | using a `for` loop. This is equivalent to matching it with `if let Some(..)`, 45 | and in most cases you should prefer the latter. 46 | 47 | ## See also 48 | 49 | - [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is an 50 | iterator which yields exactly one element. It's a more readable alternative to 51 | `Some(foo).into_iter()`. 52 | 53 | - [`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) 54 | is a version of 55 | [`Iterator::map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map), 56 | specialized to mapping functions which return `Option`. 57 | 58 | - The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions 59 | for converting an `Option` to a zero- or one-element slice. 60 | 61 | - [Documentation for `Option`](https://doc.rust-lang.org/std/option/enum.Option.html) 62 | -------------------------------------------------------------------------------- /src/idioms/pass-var-to-closure.md: -------------------------------------------------------------------------------- 1 | # Pass variables to closure 2 | 3 | ## Description 4 | 5 | By default, closures capture their environment by borrowing. Or you can use a 6 | `move`-closure to move the whole environment. However, often you want to move 7 | just some variables to the closure, give it a copy of some data, pass by 8 | reference, or perform some other transformation. 9 | 10 | Use variable rebinding in a separate scope for that. 11 | 12 | ## Example 13 | 14 | Use 15 | 16 | ```rust 17 | use std::rc::Rc; 18 | 19 | let num1 = Rc::new(1); 20 | let num2 = Rc::new(2); 21 | let num3 = Rc::new(3); 22 | let closure = { 23 | // `num1` is moved 24 | let num2 = num2.clone(); // `num2` is cloned 25 | let num3 = num3.as_ref(); // `num3` is borrowed 26 | move || { 27 | *num1 + *num2 + *num3; 28 | } 29 | }; 30 | ``` 31 | 32 | instead of 33 | 34 | ```rust 35 | use std::rc::Rc; 36 | 37 | let num1 = Rc::new(1); 38 | let num2 = Rc::new(2); 39 | let num3 = Rc::new(3); 40 | 41 | let num2_cloned = num2.clone(); 42 | let num3_borrowed = num3.as_ref(); 43 | let closure = move || { 44 | *num1 + *num2_cloned + *num3_borrowed; 45 | }; 46 | ``` 47 | 48 | ## Advantages 49 | 50 | Copied data are grouped together with the closure definition, so their purpose 51 | is more clear, and they will be dropped immediately even if they are not 52 | consumed by the closure. 53 | 54 | The closure uses the same variable names as the surrounding code, whether data 55 | are copied or moved. 56 | 57 | ## Disadvantages 58 | 59 | Additional indentation of the closure body. 60 | -------------------------------------------------------------------------------- /src/idioms/priv-extend.md: -------------------------------------------------------------------------------- 1 | # `#[non_exhaustive]` and private fields for extensibility 2 | 3 | ## Description 4 | 5 | A small set of scenarios exist where a library author may want to add public 6 | fields to a public struct or new variants to an enum without breaking backwards 7 | compatibility. 8 | 9 | Rust offers two solutions to this problem: 10 | 11 | - Use `#[non_exhaustive]` on `struct`s, `enum`s, and `enum` variants. For 12 | extensive documentation on all the places where `#[non_exhaustive]` can be 13 | used, see 14 | [the docs](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute). 15 | 16 | - You may add a private field to a struct to prevent it from being directly 17 | instantiated or matched against (see Alternative) 18 | 19 | ## Example 20 | 21 | ```rust 22 | mod a { 23 | // Public struct. 24 | #[non_exhaustive] 25 | pub struct S { 26 | pub foo: i32, 27 | } 28 | 29 | #[non_exhaustive] 30 | pub enum AdmitMoreVariants { 31 | VariantA, 32 | VariantB, 33 | #[non_exhaustive] 34 | VariantC { 35 | a: String, 36 | }, 37 | } 38 | } 39 | 40 | fn print_matched_variants(s: a::S) { 41 | // Because S is `#[non_exhaustive]`, it cannot be named here and 42 | // we must use `..` in the pattern. 43 | let a::S { foo: _, .. } = s; 44 | 45 | let some_enum = a::AdmitMoreVariants::VariantA; 46 | match some_enum { 47 | a::AdmitMoreVariants::VariantA => println!("it's an A"), 48 | a::AdmitMoreVariants::VariantB => println!("it's a b"), 49 | 50 | // .. required because this variant is non-exhaustive as well 51 | a::AdmitMoreVariants::VariantC { a, .. } => println!("it's a c"), 52 | 53 | // The wildcard match is required because more variants may be 54 | // added in the future 55 | _ => println!("it's a new variant"), 56 | } 57 | } 58 | ``` 59 | 60 | ## Alternative: `Private fields` for structs 61 | 62 | `#[non_exhaustive]` only works across crate boundaries. Within a crate, the 63 | private field method may be used. 64 | 65 | Adding a field to a struct is a mostly backwards compatible change. However, if 66 | a client uses a pattern to deconstruct a struct instance, they might name all 67 | the fields in the struct and adding a new one would break that pattern. The 68 | client could name some fields and use `..` in the pattern, in which case adding 69 | another field is backwards compatible. Making at least one of the struct's 70 | fields private forces clients to use the latter form of patterns, ensuring that 71 | the struct is future-proof. 72 | 73 | The downside of this approach is that you might need to add an otherwise 74 | unneeded field to the struct. You can use the `()` type so that there is no 75 | runtime overhead and prepend `_` to the field name to avoid the unused field 76 | warning. 77 | 78 | ```rust 79 | pub struct S { 80 | pub a: i32, 81 | // Because `b` is private, you cannot match on `S` without using `..` and `S` 82 | // cannot be directly instantiated or matched against 83 | _b: (), 84 | } 85 | ``` 86 | 87 | ## Discussion 88 | 89 | On `struct`s, `#[non_exhaustive]` allows adding additional fields in a backwards 90 | compatible way. It will also prevent clients from using the struct constructor, 91 | even if all the fields are public. This may be helpful, but it's worth 92 | considering if you *want* an additional field to be found by clients as a 93 | compiler error rather than something that may be silently undiscovered. 94 | 95 | `#[non_exhaustive]` can be applied to enum variants as well. A 96 | `#[non_exhaustive]` variant behaves in the same way as a `#[non_exhaustive]` 97 | struct. 98 | 99 | Use this deliberately and with caution: incrementing the major version when 100 | adding fields or variants is often a better option. `#[non_exhaustive]` may be 101 | appropriate in scenarios where you're modeling an external resource that may 102 | change out-of-sync with your library, but is not a general purpose tool. 103 | 104 | ### Disadvantages 105 | 106 | `#[non_exhaustive]` can make your code much less ergonomic to use, especially 107 | when forced to handle unknown enum variants. It should only be used when these 108 | sorts of evolutions are required **without** incrementing the major version. 109 | 110 | When `#[non_exhaustive]` is applied to `enum`s, it forces clients to handle a 111 | wildcard variant. If there is no sensible action to take in this case, this may 112 | lead to awkward code and code paths that are only executed in extremely rare 113 | circumstances. If a client decides to `panic!()` in this scenario, it may have 114 | been better to expose this error at compile time. In fact, `#[non_exhaustive]` 115 | forces clients to handle the "Something else" case; there is rarely a sensible 116 | action to take in this scenario. 117 | 118 | ## See also 119 | 120 | - [RFC introducing #[non_exhaustive] attribute for enums and structs](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md) 121 | -------------------------------------------------------------------------------- /src/idioms/return-consumed-arg-on-error.md: -------------------------------------------------------------------------------- 1 | # Return consumed argument on error 2 | 3 | ## Description 4 | 5 | If a fallible function consumes (moves) an argument, return that argument back 6 | inside an error. 7 | 8 | ## Example 9 | 10 | ```rust 11 | pub fn send(value: String) -> Result<(), SendError> { 12 | println!("using {value} in a meaningful way"); 13 | // Simulate non-deterministic fallible action. 14 | use std::time::SystemTime; 15 | let period = SystemTime::now() 16 | .duration_since(SystemTime::UNIX_EPOCH) 17 | .unwrap(); 18 | if period.subsec_nanos() % 2 == 1 { 19 | Ok(()) 20 | } else { 21 | Err(SendError(value)) 22 | } 23 | } 24 | 25 | pub struct SendError(String); 26 | 27 | fn main() { 28 | let mut value = "imagine this is very long string".to_string(); 29 | 30 | let success = 's: { 31 | // Try to send value two times. 32 | for _ in 0..2 { 33 | value = match send(value) { 34 | Ok(()) => break 's true, 35 | Err(SendError(value)) => value, 36 | } 37 | } 38 | false 39 | }; 40 | 41 | println!("success: {success}"); 42 | } 43 | ``` 44 | 45 | ## Motivation 46 | 47 | In case of error you may want to try some alternative way or to retry action in 48 | case of non-deterministic function. But if the argument is always consumed, you 49 | are forced to clone it on every call, which is not very efficient. 50 | 51 | The standard library uses this approach in e.g. `String::from_utf8` method. When 52 | given a vector that doesn't contain valid UTF-8, a `FromUtf8Error` is returned. 53 | You can get original vector back using `FromUtf8Error::into_bytes` method. 54 | 55 | ## Advantages 56 | 57 | Better performance because of moving arguments whenever possible. 58 | 59 | ## Disadvantages 60 | 61 | Slightly more complex error types. 62 | -------------------------------------------------------------------------------- /src/idioms/rustdoc-init.md: -------------------------------------------------------------------------------- 1 | # Easy doc initialization 2 | 3 | ## Description 4 | 5 | If a struct takes significant effort to initialize when writing docs, it can be 6 | quicker to wrap your example with a helper function which takes the struct as an 7 | argument. 8 | 9 | ## Motivation 10 | 11 | Sometimes there is a struct with multiple or complicated parameters and several 12 | methods. Each of these methods should have examples. 13 | 14 | For example: 15 | 16 | ````rust,ignore 17 | struct Connection { 18 | name: String, 19 | stream: TcpStream, 20 | } 21 | 22 | impl Connection { 23 | /// Sends a request over the connection. 24 | /// 25 | /// # Example 26 | /// ```no_run 27 | /// # // Boilerplate are required to get an example working. 28 | /// # let stream = TcpStream::connect("127.0.0.1:34254"); 29 | /// # let connection = Connection { name: "foo".to_owned(), stream }; 30 | /// # let request = Request::new("RequestId", RequestType::Get, "payload"); 31 | /// let response = connection.send_request(request); 32 | /// assert!(response.is_ok()); 33 | /// ``` 34 | fn send_request(&self, request: Request) -> Result { 35 | // ... 36 | } 37 | 38 | /// Oh no, all that boilerplate needs to be repeated here! 39 | fn check_status(&self) -> Status { 40 | // ... 41 | } 42 | } 43 | ```` 44 | 45 | ## Example 46 | 47 | Instead of typing all of this boilerplate to create a `Connection` and 48 | `Request`, it is easier to just create a wrapping helper function which takes 49 | them as arguments: 50 | 51 | ````rust,ignore 52 | struct Connection { 53 | name: String, 54 | stream: TcpStream, 55 | } 56 | 57 | impl Connection { 58 | /// Sends a request over the connection. 59 | /// 60 | /// # Example 61 | /// ``` 62 | /// # fn call_send(connection: Connection, request: Request) { 63 | /// let response = connection.send_request(request); 64 | /// assert!(response.is_ok()); 65 | /// # } 66 | /// ``` 67 | fn send_request(&self, request: Request) { 68 | // ... 69 | } 70 | } 71 | ```` 72 | 73 | **Note** in the above example the line `assert!(response.is_ok());` will not 74 | actually run while testing because it is inside a function which is never 75 | invoked. 76 | 77 | ## Advantages 78 | 79 | This is much more concise and avoids repetitive code in examples. 80 | 81 | ## Disadvantages 82 | 83 | As example is in a function, the code will not be tested. Though it will still 84 | be checked to make sure it compiles when running a `cargo test`. So this pattern 85 | is most useful when you need `no_run`. With this, you do not need to add 86 | `no_run`. 87 | 88 | ## Discussion 89 | 90 | If assertions are not required this pattern works well. 91 | 92 | If they are, an alternative can be to create a public method to create a helper 93 | instance which is annotated with `#[doc(hidden)]` (so that users won't see it). 94 | Then this method can be called inside of rustdoc because it is part of the 95 | crate's public API. 96 | -------------------------------------------------------------------------------- /src/idioms/temporary-mutability.md: -------------------------------------------------------------------------------- 1 | # Temporary mutability 2 | 3 | ## Description 4 | 5 | Often it is necessary to prepare and process some data, but after that data are 6 | only inspected and never modified. The intention can be made explicit by 7 | redefining the mutable variable as immutable. 8 | 9 | It can be done either by processing data within a nested block or by redefining 10 | the variable. 11 | 12 | ## Example 13 | 14 | Say, vector must be sorted before usage. 15 | 16 | Using nested block: 17 | 18 | ```rust,ignore 19 | let data = { 20 | let mut data = get_vec(); 21 | data.sort(); 22 | data 23 | }; 24 | 25 | // Here `data` is immutable. 26 | ``` 27 | 28 | Using variable rebinding: 29 | 30 | ```rust,ignore 31 | let mut data = get_vec(); 32 | data.sort(); 33 | let data = data; 34 | 35 | // Here `data` is immutable. 36 | ``` 37 | 38 | ## Advantages 39 | 40 | Compiler ensures that you don't accidentally mutate data after some point. 41 | 42 | ## Disadvantages 43 | 44 | Nested block requires additional indentation of block body. One more line to 45 | return data from block or redefine variable. 46 | -------------------------------------------------------------------------------- /src/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | ## Participation 4 | 5 | If you are interested in contributing to this book, check out the 6 | [contribution guidelines](https://github.com/rust-unofficial/patterns/blob/master/CONTRIBUTING.md). 7 | 8 | ## News 9 | 10 | - **2024-03-17**: You can now download the book in PDF format from 11 | [this link](https://rust-unofficial.github.io/patterns/rust-design-patterns.pdf). 12 | 13 | ## Design patterns 14 | 15 | In software development, we often come across problems that share similarities 16 | regardless of the environment they appear in. Although the implementation 17 | details are crucial to solve the task at hand, we may abstract from these 18 | particularities to find the common practices that are generically applicable. 19 | 20 | Design patterns are a collection of reusable and tested solutions to recurring 21 | problems in engineering. They make our software more modular, maintainable, and 22 | extensible. Moreover, these patterns provide a common language for developers, 23 | making them an excellent tool for effective communication when problem-solving 24 | in teams. 25 | 26 | Keep in mind: Each pattern comes with its own set of trade-offs. It's crucial to 27 | focus on why you choose a particular pattern rather than just on how to 28 | implement it.[^1] 29 | 30 | ## Design patterns in Rust 31 | 32 | Rust is not object-oriented, and the combination of all its characteristics, 33 | such as functional elements, a strong type system, and the borrow checker, makes 34 | it unique. Because of this, Rust design patterns vary with respect to other 35 | traditional object-oriented programming languages. That's why we decided to 36 | write this book. We hope you enjoy reading it! The book is divided in three main 37 | chapters: 38 | 39 | - [Idioms](./idioms/index.md): guidelines to follow when coding. They are the 40 | social norms of the community. You should break them only if you have a good 41 | reason for it. 42 | - [Design patterns](./patterns/index.md): methods to solve common problems when 43 | coding. 44 | - [Anti-patterns](./anti_patterns/index.md): methods to solve common problems 45 | when coding. However, while design patterns give us benefits, anti-patterns 46 | create more problems. 47 | 48 | [^1]: https://web.archive.org/web/20240124025806/https://www.infoq.com/podcasts/software-architecture-hard-parts/ 49 | -------------------------------------------------------------------------------- /src/patterns/behavioural/RAII.md: -------------------------------------------------------------------------------- 1 | # RAII with guards 2 | 3 | ## Description 4 | 5 | [RAII][wikipedia] stands for "Resource Acquisition is Initialisation" which is a 6 | terrible name. The essence of the pattern is that resource initialisation is 7 | done in the constructor of an object and finalisation in the destructor. This 8 | pattern is extended in Rust by using a RAII object as a guard of some resource 9 | and relying on the type system to ensure that access is always mediated by the 10 | guard object. 11 | 12 | ## Example 13 | 14 | Mutex guards are the classic example of this pattern from the std library (this 15 | is a simplified version of the real implementation): 16 | 17 | ```rust,ignore 18 | use std::ops::Deref; 19 | 20 | struct Foo {} 21 | 22 | struct Mutex { 23 | // We keep a reference to our data: T here. 24 | //.. 25 | } 26 | 27 | struct MutexGuard<'a, T: 'a> { 28 | data: &'a T, 29 | //.. 30 | } 31 | 32 | // Locking the mutex is explicit. 33 | impl Mutex { 34 | fn lock(&self) -> MutexGuard { 35 | // Lock the underlying OS mutex. 36 | //.. 37 | 38 | // MutexGuard keeps a reference to self 39 | MutexGuard { 40 | data: self, 41 | //.. 42 | } 43 | } 44 | } 45 | 46 | // Destructor for unlocking the mutex. 47 | impl<'a, T> Drop for MutexGuard<'a, T> { 48 | fn drop(&mut self) { 49 | // Unlock the underlying OS mutex. 50 | //.. 51 | } 52 | } 53 | 54 | // Implementing Deref means we can treat MutexGuard like a pointer to T. 55 | impl<'a, T> Deref for MutexGuard<'a, T> { 56 | type Target = T; 57 | 58 | fn deref(&self) -> &T { 59 | self.data 60 | } 61 | } 62 | 63 | fn baz(x: Mutex) { 64 | let xx = x.lock(); 65 | xx.foo(); // foo is a method on Foo. 66 | // The borrow checker ensures we can't store a reference to the underlying 67 | // Foo which will outlive the guard xx. 68 | 69 | // x is unlocked when we exit this function and xx's destructor is executed. 70 | } 71 | ``` 72 | 73 | ## Motivation 74 | 75 | Where a resource must be finalised after use, RAII can be used to do this 76 | finalisation. If it is an error to access that resource after finalisation, then 77 | this pattern can be used to prevent such errors. 78 | 79 | ## Advantages 80 | 81 | Prevents errors where a resource is not finalised and where a resource is used 82 | after finalisation. 83 | 84 | ## Discussion 85 | 86 | RAII is a useful pattern for ensuring resources are properly deallocated or 87 | finalised. We can make use of the borrow checker in Rust to statically prevent 88 | errors stemming from using resources after finalisation takes place. 89 | 90 | The core aim of the borrow checker is to ensure that references to data do not 91 | outlive that data. The RAII guard pattern works because the guard object 92 | contains a reference to the underlying resource and only exposes such 93 | references. Rust ensures that the guard cannot outlive the underlying resource 94 | and that references to the resource mediated by the guard cannot outlive the 95 | guard. To see how this works it is helpful to examine the signature of `deref` 96 | without lifetime elision: 97 | 98 | ```rust,ignore 99 | fn deref<'a>(&'a self) -> &'a T { 100 | //.. 101 | } 102 | ``` 103 | 104 | The returned reference to the resource has the same lifetime as `self` (`'a`). 105 | The borrow checker therefore ensures that the lifetime of the reference to `T` 106 | is shorter than the lifetime of `self`. 107 | 108 | Note that implementing `Deref` is not a core part of this pattern, it only makes 109 | using the guard object more ergonomic. Implementing a `get` method on the guard 110 | works just as well. 111 | 112 | ## See also 113 | 114 | [Finalisation in destructors idiom](../../idioms/dtor-finally.md) 115 | 116 | RAII is a common pattern in C++: 117 | [cppreference.com](http://en.cppreference.com/w/cpp/language/raii), 118 | [wikipedia][wikipedia]. 119 | 120 | [wikipedia]: https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization 121 | 122 | [Style guide entry](https://doc.rust-lang.org/1.0.0/style/ownership/raii.html) 123 | (currently just a placeholder). 124 | -------------------------------------------------------------------------------- /src/patterns/behavioural/command.md: -------------------------------------------------------------------------------- 1 | # Command 2 | 3 | ## Description 4 | 5 | The basic idea of the Command pattern is to separate out actions into its own 6 | objects and pass them as parameters. 7 | 8 | ## Motivation 9 | 10 | Suppose we have a sequence of actions or transactions encapsulated as objects. 11 | We want these actions or commands to be executed or invoked in some order later 12 | at different time. These commands may also be triggered as a result of some 13 | event. For example, when a user pushes a button, or on arrival of a data packet. 14 | In addition, these commands might be undoable. This may come in useful for 15 | operations of an editor. We might want to store logs of executed commands so 16 | that we could reapply the changes later if the system crashes. 17 | 18 | ## Example 19 | 20 | Define two database operations `create table` and `add field`. Each of these 21 | operations is a command which knows how to undo the command, e.g., `drop table` 22 | and `remove field`. When a user invokes a database migration operation then each 23 | command is executed in the defined order, and when the user invokes the rollback 24 | operation then the whole set of commands is invoked in reverse order. 25 | 26 | ## Approach: Using trait objects 27 | 28 | We define a common trait which encapsulates our command with two operations 29 | `execute` and `rollback`. All command `structs` must implement this trait. 30 | 31 | ```rust 32 | pub trait Migration { 33 | fn execute(&self) -> &str; 34 | fn rollback(&self) -> &str; 35 | } 36 | 37 | pub struct CreateTable; 38 | impl Migration for CreateTable { 39 | fn execute(&self) -> &str { 40 | "create table" 41 | } 42 | fn rollback(&self) -> &str { 43 | "drop table" 44 | } 45 | } 46 | 47 | pub struct AddField; 48 | impl Migration for AddField { 49 | fn execute(&self) -> &str { 50 | "add field" 51 | } 52 | fn rollback(&self) -> &str { 53 | "remove field" 54 | } 55 | } 56 | 57 | struct Schema { 58 | commands: Vec>, 59 | } 60 | 61 | impl Schema { 62 | fn new() -> Self { 63 | Self { commands: vec![] } 64 | } 65 | 66 | fn add_migration(&mut self, cmd: Box) { 67 | self.commands.push(cmd); 68 | } 69 | 70 | fn execute(&self) -> Vec<&str> { 71 | self.commands.iter().map(|cmd| cmd.execute()).collect() 72 | } 73 | fn rollback(&self) -> Vec<&str> { 74 | self.commands 75 | .iter() 76 | .rev() // reverse iterator's direction 77 | .map(|cmd| cmd.rollback()) 78 | .collect() 79 | } 80 | } 81 | 82 | fn main() { 83 | let mut schema = Schema::new(); 84 | 85 | let cmd = Box::new(CreateTable); 86 | schema.add_migration(cmd); 87 | let cmd = Box::new(AddField); 88 | schema.add_migration(cmd); 89 | 90 | assert_eq!(vec!["create table", "add field"], schema.execute()); 91 | assert_eq!(vec!["remove field", "drop table"], schema.rollback()); 92 | } 93 | ``` 94 | 95 | ## Approach: Using function pointers 96 | 97 | We could follow another approach by creating each individual command as a 98 | different function and store function pointers to invoke these functions later 99 | at a different time. Since function pointers implement all three traits `Fn`, 100 | `FnMut`, and `FnOnce` we could as well pass and store closures instead of 101 | function pointers. 102 | 103 | ```rust 104 | type FnPtr = fn() -> String; 105 | struct Command { 106 | execute: FnPtr, 107 | rollback: FnPtr, 108 | } 109 | 110 | struct Schema { 111 | commands: Vec, 112 | } 113 | 114 | impl Schema { 115 | fn new() -> Self { 116 | Self { commands: vec![] } 117 | } 118 | fn add_migration(&mut self, execute: FnPtr, rollback: FnPtr) { 119 | self.commands.push(Command { execute, rollback }); 120 | } 121 | fn execute(&self) -> Vec { 122 | self.commands.iter().map(|cmd| (cmd.execute)()).collect() 123 | } 124 | fn rollback(&self) -> Vec { 125 | self.commands 126 | .iter() 127 | .rev() 128 | .map(|cmd| (cmd.rollback)()) 129 | .collect() 130 | } 131 | } 132 | 133 | fn add_field() -> String { 134 | "add field".to_string() 135 | } 136 | 137 | fn remove_field() -> String { 138 | "remove field".to_string() 139 | } 140 | 141 | fn main() { 142 | let mut schema = Schema::new(); 143 | schema.add_migration(|| "create table".to_string(), || "drop table".to_string()); 144 | schema.add_migration(add_field, remove_field); 145 | assert_eq!(vec!["create table", "add field"], schema.execute()); 146 | assert_eq!(vec!["remove field", "drop table"], schema.rollback()); 147 | } 148 | ``` 149 | 150 | ## Approach: Using `Fn` trait objects 151 | 152 | Finally, instead of defining a common command trait we could store each command 153 | implementing the `Fn` trait separately in vectors. 154 | 155 | ```rust 156 | type Migration<'a> = Box &'a str>; 157 | 158 | struct Schema<'a> { 159 | executes: Vec>, 160 | rollbacks: Vec>, 161 | } 162 | 163 | impl<'a> Schema<'a> { 164 | fn new() -> Self { 165 | Self { 166 | executes: vec![], 167 | rollbacks: vec![], 168 | } 169 | } 170 | fn add_migration(&mut self, execute: E, rollback: R) 171 | where 172 | E: Fn() -> &'a str + 'static, 173 | R: Fn() -> &'a str + 'static, 174 | { 175 | self.executes.push(Box::new(execute)); 176 | self.rollbacks.push(Box::new(rollback)); 177 | } 178 | fn execute(&self) -> Vec<&str> { 179 | self.executes.iter().map(|cmd| cmd()).collect() 180 | } 181 | fn rollback(&self) -> Vec<&str> { 182 | self.rollbacks.iter().rev().map(|cmd| cmd()).collect() 183 | } 184 | } 185 | 186 | fn add_field() -> &'static str { 187 | "add field" 188 | } 189 | 190 | fn remove_field() -> &'static str { 191 | "remove field" 192 | } 193 | 194 | fn main() { 195 | let mut schema = Schema::new(); 196 | schema.add_migration(|| "create table", || "drop table"); 197 | schema.add_migration(add_field, remove_field); 198 | assert_eq!(vec!["create table", "add field"], schema.execute()); 199 | assert_eq!(vec!["remove field", "drop table"], schema.rollback()); 200 | } 201 | ``` 202 | 203 | ## Discussion 204 | 205 | If our commands are small and may be defined as functions or passed as a closure 206 | then using function pointers might be preferable since it does not exploit 207 | dynamic dispatch. But if our command is a whole struct with a bunch of functions 208 | and variables defined as separated module then using trait objects would be more 209 | suitable. A case of application can be found in [`actix`](https://actix.rs/), 210 | which uses trait objects when it registers a handler function for routes. In 211 | case of using `Fn` trait objects we can create and use commands in the same way 212 | as we used in case of function pointers. 213 | 214 | As performance, there is always a trade-off between performance and code 215 | simplicity and organisation. Static dispatch gives faster performance, while 216 | dynamic dispatch provides flexibility when we structure our application. 217 | 218 | ## See also 219 | 220 | - [Command pattern](https://en.wikipedia.org/wiki/Command_pattern) 221 | 222 | - [Another example for the `command` pattern](https://web.archive.org/web/20210223131236/https://chercher.tech/rust/command-design-pattern-rust) 223 | -------------------------------------------------------------------------------- /src/patterns/behavioural/interpreter.md: -------------------------------------------------------------------------------- 1 | # Interpreter 2 | 3 | ## Description 4 | 5 | If a problem occurs very often and requires long and repetitive steps to solve 6 | it, then the problem instances might be expressed in a simple language and an 7 | interpreter object could solve it by interpreting the sentences written in this 8 | simple language. 9 | 10 | Basically, for any kind of problems we define: 11 | 12 | - A 13 | [domain specific language](https://en.wikipedia.org/wiki/Domain-specific_language), 14 | - A grammar for this language, 15 | - An interpreter that solves the problem instances. 16 | 17 | ## Motivation 18 | 19 | Our goal is to translate simple mathematical expressions into postfix 20 | expressions (or 21 | [Reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation)) 22 | For simplicity, our expressions consist of ten digits `0`, ..., `9` and two 23 | operations `+`, `-`. For example, the expression `2 + 4` is translated into 24 | `2 4 +`. 25 | 26 | ## Context Free Grammar for our problem 27 | 28 | Our task is translating infix expressions into postfix ones. Let's define a 29 | context free grammar for a set of infix expressions over `0`, ..., `9`, `+`, and 30 | `-`, where: 31 | 32 | - Terminal symbols: `0`, `...`, `9`, `+`, `-` 33 | - Non-terminal symbols: `exp`, `term` 34 | - Start symbol is `exp` 35 | - And the following are production rules 36 | 37 | ```ignore 38 | exp -> exp + term 39 | exp -> exp - term 40 | exp -> term 41 | term -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 42 | ``` 43 | 44 | **NOTE:** This grammar should be further transformed depending on what we are 45 | going to do with it. For example, we might need to remove left recursion. For 46 | more details please see 47 | [Compilers: Principles,Techniques, and Tools](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools) 48 | (aka Dragon Book). 49 | 50 | ## Solution 51 | 52 | We simply implement a recursive descent parser. For simplicity's sake, the code 53 | panics when an expression is syntactically wrong (for example `2-34` or `2+5-` 54 | are wrong according to the grammar definition). 55 | 56 | ```rust 57 | pub struct Interpreter<'a> { 58 | it: std::str::Chars<'a>, 59 | } 60 | 61 | impl<'a> Interpreter<'a> { 62 | pub fn new(infix: &'a str) -> Self { 63 | Self { it: infix.chars() } 64 | } 65 | 66 | fn next_char(&mut self) -> Option { 67 | self.it.next() 68 | } 69 | 70 | pub fn interpret(&mut self, out: &mut String) { 71 | self.term(out); 72 | 73 | while let Some(op) = self.next_char() { 74 | if op == '+' || op == '-' { 75 | self.term(out); 76 | out.push(op); 77 | } else { 78 | panic!("Unexpected symbol '{op}'"); 79 | } 80 | } 81 | } 82 | 83 | fn term(&mut self, out: &mut String) { 84 | match self.next_char() { 85 | Some(ch) if ch.is_digit(10) => out.push(ch), 86 | Some(ch) => panic!("Unexpected symbol '{ch}'"), 87 | None => panic!("Unexpected end of string"), 88 | } 89 | } 90 | } 91 | 92 | pub fn main() { 93 | let mut intr = Interpreter::new("2+3"); 94 | let mut postfix = String::new(); 95 | intr.interpret(&mut postfix); 96 | assert_eq!(postfix, "23+"); 97 | 98 | intr = Interpreter::new("1-2+3-4"); 99 | postfix.clear(); 100 | intr.interpret(&mut postfix); 101 | assert_eq!(postfix, "12-3+4-"); 102 | } 103 | ``` 104 | 105 | ## Discussion 106 | 107 | There may be a wrong perception that the Interpreter design pattern is about 108 | design grammars for formal languages and implementation of parsers for these 109 | grammars. In fact, this pattern is about expressing problem instances in a more 110 | specific way and implementing functions/classes/structs that solve these problem 111 | instances. Rust language has `macro_rules!` that allow us to define special 112 | syntax and rules on how to expand this syntax into source code. 113 | 114 | In the following example we create a simple `macro_rules!` that computes 115 | [Euclidean length](https://en.wikipedia.org/wiki/Euclidean_distance) of `n` 116 | dimensional vectors. Writing `norm!(x,1,2)` might be easier to express and more 117 | efficient than packing `x,1,2` into a `Vec` and calling a function computing the 118 | length. 119 | 120 | ```rust 121 | macro_rules! norm { 122 | ($($element:expr),*) => { 123 | { 124 | let mut n = 0.0; 125 | $( 126 | n += ($element as f64)*($element as f64); 127 | )* 128 | n.sqrt() 129 | } 130 | }; 131 | } 132 | 133 | fn main() { 134 | let x = -3f64; 135 | let y = 4f64; 136 | 137 | assert_eq!(3f64, norm!(x)); 138 | assert_eq!(5f64, norm!(x, y)); 139 | assert_eq!(0f64, norm!(0, 0, 0)); 140 | assert_eq!(1f64, norm!(0.5, -0.5, 0.5, -0.5)); 141 | } 142 | ``` 143 | 144 | ## See also 145 | 146 | - [Interpreter pattern](https://en.wikipedia.org/wiki/Interpreter_pattern) 147 | - [Context free grammar](https://en.wikipedia.org/wiki/Context-free_grammar) 148 | - [macro_rules!](https://doc.rust-lang.org/rust-by-example/macros.html) 149 | -------------------------------------------------------------------------------- /src/patterns/behavioural/intro.md: -------------------------------------------------------------------------------- 1 | # Behavioural Patterns 2 | 3 | From [Wikipedia](https://en.wikipedia.org/wiki/Behavioral_pattern): 4 | 5 | > Design patterns that identify common communication patterns among objects. By 6 | > doing so, these patterns increase flexibility in carrying out communication. 7 | -------------------------------------------------------------------------------- /src/patterns/behavioural/newtype.md: -------------------------------------------------------------------------------- 1 | # Newtype 2 | 3 | What if in some cases we want a type to behave similar to another type or 4 | enforce some behaviour at compile time when using only type aliases would not be 5 | enough? 6 | 7 | For example, if we want to create a custom `Display` implementation for `String` 8 | due to security considerations (e.g. passwords). 9 | 10 | For such cases we could use the `Newtype` pattern to provide **type safety** and 11 | **encapsulation**. 12 | 13 | ## Description 14 | 15 | Use a tuple struct with a single field to make an opaque wrapper for a type. 16 | This creates a new type, rather than an alias to a type (`type` items). 17 | 18 | ## Example 19 | 20 | ```rust 21 | use std::fmt::Display; 22 | 23 | // Create Newtype Password to override the Display trait for String 24 | struct Password(String); 25 | 26 | impl Display for Password { 27 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 28 | write!(f, "****************") 29 | } 30 | } 31 | 32 | fn main() { 33 | let unsecured_password: String = "ThisIsMyPassword".to_string(); 34 | let secured_password: Password = Password(unsecured_password.clone()); 35 | println!("unsecured_password: {unsecured_password}"); 36 | println!("secured_password: {secured_password}"); 37 | } 38 | ``` 39 | 40 | ```shell 41 | unsecured_password: ThisIsMyPassword 42 | secured_password: **************** 43 | ``` 44 | 45 | ## Motivation 46 | 47 | The primary motivation for newtypes is abstraction. It allows you to share 48 | implementation details between types while precisely controlling the interface. 49 | By using a newtype rather than exposing the implementation type as part of an 50 | API, it allows you to change implementation backwards compatibly. 51 | 52 | Newtypes can be used for distinguishing units, e.g., wrapping `f64` to give 53 | distinguishable `Miles` and `Kilometres`. 54 | 55 | ## Advantages 56 | 57 | The wrapped and wrapper types are not type compatible (as opposed to using 58 | `type`), so users of the newtype will never 'confuse' the wrapped and wrapper 59 | types. 60 | 61 | Newtypes are a zero-cost abstraction - there is no runtime overhead. 62 | 63 | The privacy system ensures that users cannot access the wrapped type (if the 64 | field is private, which it is by default). 65 | 66 | ## Disadvantages 67 | 68 | The downside of newtypes (especially compared with type aliases), is that there 69 | is no special language support. This means there can be *a lot* of boilerplate. 70 | You need a 'pass through' method for every method you want to expose on the 71 | wrapped type, and an impl for every trait you want to also be implemented for 72 | the wrapper type. 73 | 74 | ## Discussion 75 | 76 | Newtypes are very common in Rust code. Abstraction or representing units are the 77 | most common uses, but they can be used for other reasons: 78 | 79 | - restricting functionality (reduce the functions exposed or traits 80 | implemented), 81 | - making a type with copy semantics have move semantics, 82 | - abstraction by providing a more concrete type and thus hiding internal types, 83 | e.g., 84 | 85 | ```rust,ignore 86 | pub struct Foo(Bar); 87 | ``` 88 | 89 | Here, `Bar` might be some public, generic type and `T1` and `T2` are some 90 | internal types. Users of our module shouldn't know that we implement `Foo` by 91 | using a `Bar`, but what we're really hiding here is the types `T1` and `T2`, and 92 | how they are used with `Bar`. 93 | 94 | ## See also 95 | 96 | - [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) 97 | - [Newtypes in Haskell](https://wiki.haskell.org/Newtype) 98 | - [Type aliases](https://doc.rust-lang.org/stable/book/ch19-04-advanced-types.html#creating-type-synonyms-with-type-aliases) 99 | - [derive_more](https://crates.io/crates/derive_more), a crate for deriving many 100 | builtin traits on newtypes. 101 | - [The Newtype Pattern In Rust](https://web.archive.org/web/20230519162111/https://www.worthe-it.co.za/blog/2020-10-31-newtype-pattern-in-rust.html) 102 | -------------------------------------------------------------------------------- /src/patterns/behavioural/strategy.md: -------------------------------------------------------------------------------- 1 | # Strategy (aka Policy) 2 | 3 | ## Description 4 | 5 | The [Strategy design pattern](https://en.wikipedia.org/wiki/Strategy_pattern) is 6 | a technique that enables separation of concerns. It also allows to decouple 7 | software modules through 8 | [Dependency Inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle). 9 | 10 | The basic idea behind the Strategy pattern is that, given an algorithm solving a 11 | particular problem, we define only the skeleton of the algorithm at an abstract 12 | level, and we separate the specific algorithm’s implementation into different 13 | parts. 14 | 15 | In this way, a client using the algorithm may choose a specific implementation, 16 | while the general algorithm workflow remains the same. In other words, the 17 | abstract specification of the class does not depend on the specific 18 | implementation of the derived class, but specific implementation must adhere to 19 | the abstract specification. This is why we call it "Dependency Inversion". 20 | 21 | ## Motivation 22 | 23 | Imagine we are working on a project that generates reports every month. We need 24 | the reports to be generated in different formats (strategies), e.g., in `JSON` 25 | or `Plain Text` formats. But things vary over time, and we don't know what kind 26 | of requirement we may get in the future. For example, we may need to generate 27 | our report in a completely new format, or just modify one of the existing 28 | formats. 29 | 30 | ## Example 31 | 32 | In this example our invariants (or abstractions) are `Formatter` and `Report`, 33 | while `Text` and `Json` are our strategy structs. These strategies have to 34 | implement the `Formatter` trait. 35 | 36 | ```rust 37 | use std::collections::HashMap; 38 | 39 | type Data = HashMap; 40 | 41 | trait Formatter { 42 | fn format(&self, data: &Data, buf: &mut String); 43 | } 44 | 45 | struct Report; 46 | 47 | impl Report { 48 | // Write should be used but we kept it as String to ignore error handling 49 | fn generate(g: T, s: &mut String) { 50 | // backend operations... 51 | let mut data = HashMap::new(); 52 | data.insert("one".to_string(), 1); 53 | data.insert("two".to_string(), 2); 54 | // generate report 55 | g.format(&data, s); 56 | } 57 | } 58 | 59 | struct Text; 60 | impl Formatter for Text { 61 | fn format(&self, data: &Data, buf: &mut String) { 62 | for (k, v) in data { 63 | let entry = format!("{k} {v}\n"); 64 | buf.push_str(&entry); 65 | } 66 | } 67 | } 68 | 69 | struct Json; 70 | impl Formatter for Json { 71 | fn format(&self, data: &Data, buf: &mut String) { 72 | buf.push('['); 73 | for (k, v) in data.into_iter() { 74 | let entry = format!(r#"{{"{}":"{}"}}"#, k, v); 75 | buf.push_str(&entry); 76 | buf.push(','); 77 | } 78 | if !data.is_empty() { 79 | buf.pop(); // remove extra , at the end 80 | } 81 | buf.push(']'); 82 | } 83 | } 84 | 85 | fn main() { 86 | let mut s = String::from(""); 87 | Report::generate(Text, &mut s); 88 | assert!(s.contains("one 1")); 89 | assert!(s.contains("two 2")); 90 | 91 | s.clear(); // reuse the same buffer 92 | Report::generate(Json, &mut s); 93 | assert!(s.contains(r#"{"one":"1"}"#)); 94 | assert!(s.contains(r#"{"two":"2"}"#)); 95 | } 96 | ``` 97 | 98 | ## Advantages 99 | 100 | The main advantage is separation of concerns. For example, in this case `Report` 101 | does not know anything about specific implementations of `Json` and `Text`, 102 | whereas the output implementations does not care about how data is preprocessed, 103 | stored, and fetched. The only thing they have to know is a specific trait to 104 | implement and its method defining the concrete algorithm implementation 105 | processing the result, i.e., `Formatter` and `format(...)`. 106 | 107 | ## Disadvantages 108 | 109 | For each strategy there must be implemented at least one module, so number of 110 | modules increases with number of strategies. If there are many strategies to 111 | choose from then users have to know how strategies differ from one another. 112 | 113 | ## Discussion 114 | 115 | In the previous example all strategies are implemented in a single file. Ways of 116 | providing different strategies includes: 117 | 118 | - All in one file (as shown in this example, similar to being separated as 119 | modules) 120 | - Separated as modules, E.g. `formatter::json` module, `formatter::text` module 121 | - Use compiler feature flags, E.g. `json` feature, `text` feature 122 | - Separated as crates, E.g. `json` crate, `text` crate 123 | 124 | Serde crate is a good example of the `Strategy` pattern in action. Serde allows 125 | [full customization](https://serde.rs/custom-serialization.html) of the 126 | serialization behavior by manually implementing `Serialize` and `Deserialize` 127 | traits for our type. For example, we could easily swap `serde_json` with 128 | `serde_cbor` since they expose similar methods. Having this makes the helper 129 | crate `serde_transcode` much more useful and ergonomic. 130 | 131 | However, we don't need to use traits in order to design this pattern in Rust. 132 | 133 | The following toy example demonstrates the idea of the Strategy pattern using 134 | Rust `closures`: 135 | 136 | ```rust 137 | struct Adder; 138 | impl Adder { 139 | pub fn add(x: u8, y: u8, f: F) -> u8 140 | where 141 | F: Fn(u8, u8) -> u8, 142 | { 143 | f(x, y) 144 | } 145 | } 146 | 147 | fn main() { 148 | let arith_adder = |x, y| x + y; 149 | let bool_adder = |x, y| { 150 | if x == 1 || y == 1 { 151 | 1 152 | } else { 153 | 0 154 | } 155 | }; 156 | let custom_adder = |x, y| 2 * x + y; 157 | 158 | assert_eq!(9, Adder::add(4, 5, arith_adder)); 159 | assert_eq!(0, Adder::add(0, 0, bool_adder)); 160 | assert_eq!(5, Adder::add(1, 3, custom_adder)); 161 | } 162 | ``` 163 | 164 | In fact, Rust already uses this idea for `Options`'s `map` method: 165 | 166 | ```rust 167 | fn main() { 168 | let val = Some("Rust"); 169 | 170 | let len_strategy = |s: &str| s.len(); 171 | assert_eq!(4, val.map(len_strategy).unwrap()); 172 | 173 | let first_byte_strategy = |s: &str| s.bytes().next().unwrap(); 174 | assert_eq!(82, val.map(first_byte_strategy).unwrap()); 175 | } 176 | ``` 177 | 178 | ## See also 179 | 180 | - [Strategy Pattern](https://en.wikipedia.org/wiki/Strategy_pattern) 181 | - [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) 182 | - [Policy Based Design](https://en.wikipedia.org/wiki/Modern_C++_Design#Policy-based_design) 183 | - [Implementing a TCP server for Space Applications in Rust using the Strategy Pattern](https://web.archive.org/web/20231003171500/https://robamu.github.io/posts/rust-strategy-pattern/) 184 | -------------------------------------------------------------------------------- /src/patterns/behavioural/visitor.md: -------------------------------------------------------------------------------- 1 | # Visitor 2 | 3 | ## Description 4 | 5 | A visitor encapsulates an algorithm that operates over a heterogeneous 6 | collection of objects. It allows multiple different algorithms to be written 7 | over the same data without having to modify the data (or their primary 8 | behaviour). 9 | 10 | Furthermore, the visitor pattern allows separating the traversal of a collection 11 | of objects from the operations performed on each object. 12 | 13 | ## Example 14 | 15 | ```rust,ignore 16 | // The data we will visit 17 | mod ast { 18 | pub enum Stmt { 19 | Expr(Expr), 20 | Let(Name, Expr), 21 | } 22 | 23 | pub struct Name { 24 | value: String, 25 | } 26 | 27 | pub enum Expr { 28 | IntLit(i64), 29 | Add(Box, Box), 30 | Sub(Box, Box), 31 | } 32 | } 33 | 34 | // The abstract visitor 35 | mod visit { 36 | use ast::*; 37 | 38 | pub trait Visitor { 39 | fn visit_name(&mut self, n: &Name) -> T; 40 | fn visit_stmt(&mut self, s: &Stmt) -> T; 41 | fn visit_expr(&mut self, e: &Expr) -> T; 42 | } 43 | } 44 | 45 | use ast::*; 46 | use visit::*; 47 | 48 | // An example concrete implementation - walks the AST interpreting it as code. 49 | struct Interpreter; 50 | impl Visitor for Interpreter { 51 | fn visit_name(&mut self, n: &Name) -> i64 { 52 | panic!() 53 | } 54 | fn visit_stmt(&mut self, s: &Stmt) -> i64 { 55 | match *s { 56 | Stmt::Expr(ref e) => self.visit_expr(e), 57 | Stmt::Let(..) => unimplemented!(), 58 | } 59 | } 60 | 61 | fn visit_expr(&mut self, e: &Expr) -> i64 { 62 | match *e { 63 | Expr::IntLit(n) => n, 64 | Expr::Add(ref lhs, ref rhs) => self.visit_expr(lhs) + self.visit_expr(rhs), 65 | Expr::Sub(ref lhs, ref rhs) => self.visit_expr(lhs) - self.visit_expr(rhs), 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | One could implement further visitors, for example a type checker, without having 72 | to modify the AST data. 73 | 74 | ## Motivation 75 | 76 | The visitor pattern is useful anywhere that you want to apply an algorithm to 77 | heterogeneous data. If data is homogeneous, you can use an iterator-like 78 | pattern. Using a visitor object (rather than a functional approach) allows the 79 | visitor to be stateful and thus communicate information between nodes. 80 | 81 | ## Discussion 82 | 83 | It is common for the `visit_*` methods to return void (as opposed to in the 84 | example). In that case it is possible to factor out the traversal code and share 85 | it between algorithms (and also to provide noop default methods). In Rust, the 86 | common way to do this is to provide `walk_*` functions for each datum. For 87 | example, 88 | 89 | ```rust,ignore 90 | pub fn walk_expr(visitor: &mut Visitor, e: &Expr) { 91 | match *e { 92 | Expr::IntLit(_) => {} 93 | Expr::Add(ref lhs, ref rhs) => { 94 | visitor.visit_expr(lhs); 95 | visitor.visit_expr(rhs); 96 | } 97 | Expr::Sub(ref lhs, ref rhs) => { 98 | visitor.visit_expr(lhs); 99 | visitor.visit_expr(rhs); 100 | } 101 | } 102 | } 103 | ``` 104 | 105 | In other languages (e.g., Java) it is common for data to have an `accept` method 106 | which performs the same duty. 107 | 108 | ## See also 109 | 110 | The visitor pattern is a common pattern in most OO languages. 111 | 112 | [Wikipedia article](https://en.wikipedia.org/wiki/Visitor_pattern) 113 | 114 | The [fold](../creational/fold.md) pattern is similar to visitor but produces a 115 | new version of the visited data structure. 116 | -------------------------------------------------------------------------------- /src/patterns/creational/builder.md: -------------------------------------------------------------------------------- 1 | # Builder 2 | 3 | ## Description 4 | 5 | Construct an object with calls to a builder helper. 6 | 7 | ## Example 8 | 9 | ```rust 10 | #[derive(Debug, PartialEq)] 11 | pub struct Foo { 12 | // Lots of complicated fields. 13 | bar: String, 14 | } 15 | 16 | impl Foo { 17 | // This method will help users to discover the builder 18 | pub fn builder() -> FooBuilder { 19 | FooBuilder::default() 20 | } 21 | } 22 | 23 | #[derive(Default)] 24 | pub struct FooBuilder { 25 | // Probably lots of optional fields. 26 | bar: String, 27 | } 28 | 29 | impl FooBuilder { 30 | pub fn new(/* ... */) -> FooBuilder { 31 | // Set the minimally required fields of Foo. 32 | FooBuilder { 33 | bar: String::from("X"), 34 | } 35 | } 36 | 37 | pub fn name(mut self, bar: String) -> FooBuilder { 38 | // Set the name on the builder itself, and return the builder by value. 39 | self.bar = bar; 40 | self 41 | } 42 | 43 | // If we can get away with not consuming the Builder here, that is an 44 | // advantage. It means we can use the FooBuilder as a template for constructing 45 | // many Foos. 46 | pub fn build(self) -> Foo { 47 | // Create a Foo from the FooBuilder, applying all settings in FooBuilder 48 | // to Foo. 49 | Foo { bar: self.bar } 50 | } 51 | } 52 | 53 | #[test] 54 | fn builder_test() { 55 | let foo = Foo { 56 | bar: String::from("Y"), 57 | }; 58 | let foo_from_builder: Foo = FooBuilder::new().name(String::from("Y")).build(); 59 | assert_eq!(foo, foo_from_builder); 60 | } 61 | ``` 62 | 63 | ## Motivation 64 | 65 | Useful when you would otherwise require many constructors or where construction 66 | has side effects. 67 | 68 | ## Advantages 69 | 70 | Separates methods for building from other methods. 71 | 72 | Prevents proliferation of constructors. 73 | 74 | Can be used for one-liner initialisation as well as more complex construction. 75 | 76 | ## Disadvantages 77 | 78 | More complex than creating a struct object directly, or a simple constructor 79 | function. 80 | 81 | ## Discussion 82 | 83 | This pattern is seen more frequently in Rust (and for simpler objects) than in 84 | many other languages because Rust lacks overloading. Since you can only have a 85 | single method with a given name, having multiple constructors is less nice in 86 | Rust than in C++, Java, or others. 87 | 88 | This pattern is often used where the builder object is useful in its own right, 89 | rather than being just a builder. For example, see 90 | [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html) 91 | is a builder for 92 | [`Child`](https://doc.rust-lang.org/std/process/struct.Child.html) (a process). 93 | In these cases, the `T` and `TBuilder` naming pattern is not used. 94 | 95 | The example takes and returns the builder by value. It is often more ergonomic 96 | (and more efficient) to take and return the builder as a mutable reference. The 97 | borrow checker makes this work naturally. This approach has the advantage that 98 | one can write code like 99 | 100 | ```rust,ignore 101 | let mut fb = FooBuilder::new(); 102 | fb.a(); 103 | fb.b(); 104 | let f = fb.build(); 105 | ``` 106 | 107 | as well as the `FooBuilder::new().a().b().build()` style. 108 | 109 | ## See also 110 | 111 | - [Description in the style guide](https://web.archive.org/web/20210104103100/https://doc.rust-lang.org/1.12.0/style/ownership/builders.html) 112 | - [derive_builder](https://crates.io/crates/derive_builder), a crate for 113 | automatically implementing this pattern while avoiding the boilerplate. 114 | - [Constructor pattern](../../idioms/ctor.md) for when construction is simpler. 115 | - [Builder pattern (wikipedia)](https://en.wikipedia.org/wiki/Builder_pattern) 116 | - [Construction of complex values](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder) 117 | -------------------------------------------------------------------------------- /src/patterns/creational/fold.md: -------------------------------------------------------------------------------- 1 | # Fold 2 | 3 | ## Description 4 | 5 | Run an algorithm over each item in a collection of data to create a new item, 6 | thus creating a whole new collection. 7 | 8 | The etymology here is unclear to me. The terms 'fold' and 'folder' are used in 9 | the Rust compiler, although it appears to me to be more like a map than a fold 10 | in the usual sense. See the discussion below for more details. 11 | 12 | ## Example 13 | 14 | ```rust,ignore 15 | // The data we will fold, a simple AST. 16 | mod ast { 17 | pub enum Stmt { 18 | Expr(Box), 19 | Let(Box, Box), 20 | } 21 | 22 | pub struct Name { 23 | value: String, 24 | } 25 | 26 | pub enum Expr { 27 | IntLit(i64), 28 | Add(Box, Box), 29 | Sub(Box, Box), 30 | } 31 | } 32 | 33 | // The abstract folder 34 | mod fold { 35 | use ast::*; 36 | 37 | pub trait Folder { 38 | // A leaf node just returns the node itself. In some cases, we can do this 39 | // to inner nodes too. 40 | fn fold_name(&mut self, n: Box) -> Box { n } 41 | // Create a new inner node by folding its children. 42 | fn fold_stmt(&mut self, s: Box) -> Box { 43 | match *s { 44 | Stmt::Expr(e) => Box::new(Stmt::Expr(self.fold_expr(e))), 45 | Stmt::Let(n, e) => Box::new(Stmt::Let(self.fold_name(n), self.fold_expr(e))), 46 | } 47 | } 48 | fn fold_expr(&mut self, e: Box) -> Box { ... } 49 | } 50 | } 51 | 52 | use fold::*; 53 | use ast::*; 54 | 55 | // An example concrete implementation - renames every name to 'foo'. 56 | struct Renamer; 57 | impl Folder for Renamer { 58 | fn fold_name(&mut self, n: Box) -> Box { 59 | Box::new(Name { value: "foo".to_owned() }) 60 | } 61 | // Use the default methods for the other nodes. 62 | } 63 | ``` 64 | 65 | The result of running the `Renamer` on an AST is a new AST identical to the old 66 | one, but with every name changed to `foo`. A real life folder might have some 67 | state preserved between nodes in the struct itself. 68 | 69 | A folder can also be defined to map one data structure to a different (but 70 | usually similar) data structure. For example, we could fold an AST into a HIR 71 | tree (HIR stands for high-level intermediate representation). 72 | 73 | ## Motivation 74 | 75 | It is common to want to map a data structure by performing some operation on 76 | each node in the structure. For simple operations on simple data structures, 77 | this can be done using `Iterator::map`. For more complex operations, perhaps 78 | where earlier nodes can affect the operation on later nodes, or where iteration 79 | over the data structure is non-trivial, using the fold pattern is more 80 | appropriate. 81 | 82 | Like the visitor pattern, the fold pattern allows us to separate traversal of a 83 | data structure from the operations performed to each node. 84 | 85 | ## Discussion 86 | 87 | Mapping data structures in this fashion is common in functional languages. In OO 88 | languages, it would be more common to mutate the data structure in place. The 89 | 'functional' approach is common in Rust, mostly due to the preference for 90 | immutability. Using fresh data structures, rather than mutating old ones, makes 91 | reasoning about the code easier in most circumstances. 92 | 93 | The trade-off between efficiency and reusability can be tweaked by changing how 94 | nodes are accepted by the `fold_*` methods. 95 | 96 | In the above example we operate on `Box` pointers. Since these own their data 97 | exclusively, the original copy of the data structure cannot be re-used. On the 98 | other hand if a node is not changed, reusing it is very efficient. 99 | 100 | If we were to operate on borrowed references, the original data structure can be 101 | reused; however, a node must be cloned even if unchanged, which can be 102 | expensive. 103 | 104 | Using a reference counted pointer gives the best of both worlds - we can reuse 105 | the original data structure, and we don't need to clone unchanged nodes. 106 | However, they are less ergonomic to use and mean that the data structures cannot 107 | be mutable. 108 | 109 | ## See also 110 | 111 | Iterators have a `fold` method, however this folds a data structure into a 112 | value, rather than into a new data structure. An iterator's `map` is more like 113 | this fold pattern. 114 | 115 | In other languages, fold is usually used in the sense of Rust's iterators, 116 | rather than this pattern. Some functional languages have powerful constructs for 117 | performing flexible maps over data structures. 118 | 119 | The [visitor](../behavioural/visitor.md) pattern is closely related to fold. 120 | They share the concept of walking a data structure performing an operation on 121 | each node. However, the visitor does not create a new data structure nor consume 122 | the old one. 123 | -------------------------------------------------------------------------------- /src/patterns/creational/intro.md: -------------------------------------------------------------------------------- 1 | # Creational Patterns 2 | 3 | From [Wikipedia](https://en.wikipedia.org/wiki/Creational_pattern): 4 | 5 | > Design patterns that deal with object creation mechanisms, trying to create 6 | > objects in a manner suitable to the situation. The basic form of object 7 | > creation could result in design problems or in added complexity to the design. 8 | > Creational design patterns solve this problem by somehow controlling this 9 | > object creation. 10 | -------------------------------------------------------------------------------- /src/patterns/ffi/export.md: -------------------------------------------------------------------------------- 1 | # Object-Based APIs 2 | 3 | ## Description 4 | 5 | When designing APIs in Rust which are exposed to other languages, there are some 6 | important design principles which are contrary to normal Rust API design: 7 | 8 | 1. All Encapsulated types should be *owned* by Rust, *managed* by the user, and 9 | *opaque*. 10 | 2. All Transactional data types should be *owned* by the user, and 11 | *transparent*. 12 | 3. All library behavior should be functions acting upon Encapsulated types. 13 | 4. All library behavior should be encapsulated into types not based on 14 | structure, but *provenance/lifetime*. 15 | 16 | ## Motivation 17 | 18 | Rust has built-in FFI support to other languages. It does this by providing a 19 | way for crate authors to provide C-compatible APIs through different ABIs 20 | (though that is unimportant to this practice). 21 | 22 | Well-designed Rust FFI follows C API design principles, while compromising the 23 | design in Rust as little as possible. There are three goals with any foreign 24 | API: 25 | 26 | 1. Make it easy to use in the target language. 27 | 2. Avoid the API dictating internal unsafety on the Rust side as much as 28 | possible. 29 | 3. Keep the potential for memory unsafety and Rust `undefined behaviour` as 30 | small as possible. 31 | 32 | Rust code must trust the memory safety of the foreign language beyond a certain 33 | point. However, every bit of `unsafe` code on the Rust side is an opportunity 34 | for bugs, or to exacerbate `undefined behaviour`. 35 | 36 | For example, if a pointer provenance is wrong, that may be a segfault due to 37 | invalid memory access. But if it is manipulated by unsafe code, it could become 38 | full-blown heap corruption. 39 | 40 | The Object-Based API design allows for writing shims that have good memory 41 | safety characteristics, and a clean boundary of what is safe and what is 42 | `unsafe`. 43 | 44 | ## Code Example 45 | 46 | The POSIX standard defines the API to access an on-file database, known as 47 | [DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/ndbm.h). 48 | It is an excellent example of an "object-based" API. 49 | 50 | Here is the definition in C, which hopefully should be easy to read for those 51 | involved in FFI. The commentary below should help explain it for those who miss 52 | the subtleties. 53 | 54 | ```C 55 | struct DBM; 56 | typedef struct { void *dptr, size_t dsize } datum; 57 | 58 | int dbm_clearerr(DBM *); 59 | void dbm_close(DBM *); 60 | int dbm_delete(DBM *, datum); 61 | int dbm_error(DBM *); 62 | datum dbm_fetch(DBM *, datum); 63 | datum dbm_firstkey(DBM *); 64 | datum dbm_nextkey(DBM *); 65 | DBM *dbm_open(const char *, int, mode_t); 66 | int dbm_store(DBM *, datum, datum, int); 67 | ``` 68 | 69 | This API defines two types: `DBM` and `datum`. 70 | 71 | The `DBM` type was called an "encapsulated" type above. It is designed to 72 | contain internal state, and acts as an entry point for the library's behavior. 73 | 74 | It is completely opaque to the user, who cannot create a `DBM` themselves since 75 | they don't know its size or layout. Instead, they must call `dbm_open`, and that 76 | only gives them *a pointer to one*. 77 | 78 | This means all `DBM`s are "owned" by the library in a Rust sense. The internal 79 | state of unknown size is kept in memory controlled by the library, not the user. 80 | The user can only manage its life cycle with `open` and `close`, and perform 81 | operations on it with the other functions. 82 | 83 | The `datum` type was called a "transactional" type above. It is designed to 84 | facilitate the exchange of information between the library and its user. 85 | 86 | The database is designed to store "unstructured data", with no pre-defined 87 | length or meaning. As a result, the `datum` is the C equivalent of a Rust slice: 88 | a bunch of bytes, and a count of how many there are. The main difference is that 89 | there is no type information, which is what `void` indicates. 90 | 91 | Keep in mind that this header is written from the library's point of view. The 92 | user likely has some type they are using, which has a known size. But the 93 | library does not care, and by the rules of C casting, any type behind a pointer 94 | can be cast to `void`. 95 | 96 | As noted earlier, this type is *transparent* to the user. But also, this type is 97 | *owned* by the user. This has subtle ramifications, due to that pointer inside 98 | it. The question is, who owns the memory that pointer points to? 99 | 100 | The answer for best memory safety is, "the user". But in cases such as 101 | retrieving a value, the user does not know how to allocate it correctly (since 102 | they don't know how long the value is). In this case, the library code is 103 | expected to use the heap that the user has access to -- such as the C library 104 | `malloc` and `free` -- and then *transfer ownership* in the Rust sense. 105 | 106 | This may all seem speculative, but this is what a pointer means in C. It means 107 | the same thing as Rust: "user defined lifetime." The user of the library needs 108 | to read the documentation in order to use it correctly. That said, there are 109 | some decisions that have fewer or greater consequences if users do it wrong. 110 | Minimizing those are what this best practice is about, and the key is to 111 | *transfer ownership of everything that is transparent*. 112 | 113 | ## Advantages 114 | 115 | This minimizes the number of memory safety guarantees the user must uphold to a 116 | relatively small number: 117 | 118 | 1. Do not call any function with a pointer not returned by `dbm_open` (invalid 119 | access or corruption). 120 | 2. Do not call any function on a pointer after close (use after free). 121 | 3. The `dptr` on any `datum` must be `NULL`, or point to a valid slice of memory 122 | at the advertised length. 123 | 124 | In addition, it avoids a lot of pointer provenance issues. To understand why, 125 | let us consider an alternative in some depth: key iteration. 126 | 127 | Rust is well known for its iterators. When implementing one, the programmer 128 | makes a separate type with a bounded lifetime to its owner, and implements the 129 | `Iterator` trait. 130 | 131 | Here is how iteration would be done in Rust for `DBM`: 132 | 133 | ```rust,ignore 134 | struct Dbm { ... } 135 | 136 | impl Dbm { 137 | /* ... */ 138 | pub fn keys<'it>(&'it self) -> DbmKeysIter<'it> { ... } 139 | /* ... */ 140 | } 141 | 142 | struct DbmKeysIter<'it> { 143 | owner: &'it Dbm, 144 | } 145 | 146 | impl<'it> Iterator for DbmKeysIter<'it> { ... } 147 | ``` 148 | 149 | This is clean, idiomatic, and safe. thanks to Rust's guarantees. However, 150 | consider what a straightforward API translation would look like: 151 | 152 | ```rust,ignore 153 | #[no_mangle] 154 | pub extern "C" fn dbm_iter_new(owner: *const Dbm) -> *mut DbmKeysIter { 155 | // THIS API IS A BAD IDEA! For real applications, use object-based design instead. 156 | } 157 | #[no_mangle] 158 | pub extern "C" fn dbm_iter_next( 159 | iter: *mut DbmKeysIter, 160 | key_out: *const datum 161 | ) -> libc::c_int { 162 | // THIS API IS A BAD IDEA! For real applications, use object-based design instead. 163 | } 164 | #[no_mangle] 165 | pub extern "C" fn dbm_iter_del(*mut DbmKeysIter) { 166 | // THIS API IS A BAD IDEA! For real applications, use object-based design instead. 167 | } 168 | ``` 169 | 170 | This API loses a key piece of information: the lifetime of the iterator must not 171 | exceed the lifetime of the `Dbm` object that owns it. A user of the library 172 | could use it in a way which causes the iterator to outlive the data it is 173 | iterating on, resulting in reading uninitialized memory. 174 | 175 | This example written in C contains a bug that will be explained afterwards: 176 | 177 | ```C 178 | int count_key_sizes(DBM *db) { 179 | // DO NOT USE THIS FUNCTION. IT HAS A SUBTLE BUT SERIOUS BUG! 180 | datum key; 181 | int len = 0; 182 | 183 | if (!dbm_iter_new(db)) { 184 | dbm_close(db); 185 | return -1; 186 | } 187 | 188 | int l; 189 | while ((l = dbm_iter_next(owner, &key)) >= 0) { // an error is indicated by -1 190 | free(key.dptr); 191 | len += key.dsize; 192 | if (l == 0) { // end of the iterator 193 | dbm_close(owner); 194 | } 195 | } 196 | if l >= 0 { 197 | return -1; 198 | } else { 199 | return len; 200 | } 201 | } 202 | ``` 203 | 204 | This bug is a classic. Here's what happens when the iterator returns the 205 | end-of-iteration marker: 206 | 207 | 1. The loop condition sets `l` to zero, and enters the loop because `0 >= 0`. 208 | 2. The length is incremented, in this case by zero. 209 | 3. The if statement is true, so the database is closed. There should be a break 210 | statement here. 211 | 4. The loop condition executes again, causing a `next` call on the closed 212 | object. 213 | 214 | The worst part about this bug? If the Rust implementation was careful, this code 215 | will work most of the time! If the memory for the `Dbm` object is not 216 | immediately reused, an internal check will almost certainly fail, resulting in 217 | the iterator returning a `-1` indicating an error. But occasionally, it will 218 | cause a segmentation fault, or even worse, nonsensical memory corruption! 219 | 220 | None of this can be avoided by Rust. From its perspective, it put those objects 221 | on its heap, returned pointers to them, and gave up control of their lifetimes. 222 | The C code simply must "play nice". 223 | 224 | The programmer must read and understand the API documentation. While some 225 | consider that par for the course in C, a good API design can mitigate this risk. 226 | The POSIX API for `DBM` did this by *consolidating the ownership* of the 227 | iterator with its parent: 228 | 229 | ```C 230 | datum dbm_firstkey(DBM *); 231 | datum dbm_nextkey(DBM *); 232 | ``` 233 | 234 | Thus, all the lifetimes were bound together, and such unsafety was prevented. 235 | 236 | ## Disadvantages 237 | 238 | However, this design choice also has a number of drawbacks, which should be 239 | considered as well. 240 | 241 | First, the API itself becomes less expressive. With POSIX DBM, there is only one 242 | iterator per object, and every call changes its state. This is much more 243 | restrictive than iterators in almost any language, even though it is safe. 244 | Perhaps with other related objects, whose lifetimes are less hierarchical, this 245 | limitation is more of a cost than the safety. 246 | 247 | Second, depending on the relationships of the API's parts, significant design 248 | effort may be involved. Many of the easier design points have other patterns 249 | associated with them: 250 | 251 | - [Wrapper Type Consolidation](./wrappers.md) groups multiple Rust types 252 | together into an opaque "object" 253 | 254 | - [FFI Error Passing](../../idioms/ffi/errors.md) explains error handling with 255 | integer codes and sentinel return values (such as `NULL` pointers) 256 | 257 | - [Accepting Foreign Strings](../../idioms/ffi/accepting-strings.md) allows 258 | accepting strings with minimal unsafe code, and is easier to get right than 259 | [Passing Strings to FFI](../../idioms/ffi/passing-strings.md) 260 | 261 | However, not every API can be done this way. It is up to the best judgement of 262 | the programmer as to who their audience is. 263 | -------------------------------------------------------------------------------- /src/patterns/ffi/intro.md: -------------------------------------------------------------------------------- 1 | # FFI Patterns 2 | 3 | Writing FFI code is an entire course in itself. However, there are several 4 | idioms here that can act as pointers, and avoid traps for inexperienced users of 5 | unsafe Rust. 6 | 7 | This section contains design patterns that may be useful when doing FFI. 8 | 9 | 1. [Object-Based API](./export.md) design that has good memory safety 10 | characteristics, and a clean boundary of what is safe and what is unsafe 11 | 12 | 2. [Type Consolidation into Wrappers](./wrappers.md) - group multiple Rust types 13 | together into an opaque "object" 14 | -------------------------------------------------------------------------------- /src/patterns/ffi/wrappers.md: -------------------------------------------------------------------------------- 1 | # Type Consolidation into Wrappers 2 | 3 | ## Description 4 | 5 | This pattern is designed to allow gracefully handling multiple related types, 6 | while minimizing the surface area for memory unsafety. 7 | 8 | One of the cornerstones of Rust's aliasing rules is lifetimes. This ensures that 9 | many patterns of access between types can be memory safe, data race safety 10 | included. 11 | 12 | However, when Rust types are exported to other languages, they are usually 13 | transformed into pointers. In Rust, a pointer means "the user manages the 14 | lifetime of the pointee." It is their responsibility to avoid memory unsafety. 15 | 16 | Some level of trust in the user code is thus required, notably around 17 | use-after-free which Rust can do nothing about. However, some API designs place 18 | higher burdens than others on the code written in the other language. 19 | 20 | The lowest risk API is the "consolidated wrapper", where all possible 21 | interactions with an object are folded into a "wrapper type", while keeping the 22 | Rust API clean. 23 | 24 | ## Code Example 25 | 26 | To understand this, let us look at a classic example of an API to export: 27 | iteration through a collection. 28 | 29 | That API looks like this: 30 | 31 | 1. The iterator is initialized with `first_key`. 32 | 2. Each call to `next_key` will advance the iterator. 33 | 3. Calls to `next_key` if the iterator is at the end will do nothing. 34 | 4. As noted above, the iterator is "wrapped into" the collection (unlike the 35 | native Rust API). 36 | 37 | If the iterator implements `nth()` efficiently, then it is possible to make it 38 | ephemeral to each function call: 39 | 40 | ```rust,ignore 41 | struct MySetWrapper { 42 | myset: MySet, 43 | iter_next: usize, 44 | } 45 | 46 | impl MySetWrapper { 47 | pub fn first_key(&mut self) -> Option<&Key> { 48 | self.iter_next = 0; 49 | self.next_key() 50 | } 51 | pub fn next_key(&mut self) -> Option<&Key> { 52 | if let Some(next) = self.myset.keys().nth(self.iter_next) { 53 | self.iter_next += 1; 54 | Some(next) 55 | } else { 56 | None 57 | } 58 | } 59 | } 60 | ``` 61 | 62 | As a result, the wrapper is simple and contains no `unsafe` code. 63 | 64 | ## Advantages 65 | 66 | This makes APIs safer to use, avoiding issues with lifetimes between types. See 67 | [Object-Based APIs](./export.md) for more on the advantages and pitfalls this 68 | avoids. 69 | 70 | ## Disadvantages 71 | 72 | Often, wrapping types is quite difficult, and sometimes a Rust API compromise 73 | would make things easier. 74 | 75 | As an example, consider an iterator which does not efficiently implement 76 | `nth()`. It would definitely be worth putting in special logic to make the 77 | object handle iteration internally, or to support a different access pattern 78 | efficiently that only the Foreign Function API will use. 79 | 80 | ### Trying to Wrap Iterators (and Failing) 81 | 82 | To wrap any type of iterator into the API correctly, the wrapper would need to 83 | do what a C version of the code would do: erase the lifetime of the iterator, 84 | and manage it manually. 85 | 86 | Suffice it to say, this is *incredibly* difficult. 87 | 88 | Here is an illustration of just *one* pitfall. 89 | 90 | A first version of `MySetWrapper` would look like this: 91 | 92 | ```rust,ignore 93 | struct MySetWrapper { 94 | myset: MySet, 95 | iter_next: usize, 96 | // created from a transmuted Box 97 | iterator: Option>>, 98 | } 99 | ``` 100 | 101 | With `transmute` being used to extend a lifetime, and a pointer to hide it, it's 102 | ugly already. But it gets even worse: *any other operation can cause Rust 103 | `undefined behaviour`*. 104 | 105 | Consider that the `MySet` in the wrapper could be manipulated by other functions 106 | during iteration, such as storing a new value to the key it was iterating over. 107 | The API doesn't discourage this, and in fact some similar C libraries expect it. 108 | 109 | A simple implementation of `myset_store` would be: 110 | 111 | ```rust,ignore 112 | pub mod unsafe_module { 113 | 114 | // other module content 115 | 116 | pub fn myset_store(myset: *mut MySetWrapper, key: datum, value: datum) -> libc::c_int { 117 | // DO NOT USE THIS CODE. IT IS UNSAFE TO DEMONSTRATE A PROBLEM. 118 | 119 | let myset: &mut MySet = unsafe { 120 | // SAFETY: whoops, UB occurs in here! 121 | &mut (*myset).myset 122 | }; 123 | 124 | /* ...check and cast key and value data... */ 125 | 126 | match myset.store(casted_key, casted_value) { 127 | Ok(_) => 0, 128 | Err(e) => e.into(), 129 | } 130 | } 131 | } 132 | ``` 133 | 134 | If the iterator exists when this function is called, we have violated one of 135 | Rust's aliasing rules. According to Rust, the mutable reference in this block 136 | must have *exclusive* access to the object. If the iterator simply exists, it's 137 | not exclusive, so we have `undefined behaviour`! [^1] 138 | 139 | To avoid this, we must have a way of ensuring that mutable reference really is 140 | exclusive. That basically means clearing out the iterator's shared reference 141 | while it exists, and then reconstructing it. In most cases, that will still be 142 | less efficient than the C version. 143 | 144 | Some may ask: how can C do this more efficiently? The answer is, it cheats. 145 | Rust's aliasing rules are the problem, and C simply ignores them for its 146 | pointers. In exchange, it is common to see code that is declared in the manual 147 | as "not thread safe" under some or all circumstances. In fact, the 148 | [GNU C library](https://manpages.debian.org/buster/manpages/attributes.7.en.html) 149 | has an entire lexicon dedicated to concurrent behavior! 150 | 151 | Rust would rather make everything memory safe all the time, for both safety and 152 | optimizations that C code cannot attain. Being denied access to certain 153 | shortcuts is the price Rust programmers need to pay. 154 | 155 | [^1]: For the C programmers out there scratching their heads, the iterator need 156 | not be read *during* this code to cause the UB. The exclusivity rule also 157 | enables compiler optimizations which may cause inconsistent observations by the 158 | iterator's shared reference (e.g. stack spills or reordering instructions for 159 | efficiency). These observations may happen *any time after* the mutable 160 | reference is created. 161 | -------------------------------------------------------------------------------- /src/patterns/index.md: -------------------------------------------------------------------------------- 1 | # Design Patterns 2 | 3 | [Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) are 4 | "general reusable solutions to a commonly occurring problem within a given 5 | context in software design". Design patterns are a great way to describe the 6 | culture of a programming language. Design patterns are very language-specific - 7 | what is a pattern in one language may be unnecessary in another due to a 8 | language feature, or impossible to express due to a missing feature. 9 | 10 | If overused, design patterns can add unnecessary complexity to programs. 11 | However, they are a great way to share intermediate and advanced level knowledge 12 | about a programming language. 13 | 14 | ## Design patterns in Rust 15 | 16 | Rust has many unique features. These features give us great benefit by removing 17 | whole classes of problems. Some of them are also patterns that are *unique* to 18 | Rust. 19 | 20 | ## YAGNI 21 | 22 | YAGNI is an acronym that stands for `You Aren't Going to Need It`. It's a vital 23 | software design principle to apply as you write code. 24 | 25 | > The best code I ever wrote was code I never wrote. 26 | 27 | If we apply YAGNI to design patterns, we see that the features of Rust allow us 28 | to throw out many patterns. For instance, there is no need for the 29 | [strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern) in Rust 30 | because we can just use [traits](https://doc.rust-lang.org/book/traits.html). 31 | -------------------------------------------------------------------------------- /src/patterns/structural/compose-structs.md: -------------------------------------------------------------------------------- 1 | # Struct decomposition for independent borrowing 2 | 3 | ## Description 4 | 5 | Sometimes a large struct will cause issues with the borrow checker - although 6 | fields can be borrowed independently, sometimes the whole struct ends up being 7 | used at once, preventing other uses. A solution might be to decompose the struct 8 | into several smaller structs. Then compose these together into the original 9 | struct. Then each struct can be borrowed separately and have more flexible 10 | behaviour. 11 | 12 | This will often lead to a better design in other ways: applying this design 13 | pattern often reveals smaller units of functionality. 14 | 15 | ## Example 16 | 17 | Here is a contrived example of where the borrow checker foils us in our plan to 18 | use a struct: 19 | 20 | ```rust,ignore 21 | struct Database { 22 | connection_string: String, 23 | timeout: u32, 24 | pool_size: u32, 25 | } 26 | 27 | fn print_database(database: &Database) { 28 | println!("Connection string: {}", database.connection_string); 29 | println!("Timeout: {}", database.timeout); 30 | println!("Pool size: {}", database.pool_size); 31 | } 32 | 33 | fn main() { 34 | let mut db = Database { 35 | connection_string: "initial string".to_string(), 36 | timeout: 30, 37 | pool_size: 100, 38 | }; 39 | 40 | let connection_string = &mut db.connection_string; 41 | print_database(&db); 42 | *connection_string = "new string".to_string(); 43 | } 44 | ``` 45 | 46 | The compiler throws following errors: 47 | 48 | ```ignore 49 | let connection_string = &mut db.connection_string; 50 | ------------------------- mutable borrow occurs here 51 | print_database(&db); 52 | ^^^ immutable borrow occurs here 53 | *connection_string = "new string".to_string(); 54 | ------------------ mutable borrow later used here 55 | ``` 56 | 57 | We can apply this design pattern and refactor `Database` into three smaller 58 | structs, thus solving the borrow checking issue: 59 | 60 | ```rust 61 | // Database is now composed of three structs - ConnectionString, Timeout and PoolSize. 62 | // Let's decompose it into smaller structs 63 | #[derive(Debug, Clone)] 64 | struct ConnectionString(String); 65 | 66 | #[derive(Debug, Clone, Copy)] 67 | struct Timeout(u32); 68 | 69 | #[derive(Debug, Clone, Copy)] 70 | struct PoolSize(u32); 71 | 72 | // We then compose these smaller structs back into `Database` 73 | struct Database { 74 | connection_string: ConnectionString, 75 | timeout: Timeout, 76 | pool_size: PoolSize, 77 | } 78 | 79 | // print_database can then take ConnectionString, Timeout and Poolsize struct instead 80 | fn print_database(connection_str: ConnectionString, timeout: Timeout, pool_size: PoolSize) { 81 | println!("Connection string: {connection_str:?}"); 82 | println!("Timeout: {timeout:?}"); 83 | println!("Pool size: {pool_size:?}"); 84 | } 85 | 86 | fn main() { 87 | // Initialize the Database with the three structs 88 | let mut db = Database { 89 | connection_string: ConnectionString("localhost".to_string()), 90 | timeout: Timeout(30), 91 | pool_size: PoolSize(100), 92 | }; 93 | 94 | let connection_string = &mut db.connection_string; 95 | print_database(connection_string.clone(), db.timeout, db.pool_size); 96 | *connection_string = ConnectionString("new string".to_string()); 97 | } 98 | ``` 99 | 100 | ## Motivation 101 | 102 | This pattern is most useful, when you have a struct that ended up with a lot of 103 | fields that you want to borrow independently. Thus having a more flexible 104 | behaviour in the end. 105 | 106 | ## Advantages 107 | 108 | Decomposition of structs lets you work around limitations in the borrow checker. 109 | And it often produces a better design. 110 | 111 | ## Disadvantages 112 | 113 | It can lead to more verbose code. And sometimes, the smaller structs are not 114 | good abstractions, and so we end up with a worse design. That is probably a 115 | 'code smell', indicating that the program should be refactored in some way. 116 | 117 | ## Discussion 118 | 119 | This pattern is not required in languages that don't have a borrow checker, so 120 | in that sense is unique to Rust. However, making smaller units of functionality 121 | often leads to cleaner code: a widely acknowledged principle of software 122 | engineering, independent of the language. 123 | 124 | This pattern relies on Rust's borrow checker to be able to borrow fields 125 | independently of each other. In the example, the borrow checker knows that `a.b` 126 | and `a.c` are distinct and can be borrowed independently, it does not try to 127 | borrow all of `a`, which would make this pattern useless. 128 | -------------------------------------------------------------------------------- /src/patterns/structural/intro.md: -------------------------------------------------------------------------------- 1 | # Structural Patterns 2 | 3 | From [Wikipedia](https://en.wikipedia.org/wiki/Structural_pattern): 4 | 5 | > Design patterns that ease the design by identifying a simple way to realize 6 | > relationships among entities. 7 | -------------------------------------------------------------------------------- /src/patterns/structural/small-crates.md: -------------------------------------------------------------------------------- 1 | # Prefer small crates 2 | 3 | ## Description 4 | 5 | Prefer small crates that do one thing well. 6 | 7 | Cargo and crates.io make it easy to add third-party libraries, much more so than 8 | in say C or C++. Moreover, since packages on crates.io cannot be edited or 9 | removed after publication, any build that works now should continue to work in 10 | the future. We should take advantage of this tooling, and use smaller, more 11 | fine-grained dependencies. 12 | 13 | ## Advantages 14 | 15 | - Small crates are easier to understand, and encourage more modular code. 16 | - Crates allow for re-using code between projects. For example, the `url` crate 17 | was developed as part of the Servo browser engine, but has since found wide 18 | use outside the project. 19 | - Since the compilation unit of Rust is the crate, splitting a project into 20 | multiple crates can allow more of the code to be built in parallel. 21 | 22 | ## Disadvantages 23 | 24 | - This can lead to "dependency hell", when a project depends on multiple 25 | conflicting versions of a crate at the same time. For example, the `url` crate 26 | has both versions 1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url` 27 | from `url:0.5` are different types, an HTTP client that uses `url:0.5` would 28 | not accept `Url` values from a web scraper that uses `url:1.0`. 29 | - Packages on crates.io are not curated. A crate may be poorly written, have 30 | unhelpful documentation, or be outright malicious. 31 | - Two small crates may be less optimized than one large one, since the compiler 32 | does not perform link-time optimization (LTO) by default. 33 | 34 | ## Examples 35 | 36 | The [`url`](https://crates.io/crates/url) crate provides tools for working with 37 | URLs. 38 | 39 | The [`num_cpus`](https://crates.io/crates/num_cpus) crate provides a function to 40 | query the number of CPUs on a machine. 41 | 42 | The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions 43 | for converting `&T` to `&[T]`. (Historical example) 44 | 45 | ## See also 46 | 47 | - [crates.io: The Rust community crate host](https://crates.io/) 48 | -------------------------------------------------------------------------------- /src/patterns/structural/unsafe-mods.md: -------------------------------------------------------------------------------- 1 | # Contain unsafety in small modules 2 | 3 | ## Description 4 | 5 | If you have `unsafe` code, create the smallest possible module that can uphold 6 | the needed invariants to build a minimal safe interface upon the unsafety. Embed 7 | this into a larger module that contains only safe code and presents an ergonomic 8 | interface. Note that the outer module can contain unsafe functions and methods 9 | that call directly into the unsafe code. Users may use this to gain speed 10 | benefits. 11 | 12 | ## Advantages 13 | 14 | - This restricts the unsafe code that must be audited 15 | - Writing the outer module is much easier, since you can count on the guarantees 16 | of the inner module 17 | 18 | ## Disadvantages 19 | 20 | - Sometimes, it may be hard to find a suitable interface. 21 | - The abstraction may introduce inefficiencies. 22 | 23 | ## Examples 24 | 25 | - The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe 26 | operations in submodules, presenting a safe interface to users. 27 | - `std`'s `String` class is a wrapper over `Vec` with the added invariant 28 | that the contents must be valid UTF-8. The operations on `String` ensure this 29 | behavior. However, users have the option of using an `unsafe` method to create 30 | a `String`, in which case the onus is on them to guarantee the validity of the 31 | contents. 32 | 33 | ## See also 34 | 35 | - [Ralf Jung's Blog about invariants in unsafe code](https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html) 36 | -------------------------------------------------------------------------------- /src/refactoring/index.md: -------------------------------------------------------------------------------- 1 | # Refactoring 2 | 3 | Refactoring is very important in relation to these topics. Just as important as 4 | the other topics covered here, is how to take good code and turn it into great 5 | code. 6 | 7 | We can use [design patterns](../patterns/index.md) to [DRY] up code and 8 | generalize abstractions. We must avoid 9 | [anti-patterns](../anti_patterns/index.md) while we do this. While they may be 10 | tempting to employ, their costs outweigh their benefits. 11 | 12 | > Shortcuts make for long days. 13 | 14 | We can also use [idioms](../idioms/index.md) to structure our code in a way that 15 | is understandable. 16 | 17 | ## Tests 18 | 19 | Tests are of vital importance during refactoring. 20 | 21 | ## Small changes 22 | 23 | [DRY]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself 24 | -------------------------------------------------------------------------------- /src/translations.md: -------------------------------------------------------------------------------- 1 | # Translations 2 | 3 | We are utilizing 4 | [mdbook-i18n-helper](https://github.com/google/mdbook-i18n-helpers). Please read 5 | up on how to *add* and *update* translations in 6 | [their repository](https://github.com/google/mdbook-i18n-helpers#creating-and-updating-translations) 7 | 8 | ## External translations 9 | 10 | - [简体中文](https://fomalhauthmj.github.io/patterns/) 11 | 12 | If you want to add a translation, please open an issue in the 13 | [main repository](https://github.com/rust-unofficial/patterns). 14 | -------------------------------------------------------------------------------- /styles/last-changed.css: -------------------------------------------------------------------------------- 1 | footer { 2 | font-size: 0.8em; 3 | text-align: center; 4 | border-top: 1px solid grey; 5 | padding: 1.25em 0; 6 | margin-top: 1.25em; 7 | } 8 | -------------------------------------------------------------------------------- /template.md: -------------------------------------------------------------------------------- 1 | # A succinct name for the pattern 2 | 3 | ## Description 4 | 5 | A short, prose description of the pattern. 6 | 7 | ## Example 8 | 9 | ```rust 10 | // An example of the pattern in action, should be mostly code, commented 11 | // liberally. 12 | ``` 13 | 14 | When writing examples, please try to make them compile. This allows us to test 15 | them. If you fail to write an example that is both complete and readable, please 16 | at least mark your example code with `ignore` as in here: 17 | 18 | ```rust,ignore 19 | // A non-runnable example of the pattern in action, should be mostly code, commented 20 | // liberally. 21 | ``` 22 | 23 | ## Motivation 24 | 25 | Why and where you should use the pattern 26 | 27 | ## Advantages 28 | 29 | Good things about this pattern. 30 | 31 | ## Disadvantages 32 | 33 | Bad things about this pattern. Possible contraindications. 34 | 35 | ## Discussion 36 | 37 | A deeper discussion about this pattern. You might want to cover how this is done 38 | in other languages, alternative approaches, why this is particularly nice in 39 | Rust, etc. 40 | 41 | ## See also 42 | 43 | Related patterns (link to the pattern file). Versions of this pattern in other 44 | languages. 45 | -------------------------------------------------------------------------------- /theme/book.js: -------------------------------------------------------------------------------- 1 | ../third_party/mdbook/book.js -------------------------------------------------------------------------------- /theme/css/language-picker.css: -------------------------------------------------------------------------------- 1 | #language-list { 2 | left: auto; 3 | right: 10px; 4 | } 5 | 6 | [dir="rtl"] #language-list { 7 | left: 10px; 8 | right: auto; 9 | } 10 | 11 | #language-list a { 12 | color: inherit; 13 | } 14 | -------------------------------------------------------------------------------- /theme/head.hbs: -------------------------------------------------------------------------------- 1 | {{! Move to template code after fixing this issue: 2 | https://github.com/google/mdbook-i18n-helpers/issues/70 }} 3 | -------------------------------------------------------------------------------- /third_party/mdbook/LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. "Contributor" 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. "Contributor Version" 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. "Covered Software" 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the terms of 34 | a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. "Larger Work" 41 | 42 | means a work that combines Covered Software with other material, in a 43 | separate file or files, that is not Covered Software. 44 | 45 | 1.8. "License" 46 | 47 | means this document. 48 | 49 | 1.9. "Licensable" 50 | 51 | means having the right to grant, to the maximum extent possible, whether 52 | at the time of the initial grant or subsequently, any and all of the 53 | rights conveyed by this License. 54 | 55 | 1.10. "Modifications" 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, 60 | deletion from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. "Patent Claims" of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, 67 | process, and apparatus claims, in any patent Licensable by such 68 | Contributor that would be infringed, but for the grant of the License, 69 | by the making, using, selling, offering for sale, having made, import, 70 | or transfer of either its Contributions or its Contributor Version. 71 | 72 | 1.12. "Secondary License" 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. "Source Code Form" 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. "You" (or "Your") 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, "You" includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, "control" means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or 104 | as part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its 108 | Contributions or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution 113 | become effective for each Contribution on the date the Contributor first 114 | distributes such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under 119 | this License. No additional rights or licenses will be implied from the 120 | distribution or licensing of Covered Software under this License. 121 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 122 | Contributor: 123 | 124 | a. for any code that a Contributor has removed from Covered Software; or 125 | 126 | b. for infringements caused by: (i) Your and any other third party's 127 | modifications of Covered Software, or (ii) the combination of its 128 | Contributions with other software (except as part of its Contributor 129 | Version); or 130 | 131 | c. under Patent Claims infringed by Covered Software in the absence of 132 | its Contributions. 133 | 134 | This License does not grant any rights in the trademarks, service marks, 135 | or logos of any Contributor (except as may be necessary to comply with 136 | the notice requirements in Section 3.4). 137 | 138 | 2.4. Subsequent Licenses 139 | 140 | No Contributor makes additional grants as a result of Your choice to 141 | distribute the Covered Software under a subsequent version of this 142 | License (see Section 10.2) or under the terms of a Secondary License (if 143 | permitted under the terms of Section 3.3). 144 | 145 | 2.5. Representation 146 | 147 | Each Contributor represents that the Contributor believes its 148 | Contributions are its original creation(s) or it has sufficient rights to 149 | grant the rights to its Contributions conveyed by this License. 150 | 151 | 2.6. Fair Use 152 | 153 | This License is not intended to limit any rights You have under 154 | applicable copyright doctrines of fair use, fair dealing, or other 155 | equivalents. 156 | 157 | 2.7. Conditions 158 | 159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 160 | Section 2.1. 161 | 162 | 163 | 3. Responsibilities 164 | 165 | 3.1. Distribution of Source Form 166 | 167 | All distribution of Covered Software in Source Code Form, including any 168 | Modifications that You create or to which You contribute, must be under 169 | the terms of this License. You must inform recipients that the Source 170 | Code Form of the Covered Software is governed by the terms of this 171 | License, and how they can obtain a copy of this License. You may not 172 | attempt to alter or restrict the recipients' rights in the Source Code 173 | Form. 174 | 175 | 3.2. Distribution of Executable Form 176 | 177 | If You distribute Covered Software in Executable Form then: 178 | 179 | a. such Covered Software must also be made available in Source Code Form, 180 | as described in Section 3.1, and You must inform recipients of the 181 | Executable Form how they can obtain a copy of such Source Code Form by 182 | reasonable means in a timely manner, at a charge no more than the cost 183 | of distribution to the recipient; and 184 | 185 | b. You may distribute such Executable Form under the terms of this 186 | License, or sublicense it under different terms, provided that the 187 | license for the Executable Form does not attempt to limit or alter the 188 | recipients' rights in the Source Code Form under this License. 189 | 190 | 3.3. Distribution of a Larger Work 191 | 192 | You may create and distribute a Larger Work under terms of Your choice, 193 | provided that You also comply with the requirements of this License for 194 | the Covered Software. If the Larger Work is a combination of Covered 195 | Software with a work governed by one or more Secondary Licenses, and the 196 | Covered Software is not Incompatible With Secondary Licenses, this 197 | License permits You to additionally distribute such Covered Software 198 | under the terms of such Secondary License(s), so that the recipient of 199 | the Larger Work may, at their option, further distribute the Covered 200 | Software under the terms of either this License or such Secondary 201 | License(s). 202 | 203 | 3.4. Notices 204 | 205 | You may not remove or alter the substance of any license notices 206 | (including copyright notices, patent notices, disclaimers of warranty, or 207 | limitations of liability) contained within the Source Code Form of the 208 | Covered Software, except that You may alter any license notices to the 209 | extent required to remedy known factual inaccuracies. 210 | 211 | 3.5. Application of Additional Terms 212 | 213 | You may choose to offer, and to charge a fee for, warranty, support, 214 | indemnity or liability obligations to one or more recipients of Covered 215 | Software. However, You may do so only on Your own behalf, and not on 216 | behalf of any Contributor. You must make it absolutely clear that any 217 | such warranty, support, indemnity, or liability obligation is offered by 218 | You alone, and You hereby agree to indemnify every Contributor for any 219 | liability incurred by such Contributor as a result of warranty, support, 220 | indemnity or liability terms You offer. You may include additional 221 | disclaimers of warranty and limitations of liability specific to any 222 | jurisdiction. 223 | 224 | 4. Inability to Comply Due to Statute or Regulation 225 | 226 | If it is impossible for You to comply with any of the terms of this License 227 | with respect to some or all of the Covered Software due to statute, 228 | judicial order, or regulation then You must: (a) comply with the terms of 229 | this License to the maximum extent possible; and (b) describe the 230 | limitations and the code they affect. Such description must be placed in a 231 | text file included with all distributions of the Covered Software under 232 | this License. Except to the extent prohibited by statute or regulation, 233 | such description must be sufficiently detailed for a recipient of ordinary 234 | skill to be able to understand it. 235 | 236 | 5. Termination 237 | 238 | 5.1. The rights granted under this License will terminate automatically if You 239 | fail to comply with any of its terms. However, if You become compliant, 240 | then the rights granted under this License from a particular Contributor 241 | are reinstated (a) provisionally, unless and until such Contributor 242 | explicitly and finally terminates Your grants, and (b) on an ongoing 243 | basis, if such Contributor fails to notify You of the non-compliance by 244 | some reasonable means prior to 60 days after You have come back into 245 | compliance. Moreover, Your grants from a particular Contributor are 246 | reinstated on an ongoing basis if such Contributor notifies You of the 247 | non-compliance by some reasonable means, this is the first time You have 248 | received notice of non-compliance with this License from such 249 | Contributor, and You become compliant prior to 30 days after Your receipt 250 | of the notice. 251 | 252 | 5.2. If You initiate litigation against any entity by asserting a patent 253 | infringement claim (excluding declaratory judgment actions, 254 | counter-claims, and cross-claims) alleging that a Contributor Version 255 | directly or indirectly infringes any patent, then the rights granted to 256 | You by any and all Contributors for the Covered Software under Section 257 | 2.1 of this License shall terminate. 258 | 259 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 260 | license agreements (excluding distributors and resellers) which have been 261 | validly granted by You or Your distributors under this License prior to 262 | termination shall survive termination. 263 | 264 | 6. Disclaimer of Warranty 265 | 266 | Covered Software is provided under this License on an "as is" basis, 267 | without warranty of any kind, either expressed, implied, or statutory, 268 | including, without limitation, warranties that the Covered Software is free 269 | of defects, merchantable, fit for a particular purpose or non-infringing. 270 | The entire risk as to the quality and performance of the Covered Software 271 | is with You. Should any Covered Software prove defective in any respect, 272 | You (not any Contributor) assume the cost of any necessary servicing, 273 | repair, or correction. This disclaimer of warranty constitutes an essential 274 | part of this License. No use of any Covered Software is authorized under 275 | this License except under this disclaimer. 276 | 277 | 7. Limitation of Liability 278 | 279 | Under no circumstances and under no legal theory, whether tort (including 280 | negligence), contract, or otherwise, shall any Contributor, or anyone who 281 | distributes Covered Software as permitted above, be liable to You for any 282 | direct, indirect, special, incidental, or consequential damages of any 283 | character including, without limitation, damages for lost profits, loss of 284 | goodwill, work stoppage, computer failure or malfunction, or any and all 285 | other commercial damages or losses, even if such party shall have been 286 | informed of the possibility of such damages. This limitation of liability 287 | shall not apply to liability for death or personal injury resulting from 288 | such party's negligence to the extent applicable law prohibits such 289 | limitation. Some jurisdictions do not allow the exclusion or limitation of 290 | incidental or consequential damages, so this exclusion and limitation may 291 | not apply to You. 292 | 293 | 8. Litigation 294 | 295 | Any litigation relating to this License may be brought only in the courts 296 | of a jurisdiction where the defendant maintains its principal place of 297 | business and such litigation shall be governed by laws of that 298 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 299 | in this Section shall prevent a party's ability to bring cross-claims or 300 | counter-claims. 301 | 302 | 9. Miscellaneous 303 | 304 | This License represents the complete agreement concerning the subject 305 | matter hereof. If any provision of this License is held to be 306 | unenforceable, such provision shall be reformed only to the extent 307 | necessary to make it enforceable. Any law or regulation which provides that 308 | the language of a contract shall be construed against the drafter shall not 309 | be used to construe this License against a Contributor. 310 | 311 | 312 | 10. Versions of the License 313 | 314 | 10.1. New Versions 315 | 316 | Mozilla Foundation is the license steward. Except as provided in Section 317 | 10.3, no one other than the license steward has the right to modify or 318 | publish new versions of this License. Each version will be given a 319 | distinguishing version number. 320 | 321 | 10.2. Effect of New Versions 322 | 323 | You may distribute the Covered Software under the terms of the version 324 | of the License under which You originally received the Covered Software, 325 | or under the terms of any subsequent version published by the license 326 | steward. 327 | 328 | 10.3. Modified Versions 329 | 330 | If you create software not governed by this License, and you want to 331 | create a new license for such software, you may create and use a 332 | modified version of this License if you rename the license and remove 333 | any references to the name of the license steward (except to note that 334 | such modified license differs from this License). 335 | 336 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 337 | Licenses If You choose to distribute Source Code Form that is 338 | Incompatible With Secondary Licenses under the terms of this version of 339 | the License, the notice described in Exhibit B of this License must be 340 | attached. 341 | 342 | Exhibit A - Source Code Form License Notice 343 | 344 | This Source Code Form is subject to the 345 | terms of the Mozilla Public License, v. 346 | 2.0. If a copy of the MPL was not 347 | distributed with this file, You can 348 | obtain one at 349 | http://mozilla.org/MPL/2.0/. 350 | 351 | If it is not possible or desirable to put the notice in a particular file, 352 | then You may include the notice in a location (such as a LICENSE file in a 353 | relevant directory) where a recipient would be likely to look for such a 354 | notice. 355 | 356 | You may add additional accurate notices of copyright ownership. 357 | 358 | Exhibit B - "Incompatible With Secondary Licenses" Notice 359 | 360 | This Source Code Form is "Incompatible 361 | With Secondary Licenses", as defined by 362 | the Mozilla Public License, v. 2.0. 363 | -------------------------------------------------------------------------------- /third_party/mdbook/README.md: -------------------------------------------------------------------------------- 1 | # mdBook 2 | 3 | This directory contains files copied from mdBook. Please see 4 | for the full project. 5 | 6 | ## License 7 | 8 | mdBook is licensed under the Mozilla Public License v2.0 ([LICENSE](LICENSE)). 9 | --------------------------------------------------------------------------------