Refactor repository structure to include translations (#352)

pull/358/head
simonsan 1 year ago committed by GitHub
parent f5f8078f46
commit c30a6eb236
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1 +1,2 @@
MDBOOK_VERSION=0.4.28
MDBOOK_I8N_HELPERS_VERSION=0.1.0

@ -0,0 +1,119 @@
name: Test mdbook chapters
on:
pull_request:
push:
branches:
- main
env:
CARGO_TERM_COLOR: always
jobs:
mdbook-test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Rust cache
uses: ./.github/workflows/setup-rust-cache
- name: Install mdbook
uses: ./.github/workflows/install-mdbook
- name: Test code snippets
run: mdbook test
# TODO: Activate when first translation is available
# i18n-helpers:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout
# uses: actions/checkout@v3
# - name: Install Gettext
# run: sudo apt install gettext
# - name: Setup Rust cache
# uses: ./.github/workflows/setup-rust-cache
# - name: Install mdbook
# uses: ./.github/workflows/install-mdbook
# - name: Generate po/messages.pot
# run: mdbook build -d po
# env:
# MDBOOK_OUTPUT: '{"xgettext": {"pot-file": "messages.pot"}}'
# - name: Test messages.pot
# run: msgfmt --statistics -o /dev/null po/messages.pot
# - name: Expand includes without translation
# run: mdbook build -d expanded
# env:
# MDBOOK_OUTPUT: '{"markdown": {}}'
# - name: Expand includes with no-op translation
# run: mdbook build -d no-op
# env:
# MDBOOK_OUTPUT: '{"markdown": {}}'
# MDBOOK_PREPROCESSOR__GETTEXT__PO_FILE: po/messages.pot
# - name: Compare no translation to no-op translation
# run: diff --color=always --unified --recursive expanded no-op
# find-translations:
# runs-on: ubuntu-latest
# outputs:
# languages: ${{ steps.find-translations.outputs.languages }}
# steps:
# - name: Checkout
# uses: actions/checkout@v3
# - name: Find translations
# id: find-translations
# shell: python
# run: |
# import os, json, pathlib
# languages = [p.stem for p in pathlib.Path("po").iterdir() if p.suffix == ".po"]
# github_output = open(os.environ["GITHUB_OUTPUT"], "a")
# github_output.write("languages=")
# json.dump(sorted(languages), github_output)
# translations:
# runs-on: ubuntu-latest
# needs:
# - find-translations
# strategy:
# matrix:
# language: ${{ fromJSON(needs.find-translations.outputs.languages) }}
# env:
# MDBOOK_BOOK__LANGUAGE: ${{ matrix.language }}
# steps:
# - name: Checkout
# uses: actions/checkout@v3
# - name: Install Gettext
# run: sudo apt install gettext
# - name: Setup Rust cache
# uses: ./.github/workflows/setup-rust-cache
# - name: Install mdbook
# uses: ./.github/workflows/install-mdbook
# - name: Test ${{ matrix.language }} translation
# run: msgfmt --statistics -o /dev/null po/${{ matrix.language }}.po
# - name: Build book with ${{ matrix.language }} translation
# run: mdbook build
# - name: Upload ${{ matrix.language }} translation
# uses: actions/upload-artifact@v3
# with:
# name: rust-design-patterns-${{ matrix.language }}
# path: book/
# - name: Test code snippets with ${{ matrix.language }} translation
# run: mdbook test

@ -1,53 +0,0 @@
name: Continuous Integration
on:
push:
branches: [main]
pull_request:
jobs:
deploy-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Read .env
id: mdbook-version
run: |
. ./.env
echo "::set-output name=MDBOOK_VERSION::${MDBOOK_VERSION}"
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: '${{ steps.mdbook-version.outputs.MDBOOK_VERSION }}'
- run: mdbook build
doc-test:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Read .env
id: mdbook-version
run: |
. ./.env
echo "::set-output name=MDBOOK_VERSION::${MDBOOK_VERSION}"
- name: Setup mdBook
uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: '${{ steps.mdbook-version.outputs.MDBOOK_VERSION }}'
- run: mdbook test
markdown-lint:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Lint all files recursively
uses: avto-dev/markdown-lint@v1
with:
config: '.markdownlint.yaml'
args: '**/*.md'

@ -1,31 +0,0 @@
name: github pages
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Read .env
id: mdbook-version
run: |
. ./.env
echo "::set-output name=MDBOOK_VERSION::${MDBOOK_VERSION}"
- name: Setup mdbook
uses: peaceiris/actions-mdbook@v1
with:
mdbook-version: '${{ steps.mdbook-version.outputs.MDBOOK_VERSION }}'
- run: mdbook build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./book

@ -0,0 +1,29 @@
name: Install mdbook and dependencies
description: Install the mdbook with the dependencies we need.
runs:
using: composite
steps:
- name: Read mdbook version from .env
id: mdbook-version
run: |
. ./.env
echo "::set-output name=MDBOOK_VERSION::${MDBOOK_VERSION}"
shell: bash
- name: Read mdbook-i8n-helpers version from .env
id: mdbook-i8n-helpers-version
run: |
. ./.env
echo "::set-output name=MDBOOK_I8N_HELPERS_VERSION::${MDBOOK_I8N_HELPERS_VERSION}"
shell: bash
# The --locked flag is important for reproducible builds.
- name: Install mdbook
run: cargo install mdbook --locked --version '${{ steps.mdbook-version.outputs.MDBOOK_VERSION }}'
shell: bash
- name: Install i18n-helpers
run: cargo install mdbook-i18n-helpers --locked --version '${{ steps.mdbook-i8n-helpers-version.outputs.MDBOOK_I8N_HELPERS_VERSION }}'
shell: bash

@ -0,0 +1,14 @@
name: Lint Markdown
on:
push:
branches: [main]
pull_request:
jobs:
style:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dprint/check@v2.2

@ -0,0 +1,67 @@
name: Deploy mdBook sites to GH Pages
on:
push:
branches: ["main"]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
# Allow one concurrent deployment
concurrency:
group: "pages"
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
# TODO: Update the language picker in index.hbs to link new languages.
# TODO: As long as https://github.com/google/mdbook-i18n-helpers/issues/12 is not implemented, yet.
# TODO: Activate when first translation is available
# These are the languages in addition to 'en', which is the main language
# LANGUAGES: xx
jobs:
publish:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Rust cache
uses: ./.github/workflows/setup-rust-cache
- name: Install mdbook
uses: ./.github/workflows/install-mdbook
- name: Build course in English
run: mdbook build -d book
# TODO: Activate when first translation is available
# - name: Build all translations
# run: |
# for po_lang in ${{ env.LANGUAGES }}; do
# echo "::group::Building $po_lang translation"
# MDBOOK_BOOK__LANGUAGE=$po_lang \
# MDBOOK_OUTPUT__HTML__SITE_URL=/patterns/$po_lang/ \
# mdbook build -d book/$po_lang
# echo "::endgroup::"
# done
- name: Setup Pages
id: pages
uses: actions/configure-pages@v3
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: ./book
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2

@ -0,0 +1,11 @@
name: Setup Rust cache
description: Configure the rust-cache workflow.
runs:
using: composite
steps:
- name: Setup Rust cache
uses: Swatinem/rust-cache@v2
with:
prefix-key: v1

@ -7,7 +7,7 @@ idioms and other explanations. It is a compilation of collective (sometimes
implicit) knowledge as well as experiences that have emerged through
collaborative work.
The patterns described here are __not rules__, but should be taken as
The patterns described here are **not rules**, but should be taken as
guidelines for writing idiomatic code in Rust. We are collecting Rust patterns
in this book so people can learn the tradeoffs between Rust idioms and use them
properly in their own code.

@ -4,19 +4,33 @@ authors = ["the rust-unofficial authors"]
description = "A catalogue of Rust design patterns, anti-patterns and idioms"
language = "en"
multilingual = false
src = "."
src = "src"
[build]
create-missing = false
extra-watch-dirs = ["po", "third_party"]
[preprocessor.gettext]
after = ["links"]
[rust]
edition = "2018"
[output.html]
curly-quotes = true
default-theme = "rust"
site-url = "/patterns/"
git-repository-url = "https://github.com/rust-unofficial/patterns"
git-repository-icon = "fa-github"
edit-url-template = "https://github.com/rust-unofficial/patterns/edit/main/{path}"
additional-css = ["./language-picker.css"]
additional-js = ["./third_party/mdbook/book.js"]
[output.html.fold]
enable = true
level = 1
[output.html.playground]
editable = false
# [output.linkcheck] # enable the "mdbook-linkcheck" renderer, disabled due to gh-actions

@ -0,0 +1,11 @@
{
"lineWidth": 80,
"markdown": {},
"includes": [
"**/*.{md}"
],
"excludes": [],
"plugins": [
"https://plugins.dprint.dev/markdown-0.15.2.wasm"
]
}

@ -0,0 +1,8 @@
#language-list {
left: auto;
right: 10px;
}
#language-list a {
color: inherit;
}

@ -1,34 +0,0 @@
# Contain unsafety in small modules
## Description
If you have `unsafe` code, create the smallest possible module that can uphold
the needed invariants to build a minimal safe interface upon the unsafety. Embed
this into a larger module that contains only safe code and presents an ergonomic
interface. Note that the outer module can contain unsafe functions and methods
that call directly into the unsafe code. Users may use this to gain speed benefits.
## Advantages
* This restricts the unsafe code that must be audited
* Writing the outer module is much easier, since you can count on the guarantees
of the inner module
## Disadvantages
* Sometimes, it may be hard to find a suitable interface.
* The abstraction may introduce inefficiencies.
## Examples
* The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe operations
in submodules, presenting a safe interface to users.
* `std`'s `String` class is a wrapper over `Vec<u8>` with the added invariant
that the contents must be valid UTF-8. The operations on `String` ensure this
behavior.
However, users have the option of using an `unsafe` method to create a `String`,
in which case the onus is on them to guarantee the validity of the contents.
## See also
* [Ralf Jung's Blog about invariants in unsafe code](https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html)

File diff suppressed because it is too large Load Diff

@ -4,9 +4,9 @@
- [Translations](./translations.md)
- [Idioms](./idioms/index.md)
- [Use borrowed types for arguments](./idioms/coercion-arguments.md)
- [Concatenating Strings with `format!`](./idioms/concat-format.md)
- [Concatenating Strings with format!](./idioms/concat-format.md)
- [Constructor](./idioms/ctor.md)
- [The `Default` Trait](./idioms/default.md)
- [The Default Trait](./idioms/default.md)
- [Collections Are Smart Pointers](./idioms/deref.md)
- [Finalisation in Destructors](./idioms/dtor-finally.md)
- [`mem::{take(_), replace(_)}`](./idioms/mem-replace.md)
@ -15,7 +15,7 @@
- [Idiomatic Errors](./idioms/ffi/errors.md)
- [Accepting Strings](./idioms/ffi/accepting-strings.md)
- [Passing Strings](./idioms/ffi/passing-strings.md)
- [Iterating over an `Option`](./idioms/option-iter.md)
- [Iterating over an Option](./idioms/option-iter.md)
- [Pass Variables to Closure](./idioms/pass-var-to-closure.md)
- [Privacy For Extensibility](./idioms/priv-extend.md)
- [Easy doc initialization](./idioms/rustdoc-init.md)

@ -47,7 +47,7 @@ explicitly.
The following command line will build with all warnings set to `deny`:
```RUSTFLAGS="-D warnings" cargo build```
`RUSTFLAGS="-D warnings" cargo build`
This can be done by any individual developer (or be set in a CI tool like
Travis, but remember that this may break the build when something changes)
@ -98,7 +98,7 @@ certain that there will be more deprecated APIs in the future.
- [A collection of all clippy lints](https://rust-lang.github.io/rust-clippy/master)
- [deprecate attribute] documentation
- Type `rustc -W help` for a list of lints on your system. Also type
`rustc --help` for a general list of options
`rustc --help` for a general list of options
- [rust-clippy] is a collection of lints for better Rust code
[rust-clippy]: https://github.com/Manishearth/rust-clippy

@ -11,17 +11,17 @@ of Rust's compile time guarantees.
A key part of this idea is the way generic types work. In C++ and Java, for
example, generic types are a meta-programming construct for the compiler.
`vector<int>` and `vector<char>` in C++ are just two different copies of the
same boilerplate code for a `vector` type (known as a `template`) with two
same boilerplate code for a `vector` type (known as a `template`) with two
different types filled in.
In Rust, a generic type parameter creates what is known in functional languages
as a "type class constraint", and each different parameter filled in by an end
user *actually changes the type*. In other words, `Vec<isize>` and `Vec<char>`
*are two different types*, which are recognized as distinct by all parts of the
user _actually changes the type_. In other words, `Vec<isize>` and `Vec<char>`
_are two different types_, which are recognized as distinct by all parts of the
type system.
This is called **monomorphization**, where different types are created from
**polymorphic** code. This special behavior requires `impl` blocks to specify
**polymorphic** code. This special behavior requires `impl` blocks to specify
generic parameters. Different values for the generic type cause different types,
and different types can have different `impl` blocks.
@ -48,11 +48,10 @@ and security controls for the actual files.
The requests from machines in the lab for files contain the same basic
information, no matter what protocol they came from: an authentication method,
and a file name to retrieve. A straightforward implementation would look
and a file name to retrieve. A straightforward implementation would look
something like this:
```rust,ignore
enum AuthInfo {
Nfs(crate::nfs::AuthInfo),
Bootp(crate::bootp::AuthInfo),
@ -65,7 +64,7 @@ struct FileDownloadRequest {
```
This design might work well enough. But now suppose you needed to support
adding metadata that was *protocol specific*. For example, with NFS, you
adding metadata that was _protocol specific_. For example, with NFS, you
wanted to determine what their mount point was in order to enforce additional
security rules.
@ -102,7 +101,7 @@ request types were confused. After all, the entire path of the user's code,
including what functions from the library they use, will know whether a request
is an NFS request or a BOOTP request.
In Rust, this is actually possible! The solution is to *add a generic type* in
In Rust, this is actually possible! The solution is to _add a generic type_ in
order to split the API.
Here is what that looks like:
@ -227,60 +226,53 @@ improve in the future.
## Alternatives
* If a type seems to need a "split API" due to construction or partial
initialization, consider the
[Builder Pattern](../patterns/creational/builder.md) instead.
- If a type seems to need a "split API" due to construction or partial
initialization, consider the
[Builder Pattern](../patterns/creational/builder.md) instead.
* If the API between types does not change -- only the behavior does -- then
the [Strategy Pattern](../patterns/behavioural/strategy.md) is better used
instead.
- If the API between types does not change -- only the behavior does -- then
the [Strategy Pattern](../patterns/behavioural/strategy.md) is better used
instead.
## See also
This pattern is used throughout the standard library:
* `Vec<u8>` can be cast from a String, unlike every other type of `Vec<T>`.[^1]
* They can also be cast into a binary heap, but only if they contain a type
- `Vec<u8>` can be cast from a String, unlike every other type of `Vec<T>`.[^1]
- They can also be cast into a binary heap, but only if they contain a type
that implements the `Ord` trait.[^2]
* The `to_string` method was specialized for `Cow` only of type `str`.[^3]
- The `to_string` method was specialized for `Cow` only of type `str`.[^3]
It is also used by several popular crates to allow API flexibility:
* The `embedded-hal` ecosystem used for embedded devices makes extensive use of
- The `embedded-hal` ecosystem used for embedded devices makes extensive use of
this pattern. For example, it allows statically verifying the configuration of
device registers used to control embedded pins. When a pin is put into a mode,
it returns a `Pin<MODE>` struct, whose generic determines the functions
usable in that mode, which are not on the `Pin` itself. [^4]
* The `hyper` HTTP client library uses this to expose rich APIs for different
- The `hyper` HTTP client library uses this to expose rich APIs for different
pluggable requests. Clients with different connectors have different methods
on them as well as different trait implementations, while a core set of
methods apply to any connector. [^5]
* The "type state" pattern -- where an object gains and loses API based on an
- The "type state" pattern -- where an object gains and loses API based on an
internal state or invariant -- is implemented in Rust using the same basic
concept, and a slightly different technique. [^6]
[^1]: See: [impl From\<CString\> for Vec\<u8\>](
https://doc.rust-lang.org/1.59.0/src/std/ffi/c_str.rs.html#803-811)
[^1]: See: [impl From\<CString\> for Vec\<u8\>](https://doc.rust-lang.org/1.59.0/src/std/ffi/c_str.rs.html#803-811)
[^2]: See: [impl\<T\> From\<Vec\<T, Global\>\> for BinaryHeap\<T\>](
https://doc.rust-lang.org/stable/src/alloc/collections/binary_heap.rs.html#1345-1354)
[^2]: See: [impl\<T\> From\<Vec\<T, Global\>\> for BinaryHeap\<T\>](https://doc.rust-lang.org/stable/src/alloc/collections/binary_heap.rs.html#1345-1354)
[^3]: See: [impl\<'\_\> ToString for Cow\<'\_, str>](
https://doc.rust-lang.org/stable/src/alloc/string.rs.html#2235-2240)
[^3]: See: [impl\<'\_\> ToString for Cow\<'\_, str>](https://doc.rust-lang.org/stable/src/alloc/string.rs.html#2235-2240)
[^4]: Example:
[https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html](
https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html)
[https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html](https://docs.rs/stm32f30x-hal/0.1.0/stm32f30x_hal/gpio/gpioa/struct.PA0.html)
[^5]: See:
[https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html](
https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html)
[https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html](https://docs.rs/hyper/0.14.5/hyper/client/struct.Client.html)
[^6]: See:
[The Case for the Type State Pattern](
https://web.archive.org/web/20210325065112/https://www.novatec-gmbh.de/en/blog/the-case-for-the-typestate-pattern-the-typestate-pattern-itself/)
[The Case for the Type State Pattern](https://web.archive.org/web/20210325065112/https://www.novatec-gmbh.de/en/blog/the-case-for-the-typestate-pattern-the-typestate-pattern-itself/)
and
[Rusty Typestate Series (an extensive thesis)](
https://web.archive.org/web/20210328164854/https://rustype.github.io/notes/notes/rust-typestate-series/rust-typestate-index)
[Rusty Typestate Series (an extensive thesis)](https://web.archive.org/web/20210328164854/https://rustype.github.io/notes/notes/rust-typestate-series/rust-typestate-index)

@ -3,7 +3,7 @@
Rust is an imperative language, but it follows many
[functional programming](https://en.wikipedia.org/wiki/Functional_programming) paradigms.
> In computer science, *functional programming* is a programming paradigm where
> In computer science, _functional programming_ is a programming paradigm where
> programs are constructed by applying and composing functions.
> It is a declarative programming paradigm in which function definitions are
> trees of expressions that each return a value, rather than a sequence of

@ -102,13 +102,12 @@ fn unique_ids_iter<I>(iterator: I) -> HashSet<u64>
```
Lenses, however, allow the code supporting customer ID to be moved from the
*type* to the *accessor function*.
_type_ to the _accessor function_.
Rather than implementing a trait on each type, all matching structures can
simply be accessed the same way.
While the Rust language itself does not support this (type erasure is the
preferred solution to this problem), the [lens-rs
crate](https://github.com/TOETOE55/lens-rs/blob/master/guide.md) allows code
preferred solution to this problem), the [lens-rs crate](https://github.com/TOETOE55/lens-rs/blob/master/guide.md) allows code
that feels like this to be written with macros:
```rust,ignore
@ -161,7 +160,7 @@ the code.
That is what the `optics!` and `view_ref` in the example above simulates.
The functional approach need not be restricted to accessing members.
More powerful lenses can be created which both *set* and *get* data in a
More powerful lenses can be created which both _set_ and _get_ data in a
structure.
But the concept really becomes interesting when used as a building block for
composition.
@ -170,7 +169,7 @@ That is where the concept appears more clearly in Rust.
## Prisms: A Higher-Order form of "Optics"
A simple function such as `unique_ids_lens` above operates on a single lens.
A *prism* is a function that operates on a *family* of lenses.
A _prism_ is a function that operates on a _family_ of lenses.
It is one conceptual level higher, using lenses as a building block, and
continuing the metaphor, is part of a family of "optics".
It is the main one that is useful in understanding Rust APIs, so will be the
@ -179,9 +178,9 @@ focus here.
The same way that traits allow "lens-like" design with static polymorphism and
dynamic dispatch, prism-like designs appear in Rust APIs which split problems
into multiple associated types to be composed.
A good example of this is the traits in the parsing crate *Serde*.
A good example of this is the traits in the parsing crate _Serde_.
Trying to understand the way *Serde* works by only reading the API is a
Trying to understand the way _Serde_ works by only reading the API is a
challenge, especially the first time.
Consider the `Deserializer` trait, implemented by some type in any library
which parses a new format:
@ -230,7 +229,7 @@ pub trait Visitor<'de>: Sized {
}
```
The job of the `Visitor` type is to construct values in the *Serde* data model,
The job of the `Visitor` type is to construct values in the _Serde_ data model,
which are represented by its associated `Value` type.
These values represent parts of the Rust value being deserialized.
@ -243,14 +242,14 @@ it parsed.
The `Value` trait is acting like a lens in functional programming languages.
But unlike the `CustomerId` trait, the return types of `Visitor` methods are
*generic*, and the concrete `Value` type is *determined by the Visitor itself*.
_generic_, and the concrete `Value` type is _determined by the Visitor itself_.
Instead of acting as one lens, it effectively acts as a family of
lenses, one for each concrete type of `Visitor`.
The `Deserializer` API is based on having a generic set of "lenses" work across
a set of other generic types for "observation".
It is a *prism*.
It is a _prism_.
For example, consider the identity record from earlier but simplified:
@ -260,7 +259,7 @@ For example, consider the identity record from earlier but simplified:
}
```
How would the *Serde* library deserialize this JSON into `struct CreditRecord`?
How would the _Serde_ library deserialize this JSON into `struct CreditRecord`?
1. The user would call a library function to deserialize the data. This would
create a `Deserializer` based on the JSON format.
@ -273,7 +272,7 @@ How would the *Serde* library deserialize this JSON into `struct CreditRecord`?
For our very simple structure above, the expected pattern would be:
1. Visit a map (*Serde*'s equvialent to `HashMap` or JSON's dictionary).
1. Visit a map (_Serde_'s equvialent to `HashMap` or JSON's dictionary).
1. Visit a string key called "name".
1. Visit a string value, which will go into the `name` field.
1. Visit a string key called "customer_id".
@ -287,7 +286,7 @@ reflection of each type based on the type itself.
Rust does not support that, so every single type would need to have its own
code written based on its fields and their properties.
*Serde* solves this usability challenge with a derive macro:
_Serde_ solves this usability challenge with a derive macro:
```rust,ignore
use serde::Deserialize;
@ -323,10 +322,9 @@ The `deserialize` code will then create a `Visitor` which will have its calls
If everything goes well, eventually that `Visitor` will construct a value
corresponding to the type being parsed and return it.
For a complete example, see the [*Serde*
documentation](https://serde.rs/deserialize-struct.html).
For a complete example, see the [_Serde_ documentation](https://serde.rs/deserialize-struct.html).
To wrap up, this is the power of *Serde*:
To wrap up, this is the power of _Serde_:
1. The structure being parsed is represented by an `impl` block for `Deserialize`
1. The input data format (e.g. JSON) is represented by a `Deserializer` called
@ -359,8 +357,7 @@ But it may also need procedural macros to create bridges for its generics.
- [luminance](https://github.com/phaazon/luminance-rs) is a crate for drawing
computer graphics that uses lens API design, including proceducal macros to
create full prisms for buffers of different pixel types that remain generic
- [An Article about Lenses in
Scala](https://web.archive.org/web/20221128185849/https://medium.com/zyseme-technology/functional-references-lens-and-other-optics-in-scala-e5f7e2fdafe)
- [An Article about Lenses in Scala](https://web.archive.org/web/20221128185849/https://medium.com/zyseme-technology/functional-references-lens-and-other-optics-in-scala-e5f7e2fdafe)
that is very readable even without Scala expertise.
- [Paper: Profunctor Optics: Modular Data
Accessors](https://web.archive.org/web/20220701102832/https://arxiv.org/ftp/arxiv/papers/1703/1703.10857.pdf)

@ -2,8 +2,8 @@
One of the biggest hurdles to understanding functional programs when coming
from an imperative background is the shift in thinking. Imperative programs
describe __how__ to do something, whereas declarative programs describe
__what__ to do. Let's sum the numbers from 1 to 10 to show this.
describe **how** to do something, whereas declarative programs describe
**what** to do. Let's sum the numbers from 1 to 10 to show this.
## Imperative
@ -22,17 +22,17 @@ Each time through the loop, we add the corresponding value in the range.
Then we print it out.
| `i` | `sum` |
|:---:|:-----:|
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
| 6 | 21 |
| 7 | 28 |
| 8 | 36 |
| 9 | 45 |
| 10 | 55 |
| :-: | :---: |
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
| 6 | 21 |
| 7 | 28 |
| 8 | 36 |
| 9 | 45 |
| 10 | 55 |
This is how most of us start out programming. We learn that a program is a set
of steps.
@ -44,8 +44,8 @@ println!("{}", (1..11).fold(0, |a, b| a + b));
```
Whoa! This is really different! What's going on here?
Remember that with declarative programs we are describing __what__ to do,
rather than __how__ to do it. `fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition)
Remember that with declarative programs we are describing **what** to do,
rather than **how** to do it. `fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition)
functions. The name is a convention from Haskell.
Here, we are composing functions of addition (this closure: `|a, b| a + b`)
@ -56,14 +56,14 @@ result. This process continues until we get to the last element in the range,
`10`.
| `a` | `b` | result |
|:---:|:---:|:------:|
| 0 | 1 | 1 |
| 1 | 2 | 3 |
| 3 | 3 | 6 |
| 6 | 4 | 10 |
| 10 | 5 | 15 |
| 15 | 6 | 21 |
| 21 | 7 | 28 |
| 28 | 8 | 36 |
| 36 | 9 | 45 |
| 45 | 10 | 55 |
| :-: | :-: | :----: |
| 0 | 1 | 1 |
| 1 | 2 | 3 |
| 3 | 3 | 6 |
| 6 | 4 | 10 |
| 10 | 5 | 15 |
| 15 | 6 | 21 |
| 21 | 7 | 28 |
| 28 | 8 | 36 |
| 36 | 9 | 45 |
| 45 | 10 | 55 |

@ -7,8 +7,8 @@ when you are deciding which argument type to use for a function argument.
In this way, the function will accept more input types.
This is not limited to slice-able or fat pointer types.
In fact, you should always prefer using the __borrowed type__ over
__borrowing the owned type__.
In fact, you should always prefer using the **borrowed type** over
**borrowing the owned type**.
Such as `&str` over `&String`, `&[T]` over `&Vec<T>`, or `&T` over `&Box<T>`.
Using borrowed types you can avoid layers of indirection for those instances

@ -3,9 +3,9 @@
## Description
Rust does not have constructors as a language construct. Instead, the
convention is to use an [associated function][] `new` to create an object:
convention is to use an [associated function][associated function] `new` to create an object:
```rust
````rust
/// Time in seconds.
///
/// # Example
@ -30,13 +30,13 @@ impl Second {
self.value
}
}
```
````
## Default Constructors
Rust supports default constructors with the [`Default`][std-default] trait:
```rust
````rust
/// Time in seconds.
///
/// # Example
@ -61,12 +61,12 @@ impl Default for Second {
Self { value: 0 }
}
}
```
````
`Default` can also be derived if all types of all fields implement `Default`,
like they do with `Second`:
```rust
````rust
/// Time in seconds.
///
/// # Example
@ -86,7 +86,7 @@ impl Second {
self.value
}
}
```
````
**Note:** It is common and expected for types to implement both
`Default` and an empty `new` constructor. `new` is the constructor

@ -2,7 +2,7 @@
## Description
Many types in Rust have a [constructor]. However, this is *specific* to the
Many types in Rust have a [constructor]. However, this is _specific_ to the
type; Rust cannot abstract over "everything that has a `new()` method". To
allow this, the [`Default`] trait was conceived, which can be used with
containers and other generic types (e.g. see [`Option::unwrap_or_default()`]).
@ -58,7 +58,7 @@ fn main() {
## See also
- The [constructor] idiom is another way to generate instances that may or may
not be "default"
not be "default"
- The [`Default`] documentation (scroll down for the list of implementors)
- [`Option::unwrap_or_default()`]
- [`derive(new)`]

@ -71,7 +71,7 @@ The example is is written to ensure that:
1. The `unsafe` block is as small as possible.
2. The pointer with an "untracked" lifetime becomes a "tracked" shared
reference
reference
Consider an alternative, where the string is actually copied:
@ -120,15 +120,15 @@ pub mod unsafe_module {
This code in inferior to the original in two respects:
1. There is much more `unsafe` code, and more importantly, more invariants it
must uphold.
must uphold.
2. Due to the extensive arithmetic required, there is a bug in this version
that cases Rust `undefined behaviour`.
that cases Rust `undefined behaviour`.
The bug here is a simple mistake in pointer arithmetic: the string was copied,
all `msg_len` bytes of it. However, the `NUL` terminator at the end was not.
The Vector then had its size *set* to the length of the *zero padded string* --
rather than *resized* to it, which could have added a zero at the end.
The Vector then had its size _set_ to the length of the _zero padded string_ --
rather than _resized_ to it, which could have added a zero at the end.
As a result, the last byte in the Vector is uninitialized memory.
When the `CString` is created at the bottom of the block, its read of the
Vector will cause `undefined behaviour`!

@ -11,7 +11,7 @@ in a usable way:
1. Flat Enums should be converted to integers and returned as codes.
2. Structured Enums should be converted to an integer code with a string error
message for detail.
message for detail.
3. Custom Error Types should become "transparent", with a C representation.
## Code Example

@ -7,7 +7,7 @@ traps for inexperienced users of `unsafe` Rust.
This section contains idioms that may be useful when doing FFI.
1. [Idiomatic Errors](./errors.md) - Error handling with integer codes and
sentinel return values (such as `NULL` pointers)
sentinel return values (such as `NULL` pointers)
2. [Accepting Strings](./accepting-strings.md) with minimal unsafe code

@ -9,7 +9,7 @@ followed:
2. Minimize `unsafe` code during the conversion.
3. If the C code can modify the string data, use `Vec` instead of `CString`.
4. Unless the Foreign Function API requires it, the ownership of the string
should not transfer to the callee.
should not transfer to the callee.
## Motivation
@ -19,7 +19,7 @@ are being sent to a foreign function call from a Rust function.
The best practice is simple: use `CString` in such a way as to minimize
`unsafe` code. However, a secondary caveat is that
*the object must live long enough*, meaning the lifetime should be maximized.
_the object must live long enough_, meaning the lifetime should be maximized.
In addition, the documentation explains that "round-tripping" a `CString` after
modification is UB, so additional work is necessary in that case.

@ -63,17 +63,16 @@ its parts to decide what to do next. In the second phase we may conditionally
change the value (as in the example above).
The borrow checker won't allow us to take out `name` of the enum (because
*something* must be there.) We could of course `.clone()` name and put the clone
into our `MyEnum::B`, but that would be an instance of the [Clone to satisfy
the borrow checker](../anti_patterns/borrow_clone.md) anti-pattern. Anyway, we
_something_ must be there.) We could of course `.clone()` name and put the clone
into our `MyEnum::B`, but that would be an instance of the [Clone to satisfy the borrow checker](../anti_patterns/borrow_clone.md) anti-pattern. Anyway, we
can avoid the extra allocation by changing `e` with only a mutable borrow.
`mem::take` lets us swap out the value, replacing it with it's default value,
and returning the previous value. For `String`, the default value is an empty
`String`, which does not need to allocate. As a result, we get the original
`name` *as an owned value*. We can then wrap this in another enum.
`name` _as an owned value_. We can then wrap this in another enum.
__NOTE:__ `mem::replace` is very similar, but allows us to specify what to
**NOTE:** `mem::replace` is very similar, but allows us to specify what to
replace the value with. An equivalent to our `mem::take` line would be
`mem::replace(name, String::new())`.
@ -92,8 +91,7 @@ borrow checker. The compiler may fail to optimize away the double store,
resulting in reduced performance as opposed to what you'd do in unsafe
languages.
Furthermore, the type you are taking needs to implement the [`Default`
trait](./default.md). However, if the type you're working with doesn't
Furthermore, the type you are taking needs to implement the [`Default` trait](./default.md). However, if the type you're working with doesn't
implement this, you can instead use `mem::replace`.
## Discussion

@ -68,26 +68,25 @@ let readable: Box<dyn io::Read> = if arg == "-" {
## Discussion
Rust newcomers will usually learn that Rust requires all variables to be
initialized *before use*, so it's easy to overlook the fact that *unused*
initialized _before use_, so it's easy to overlook the fact that _unused_
variables may well be uninitialized. Rust works quite hard to ensure that this
works out fine and only the initialized values are dropped at the end of their
scope.
The example meets all the constraints Rust places on us:
* All variables are initialized before using (in this case borrowing) them
* Each variable only holds values of a single type. In our example, `stdin` is
of type `Stdin`, `file` is of type `File` and `readable` is of type `&mut dyn
Read`
* Each borrowed value outlives all the references borrowed from it
- All variables are initialized before using (in this case borrowing) them
- Each variable only holds values of a single type. In our example, `stdin` is
of type `Stdin`, `file` is of type `File` and `readable` is of type `&mut dyn Read`
- Each borrowed value outlives all the references borrowed from it
## See also
* [Finalisation in destructors](dtor-finally.md) and
[RAII guards](../patterns/behavioural/RAII.md) can benefit from tight control over
lifetimes.
* For conditionally filled `Option<&T>`s of (mutable) references, one can
initialize an `Option<T>` directly and use its [`.as_ref()`] method to get an
optional reference.
- [Finalisation in destructors](dtor-finally.md) and
[RAII guards](../patterns/behavioural/RAII.md) can benefit from tight control over
lifetimes.
- For conditionally filled `Option<&T>`s of (mutable) references, one can
initialize an `Option<T>` directly and use its [`.as_ref()`] method to get an
optional reference.
[`.as_ref()`]: https://doc.rust-lang.org/std/option/enum.Option.html#method.as_ref

@ -45,15 +45,15 @@ and in most cases you should prefer the latter.
## See also
* [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is an
iterator which yields exactly one element. It's a more readable alternative to
`Some(foo).into_iter()`.
- [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is an
iterator which yields exactly one element. It's a more readable alternative to
`Some(foo).into_iter()`.
* [`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map)
- [`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map)
is a version of [`Iterator::map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.map),
specialized to mapping functions which return `Option`.
* The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions
- The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions
for converting an `Option` to a zero- or one-element slice.
* [Documentation for `Option<T>`](https://doc.rust-lang.org/std/option/enum.Option.html)
- [Documentation for `Option<T>`](https://doc.rust-lang.org/std/option/enum.Option.html)

@ -13,7 +13,7 @@ methods. Each of these methods should have examples.
For example:
```rust,ignore
````rust,ignore
struct Connection {
name: String,
stream: TcpStream,
@ -40,7 +40,7 @@ impl Connection {
// ...
}
}
```
````
## Example
@ -48,7 +48,7 @@ Instead of typing all of this boilerplate to create a `Connection` and
`Request`, it is easier to just create a wrapping helper function which takes
them as arguments:
```rust,ignore
````rust,ignore
struct Connection {
name: String,
stream: TcpStream,
@ -68,7 +68,7 @@ impl Connection {
// ...
}
}
```
````
**Note** in the above example the line `assert!(response.is_ok());` will not
actually run while testing because it is inside a function which is never

@ -39,10 +39,9 @@ exp -> term
term -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
```
__NOTE:__ This grammar should be further transformed depending on what we are going
**NOTE:** This grammar should be further transformed depending on what we are going
to do with it. For example, we might need to remove left recursion. For more
details please see [Compilers: Principles,Techniques, and Tools
](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools)
details please see [Compilers: Principles,Techniques, and Tools](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools)
(aka Dragon Book).
## Solution

@ -7,8 +7,8 @@ not be enough?
For example, if we want to create a custom `Display` implementation for `String`
due to security considerations (e.g. passwords).
For such cases we could use the `Newtype` pattern to provide __type safety__
and __encapsulation__.
For such cases we could use the `Newtype` pattern to provide **type safety**
and **encapsulation**.
## Description
@ -77,7 +77,7 @@ field is private, which it is by default).
## Disadvantages
The downside of newtypes (especially compared with type aliases), is that there
is no special language support. This means there can be *a lot* of boilerplate.
is no special language support. This means there can be _a lot_ of boilerplate.
You need a 'pass through' method for every method you want to expose on the
wrapped type, and an impl for every trait you want to also be implemented for
the wrapper type.

@ -155,7 +155,6 @@ fn main() {
assert_eq!(0, Adder::add(0, 0, bool_adder));
assert_eq!(5, Adder::add(1, 3, custom_adder));
}
```
In fact, Rust already uses this idea for `Options`'s `map` method:

@ -5,12 +5,12 @@
When designing APIs in Rust which are exposed to other languages, there are some
important design principles which are contrary to normal Rust API design:
1. All Encapsulated types should be *owned* by Rust, *managed* by the user,
and *opaque*.
2. All Transactional data types should be *owned* by the user, and *transparent*.
1. All Encapsulated types should be _owned_ by Rust, _managed_ by the user,
and _opaque_.
2. All Transactional data types should be _owned_ by the user, and _transparent_.
3. All library behavior should be functions acting upon Encapsulated types.
4. All library behavior should be encapsulated into types not based on structure,
but *provenance/lifetime*.
but _provenance/lifetime_.
## Motivation
@ -24,7 +24,7 @@ design in Rust as little as possible. There are three goals with any foreign API
1. Make it easy to use in the target language.
2. Avoid the API dictating internal unsafety on the Rust side as much as possible.
3. Keep the potential for memory unsafety and Rust `undefined behaviour` as small
as possible.
as possible.
Rust code must trust the memory safety of the foreign language beyond a certain
point. However, every bit of `unsafe` code on the Rust side is an opportunity for
@ -69,7 +69,7 @@ library's behavior.
It is completely opaque to the user, who cannot create a `DBM` themselves since
they don't know its size or layout. Instead, they must call `dbm_open`, and that
only gives them *a pointer to one*.
only gives them _a pointer to one_.
This means all `DBM`s are "owned" by the library in a Rust sense.
The internal state of unknown size is kept in memory controlled by the library,
@ -90,8 +90,8 @@ The user likely has some type they are using, which has a known size.
But the library does not care, and by the rules of C casting, any type behind a
pointer can be cast to `void`.
As noted earlier, this type is *transparent* to the user. But also, this type is
*owned* by the user.
As noted earlier, this type is _transparent_ to the user. But also, this type is
_owned_ by the user.
This has subtle ramifications, due to that pointer inside it.
The question is, who owns the memory that pointer points to?
@ -99,14 +99,14 @@ The answer for best memory safety is, "the user".
But in cases such as retrieving a value, the user does not know how to allocate
it correctly (since they don't know how long the value is). In this case, the library
code is expected to use the heap that the user has access to -- such as the C library
`malloc` and `free` -- and then *transfer ownership* in the Rust sense.
`malloc` and `free` -- and then _transfer ownership_ in the Rust sense.
This may all seem speculative, but this is what a pointer means in C.
It means the same thing as Rust: "user defined lifetime."
The user of the library needs to read the documentation in order to use it correctly.
That said, there are some decisions that have fewer or greater consequences if users
do it wrong. Minimizing those are what this best practice is about, and the key
is to *transfer ownership of everything that is transparent*.
is to _transfer ownership of everything that is transparent_.
## Advantages
@ -114,10 +114,10 @@ This minimizes the number of memory safety guarantees the user must uphold to a
relatively small number:
1. Do not call any function with a pointer not returned by `dbm_open` (invalid
access or corruption).
access or corruption).
2. Do not call any function on a pointer after close (use after free).
3. The `dptr` on any `datum` must be `NULL`, or point to a valid slice of memory
at the advertised length.
at the advertised length.
In addition, it avoids a lot of pointer provenance issues.
To understand why, let us consider an alternative in some depth: key iteration.
@ -205,7 +205,7 @@ end-of-iteration marker:
1. The loop condition sets `l` to zero, and enters the loop because `0 >= 0`.
2. The length is incremented, in this case by zero.
3. The if statement is true, so the database is closed. There should be a break
statement here.
statement here.
4. The loop condition executes again, causing a `next` call on the closed object.
The worst part about this bug?
@ -221,7 +221,7 @@ and gave up control of their lifetimes. The C code simply must "play nice".
The programmer must read and understand the API documentation.
While some consider that par for the course in C, a good API design can mitigate
this risk. The POSIX API for `DBM` did this by *consolidating the ownership* of
this risk. The POSIX API for `DBM` did this by _consolidating the ownership_ of
the iterator with its parent:
```C

@ -7,7 +7,7 @@ for inexperienced users of unsafe Rust.
This section contains design patterns that may be useful when doing FFI.
1. [Object-Based API](./export.md) design that has good memory safety characteristics,
and a clean boundary of what is safe and what is unsafe
and a clean boundary of what is safe and what is unsafe
2. [Type Consolidation into Wrappers](./wrappers.md) - group multiple Rust types
together into an opaque "object"
together into an opaque "object"

@ -31,7 +31,7 @@ That API looks like this:
2. Each call to `next_key` will advance the iterator.
3. Calls to `next_key` if the iterator is at the end will do nothing.
4. As noted above, the iterator is "wrapped into" the collection (unlike the native
Rust API).
Rust API).
If the iterator implements `nth()` efficiently, then it is possible to make it
ephemeral to each function call:
@ -82,9 +82,9 @@ To wrap any type of iterator into the API correctly, the wrapper would need to
do what a C version of the code would do: erase the lifetime of the iterator,
and manage it manually.
Suffice it to say, this is *incredibly* difficult.
Suffice it to say, this is _incredibly_ difficult.
Here is an illustration of just *one* pitfall.
Here is an illustration of just _one_ pitfall.
A first version of `MySetWrapper` would look like this:
@ -98,8 +98,8 @@ struct MySetWrapper {
```
With `transmute` being used to extend a lifetime, and a pointer to hide it,
it's ugly already. But it gets even worse: *any other operation can cause
Rust `undefined behaviour`*.
it's ugly already. But it gets even worse: _any other operation can cause
Rust `undefined behaviour`_.
Consider that the `MySet` in the wrapper could be manipulated by other
functions during iteration, such as storing a new value to the key it was
@ -136,7 +136,7 @@ pub mod unsafe_module {
If the iterator exists when this function is called, we have violated one of Rust's
aliasing rules. According to Rust, the mutable reference in this block must have
*exclusive* access to the object. If the iterator simply exists, it's not exclusive,
_exclusive_ access to the object. If the iterator simply exists, it's not exclusive,
so we have `undefined behaviour`! [^1]
To avoid this, we must have a way of ensuring that mutable reference really is exclusive.
@ -156,7 +156,7 @@ optimizations that C code cannot attain. Being denied access to certain shortcut
is the price Rust programmers need to pay.
[^1]: For the C programmers out there scratching their heads, the iterator need
not be read *during* this code cause the UB. The exclusivity rule also enables
compiler optimizations which may cause inconsistent observations by the iterator's
shared reference (e.g. stack spills or reordering instructions for efficiency).
These observations may happen *any time after* the mutable reference is created.
not be read _during_ this code cause the UB. The exclusivity rule also enables
compiler optimizations which may cause inconsistent observations by the iterator's
shared reference (e.g. stack spills or reordering instructions for efficiency).
These observations may happen _any time after_ the mutable reference is created.

@ -11,24 +11,24 @@ We should take advantage of this tooling, and use smaller, more fine-grained dep
## Advantages
* Small crates are easier to understand, and encourage more modular code.
* Crates allow for re-using code between projects.
- Small crates are easier to understand, and encourage more modular code.
- Crates allow for re-using code between projects.
For example, the `url` crate was developed as part of the Servo browser engine,
but has since found wide use outside the project.
* Since the compilation unit
- Since the compilation unit
of Rust is the crate, splitting a project into multiple crates can allow more of
the code to be built in parallel.
## Disadvantages
* This can lead to "dependency hell", when a project depends on multiple conflicting
- This can lead to "dependency hell", when a project depends on multiple conflicting
versions of a crate at the same time. For example, the `url` crate has both versions
1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url` from `url:0.5` are
different types, an HTTP client that uses `url:0.5` would not accept `Url` values
from a web scraper that uses `url:1.0`.
* Packages on crates.io are not curated. A crate may be poorly written, have
- Packages on crates.io are not curated. A crate may be poorly written, have
unhelpful documentation, or be outright malicious.
* Two small crates may be less optimized than one large one, since the compiler
- Two small crates may be less optimized than one large one, since the compiler
does not perform link-time optimization (LTO) by default.
## Examples
@ -44,4 +44,4 @@ query the number of CPUs on a machine.
## See also
* [crates.io: The Rust community crate host](https://crates.io/)
- [crates.io: The Rust community crate host](https://crates.io/)

@ -0,0 +1,34 @@
# Contain unsafety in small modules
## Description
If you have `unsafe` code, create the smallest possible module that can uphold
the needed invariants to build a minimal safe interface upon the unsafety. Embed
this into a larger module that contains only safe code and presents an ergonomic
interface. Note that the outer module can contain unsafe functions and methods
that call directly into the unsafe code. Users may use this to gain speed benefits.
## Advantages
- This restricts the unsafe code that must be audited
- Writing the outer module is much easier, since you can count on the guarantees
of the inner module
## Disadvantages
- Sometimes, it may be hard to find a suitable interface.
- The abstraction may introduce inefficiencies.
## Examples
- The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe operations
in submodules, presenting a safe interface to users.
- `std`'s `String` class is a wrapper over `Vec<u8>` with the added invariant
that the contents must be valid UTF-8. The operations on `String` ensure this
behavior.
However, users have the option of using an `unsafe` method to create a `String`,
in which case the onus is on them to guarantee the validity of the contents.
## See also
- [Ralf Jung's Blog about invariants in unsafe code](https://www.ralfj.de/blog/2018/08/22/two-kinds-of-invariants.html)

@ -0,0 +1,11 @@
# Translations
We are utilizing [mdbook-i18n-helper](https://github.com/google/mdbook-i18n-helpers).
Please read up on how to _add_ and _update_ translations in [their repository](https://github.com/google/mdbook-i18n-helpers#creating-and-updating-translations)
## External translations
- [简体中文](https://fomalhauthmj.github.io/patterns/)
If you want to add a translation, please open an issue in the
[main repository](https://github.com/rust-unofficial/patterns).

@ -0,0 +1 @@
../third_party/mdbook/book.js

@ -0,0 +1,365 @@
<!DOCTYPE HTML>
<html lang="{{ language }}" class="sidebar-visible no-js {{ default_theme }}">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>{{ title }}</title>
{{#if is_print }}
<meta name="robots" content="noindex" />
{{/if}}
{{#if base_url}}
<base href="{{ base_url }}">
{{/if}}
<!-- Custom HTML head -->
{{> head}}
<meta name="description" content="{{ description }}">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
{{#if favicon_svg}}
<link rel="icon" href="{{ path_to_root }}favicon.svg">
{{/if}}
{{#if favicon_png}}
<link rel="shortcut icon" href="{{ path_to_root }}favicon.png">
{{/if}}
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
{{#if print_enable}}
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
{{/if}}
<!-- Fonts -->
<link rel="stylesheet" href="{{ path_to_root }}FontAwesome/css/font-awesome.css">
{{#if copy_fonts}}
<link rel="stylesheet" href="{{ path_to_root }}fonts/fonts.css">
{{/if}}
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="{{ path_to_root }}highlight.css">
<link rel="stylesheet" href="{{ path_to_root }}tomorrow-night.css">
<link rel="stylesheet" href="{{ path_to_root }}ayu-highlight.css">
<!-- Custom theme stylesheets -->
{{#each additional_css}}
<link rel="stylesheet" href="{{ ../path_to_root }}{{ this }}">
{{/each}}
{{#if mathjax_support}}
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
{{/if}}
</head>
<body>
<!-- Provide site root to javascript -->
<script>
var path_to_root = "{{ path_to_root }}";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "{{ preferred_dark_theme }}" : "{{ default_theme }}";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('{{ default_theme }}')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
{{#toc}}{{/toc}}
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
{{> header}}
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
{{#if search_enabled}}
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
{{/if}}
</div>
<h1 class="menu-title">{{ book_title }}</h1>
<div class="right-buttons">
<button id="language-toggle" class="icon-button" type="button"
title="Change language" aria-label="Change language"
aria-haspopup="true" aria-expanded="false"
aria-controls="language-list">
<i class="fa fa-globe"></i>
</button>
<ul id="language-list" class="theme-popup" aria-label="Languages" role="menu">
<li role="none"><button role="menuitem" class="theme">
<a id="en">English</a>
</button></li>
{{!-- <li role="none"><button role="menuitem" class="theme">
<a id="pt-BR">Brazilian Portuguese</a>
</button></li>
<li role="none"><button role="menuitem" class="theme">
<a id="ko">한국어 (Korean)</a>
</button></li> --}}
</ul>
<script>
let langToggle = document.getElementById("language-toggle");
let langList = document.getElementById("language-list");
langToggle.addEventListener("click", (event) => {
langList.style.display = langList.style.display == "block" ? "none" : "block";
});
let selectedLang = document.getElementById("{{ language }}");
selectedLang.parentNode.classList.add("theme-selected");
// The path to the root, taking the current
// language into account.
{{#if (eq language "en")}}
let full_path_to_root = "{{ path_to_root }}";
{{else}}
let full_path_to_root = "{{ path_to_root }}../";
{{/if}}
// The page path (mdbook only gives us
// access to the path to the Markdown file).
let path = "{{ path }}".replace(/\.md$/, ".html");
for (let lang of langList.querySelectorAll("a")) {
if (lang.id == "en") {
lang.href = `${full_path_to_root}${path}`;
} else {
lang.href = `${full_path_to_root}${lang.id}/${path}`;
}
}
</script>
{{#if print_enable}}
<a href="{{ path_to_root }}print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
{{/if}}
{{#if git_repository_url}}
<a href="{{git_repository_url}}" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa {{git_repository_icon}}"></i>
</a>
{{/if}}
{{#if git_repository_edit_url}}
{{#if (eq language "en")}}
<a href="{{git_repository_edit_url}}" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
{{else}}
<a href="https://github.com/rust-unofficial/patterns/edit/main/po/{{language}}.po"
title="Suggest an edit to the translation" aria-label="Suggest an edit to the translation">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
{{/if}}
{{/if}}
</div>
</div>
{{#if search_enabled}}
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
{{/if}}
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
{{{ content }}}
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
{{#previous}}
<a rel="prev" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
{{#next}}
<a rel="next" href="{{ path_to_root }}{{link}}" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
{{/next}}
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
{{#previous}}
<a rel="prev" href="{{ path_to_root }}{{link}}" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
{{/previous}}
{{#next}}
<a rel="next" href="{{ path_to_root }}{{link}}" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
{{/next}}
</nav>
</div>
{{#if live_reload_endpoint}}
<!-- Livereload script (if served using the cli tool) -->
<script>
const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsAddress = wsProtocol + "//" + location.host + "/" + "{{{live_reload_endpoint}}}";
const socket = new WebSocket(wsAddress);
socket.onmessage = function (event) {
if (event.data === "reload") {
socket.close();
location.reload();
}
};
window.onbeforeunload = function() {
socket.close();
}
</script>
{{/if}}
{{#if google_analytics}}
<!-- Google Analytics Tag -->
<script>
var localAddrs = ["localhost", "127.0.0.1", ""];
// make sure we don't activate google analytics if the developer is
// inspecting the book locally...
if (localAddrs.indexOf(document.location.hostname) === -1) {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', '{{google_analytics}}', 'auto');
ga('send', 'pageview');
}
</script>
{{/if}}
{{#if playground_line_numbers}}
<script>
window.playground_line_numbers = true;
</script>
{{/if}}
{{#if playground_copyable}}
<script>
window.playground_copyable = true;
</script>
{{/if}}
{{#if playground_js}}
<script src="{{ path_to_root }}ace.js"></script>
<script src="{{ path_to_root }}editor.js"></script>
<script src="{{ path_to_root }}mode-rust.js"></script>
<script src="{{ path_to_root }}theme-dawn.js"></script>
<script src="{{ path_to_root }}theme-tomorrow_night.js"></script>
{{/if}}
{{#if search_js}}
<script src="{{ path_to_root }}elasticlunr.min.js"></script>
<script src="{{ path_to_root }}mark.min.js"></script>
<script src="{{ path_to_root }}searcher.js"></script>
{{/if}}
<script src="{{ path_to_root }}clipboard.min.js"></script>
<script src="{{ path_to_root }}highlight.js"></script>
<script src="{{ path_to_root }}book.js"></script>
<!-- Custom JS scripts -->
{{#each additional_js}}
<script src="{{ ../path_to_root }}{{this}}"></script>
{{/each}}
{{#if is_print}}
{{#if mathjax_support}}
<script>
window.addEventListener('load', function() {
MathJax.Hub.Register.StartupHook('End', function() {
window.setTimeout(window.print, 100);
});
});
</script>
{{else}}
<script>
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
{{/if}}
{{/if}}
</body>
</html>

@ -0,0 +1,362 @@
Mozilla Public License, version 2.0
1. Definitions
1.1. "Contributor"
means each individual or legal entity that creates, contributes to the
creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used by a
Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached the
notice in Exhibit A, the Executable Form of such Source Code Form, and
Modifications of such Source Code Form, in each case including portions
thereof.
1.5. "Incompatible With Secondary Licenses"
means
a. that the initial Contributor has attached the notice described in
Exhibit B to the Covered Software; or
b. that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the terms of
a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in a
separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible, whether
at the time of the initial grant or subsequently, any and all of the
rights conveyed by this License.
1.10. "Modifications"
means any of the following:
a. any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered Software; or
b. any new file in Source Code Form that contains any Covered Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the License,
by the making, using, selling, offering for sale, having made, import,
or transfer of either its Contributions or its Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU Lesser
General Public License, Version 2.1, the GNU Affero General Public
License, Version 3.0, or any later versions of those licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that controls, is
controlled by, or is under common control with You. For purposes of this
definition, "control" means (a) the power, direct or indirect, to cause
the direction or management of such entity, whether by contract or
otherwise, or (b) ownership of more than fifty percent (50%) of the
outstanding shares or beneficial ownership of such entity.
2. License Grants and Conditions
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
a. under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
b. under Patent Claims of such Contributor to make, use, sell, offer for
sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
a. for any code that a Contributor has removed from Covered Software; or
b. for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
c. under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights to
grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
Section 2.1.
3. Responsibilities
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
a. such Covered Software must also be made available in Source Code Form,
as described in Section 3.1, and You must inform recipients of the
Executable Form how they can obtain a copy of such Source Code Form by
reasonable means in a timely manner, at a charge no more than the cost
of distribution to the recipient; and
b. You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter the
recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty, or
limitations of liability) contained within the Source Code Form of the
Covered Software, except that You may alter any license notices to the
extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
If it is impossible for You to comply with any of the terms of this License
with respect to some or all of the Covered Software due to statute,
judicial order, or regulation then You must: (a) comply with the terms of
this License to the maximum extent possible; and (b) describe the
limitations and the code they affect. Such description must be placed in a
text file included with all distributions of the Covered Software under
this License. Except to the extent prohibited by statute or regulation,
such description must be sufficiently detailed for a recipient of ordinary
skill to be able to understand it.
5. Termination
5.1. The rights granted under this License will terminate automatically if You
fail to comply with any of its terms. However, if You become compliant,
then the rights granted under this License from a particular Contributor
are reinstated (a) provisionally, unless and until such Contributor
explicitly and finally terminates Your grants, and (b) on an ongoing
basis, if such Contributor fails to notify You of the non-compliance by
some reasonable means prior to 60 days after You have come back into
compliance. Moreover, Your grants from a particular Contributor are
reinstated on an ongoing basis if such Contributor notifies You of the
non-compliance by some reasonable means, this is the first time You have
received notice of non-compliance with this License from such
Contributor, and You become compliant prior to 30 days after Your receipt
of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
license agreements (excluding distributors and resellers) which have been
validly granted by You or Your distributors under this License prior to
termination shall survive termination.
6. Disclaimer of Warranty
Covered Software is provided under this License on an "as is" basis,
without warranty of any kind, either expressed, implied, or statutory,
including, without limitation, warranties that the Covered Software is free
of defects, merchantable, fit for a particular purpose or non-infringing.
The entire risk as to the quality and performance of the Covered Software
is with You. Should any Covered Software prove defective in any respect,
You (not any Contributor) assume the cost of any necessary servicing,
repair, or correction. This disclaimer of warranty constitutes an essential
part of this License. No use of any Covered Software is authorized under
this License except under this disclaimer.
7. Limitation of Liability
Under no circumstances and under no legal theory, whether tort (including
negligence), contract, or otherwise, shall any Contributor, or anyone who
distributes Covered Software as permitted above, be liable to You for any
direct, indirect, special, incidental, or consequential damages of any
character including, without limitation, damages for lost profits, loss of
goodwill, work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses, even if such party shall have been
informed of the possibility of such damages. This limitation of liability
shall not apply to liability for death or personal injury resulting from
such party's negligence to the extent applicable law prohibits such
limitation. Some jurisdictions do not allow the exclusion or limitation of
incidental or consequential damages, so this exclusion and limitation may
not apply to You.
8. Litigation
Any litigation relating to this License may be brought only in the courts
of a jurisdiction where the defendant maintains its principal place of
business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions. Nothing
in this Section shall prevent a party's ability to bring cross-claims or
counter-claims.
9. Miscellaneous
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides that
the language of a contract shall be construed against the drafter shall not
be used to construe this License against a Contributor.
10. Versions of the License
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses If You choose to distribute Source Code Form that is
Incompatible With Secondary Licenses under the terms of this version of
the License, the notice described in Exhibit B of this License must be
attached.
Exhibit A - Source Code Form License Notice
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular file,
then You may include the notice in a location (such as a LICENSE file in a
relevant directory) where a recipient would be likely to look for such a
notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
This Source Code Form is "Incompatible
With Secondary Licenses", as defined by
the Mozilla Public License, v. 2.0.

@ -0,0 +1,8 @@
# mdBook
This directory contains files copied from mdBook. Please see
<https://github.com/rust-lang/mdBook/> for the full project.
## License
mdBook is licensed under the Mozilla Public License v2.0 ([LICENSE](LICENSE)).

@ -0,0 +1,715 @@
"use strict";
// Fix back button cache problem
window.onunload = function () { };
function isPlaygroundModified(playground) {
let code_block = playground.querySelector("code");
if (window.ace && code_block.classList.contains("editable")) {
let editor = window.ace.edit(code_block);
return editor.getValue() != editor.originalCode;
} else {
return false;
}
}
// Global variable, shared between modules
function playground_text(playground, hidden = true) {
let code_block = playground.querySelector("code");
if (window.ace && code_block.classList.contains("editable")) {
let editor = window.ace.edit(code_block);
return editor.getValue();
} else if (hidden) {
return code_block.textContent;
} else {
return code_block.innerText;
}
}
(function codeSnippets() {
function fetch_with_timeout(url, options, timeout = 15000) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), timeout))
]);
}
var playgrounds = Array.from(document.querySelectorAll(".playground"));
if (playgrounds.length > 0) {
fetch_with_timeout("https://play.rust-lang.org/meta/crates", {
headers: {
'Content-Type': "application/json",
},
method: 'POST',
mode: 'cors',
})
.then(response => response.json())
.then(response => {
// get list of crates available in the rust playground
let playground_crates = response.crates.map(item => item["id"]);
playgrounds.forEach(block => handle_crate_list_update(block, playground_crates));
});
}
function handle_crate_list_update(playground_block, playground_crates) {
// update the play buttons after receiving the response
update_play_button(playground_block, playground_crates);
// and install on change listener to dynamically update ACE editors
if (window.ace) {
let code_block = playground_block.querySelector("code");
if (code_block.classList.contains("editable")) {
let editor = window.ace.edit(code_block);
editor.addEventListener("change", function (e) {
update_play_button(playground_block, playground_crates);
});
// add Ctrl-Enter command to execute rust code
editor.commands.addCommand({
name: "run",
bindKey: {
win: "Ctrl-Enter",
mac: "Ctrl-Enter"
},
exec: _editor => run_rust_code(playground_block)
});
}
}
}
// updates the visibility of play button based on `no_run` class and
// used crates vs ones available on http://play.rust-lang.org
function update_play_button(pre_block, playground_crates) {
var play_button = pre_block.querySelector(".play-button");
// skip if code is `no_run`
if (pre_block.querySelector('code').classList.contains("no_run")) {
play_button.classList.add("hidden");
return;
}
// get list of `extern crate`'s from snippet
var txt = playground_text(pre_block);
var re = /extern\s+crate\s+([a-zA-Z_0-9]+)\s*;/g;
var snippet_crates = [];
var item;
while (item = re.exec(txt)) {
snippet_crates.push(item[1]);
}
// check if all used crates are available on play.rust-lang.org
var all_available = snippet_crates.every(function (elem) {
return playground_crates.indexOf(elem) > -1;
});
if (all_available) {
play_button.classList.remove("hidden");
} else {
play_button.classList.add("hidden");
}
}
function run_rust_code(code_block) {
var result_block = code_block.querySelector(".result");
if (!result_block) {
result_block = document.createElement('code');
result_block.className = 'result hljs language-bash';
code_block.append(result_block);
}
let text = playground_text(code_block);
let classes = code_block.querySelector('code').classList;
let edition = "2015";
if(classes.contains("edition2018")) {
edition = "2018";
} else if(classes.contains("edition2021")) {
edition = "2021";
}
var params = {
version: "stable",
optimize: "0",
code: text,
edition: edition
};
if (text.indexOf("#![feature") !== -1) {
params.version = "nightly";
}
result_block.innerText = "Running...";
const playgroundModified = isPlaygroundModified(code_block);
const startTime = window.performance.now();
fetch_with_timeout("https://play.rust-lang.org/evaluate.json", {
headers: {
'Content-Type': "application/json",
},
method: 'POST',
mode: 'cors',
body: JSON.stringify(params)
})
.then(response => response.json())
.then(response => {
const endTime = window.performance.now();
gtag("event", "playground", {
"modified": playgroundModified,
"error": (response.error == null) ? null : 'compilation_error',
"latency": (endTime - startTime) / 1000,
});
if (response.result.trim() === '') {
result_block.innerText = "No output";
result_block.classList.add("result-no-output");
} else {
result_block.innerText = response.result;
result_block.classList.remove("result-no-output");
}
})
.catch(error => {
const endTime = window.performance.now();
gtag("event", "playground", {
"modified": playgroundModified,
"error": error.message,
"latency": (endTime - startTime) / 1000,
});
result_block.innerText = "Playground Communication: " + error.message
});
}
// Syntax highlighting Configuration
hljs.configure({
tabReplace: ' ', // 4 spaces
languages: [], // Languages used for auto-detection
});
let code_nodes = Array
.from(document.querySelectorAll('code'))
// Don't highlight `inline code` blocks in headers.
.filter(function (node) {return !node.parentElement.classList.contains("header"); });
if (window.ace) {
// language-rust class needs to be removed for editable
// blocks or highlightjs will capture events
code_nodes
.filter(function (node) {return node.classList.contains("editable"); })
.forEach(function (block) { block.classList.remove('language-rust'); });
code_nodes
.filter(function (node) {return !node.classList.contains("editable"); })
.forEach(function (block) { hljs.highlightBlock(block); });
} else {
code_nodes.forEach(function (block) { hljs.highlightBlock(block); });
}
// Adding the hljs class gives code blocks the color css
// even if highlighting doesn't apply
code_nodes.forEach(function (block) { block.classList.add('hljs'); });
Array.from(document.querySelectorAll("code.language-rust")).forEach(function (block) {
var lines = Array.from(block.querySelectorAll('.boring'));
// If no lines were hidden, return
if (!lines.length) { return; }
block.classList.add("hide-boring");
var buttons = document.createElement('div');
buttons.className = 'buttons';
buttons.innerHTML = "<button class=\"fa fa-eye\" title=\"Show hidden lines\" aria-label=\"Show hidden lines\"></button>";
// add expand button
var pre_block = block.parentNode;
pre_block.insertBefore(buttons, pre_block.firstChild);
pre_block.querySelector('.buttons').addEventListener('click', function (e) {
if (e.target.classList.contains('fa-eye')) {
e.target.classList.remove('fa-eye');
e.target.classList.add('fa-eye-slash');
e.target.title = 'Hide lines';
e.target.setAttribute('aria-label', e.target.title);
block.classList.remove('hide-boring');
} else if (e.target.classList.contains('fa-eye-slash')) {
e.target.classList.remove('fa-eye-slash');
e.target.classList.add('fa-eye');
e.target.title = 'Show hidden lines';
e.target.setAttribute('aria-label', e.target.title);
block.classList.add('hide-boring');
}
});
});
if (window.playground_copyable) {
Array.from(document.querySelectorAll('pre code')).forEach(function (block) {
var pre_block = block.parentNode;
if (!pre_block.classList.contains('playground')) {
var buttons = pre_block.querySelector(".buttons");
if (!buttons) {
buttons = document.createElement('div');
buttons.className = 'buttons';
pre_block.insertBefore(buttons, pre_block.firstChild);
}
var clipButton = document.createElement('button');
clipButton.className = 'fa fa-copy clip-button';
clipButton.title = 'Copy to clipboard';
clipButton.setAttribute('aria-label', clipButton.title);
clipButton.innerHTML = '<i class=\"tooltiptext\"></i>';
buttons.insertBefore(clipButton, buttons.firstChild);
}
});
}
// Process playground code blocks
Array.from(document.querySelectorAll(".playground")).forEach(function (pre_block) {
// Add play button
var buttons = pre_block.querySelector(".buttons");
if (!buttons) {
buttons = document.createElement('div');
buttons.className = 'buttons';
pre_block.insertBefore(buttons, pre_block.firstChild);
}
var runCodeButton = document.createElement('button');
runCodeButton.className = 'fa fa-play play-button';
runCodeButton.hidden = true;
runCodeButton.title = 'Run this code';
runCodeButton.setAttribute('aria-label', runCodeButton.title);
buttons.insertBefore(runCodeButton, buttons.firstChild);
runCodeButton.addEventListener('click', function (e) {
run_rust_code(pre_block);
});
if (window.playground_copyable) {
var copyCodeClipboardButton = document.createElement('button');
copyCodeClipboardButton.className = 'fa fa-copy clip-button';
copyCodeClipboardButton.innerHTML = '<i class="tooltiptext"></i>';
copyCodeClipboardButton.title = 'Copy to clipboard';
copyCodeClipboardButton.setAttribute('aria-label', copyCodeClipboardButton.title);
buttons.insertBefore(copyCodeClipboardButton, buttons.firstChild);
}
let code_block = pre_block.querySelector("code");
if (window.ace && code_block.classList.contains("editable")) {
var undoChangesButton = document.createElement('button');
undoChangesButton.className = 'fa fa-history reset-button';
undoChangesButton.title = 'Undo changes';
undoChangesButton.setAttribute('aria-label', undoChangesButton.title);
buttons.insertBefore(undoChangesButton, buttons.firstChild);
undoChangesButton.addEventListener('click', function () {
let editor = window.ace.edit(code_block);
editor.setValue(editor.originalCode);
editor.clearSelection();
});
}
});
})();
(function themes() {
var html = document.querySelector('html');
var themeToggleButton = document.getElementById('theme-toggle');
var themePopup = document.getElementById('theme-list');
var themeColorMetaTag = document.querySelector('meta[name="theme-color"]');
var stylesheets = {
ayuHighlight: document.querySelector("[href$='ayu-highlight.css']"),
tomorrowNight: document.querySelector("[href$='tomorrow-night.css']"),
highlight: document.querySelector("[href$='highlight.css']"),
};
function showThemes() {
themePopup.style.display = 'block';
themeToggleButton.setAttribute('aria-expanded', true);
themePopup.querySelector("button#" + get_theme()).focus();
}
function updateThemeSelected() {
themePopup.querySelectorAll('.theme-selected').forEach(function (el) {
el.classList.remove('theme-selected');
});
themePopup.querySelector("button#" + get_theme()).classList.add('theme-selected');
}
function hideThemes() {
themePopup.style.display = 'none';
themeToggleButton.setAttribute('aria-expanded', false);
themeToggleButton.focus();
}
function get_theme() {
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { }
if (theme === null || theme === undefined) {
return default_theme;
} else {
return theme;
}
}
function set_theme(theme, store = true) {
let ace_theme;
if (theme == 'coal' || theme == 'navy') {
stylesheets.ayuHighlight.disabled = true;
stylesheets.tomorrowNight.disabled = false;
stylesheets.highlight.disabled = true;
ace_theme = "ace/theme/tomorrow_night";
} else if (theme == 'ayu') {
stylesheets.ayuHighlight.disabled = false;
stylesheets.tomorrowNight.disabled = true;
stylesheets.highlight.disabled = true;
ace_theme = "ace/theme/tomorrow_night";
} else {
stylesheets.ayuHighlight.disabled = true;
stylesheets.tomorrowNight.disabled = true;
stylesheets.highlight.disabled = false;
ace_theme = "ace/theme/dawn";
}
setTimeout(function () {
themeColorMetaTag.content = getComputedStyle(document.body).backgroundColor;
}, 1);
if (window.ace && window.editors) {
window.editors.forEach(function (editor) {
editor.setTheme(ace_theme);
});
}
var previousTheme = get_theme();
if (store) {
try { localStorage.setItem('mdbook-theme', theme); } catch (e) { }
}
html.classList.remove(previousTheme);
html.classList.add(theme);
updateThemeSelected();
}
// Set theme
var theme = get_theme();
set_theme(theme, false);
themeToggleButton.addEventListener('click', function () {
if (themePopup.style.display === 'block') {
hideThemes();
} else {
showThemes();
}
});
themePopup.addEventListener('click', function (e) {
var theme;
if (e.target.className === "theme") {
theme = e.target.id;
} else if (e.target.parentElement.className === "theme") {
theme = e.target.parentElement.id;
} else {
return;
}
set_theme(theme);
});
themePopup.addEventListener('focusout', function(e) {
// e.relatedTarget is null in Safari and Firefox on macOS (see workaround below)
if (!!e.relatedTarget && !themeToggleButton.contains(e.relatedTarget) && !themePopup.contains(e.relatedTarget)) {
hideThemes();
}
});
// Should not be needed, but it works around an issue on macOS & iOS: https://github.com/rust-lang/mdBook/issues/628
document.addEventListener('click', function(e) {
if (themePopup.style.display === 'block' && !themeToggleButton.contains(e.target) && !themePopup.contains(e.target)) {
hideThemes();
}
});
document.addEventListener('keydown', function (e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
if (!themePopup.contains(e.target)) { return; }
switch (e.key) {
case 'Escape':
e.preventDefault();
hideThemes();
break;
case 'ArrowUp':
e.preventDefault();
var li = document.activeElement.parentElement;
if (li && li.previousElementSibling) {
li.previousElementSibling.querySelector('button').focus();
}
break;
case 'ArrowDown':
e.preventDefault();
var li = document.activeElement.parentElement;
if (li && li.nextElementSibling) {
li.nextElementSibling.querySelector('button').focus();
}
break;
case 'Home':
e.preventDefault();
themePopup.querySelector('li:first-child button').focus();
break;
case 'End':
e.preventDefault();
themePopup.querySelector('li:last-child button').focus();
break;
}
});
})();
(function sidebar() {
var html = document.querySelector("html");
var sidebar = document.getElementById("sidebar");
var sidebarLinks = document.querySelectorAll('#sidebar a');
var sidebarToggleButton = document.getElementById("sidebar-toggle");
var sidebarResizeHandle = document.getElementById("sidebar-resize-handle");
var firstContact = null;
function showSidebar() {
html.classList.remove('sidebar-hidden')
html.classList.add('sidebar-visible');
Array.from(sidebarLinks).forEach(function (link) {
link.setAttribute('tabIndex', 0);
});
sidebarToggleButton.setAttribute('aria-expanded', true);
sidebar.setAttribute('aria-hidden', false);
try { localStorage.setItem('mdbook-sidebar', 'visible'); } catch (e) { }
}
var sidebarAnchorToggles = document.querySelectorAll('#sidebar a.toggle');
function toggleSection(ev) {
ev.currentTarget.parentElement.classList.toggle('expanded');
}
Array.from(sidebarAnchorToggles).forEach(function (el) {
el.addEventListener('click', toggleSection);
});
function hideSidebar() {
html.classList.remove('sidebar-visible')
html.classList.add('sidebar-hidden');
Array.from(sidebarLinks).forEach(function (link) {
link.setAttribute('tabIndex', -1);
});
sidebarToggleButton.setAttribute('aria-expanded', false);
sidebar.setAttribute('aria-hidden', true);
try { localStorage.setItem('mdbook-sidebar', 'hidden'); } catch (e) { }
}
// Toggle sidebar
sidebarToggleButton.addEventListener('click', function sidebarToggle() {
if (html.classList.contains("sidebar-hidden")) {
var current_width = parseInt(
document.documentElement.style.getPropertyValue('--sidebar-width'), 10);
if (current_width < 150) {
document.documentElement.style.setProperty('--sidebar-width', '150px');
}
showSidebar();
} else if (html.classList.contains("sidebar-visible")) {
hideSidebar();
} else {
if (getComputedStyle(sidebar)['transform'] === 'none') {
hideSidebar();
} else {
showSidebar();
}
}
});
sidebarResizeHandle.addEventListener('mousedown', initResize, false);
function initResize(e) {
window.addEventListener('mousemove', resize, false);
window.addEventListener('mouseup', stopResize, false);
html.classList.add('sidebar-resizing');
}
function resize(e) {
var pos = (e.clientX - sidebar.offsetLeft);
if (pos < 20) {
hideSidebar();
} else {
if (html.classList.contains("sidebar-hidden")) {
showSidebar();
}
pos = Math.min(pos, window.innerWidth - 100);
document.documentElement.style.setProperty('--sidebar-width', pos + 'px');
}
}
//on mouseup remove windows functions mousemove & mouseup
function stopResize(e) {
html.classList.remove('sidebar-resizing');
window.removeEventListener('mousemove', resize, false);
window.removeEventListener('mouseup', stopResize, false);
}
document.addEventListener('touchstart', function (e) {
firstContact = {
x: e.touches[0].clientX,
time: Date.now()
};
}, { passive: true });
document.addEventListener('touchmove', function (e) {
if (!firstContact)
return;
var curX = e.touches[0].clientX;
var xDiff = curX - firstContact.x,
tDiff = Date.now() - firstContact.time;
if (tDiff < 250 && Math.abs(xDiff) >= 150) {
if (xDiff >= 0 && firstContact.x < Math.min(document.body.clientWidth * 0.25, 300))
showSidebar();
else if (xDiff < 0 && curX < 300)
hideSidebar();
firstContact = null;
}
}, { passive: true });
// Scroll sidebar to current active section
var activeSection = document.getElementById("sidebar").querySelector(".active");
if (activeSection) {
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView
activeSection.scrollIntoView({ block: 'center' });
}
})();
(function chapterNavigation() {
document.addEventListener('keydown', function (e) {
if (e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) { return; }
if (window.search && window.search.hasFocus()) { return; }
switch (e.key) {
case 'ArrowRight':
e.preventDefault();
var nextButton = document.querySelector('.nav-chapters.next');
if (nextButton) {
window.location.href = nextButton.href;
}
break;
case 'ArrowLeft':
e.preventDefault();
var previousButton = document.querySelector('.nav-chapters.previous');
if (previousButton) {
window.location.href = previousButton.href;
}
break;
}
});
})();
(function clipboard() {
var clipButtons = document.querySelectorAll('.clip-button');
function hideTooltip(elem) {
elem.firstChild.innerText = "";
elem.className = 'fa fa-copy clip-button';
}
function showTooltip(elem, msg) {
elem.firstChild.innerText = msg;
elem.className = 'fa fa-copy tooltipped';
}
var clipboardSnippets = new ClipboardJS('.clip-button', {
text: function (trigger) {
hideTooltip(trigger);
let playground = trigger.closest("pre");
return playground_text(playground, false);
}
});
Array.from(clipButtons).forEach(function (clipButton) {
clipButton.addEventListener('mouseout', function (e) {
hideTooltip(e.currentTarget);
});
});
clipboardSnippets.on('success', function (e) {
e.clearSelection();
showTooltip(e.trigger, "Copied!");
});
clipboardSnippets.on('error', function (e) {
showTooltip(e.trigger, "Clipboard error!");
});
})();
(function scrollToTop () {
var menuTitle = document.querySelector('.menu-title');
menuTitle.addEventListener('click', function () {
document.scrollingElement.scrollTo({ top: 0, behavior: 'smooth' });
});
})();
(function controllMenu() {
var menu = document.getElementById('menu-bar');
(function controllPosition() {
var scrollTop = document.scrollingElement.scrollTop;
var prevScrollTop = scrollTop;
var minMenuY = -menu.clientHeight - 50;
// When the script loads, the page can be at any scroll (e.g. if you reforesh it).
menu.style.top = scrollTop + 'px';
// Same as parseInt(menu.style.top.slice(0, -2), but faster
var topCache = menu.style.top.slice(0, -2);
menu.classList.remove('sticky');
var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
document.addEventListener('scroll', function () {
scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
// `null` means that it doesn't need to be updated
var nextSticky = null;
var nextTop = null;
var scrollDown = scrollTop > prevScrollTop;
var menuPosAbsoluteY = topCache - scrollTop;
if (scrollDown) {
nextSticky = false;
if (menuPosAbsoluteY > 0) {
nextTop = prevScrollTop;
}
} else {
if (menuPosAbsoluteY > 0) {
nextSticky = true;
} else if (menuPosAbsoluteY < minMenuY) {
nextTop = prevScrollTop + minMenuY;
}
}
if (nextSticky === true && stickyCache === false) {
menu.classList.add('sticky');
stickyCache = true;
} else if (nextSticky === false && stickyCache === true) {
menu.classList.remove('sticky');
stickyCache = false;
}
if (nextTop !== null) {
menu.style.top = nextTop + 'px';
topCache = nextTop;
}
prevScrollTop = scrollTop;
}, { passive: true });
})();
(function controllBorder() {
menu.classList.remove('bordered');
document.addEventListener('scroll', function () {
if (menu.offsetTop === 0) {
menu.classList.remove('bordered');
} else {
menu.classList.add('bordered');
}
}, { passive: true });
})();
})();

@ -1,6 +0,0 @@
# Translations
- [简体中文](https://fomalhauthmj.github.io/patterns/)
If you want to add a translation, please open an issue in the
[main repository](https://github.com/rust-unofficial/patterns).
Loading…
Cancel
Save