chore: reformat repository with dprint (#370)

* chore: reformat repository with dprint
* ci: change action name to reflect that it's not only markdown
* fix: broken link
pull/371/head
simonsan 9 months ago committed by GitHub
parent c455f960ca
commit 12f6f988d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,4 +1,4 @@
name: Lint Markdown name: Lint with dprint
on: on:
push: push:

@ -1,7 +1,7 @@
{ {
"ignorePatterns": [ "ignorePatterns": [
{ {
"pattern": "^(http|https)://crates.io/" "pattern": "^(http|https)://crates.io/"
} }
] ]
} }

1
.gitignore vendored

@ -1,3 +1,4 @@
# Generated output of mdbook # Generated output of mdbook
/book /book
.DS_Store .DS_Store
.vscode/settings.json

@ -2,15 +2,14 @@
## Introduction ## Introduction
This book is a catalogue of Rust programming techniques, (anti-)patterns, This book is a catalogue of Rust programming techniques, (anti-)patterns, idioms
idioms and other explanations. It is a compilation of collective (sometimes and other explanations. It is a compilation of collective (sometimes implicit)
implicit) knowledge as well as experiences that have emerged through knowledge as well as experiences that have emerged through collaborative work.
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
guidelines for writing idiomatic code in Rust. We are collecting Rust patterns for writing idiomatic code in Rust. We are collecting Rust patterns in this book
in this book so people can learn the tradeoffs between Rust idioms and use them so people can learn the tradeoffs between Rust idioms and use them properly in
properly in their own code. their own code.
If you want to be part of this effort here are some ways you can participate: If you want to be part of this effort here are some ways you can participate:
@ -18,7 +17,8 @@ If you want to be part of this effort here are some ways you can participate:
If you have a question or an idea regarding certain content, but you want to If you have a question or an idea regarding certain content, but you want to
have feedback of fellow community members, and you think it may not be have feedback of fellow community members, and you think it may not be
appropriate to file an issue open a discussion in our [discussion board](https://github.com/rust-unofficial/patterns/discussions). appropriate to file an issue open a discussion in our
[discussion board](https://github.com/rust-unofficial/patterns/discussions).
## Writing a new article ## Writing a new article
@ -30,11 +30,13 @@ there is an existing discussion or if someone is already working on that topic:
- [Pull Requests](https://github.com/rust-unofficial/patterns/pulls) - [Pull Requests](https://github.com/rust-unofficial/patterns/pulls)
If you don't find an issue regarding your topic, and you are sure it is not more If you don't find an issue regarding your topic, and you are sure it is not more
feasible to open a thread in the [discussion board](https://github.com/rust-unofficial/patterns/discussions) feasible to open a thread in the
please open a new issue, so we can discuss the ideas and future content [discussion board](https://github.com/rust-unofficial/patterns/discussions)
of the article together and maybe give some feedback/input on it. please open a new issue, so we can discuss the ideas and future content of the
article together and maybe give some feedback/input on it.
When writing a new article it's recommended to copy the [pattern template](https://github.com/rust-unofficial/patterns/blob/master/template.md) When writing a new article it's recommended to copy the
[pattern template](https://github.com/rust-unofficial/patterns/blob/master/template.md)
into the appropriate directory and start editing it. You may not want to fill into the appropriate directory and start editing it. You may not want to fill
out every section and remove it, or you might want to add extra sections. out every section and remove it, or you might want to add extra sections.
@ -43,12 +45,13 @@ Consider writing your article in a way that has a low barrier of entry so also
the thought process behind it. So we can encourage people to use these patterns the thought process behind it. So we can encourage people to use these patterns
early on. early on.
We encourage you to write idiomatic Rust code that builds in the [playground](https://play.rust-lang.org/). We encourage you to write idiomatic Rust code that builds in the
[playground](https://play.rust-lang.org/).
If you use links to blogposts or in general content that is not to be sure If you use links to blogposts or in general content that is not to be sure
existing in a few years (e.g. pdfs) please take a snapshot with the existing in a few years (e.g. pdfs) please take a snapshot with the
[Wayback Machine](https://web.archive.org/) and use the link to that snapshot [Wayback Machine](https://web.archive.org/) and use the link to that snapshot in
in your article. your article.
Don't forget to add your new article to the `SUMMARY.md` to let it be rendered Don't forget to add your new article to the `SUMMARY.md` to let it be rendered
to the book. to the book.
@ -60,8 +63,10 @@ give early feedback (see the following section).
In order to have a consistent style across the book, we suggest to: In order to have a consistent style across the book, we suggest to:
- Follow the official Rust book's [style guide](https://github.com/rust-lang/book/blob/master/style-guide.md). - Follow the official Rust book's
- Follow [RFC 1574](https://github.com/rust-lang/rfcs/blob/master/text/1574-more-api-documentation-conventions.md#appendix-a-full-conventions-text). [style guide](https://github.com/rust-lang/book/blob/master/style-guide.md).
- Follow
[RFC 1574](https://github.com/rust-lang/rfcs/blob/master/text/1574-more-api-documentation-conventions.md#appendix-a-full-conventions-text).
Tl;dr: Tl;dr:
- Prefer full types name. For example `Option<T>` instead of `Option`. - Prefer full types name. For example `Option<T>` instead of `Option`.
- Prefer line comments (`//`) over block comments (`/* */`) where applicable. - Prefer line comments (`//`) over block comments (`/* */`) where applicable.
@ -73,10 +78,11 @@ the book builds and `mdbook test` to make sure that code examples are correct.
### Markdown lint ### Markdown lint
To make sure the files comply with our Markdown style we use [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli). To make sure the files comply with our Markdown style we use
To spare you some manual work to get through the CI test you can use the [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli). To spare
following commands to automatically fix most of the emerging problems when you some manual work to get through the CI test you can use the following
writing Markdown files. commands to automatically fix most of the emerging problems when writing
Markdown files.
- Install: - Install:
@ -96,8 +102,8 @@ writing Markdown files.
"Release early and often!" also applies to pull requests! "Release early and often!" also applies to pull requests!
Once your article has some visible work, create a `[WIP]` draft pull request Once your article has some visible work, create a `[WIP]` draft pull request and
and give it a description of what you did or want to do. Early reviews of the give it a description of what you did or want to do. Early reviews of the
community are not meant as an offense but to give feedback. community are not meant as an offense but to give feedback.
A good principle: "Work together, share ideas, teach others." A good principle: "Work together, share ideas, teach others."

@ -6,22 +6,24 @@ language that you can read [here](https://rust-unofficial.github.io/patterns/).
## Contributing ## Contributing
You are missing content in this repository that can be helpful for others, and You are missing content in this repository that can be helpful for others, and
you are eager to explain it? Awesome! We are always happy about new contributions you are eager to explain it? Awesome! We are always happy about new
(e.g. elaboration or corrections on certain topics) to this project. contributions (e.g. elaboration or corrections on certain topics) to this
project.
You can check the [Umbrella issue](https://github.com/rust-unofficial/patterns/issues/116) You can check the
for all the patterns, anti-patterns, and idioms that could be added. [Umbrella issue](https://github.com/rust-unofficial/patterns/issues/116) for all
the patterns, anti-patterns, and idioms that could be added.
We suggest reading our [Contribution guide](./CONTRIBUTING.md) to get more information We suggest reading our [Contribution guide](./CONTRIBUTING.md) to get more
on how contributing to this repository works. information on how contributing to this repository works.
## Building with mdbook ## Building with mdbook
This book is built with [mdbook](https://rust-lang.github.io/mdBook/). You can This book is built with [mdbook](https://rust-lang.github.io/mdBook/). You can
install it by running `cargo install mdbook`. install it by running `cargo install mdbook`.
If you want to build it locally you can run one of these two commands in the root If you want to build it locally you can run one of these two commands in the
directory of the repository: root directory of the repository:
- `mdbook build` - `mdbook build`
@ -35,4 +37,5 @@ directory of the repository:
## License ## License
The content of this repository is licensed under **MPL-2.0**; see [LICENSE](./LICENSE). The content of this repository is licensed under **MPL-2.0**; see
[LICENSE](./LICENSE).

@ -1,11 +1,29 @@
{ {
"lineWidth": 80, "lineWidth": 80,
"markdown": {}, "markdown": {
"includes": [ "lineWidth": 80,
"**/*.{md}" "emphasisKind": "asterisks",
], "strongKind": "asterisks",
"excludes": [], "textWrap": "always"
"plugins": [ },
"https://plugins.dprint.dev/markdown-0.15.2.wasm" "toml": {
] "lineWidth": 80
} },
"json": {
"lineWidth": 80,
"indentWidth": 4
},
"includes": [
"**/*.{md}",
"**/*.{toml}",
"**/*.{json}"
],
"excludes": [
"target/**/*"
],
"plugins": [
"https://plugins.dprint.dev/markdown-0.15.2.wasm",
"https://plugins.dprint.dev/toml-0.5.4.wasm",
"https://plugins.dprint.dev/json-0.17.4.wasm"
]
}

@ -7,8 +7,8 @@
## [SOLID](https://en.wikipedia.org/wiki/SOLID) ## [SOLID](https://en.wikipedia.org/wiki/SOLID)
- [Single Responsibility Principle (SRP)](https://en.wikipedia.org/wiki/Single-responsibility_principle): - [Single Responsibility Principle (SRP)](https://en.wikipedia.org/wiki/Single-responsibility_principle):
A class should only have a single responsibility, that is, only changes to A class should only have a single responsibility, that is, only changes to one
one part of the software's specification should be able to affect the part of the software's specification should be able to affect the
specification of the class. specification of the class.
- [Open/Closed Principle (OCP)](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle): - [Open/Closed Principle (OCP)](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle):
"Software entities ... should be open for extension, but closed for "Software entities ... should be open for extension, but closed for
@ -30,8 +30,8 @@ representation within a system"
## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle) ## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle)
most systems work best if they are kept simple rather than made complicated; most systems work best if they are kept simple rather than made complicated;
therefore, simplicity should be a key goal in design, and unnecessary therefore, simplicity should be a key goal in design, and unnecessary complexity
complexity should be avoided should be avoided
## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter) ## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter)
@ -54,14 +54,14 @@ unauthorized parties' direct access to them.
## [Command-Query-Separation(CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation) ## [Command-Query-Separation(CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation)
“Functions should not produce abstract side effects...only commands “Functions should not produce abstract side effects...only commands (procedures)
(procedures) will be permitted to produce side effects.” - Bertrand Meyer: will be permitted to produce side effects.” - Bertrand Meyer: Object-Oriented
Object-Oriented Software Construction Software Construction
## [Principle of least astonishment (POLA)](https://en.wikipedia.org/wiki/Principle_of_least_astonishment) ## [Principle of least astonishment (POLA)](https://en.wikipedia.org/wiki/Principle_of_least_astonishment)
a component of a system should behave in a way that most users will expect it a component of a system should behave in a way that most users will expect it to
to behave. The behavior should not astonish or surprise users behave. The behavior should not astonish or surprise users
## Linguistic-Modular-Units ## Linguistic-Modular-Units
@ -70,8 +70,8 @@ Meyer: Object-Oriented Software Construction
## Self-Documentation ## Self-Documentation
“The designer of a module should strive to make all information about the “The designer of a module should strive to make all information about the module
module part of the module itself.” - Bertrand Meyer: Object-Oriented Software part of the module itself.” - Bertrand Meyer: Object-Oriented Software
Construction Construction
## Uniform-Access ## Uniform-Access
@ -82,14 +82,13 @@ through computation.” - Bertrand Meyer: Object-Oriented Software Construction
## Single-Choice ## Single-Choice
“Whenever a software system must support a set of alternatives, one and only “Whenever a software system must support a set of alternatives, one and only one
one module in the system should know their exhaustive list.” - Bertrand Meyer: module in the system should know their exhaustive list.” - Bertrand Meyer:
Object-Oriented Software Construction Object-Oriented Software Construction
## Persistence-Closure ## Persistence-Closure
“Whenever a storage mechanism stores an object, it must store with it the “Whenever a storage mechanism stores an object, it must store with it the
dependents of that object. Whenever a retrieval mechanism retrieves a dependents of that object. Whenever a retrieval mechanism retrieves a previously
previously stored object, it must also retrieve any dependent of that object stored object, it must also retrieve any dependent of that object that has not
that has not yet been retrieved.” - Bertrand Meyer: Object-Oriented Software yet been retrieved.” - Bertrand Meyer: Object-Oriented Software Construction
Construction

@ -39,8 +39,8 @@ There are special cases -- `Rc<T>` is designed to handle clones intelligently.
It internally manages exactly one copy of the data, and cloning it will only It internally manages exactly one copy of the data, and cloning it will only
clone the reference. clone the reference.
There is also `Arc<T>` which provides shared ownership of a value of type T There is also `Arc<T>` which provides shared ownership of a value of type T that
that is allocated in the heap. Invoking `.clone()` on `Arc` produces a new `Arc` is allocated in the heap. Invoking `.clone()` on `Arc` produces a new `Arc`
instance, which points to the same allocation on the heap as the source `Arc`, instance, which points to the same allocation on the heap as the source `Arc`,
while increasing a reference count. while increasing a reference count.
@ -48,22 +48,26 @@ In general, clones should be deliberate, with full understanding of the
consequences. If a clone is used to make a borrow checker error disappear, consequences. If a clone is used to make a borrow checker error disappear,
that's a good indication this anti-pattern may be in use. that's a good indication this anti-pattern may be in use.
Even though `.clone()` is an indication of a bad pattern, sometimes Even though `.clone()` is an indication of a bad pattern, sometimes **it is fine
**it is fine to write inefficient code**, in cases such as when: to write inefficient code**, in cases such as when:
- the developer is still new to ownership - the developer is still new to ownership
- the code doesn't have great speed or memory constraints - the code doesn't have great speed or memory constraints (like hackathon
(like hackathon projects or prototypes) projects or prototypes)
- satisfying the borrow checker is really complicated, and you prefer to - satisfying the borrow checker is really complicated, and you prefer to
optimize readability over performance optimize readability over performance
If an unnecessary clone is suspected, The [Rust Book's chapter on Ownership](https://doc.rust-lang.org/book/ownership.html) If an unnecessary clone is suspected, The
should be understood fully before assessing whether the clone is required or not. [Rust Book's chapter on Ownership](https://doc.rust-lang.org/book/ownership.html)
should be understood fully before assessing whether the clone is required or
not.
Also be sure to always run `cargo clippy` in your project, which will detect some Also be sure to always run `cargo clippy` in your project, which will detect
cases in which `.clone()` is not necessary, like [1](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone), some cases in which `.clone()` is not necessary, like
[1](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone),
[2](https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy), [2](https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy),
[3](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) or [4](https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref). [3](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone) or
[4](https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref).
## See also ## See also

@ -53,8 +53,9 @@ 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) Travis, but remember that this may break the build when something changes)
without requiring a change to the code. without requiring a change to the code.
Alternatively, we can specify the lints that we want to `deny` in the code. Alternatively, we can specify the lints that we want to `deny` in the code. Here
Here is a list of warning lints that is (hopefully) safe to deny (as of Rustc 1.48.0): is a list of warning lints that is (hopefully) safe to deny (as of Rustc
1.48.0):
```rust,ignore ```rust,ignore
#![deny(bad_style, #![deny(bad_style,

@ -107,7 +107,8 @@ dispatch to `Foo` manually. We do intend to add a mechanism for inheritance
similar to this to Rust, but it is likely to be some time before it reaches similar to this to Rust, but it is likely to be some time before it reaches
stable Rust. See these [blog](http://aturon.github.io/blog/2015/09/18/reuse/) stable Rust. See these [blog](http://aturon.github.io/blog/2015/09/18/reuse/)
[posts](http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-structs-part-4-extended-enums-and-thin-traits/) [posts](http://smallcultfollowing.com/babysteps/blog/2015/10/08/virtual-structs-part-4-extended-enums-and-thin-traits/)
and this [RFC issue](https://github.com/rust-lang/rfcs/issues/349) for more details. and this [RFC issue](https://github.com/rust-lang/rfcs/issues/349) for more
details.
The `Deref` trait is designed for the implementation of custom pointer types. The `Deref` trait is designed for the implementation of custom pointer types.
The intention is that it will take a pointer-to-`T` to a `T`, not convert The intention is that it will take a pointer-to-`T` to a `T`, not convert
@ -123,6 +124,7 @@ conversion between arbitrary types.
## See also ## See also
- [Collections are smart pointers idiom](../idioms/deref.md). - [Collections are smart pointers idiom](../idioms/deref.md).
- Delegation crates for less boilerplate like [delegate](https://crates.io/crates/delegate) - Delegation crates for less boilerplate like
or [ambassador](https://crates.io/crates/ambassador) [delegate](https://crates.io/crates/delegate) or
[ambassador](https://crates.io/crates/ambassador)
- [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html). - [Documentation for `Deref` trait](https://doc.rust-lang.org/std/ops/trait.Deref.html).

@ -1,8 +1,8 @@
# Anti-patterns # Anti-patterns
An [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) is a solution to An [anti-pattern](https://en.wikipedia.org/wiki/Anti-pattern) is a solution to a
a "recurring problem that is usually ineffective and risks being highly "recurring problem that is usually ineffective and risks being highly
counterproductive". Just as valuable as knowing how to solve a problem, is counterproductive". Just as valuable as knowing how to solve a problem, is
knowing how _not_ to solve it. Anti-patterns give us great counter-examples to knowing how *not* to solve it. Anti-patterns give us great counter-examples to
consider relative to design patterns. Anti-patterns are not confined to code. consider relative to design patterns. Anti-patterns are not confined to code.
For example, a process can be an anti-pattern, too. For example, a process can be an anti-pattern, too.

@ -4,9 +4,9 @@
Rust's type system is designed more like functional languages (like Haskell) Rust's type system is designed more like functional languages (like Haskell)
rather than imperative languages (like Java and C++). As a result, Rust can turn rather than imperative languages (like Java and C++). As a result, Rust can turn
many kinds of programming problems into "static typing" problems. This is one many kinds of programming problems into "static typing" problems. This is one of
of the biggest wins of choosing a functional language, and is critical to many the biggest wins of choosing a functional language, and is critical to many of
of Rust's compile time guarantees. Rust's compile time guarantees.
A key part of this idea is the way generic types work. In C++ and Java, for 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. example, generic types are a meta-programming construct for the compiler.
@ -16,8 +16,8 @@ different types filled in.
In Rust, a generic type parameter creates what is known in functional languages 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 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>` 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 *are two different types*, which are recognized as distinct by all parts of the
type system. type system.
This is called **monomorphization**, where different types are created from This is called **monomorphization**, where different types are created from
@ -37,9 +37,9 @@ makes them more usable while remaining safe.
## Example ## Example
Suppose you are designing a storage server for a series of lab machines. Suppose you are designing a storage server for a series of lab machines. Because
Because of the software involved, there are two different protocols you need of the software involved, there are two different protocols you need to support:
to support: BOOTP (for PXE network boot), and NFS (for remote mount storage). BOOTP (for PXE network boot), and NFS (for remote mount storage).
Your goal is to have one program, written in Rust, which can handle both of Your goal is to have one program, written in Rust, which can handle both of
them. It will have protocol handlers and listen for both kinds of requests. The them. It will have protocol handlers and listen for both kinds of requests. The
@ -63,10 +63,10 @@ struct FileDownloadRequest {
} }
``` ```
This design might work well enough. But now suppose you needed to support This design might work well enough. But now suppose you needed to support adding
adding metadata that was _protocol specific_. For example, with NFS, you metadata that was *protocol specific*. For example, with NFS, you wanted to
wanted to determine what their mount point was in order to enforce additional determine what their mount point was in order to enforce additional security
security rules. rules.
The way the current struct is designed leaves the protocol decision until The way the current struct is designed leaves the protocol decision until
runtime. That means any method that applies to one protocol and not the other runtime. That means any method that applies to one protocol and not the other
@ -101,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 including what functions from the library they use, will know whether a request
is an NFS request or a BOOTP 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. order to split the API.
Here is what that looks like: Here is what that looks like:
@ -187,8 +187,7 @@ fn main() {
} }
``` ```
With this approach, if the user were to make a mistake and use the wrong With this approach, if the user were to make a mistake and use the wrong type;
type;
```rust,ignore ```rust,ignore
fn main() { fn main() {
@ -230,8 +229,8 @@ improve in the future.
initialization, consider the initialization, consider the
[Builder Pattern](../patterns/creational/builder.md) instead. [Builder Pattern](../patterns/creational/builder.md) instead.
- If the API between types does not change -- only the behavior does -- then - If the API between types does not change -- only the behavior does -- then the
the [Strategy Pattern](../patterns/behavioural/strategy.md) is better used [Strategy Pattern](../patterns/behavioural/strategy.md) is better used
instead. instead.
## See also ## See also
@ -239,8 +238,8 @@ improve in the future.
This pattern is used throughout the standard library: This pattern is used throughout the standard library:
- `Vec<u8>` can be cast from a String, unlike every other type of `Vec<T>`.[^1] - `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 - They can also be cast into a binary heap, but only if they contain a type that
that implements the `Ord` trait.[^2] 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: It is also used by several popular crates to allow API flexibility:
@ -248,8 +247,8 @@ 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 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, 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 it returns a `Pin<MODE>` struct, whose generic determines the functions usable
usable in that mode, which are not on the `Pin` itself. [^4] 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 pluggable requests. Clients with different connectors have different methods
@ -260,11 +259,14 @@ It is also used by several popular crates to allow API flexibility:
internal state or invariant -- is implemented in Rust using the same basic internal state or invariant -- is implemented in Rust using the same basic
concept, and a slightly different technique. [^6] 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: Ord\> FromIterator\<T\> for BinaryHeap\<T\>](https://web.archive.org/web/20201030132806/https://doc.rust-lang.org/stable/src/alloc/collections/binary_heap.rs.html#1330-1335)
[^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: [^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)

@ -1,10 +1,11 @@
# Functional Usage of Rust # Functional Usage of Rust
Rust is an imperative language, but it follows many Rust is an imperative language, but it follows many
[functional programming](https://en.wikipedia.org/wiki/Functional_programming) paradigms. [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. > programs are constructed by applying and composing functions. It is a
> It is a declarative programming paradigm in which function definitions are > declarative programming paradigm in which function definitions are trees of
> trees of expressions that each return a value, rather than a sequence of > expressions that each return a value, rather than a sequence of imperative
> imperative statements which change the state of the program. > statements which change the state of the program.

@ -1,21 +1,20 @@
# Lenses and Prisms # Lenses and Prisms
This is a pure functional concept that is not frequently used in Rust. This is a pure functional concept that is not frequently used in Rust.
Nevertheless, exploring the concept may be helpful to understand other Nevertheless, exploring the concept may be helpful to understand other patterns
patterns in Rust APIs, such as [visitors](../patterns/behavioural/visitor.md). in Rust APIs, such as [visitors](../patterns/behavioural/visitor.md). They also
They also have niche use cases. have niche use cases.
## Lenses: Uniform Access Across Types ## Lenses: Uniform Access Across Types
A lens is a concept from functional programming languages that allows A lens is a concept from functional programming languages that allows accessing
accessing parts of a data type in an abstract, unified way.[^1] parts of a data type in an abstract, unified way.[^1] In basic concept, it is
In basic concept, it is similar to the way Rust traits work with type erasure, similar to the way Rust traits work with type erasure, but it has a bit more
but it has a bit more power and flexibility. power and flexibility.
For example, suppose a bank contains several JSON formats for customer For example, suppose a bank contains several JSON formats for customer data.
data. This is because they come from different databases or legacy systems. One
This is because they come from different databases or legacy systems. database contains the data needed to perform credit checks:
One database contains the data needed to perform credit checks:
```json ```json
{ "name": "Jane Doe", { "name": "Jane Doe",
@ -102,13 +101,13 @@ fn unique_ids_iter<I>(iterator: I) -> HashSet<u64>
``` ```
Lenses, however, allow the code supporting customer ID to be moved from the 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
Rather than implementing a trait on each type, all matching structures can type, all matching structures can simply be accessed the same way.
simply be accessed the same way.
While the Rust language itself does not support this (type erasure is the 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
that feels like this to be written with macros: [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 ```rust,ignore
use std::collections::HashSet; use std::collections::HashSet;
@ -146,44 +145,39 @@ where
} }
``` ```
The version of `unique_ids_lens` shown here allows any type to be in the iterator, The version of `unique_ids_lens` shown here allows any type to be in the
so long as it has an attribute called `customer_id` which can be accessed by iterator, so long as it has an attribute called `customer_id` which can be
the function. accessed by the function. This is how most functional programming languages
This is how most functional programming languages operate on lenses. operate on lenses.
Rather than macros, they achieve this with a technique known as "currying". Rather than macros, they achieve this with a technique known as "currying". That
That is, they "partially construct" the function, leaving the type of the is, they "partially construct" the function, leaving the type of the final
final parameter (the value being operated on) unfilled until the function is parameter (the value being operated on) unfilled until the function is called.
called.
Thus it can be called with different types dynamically even from one place in Thus it can be called with different types dynamically even from one place in
the code. the code. That is what the `optics!` and `view_ref` in the example above
That is what the `optics!` and `view_ref` in the example above simulates. simulates.
The functional approach need not be restricted to accessing members. The functional approach need not be restricted to accessing members. More
More powerful lenses can be created which both _set_ and _get_ data in a powerful lenses can be created which both *set* and *get* data in a structure.
structure.
But the concept really becomes interesting when used as a building block for But the concept really becomes interesting when used as a building block for
composition. composition. That is where the concept appears more clearly in Rust.
That is where the concept appears more clearly in Rust.
## Prisms: A Higher-Order form of "Optics" ## Prisms: A Higher-Order form of "Optics"
A simple function such as `unique_ids_lens` above operates on a single lens. A simple function such as `unique_ids_lens` above operates on a single lens. A
A _prism_ is a function that operates on a _family_ of lenses. *prism* is a function that operates on a *family* of lenses. It is one
It is one conceptual level higher, using lenses as a building block, and conceptual level higher, using lenses as a building block, and continuing the
continuing the metaphor, is part of a family of "optics". metaphor, is part of a family of "optics". It is the main one that is useful in
It is the main one that is useful in understanding Rust APIs, so will be the understanding Rust APIs, so will be the focus here.
focus here.
The same way that traits allow "lens-like" design with static polymorphism and 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 dynamic dispatch, prism-like designs appear in Rust APIs which split problems
into multiple associated types to be composed. into multiple associated types to be composed. A good example of this is the
A good example of this is the traits in the parsing crate _Serde_. 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. challenge, especially the first time. Consider the `Deserializer` trait,
Consider the `Deserializer` trait, implemented by some type in any library implemented by some type in any library which parses a new format:
which parses a new format:
```rust,ignore ```rust,ignore
pub trait Deserializer<'de>: Sized { pub trait Deserializer<'de>: Sized {
@ -206,8 +200,8 @@ value, this looks odd.
Why are all the return types type erased? Why are all the return types type erased?
To understand that, we need to keep the lens concept in mind and look at To understand that, we need to keep the lens concept in mind and look at the
the definition of the `Visitor` type that is passed in generically: definition of the `Visitor` type that is passed in generically:
```rust,ignore ```rust,ignore
pub trait Visitor<'de>: Sized { pub trait Visitor<'de>: Sized {
@ -229,50 +223,47 @@ 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. which are represented by its associated `Value` type.
These values represent parts of the Rust value being deserialized. These values represent parts of the Rust value being deserialized. If this
If this fails, it returns an `Error` type - an error type determined by the fails, it returns an `Error` type - an error type determined by the
`Deserializer` when its methods were called. `Deserializer` when its methods were called.
This highlights that `Deserializer` is similar to `CustomerId` from earlier, This highlights that `Deserializer` is similar to `CustomerId` from earlier,
allowing any format parser which implements it to create `Value`s based on what allowing any format parser which implements it to create `Value`s based on what
it parsed. it parsed. The `Value` trait is acting like a lens in functional programming
The `Value` trait is acting like a lens in functional programming languages. languages.
But unlike the `CustomerId` trait, the return types of `Visitor` methods are 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 Instead of acting as one lens, it effectively acts as a family of lenses, one
lenses, one for each concrete type of `Visitor`. for each concrete type of `Visitor`.
The `Deserializer` API is based on having a generic set of "lenses" work across The `Deserializer` API is based on having a generic set of "lenses" work across
a set of other generic types for "observation". 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: For example, consider the identity record from earlier but simplified:
```json ```json
{ "name": "Jane Doe", { "name": "Jane Doe", "customer_id": 1048576332 }
"customer_id": 1048576332,
}
``` ```
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 1. The user would call a library function to deserialize the data. This would
create a `Deserializer` based on the JSON format. create a `Deserializer` based on the JSON format.
1. Based on the fields in the struct, a `Visitor` would be created (more on 1. Based on the fields in the struct, a `Visitor` would be created (more on that
that in a moment) which knows how to create each type in a generic data in a moment) which knows how to create each type in a generic data model that
model that was needed to represent it: `u64` and `String`. was needed to represent it: `u64` and `String`.
1. The deserializer would make calls to the `Visitor` as it parsed items. 1. The deserializer would make calls to the `Visitor` as it parsed items.
1. The `Visitor` would indicate if the items found were expected, and if not, 1. The `Visitor` would indicate if the items found were expected, and if not,
raise an error to indicate deserialization has failed. raise an error to indicate deserialization has failed.
For our very simple structure above, the expected pattern would be: 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 key called "name".
1. Visit a string value, which will go into the `name` field. 1. Visit a string value, which will go into the `name` field.
1. Visit a string key called "customer_id". 1. Visit a string key called "customer_id".
@ -282,11 +273,11 @@ For our very simple structure above, the expected pattern would be:
But what determines which "observation" pattern is expected? But what determines which "observation" pattern is expected?
A functional programming language would be able to use currying to create A functional programming language would be able to use currying to create
reflection of each type based on the type itself. reflection of each type based on the type itself. Rust does not support that, so
Rust does not support that, so every single type would need to have its own every single type would need to have its own code written based on its fields
code written based on its fields and their properties. and their properties.
_Serde_ solves this usability challenge with a derive macro: *Serde* solves this usability challenge with a derive macro:
```rust,ignore ```rust,ignore
use serde::Deserialize; use serde::Deserialize;
@ -311,49 +302,48 @@ pub trait Deserialize<'de>: Sized {
} }
``` ```
This is the function that determines how to create the struct itself. This is the function that determines how to create the struct itself. Code is
Code is generated based on the struct's fields. generated based on the struct's fields. When the parsing library is called - in
When the parsing library is called - in our example, a JSON parsing library - our example, a JSON parsing library - it creates a `Deserializer` and calls
it creates a `Deserializer` and calls `Type::deserialize` with it as a `Type::deserialize` with it as a parameter.
parameter.
The `deserialize` code will then create a `Visitor` which will have its calls The `deserialize` code will then create a `Visitor` which will have its calls
"refracted" by the `Deserializer`. "refracted" by the `Deserializer`. If everything goes well, eventually that
If everything goes well, eventually that `Visitor` will construct a value `Visitor` will construct a value corresponding to the type being parsed and
corresponding to the type being parsed and return it. 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 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 1. The input data format (e.g. JSON) is represented by a `Deserializer` called
by `Deserialize` by `Deserialize`
1. The `Deserializer` acts like a prism which "refracts" lens-like `Visitor` 1. The `Deserializer` acts like a prism which "refracts" lens-like `Visitor`
calls which actually build the data value calls which actually build the data value
The result is that types to be deserialized only implement the "top layer" of The result is that types to be deserialized only implement the "top layer" of
the API, and file formats only need to implement the "bottom layer". the API, and file formats only need to implement the "bottom layer". Each piece
Each piece can then "just work" with the rest of the ecosystem, since generic can then "just work" with the rest of the ecosystem, since generic types will
types will bridge them. bridge them.
To emphasize, the only reason this model works on any format and any type is To emphasize, the only reason this model works on any format and any type is
because the `Deserializer` trait's output type **is specified by the because the `Deserializer` trait's output type **is specified by the implementor
implementor of `Visitor` it is passed**, rather than being tied to one specific of `Visitor` it is passed**, rather than being tied to one specific type. This
type. was not true in the account example earlier.
This was not true in the account example earlier.
Rust's generic-inspired type system can bring it close to these concepts and Rust's generic-inspired type system can bring it close to these concepts and use
use their power, as shown in this API design. their power, as shown in this API design. But it may also need procedural macros
But it may also need procedural macros to create bridges for its generics. to create bridges for its generics.
## See Also ## See Also
- [lens-rs crate](https://crates.io/crates/lens-rs) for a pre-built lenses - [lens-rs crate](https://crates.io/crates/lens-rs) for a pre-built lenses
implementation, with a cleaner interface than these examples implementation, with a cleaner interface than these examples
- [serde](https://serde.rs) itself, which makes these concepts intuitive for - [serde](https://serde.rs) itself, which makes these concepts intuitive for end
end users (i.e. defining the structs) without needing to undestand the users (i.e. defining the structs) without needing to undestand the details
details
- [luminance](https://github.com/phaazon/luminance-rs) is a crate for drawing - [luminance](https://github.com/phaazon/luminance-rs) is a crate for drawing
computer graphics that uses lens API design, including proceducal macros to computer graphics that uses lens API design, including proceducal macros to
create full prisms for buffers of different pixel types that remain generic create full prisms for buffers of different pixel types that remain generic

@ -1,9 +1,9 @@
# Programming paradigms # Programming paradigms
One of the biggest hurdles to understanding functional programs when coming One of the biggest hurdles to understanding functional programs when coming from
from an imperative background is the shift in thinking. Imperative programs an imperative background is the shift in thinking. Imperative programs describe
describe **how** to do something, whereas declarative programs describe **how** to do something, whereas declarative programs describe **what** to do.
**what** to do. Let's sum the numbers from 1 to 10 to show this. Let's sum the numbers from 1 to 10 to show this.
## Imperative ## Imperative
@ -16,9 +16,8 @@ println!("{}", sum);
``` ```
With imperative programs, we have to play compiler to see what is happening. With imperative programs, we have to play compiler to see what is happening.
Here, we start with a `sum` of `0`. Here, we start with a `sum` of `0`. Next, we iterate through the range from 1
Next, we iterate through the range from 1 to 10. to 10. Each time through the loop, we add the corresponding value in the range.
Each time through the loop, we add the corresponding value in the range.
Then we print it out. Then we print it out.
| `i` | `sum` | | `i` | `sum` |
@ -43,17 +42,17 @@ of steps.
println!("{}", (1..11).fold(0, |a, b| a + b)); println!("{}", (1..11).fold(0, |a, b| a + b));
``` ```
Whoa! This is really different! What's going on here? Whoa! This is really different! What's going on here? Remember that with
Remember that with declarative programs we are describing **what** to do, declarative programs we are describing **what** to do, rather than **how** to do
rather than **how** to do it. `fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition) it. `fold` is a function that
functions. The name is a convention from Haskell. [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`) Here, we are composing functions of addition (this closure: `|a, b| a + b`) with
with a range from 1 to 10. The `0` is the starting point, so `a` is `0` at a range from 1 to 10. The `0` is the starting point, so `a` is `0` at first. `b`
first. `b` is the first element of the range, `1`. `0 + 1 = 1` is the result. is the first element of the range, `1`. `0 + 1 = 1` is the result. So now we
So now we `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the next `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the next result. This
result. This process continues until we get to the last element in the range, process continues until we get to the last element in the range, `10`.
`10`.
| `a` | `b` | result | | `a` | `b` | result |
| :-: | :-: | :----: | | :-: | :-: | :----: |

@ -3,12 +3,11 @@
## Description ## Description
Using a target of a deref coercion can increase the flexibility of your code Using a target of a deref coercion can increase the flexibility of your code
when you are deciding which argument type to use for a function argument. when you are deciding which argument type to use for a function argument. In
In this way, the function will accept more input types. this way, the function will accept more input types.
This is not limited to slice-able or fat pointer types. This is not limited to slice-able or fat pointer types. In fact, you should
In fact, you should always prefer using the **borrowed type** over always prefer using the **borrowed type** over **borrowing the owned type**.
**borrowing the owned type**.
Such as `&str` over `&String`, `&[T]` over `&Vec<T>`, or `&T` over `&Box<T>`. 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 Using borrowed types you can avoid layers of indirection for those instances
@ -59,10 +58,10 @@ fn main() {
} }
``` ```
This works fine because we are passing a `&String` type as a parameter. This works fine because we are passing a `&String` type as a parameter. If we
If we remove the comments on the last two lines, the example will fail. This remove the comments on the last two lines, the example will fail. This is
is because a `&str` type will not coerce to a `&String` type. We can fix this because a `&str` type will not coerce to a `&String` type. We can fix this by
by simply modifying the type for our argument. simply modifying the type for our argument.
For instance, if we change our function declaration to: For instance, if we change our function declaration to:
@ -77,11 +76,11 @@ Ferris: false
Curious: true Curious: true
``` ```
But wait, that's not all! There is more to this story. But wait, that's not all! There is more to this story. It's likely that you may
It's likely that you may say to yourself: that doesn't matter, I will never be say to yourself: that doesn't matter, I will never be using a `&'static str` as
using a `&'static str` as an input anyways (as we did when we used `"Ferris"`). an input anyways (as we did when we used `"Ferris"`). Even ignoring this special
Even ignoring this special example, you may still find that using `&str` will example, you may still find that using `&str` will give you more flexibility
give you more flexibility than using a `&String`. than using a `&String`.
Let's now take an example where someone gives us a sentence, and we want to Let's now take an example where someone gives us a sentence, and we want to
determine if any of the words in the sentence contain three consecutive vowels. determine if any of the words in the sentence contain three consecutive vowels.
@ -128,7 +127,8 @@ curious has three consecutive vowels!
However, this example will not run when our function is declared with an However, this example will not run when our function is declared with an
argument type `&String`. This is because string slices are a `&str` and not a argument type `&String`. This is because string slices are a `&str` and not a
`&String` which would require an allocation to be converted to `&String` which `&String` which would require an allocation to be converted to `&String` which
is not implicit, whereas converting from `String` to `&str` is cheap and implicit. is not implicit, whereas converting from `String` to `&str` is cheap and
implicit.
## See also ## See also

@ -24,7 +24,8 @@ fn say_hello(name: &str) -> String {
## Advantages ## Advantages
Using `format!` is usually the most succinct and readable way to combine strings. Using `format!` is usually the most succinct and readable way to combine
strings.
## Disadvantages ## Disadvantages

@ -2,8 +2,9 @@
## Description ## Description
Rust does not have constructors as a language construct. Instead, the Rust does not have constructors as a language construct. Instead, the convention
convention is to use an [associated function][associated function] `new` to create an object: is to use an [associated function][associated function] `new` to create an
object:
````rust ````rust
/// Time in seconds. /// Time in seconds.
@ -88,11 +89,10 @@ impl Second {
} }
```` ````
**Note:** It is common and expected for types to implement both **Note:** It is common and expected for types to implement both `Default` and an
`Default` and an empty `new` constructor. `new` is the constructor empty `new` constructor. `new` is the constructor convention in Rust, and users
convention in Rust, and users expect it to exist, so if it is expect it to exist, so if it is reasonable for the basic constructor to take no
reasonable for the basic constructor to take no arguments, then it arguments, then it should, even if it is functionally identical to default.
should, even if it is functionally identical to default.
**Hint:** The advantage of implementing or deriving `Default` is that your type **Hint:** The advantage of implementing or deriving `Default` is that your type
can now be used where a `Default` implementation is required, most prominently, can now be used where a `Default` implementation is required, most prominently,

@ -2,11 +2,11 @@
## Description ## 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 type; Rust cannot abstract over "everything that has a `new()` method". To allow
allow this, the [`Default`] trait was conceived, which can be used with this, the [`Default`] trait was conceived, which can be used with containers and
containers and other generic types (e.g. see [`Option::unwrap_or_default()`]). other generic types (e.g. see [`Option::unwrap_or_default()`]). Notably, some
Notably, some containers already implement it where applicable. containers already implement it where applicable.
Not only do one-element containers like `Cow`, `Box` or `Arc` implement Not only do one-element containers like `Cow`, `Box` or `Arc` implement
`Default` for contained `Default` types, one can automatically `Default` for contained `Default` types, one can automatically

@ -2,9 +2,9 @@
## Description ## Description
Use the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) Use the [`Deref`](https://doc.rust-lang.org/std/ops/trait.Deref.html) trait to
trait to treat collections like smart pointers, offering owning treat collections like smart pointers, offering owning and borrowed views of
and borrowed views of data. data.
## Example ## Example
@ -36,9 +36,9 @@ Also `String` and `&str` have a similar relation.
## Motivation ## Motivation
Ownership and borrowing are key aspects of the Rust language. Data structures Ownership and borrowing are key aspects of the Rust language. Data structures
must account for these semantics properly to give a good user must account for these semantics properly to give a good user experience. When
experience. When implementing a data structure that owns its data, offering a implementing a data structure that owns its data, offering a borrowed view of
borrowed view of that data allows for more flexible APIs. that data allows for more flexible APIs.
## Advantages ## Advantages

@ -56,7 +56,8 @@ This pattern introduces some hard to notice, implicit code. Reading a function
gives no clear indication of destructors to be run on exit. This can make gives no clear indication of destructors to be run on exit. This can make
debugging tricky. debugging tricky.
Requiring an object and `Drop` impl just for finalisation is heavy on boilerplate. Requiring an object and `Drop` impl just for finalisation is heavy on
boilerplate.
## Discussion ## Discussion

@ -16,12 +16,12 @@ The strings used in C have different behaviours to those used in Rust, namely:
- C strings are null-terminated while Rust strings store their length - C strings are null-terminated while Rust strings store their length
- C strings can contain any arbitrary non-zero byte while Rust strings must be - C strings can contain any arbitrary non-zero byte while Rust strings must be
UTF-8 UTF-8
- C strings are accessed and manipulated using `unsafe` pointer operations - C strings are accessed and manipulated using `unsafe` pointer operations while
while interactions with Rust strings go through safe methods interactions with Rust strings go through safe methods
The Rust standard library comes with C equivalents of Rust's `String` and `&str` The Rust standard library comes with C equivalents of Rust's `String` and `&str`
called `CString` and `&CStr`, that allow us to avoid a lot of the complexity called `CString` and `&CStr`, that allow us to avoid a lot of the complexity and
and `unsafe` code involved in converting between C strings and Rust strings. `unsafe` code involved in converting between C strings and Rust strings.
The `&CStr` type also allows us to work with borrowed data, meaning passing The `&CStr` type also allows us to work with borrowed data, meaning passing
strings between Rust and C is a zero-cost operation. strings between Rust and C is a zero-cost operation.
@ -70,8 +70,7 @@ pub mod unsafe_module {
The example is is written to ensure that: The example is is written to ensure that:
1. The `unsafe` block is as small as possible. 1. The `unsafe` block is as small as possible.
2. The pointer with an "untracked" lifetime becomes a "tracked" shared 2. The pointer with an "untracked" lifetime becomes a "tracked" shared reference
reference
Consider an alternative, where the string is actually copied: Consider an alternative, where the string is actually copied:
@ -121,22 +120,21 @@ This code in inferior to the original in two respects:
1. There is much more `unsafe` code, and more importantly, more invariants it 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 2. Due to the extensive arithmetic required, there is a bug in this version that
that cases Rust `undefined behaviour`. cases Rust `undefined behaviour`.
The bug here is a simple mistake in pointer arithmetic: the string was copied, 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. 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_ -- 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. rather than *resized* to it, which could have added a zero at the end. As a
As a result, the last byte in the Vector is uninitialized memory. result, the last byte in the Vector is uninitialized memory. When the `CString`
When the `CString` is created at the bottom of the block, its read of the is created at the bottom of the block, its read of the Vector will cause
Vector will cause `undefined behaviour`! `undefined behaviour`!
Like many such issues, this would be difficult issue to track down. Like many such issues, this would be difficult issue to track down. Sometimes it
Sometimes it would panic because the string was not `UTF-8`, sometimes it would would panic because the string was not `UTF-8`, sometimes it would put a weird
put a weird character at the end of the string, sometimes it would just character at the end of the string, sometimes it would just completely crash.
completely crash.
## Disadvantages ## Disadvantages

@ -2,9 +2,9 @@
## Description ## Description
In foreign languages like C, errors are represented by return codes. In foreign languages like C, errors are represented by return codes. However,
However, Rust's type system allows much more rich error information to be Rust's type system allows much more rich error information to be captured and
captured and propogated through a full type. propogated through a full type.
This best practice shows different kinds of error codes, and how to expose them This best practice shows different kinds of error codes, and how to expose them
in a usable way: in a usable way:
@ -135,5 +135,5 @@ while not compromising the Rust code's API at all.
## Disadvantages ## Disadvantages
It's a lot of typing, and some types may not be able to be converted easily It's a lot of typing, and some types may not be able to be converted easily to
to C. C.

@ -1,8 +1,8 @@
# FFI Idioms # FFI Idioms
Writing FFI code is an entire course in itself. Writing FFI code is an entire course in itself. However, there are several
However, there are several idioms here that can act as pointers, and avoid idioms here that can act as pointers, and avoid traps for inexperienced users of
traps for inexperienced users of `unsafe` Rust. `unsafe` Rust.
This section contains idioms that may be useful when doing FFI. This section contains idioms that may be useful when doing FFI.

@ -17,11 +17,11 @@ Rust has built-in support for C-style strings with its `CString` and `CStr`
types. However, there are different approaches one can take with strings that types. However, there are different approaches one can take with strings that
are being sent to a foreign function call from a Rust function. 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 The best practice is simple: use `CString` in such a way as to minimize `unsafe`
`unsafe` code. However, a secondary caveat is that code. However, a secondary caveat is that *the object must live long enough*,
_the object must live long enough_, meaning the lifetime should be maximized. meaning the lifetime should be maximized. In addition, the documentation
In addition, the documentation explains that "round-tripping" a `CString` after explains that "round-tripping" a `CString` after modification is UB, so
modification is UB, so additional work is necessary in that case. additional work is necessary in that case.
## Code Example ## Code Example

@ -1,13 +1,11 @@
# Idioms # Idioms
[Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly used [Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly used
styles, guidelines and patterns largely agreed upon by a community. styles, guidelines and patterns largely agreed upon by a community. Writing
Writing idiomatic code allows other developers to understand better what is idiomatic code allows other developers to understand better what is happening.
happening.
After all, the computer only cares about the machine code that is generated After all, the computer only cares about the machine code that is generated by
by the compiler. the compiler. Instead, the source code is mainly beneficial to the developer.
Instead, the source code is mainly beneficial to the developer.
So, since we have this abstraction layer, why not make it more readable? So, since we have this abstraction layer, why not make it more readable?
Remember the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle): Remember the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle):

@ -58,27 +58,29 @@ fn swizzle(e: &mut MultiVariateEnum) {
When working with enums, we may want to change an enum value in place, perhaps When working with enums, we may want to change an enum value in place, perhaps
to another variant. This is usually done in two phases to keep the borrow to another variant. This is usually done in two phases to keep the borrow
checker happy. In the first phase, we observe the existing value and look at checker happy. In the first phase, we observe the existing value and look at its
its parts to decide what to do next. In the second phase we may conditionally parts to decide what to do next. In the second phase we may conditionally change
change the value (as in the example above). the value (as in the example above).
The borrow checker won't allow us to take out `name` of the enum (because 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 *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 into our `MyEnum::B`, but that would be an instance of the
can avoid the extra allocation by changing `e` with only a mutable borrow. [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, `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 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 `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 replace the value with. An equivalent to our `mem::take` line would be
`mem::replace(name, String::new())`. `mem::replace(name, String::new())`.
Note, however, that if we are using an `Option` and want to replace its Note, however, that if we are using an `Option` and want to replace its value
value with a `None`, `Option`s `take()` method provides a shorter and with a `None`, `Option`s `take()` method provides a shorter and more idiomatic
more idiomatic alternative. alternative.
## Advantages ## Advantages
@ -86,13 +88,13 @@ Look ma, no allocation! Also you may feel like Indiana Jones while doing it.
## Disadvantages ## Disadvantages
This gets a bit wordy. Getting it wrong repeatedly will make you hate the This gets a bit wordy. Getting it wrong repeatedly will make you hate the borrow
borrow checker. The compiler may fail to optimize away the double store, checker. The compiler may fail to optimize away the double store, resulting in
resulting in reduced performance as opposed to what you'd do in unsafe reduced performance as opposed to what you'd do in unsafe languages.
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
implement this, you can instead use `mem::replace`. [`Default` trait](./default.md). However, if the type you're working with
doesn't implement this, you can instead use `mem::replace`.
## Discussion ## Discussion
@ -107,5 +109,6 @@ like Indiana Jones, replacing the artifact with a bag of sand.
## See also ## See also
This gets rid of the [Clone to satisfy the borrow checker](../anti_patterns/borrow_clone.md) This gets rid of the
[Clone to satisfy the borrow checker](../anti_patterns/borrow_clone.md)
anti-pattern in a specific case. anti-pattern in a specific case.

@ -2,8 +2,8 @@
## Description ## Description
We can dynamically dispatch over multiple values, however, to do so, we need We can dynamically dispatch over multiple values, however, to do so, we need to
to declare multiple variables to bind differently-typed objects. To extend the declare multiple variables to bind differently-typed objects. To extend the
lifetime as necessary, we can use deferred conditional initialization, as seen lifetime as necessary, we can use deferred conditional initialization, as seen
below: below:
@ -68,7 +68,7 @@ let readable: Box<dyn io::Read> = if arg == "-" {
## Discussion ## Discussion
Rust newcomers will usually learn that Rust requires all variables to be 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 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 works out fine and only the initialized values are dropped at the end of their
scope. scope.
@ -77,14 +77,15 @@ The example meets all the constraints Rust places on us:
- All variables are initialized before using (in this case borrowing) them - 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 - 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` 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 - Each borrowed value outlives all the references borrowed from it
## See also ## See also
- [Finalisation in destructors](dtor-finally.md) and - [Finalisation in destructors](dtor-finally.md) and
[RAII guards](../patterns/behavioural/RAII.md) can benefit from tight control over [RAII guards](../patterns/behavioural/RAII.md) can benefit from tight control
lifetimes. over lifetimes.
- For conditionally filled `Option<&T>`s of (mutable) references, one can - 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 initialize an `Option<T>` directly and use its [`.as_ref()`] method to get an
optional reference. optional reference.

@ -2,9 +2,9 @@
## Description ## Description
`Option` can be viewed as a container that contains either zero or one `Option` can be viewed as a container that contains either zero or one element.
element. In particular, it implements the `IntoIterator` trait, and as such In particular, it implements the `IntoIterator` trait, and as such can be used
can be used with generic code that needs such a type. with generic code that needs such a type.
## Examples ## Examples
@ -23,8 +23,9 @@ if let Some(turing_inner) = turing {
} }
``` ```
If you need to tack an `Option` to the end of an existing iterator, you can If you need to tack an `Option` to the end of an existing iterator, you can pass
pass it to [`.chain()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain): it to
[`.chain()`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.chain):
```rust ```rust
let turing = Some("Turing"); let turing = Some("Turing");
@ -39,8 +40,8 @@ Note that if the `Option` is always `Some`, then it is more idiomatic to use
[`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) on the [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) on the
element instead. element instead.
Also, since `Option` implements `IntoIterator`, it's possible to iterate over Also, since `Option` implements `IntoIterator`, it's possible to iterate over it
it using a `for` loop. This is equivalent to matching it with `if let Some(..)`, using a `for` loop. This is equivalent to matching it with `if let Some(..)`,
and in most cases you should prefer the latter. and in most cases you should prefer the latter.
## See also ## See also
@ -50,7 +51,8 @@ and in most cases you should prefer the latter.
`Some(foo).into_iter()`. `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), 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`. 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

@ -8,9 +8,10 @@ compatibility.
Rust offers two solutions to this problem: Rust offers two solutions to this problem:
- Use `#[non_exhaustive]` on `struct`s, `enum`s, and `enum` variants. - Use `#[non_exhaustive]` on `struct`s, `enum`s, and `enum` variants. For
For extensive documentation on all the places where `#[non_exhaustive]` can be extensive documentation on all the places where `#[non_exhaustive]` can be
used, see [the docs](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute). used, see
[the docs](https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute).
- You may add a private field to a struct to prevent it from being directly - You may add a private field to a struct to prevent it from being directly
instantiated or matched against (see Alternative) instantiated or matched against (see Alternative)
@ -56,22 +57,21 @@ fn print_matched_variants(s: a::S) {
## Alternative: `Private fields` for structs ## Alternative: `Private fields` for structs
`#[non_exhaustive]` only works across crate boundaries. `#[non_exhaustive]` only works across crate boundaries. Within a crate, the
Within a crate, the private field method may be used. private field method may be used.
Adding a field to a struct is a mostly backwards compatible change. Adding a field to a struct is a mostly backwards compatible change. However, if
However, if a client uses a pattern to deconstruct a struct instance, they a client uses a pattern to deconstruct a struct instance, they might name all
might name all the fields in the struct and adding a new one would break that the fields in the struct and adding a new one would break that pattern. The
pattern. client could name some fields and use `..` in the pattern, in which case adding
The client could name some fields and use `..` in the pattern, in which case adding another field is backwards compatible. Making at least one of the struct's
another field is backwards compatible. fields private forces clients to use the latter form of patterns, ensuring that
Making at least one of the struct's fields private forces clients to use the latter the struct is future-proof.
form of patterns, ensuring that the struct is future-proof.
The downside of this approach is that you might need to add an otherwise unneeded The downside of this approach is that you might need to add an otherwise
field to the struct. unneeded field to the struct. You can use the `()` type so that there is no
You can use the `()` type so that there is no runtime overhead and prepend `_` to runtime overhead and prepend `_` to the field name to avoid the unused field
the field name to avoid the unused field warning. warning.
```rust ```rust
pub struct S { pub struct S {
@ -85,37 +85,33 @@ pub struct S {
## Discussion ## Discussion
On `struct`s, `#[non_exhaustive]` allows adding additional fields in a backwards On `struct`s, `#[non_exhaustive]` allows adding additional fields in a backwards
compatible way. compatible way. It will also prevent clients from using the struct constructor,
It will also prevent clients from using the struct constructor, even if all the even if all the fields are public. This may be helpful, but it's worth
fields are public. considering if you *want* an additional field to be found by clients as a
This may be helpful, but it's worth considering if you _want_ an additional field compiler error rather than something that may be silently undiscovered.
to be found by clients as a compiler error rather than something that may be silently
undiscovered. `#[non_exhaustive]` can be applied to enum variants as well. A
`#[non_exhaustive]` variant behaves in the same way as a `#[non_exhaustive]`
`#[non_exhaustive]` can be applied to enum variants as well. struct.
A `#[non_exhaustive]` variant behaves in the same way as a `#[non_exhaustive]` struct.
Use this deliberately and with caution: incrementing the major version when
Use this deliberately and with caution: incrementing the major version when adding adding fields or variants is often a better option. `#[non_exhaustive]` may be
fields or variants is often a better option. appropriate in scenarios where you're modeling an external resource that may
`#[non_exhaustive]` may be appropriate in scenarios where you're modeling an external change out-of-sync with your library, but is not a general purpose tool.
resource that may change out-of-sync with your library, but is not a general purpose
tool.
### Disadvantages ### Disadvantages
`#[non_exhaustive]` can make your code much less ergonomic to use, especially when `#[non_exhaustive]` can make your code much less ergonomic to use, especially
forced to handle unknown enum variants. when forced to handle unknown enum variants. It should only be used when these
It should only be used when these sorts of evolutions are required **without** sorts of evolutions are required **without** incrementing the major version.
incrementing the major version.
When `#[non_exhaustive]` is applied to `enum`s, it forces clients to handle a When `#[non_exhaustive]` is applied to `enum`s, it forces clients to handle a
wildcard variant. wildcard variant. If there is no sensible action to take in this case, this may
If there is no sensible action to take in this case, this may lead to awkward lead to awkward code and code paths that are only executed in extremely rare
code and code paths that are only executed in extremely rare circumstances. circumstances. If a client decides to `panic!()` in this scenario, it may have
If a client decides to `panic!()` in this scenario, it may have been better to been better to expose this error at compile time. In fact, `#[non_exhaustive]`
expose this error at compile time. forces clients to handle the "Something else" case; there is rarely a sensible
In fact, `#[non_exhaustive]` forces clients to handle the "Something else" case; action to take in this scenario.
there is rarely a sensible action to take in this scenario.
## See also ## See also

@ -2,8 +2,8 @@
## Description ## Description
If a fallible function consumes (moves) an argument, return that argument back inside If a fallible function consumes (moves) an argument, return that argument back
an error. inside an error.
## Example ## Example
@ -42,14 +42,12 @@ fn main() {
## Motivation ## Motivation
In case of error you may want to try some alternative way or to In case of error you may want to try some alternative way or to retry action in
retry action in case of non-deterministic function. But if the argument case of non-deterministic function. But if the argument is always consumed, you
is always consumed, you are forced to clone it on every call, which are forced to clone it on every call, which is not very efficient.
is not very efficient.
The standard library uses this approach in e.g. `String::from_utf8` method. The standard library uses this approach in e.g. `String::from_utf8` method. When
When given a vector that doesn't contain valid UTF-8, a `FromUtf8Error` given a vector that doesn't contain valid UTF-8, a `FromUtf8Error` is returned.
is returned.
You can get original vector back using `FromUtf8Error::into_bytes` method. You can get original vector back using `FromUtf8Error::into_bytes` method.
## Advantages ## Advantages

@ -80,9 +80,10 @@ This is much more concise and avoids repetitive code in examples.
## Disadvantages ## Disadvantages
As example is in a function, the code will not be tested. Though it will still be As example is in a function, the code will not be tested. Though it will still
checked to make sure it compiles when running a `cargo test`. So this pattern is be checked to make sure it compiles when running a `cargo test`. So this pattern
most useful when you need `no_run`. With this, you do not need to add `no_run`. is most useful when you need `no_run`. With this, you do not need to add
`no_run`.
## Discussion ## Discussion

@ -3,8 +3,8 @@
## Description ## Description
Often it is necessary to prepare and process some data, but after that data are Often it is necessary to prepare and process some data, but after that data are
only inspected and never modified. The intention can be made explicit by redefining only inspected and never modified. The intention can be made explicit by
the mutable variable as immutable. redefining the mutable variable as immutable.
It can be done either by processing data within a nested block or by redefining It can be done either by processing data within a nested block or by redefining
the variable. the variable.
@ -41,5 +41,5 @@ Compiler ensures that you don't accidentally mutate data after some point.
## Disadvantages ## Disadvantages
Nested block requires additional indentation of block body. Nested block requires additional indentation of block body. One more line to
One more line to return data from block or redefine variable. return data from block or redefine variable.

@ -7,34 +7,31 @@ If you are interested in contributing to this book, check out the
## Design patterns ## Design patterns
In software development, we often come across problems that share In software development, we often come across problems that share similarities
similarities regardless of the environment they appear in. Although the regardless of the environment they appear in. Although the implementation
implementation details are crucial to solve the task at hand, we may details are crucial to solve the task at hand, we may abstract from these
abstract from these particularities to find the common practices that particularities to find the common practices that are generically applicable.
are generically applicable.
Design patterns are a collection of reusable and tested solutions to recurring
Design patterns are a collection of reusable and tested solutions to problems in engineering. They make our software more modular, maintainable, and
recurring problems in engineering. They make our software more modular, extensible. Moreover, these patterns provide a common language for developers,
maintainable, and extensible. Moreover, these patterns provide a common making them an excellent tool for effective communication when problem-solving
language for developers, making them an excellent tool for effective in teams.
communication when problem-solving in teams.
## Design patterns in Rust ## Design patterns in Rust
Rust is not object-oriented, and the combination of all its characteristics, Rust is not object-oriented, and the combination of all its characteristics,
such as functional elements, a strong type system, and the borrow checker, such as functional elements, a strong type system, and the borrow checker, makes
makes it unique. it unique. Because of this, Rust design patterns vary with respect to other
Because of this, Rust design patterns vary with respect to other traditional object-oriented programming languages. That's why we decided to
traditional object-oriented programming languages. write this book. We hope you enjoy reading it! The book is divided in three main
That's why we decided to write this book. We hope you enjoy reading it! chapters:
The book is divided in three main chapters:
- [Idioms](./idioms/index.md): guidelines to follow when coding. They are the
- [Idioms](./idioms/index.md): guidelines to follow when coding. social norms of the community. You should break them only if you have a good
They are the social norms of the community. reason for it.
You should break them only if you have a good reason for it. - [Design patterns](./patterns/index.md): methods to solve common problems when
- [Design patterns](./patterns/index.md): methods to solve common problems coding.
when coding.
- [Anti-patterns](./anti_patterns/index.md): methods to solve common problems - [Anti-patterns](./anti_patterns/index.md): methods to solve common problems
when coding. when coding. However, while design patterns give us benefits, anti-patterns
However, while design patterns give us benefits, create more problems.
anti-patterns create more problems.

@ -3,10 +3,11 @@
## Description ## Description
[RAII][wikipedia] stands for "Resource Acquisition is Initialisation" which is a [RAII][wikipedia] stands for "Resource Acquisition is Initialisation" which is a
terrible name. The essence of the pattern is that resource initialisation is done terrible name. The essence of the pattern is that resource initialisation is
in the constructor of an object and finalisation in the destructor. This pattern done in the constructor of an object and finalisation in the destructor. This
is extended in Rust by using a RAII object as a guard of some resource and relying pattern is extended in Rust by using a RAII object as a guard of some resource
on the type system to ensure that access is always mediated by the guard object. and relying on the type system to ensure that access is always mediated by the
guard object.
## Example ## Example
@ -112,7 +113,8 @@ works just as well.
[Finalisation in destructors idiom](../../idioms/dtor-finally.md) [Finalisation in destructors idiom](../../idioms/dtor-finally.md)
RAII is a common pattern in C++: [cppreference.com](http://en.cppreference.com/w/cpp/language/raii), RAII is a common pattern in C++:
[cppreference.com](http://en.cppreference.com/w/cpp/language/raii),
[wikipedia][wikipedia]. [wikipedia][wikipedia].
[wikipedia]: https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization [wikipedia]: https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

@ -9,11 +9,11 @@ objects and pass them as parameters.
Suppose we have a sequence of actions or transactions encapsulated as objects. Suppose we have a sequence of actions or transactions encapsulated as objects.
We want these actions or commands to be executed or invoked in some order later We want these actions or commands to be executed or invoked in some order later
at different time. These commands may also be triggered as a result of some event. at different time. These commands may also be triggered as a result of some
For example, when a user pushes a button, or on arrival of a data packet. event. For example, when a user pushes a button, or on arrival of a data packet.
In addition, these commands might be undoable. This may come in useful for In addition, these commands might be undoable. This may come in useful for
operations of an editor. We might want to store logs of executed commands so that operations of an editor. We might want to store logs of executed commands so
we could reapply the changes later if the system crashes. that we could reapply the changes later if the system crashes.
## Example ## Example
@ -94,8 +94,8 @@ fn main() {
## Approach: Using function pointers ## Approach: Using function pointers
We could follow another approach by creating each individual command as We could follow another approach by creating each individual command as a
a different function and store function pointers to invoke these functions later different function and store function pointers to invoke these functions later
at a different time. Since function pointers implement all three traits `Fn`, at a different time. Since function pointers implement all three traits `Fn`,
`FnMut`, and `FnOnce` we could as well pass and store closures instead of `FnMut`, and `FnOnce` we could as well pass and store closures instead of
function pointers. function pointers.
@ -149,8 +149,8 @@ fn main() {
## Approach: Using `Fn` trait objects ## Approach: Using `Fn` trait objects
Finally, instead of defining a common command trait we could store Finally, instead of defining a common command trait we could store each command
each command implementing the `Fn` trait separately in vectors. implementing the `Fn` trait separately in vectors.
```rust ```rust
type Migration<'a> = Box<dyn Fn() -> &'a str>; type Migration<'a> = Box<dyn Fn() -> &'a str>;
@ -205,11 +205,11 @@ fn main() {
If our commands are small and may be defined as functions or passed as a closure If our commands are small and may be defined as functions or passed as a closure
then using function pointers might be preferable since it does not exploit then using function pointers might be preferable since it does not exploit
dynamic dispatch. But if our command is a whole struct with a bunch of functions dynamic dispatch. But if our command is a whole struct with a bunch of functions
and variables defined as seperated module then using trait objects would be and variables defined as seperated module then using trait objects would be more
more suitable. A case of application can be found in [`actix`](https://actix.rs/), suitable. A case of application can be found in [`actix`](https://actix.rs/),
which uses trait objects when it registers a handler function for routes. which uses trait objects when it registers a handler function for routes. In
In case of using `Fn` trait objects we can create and use commands in the same case of using `Fn` trait objects we can create and use commands in the same way
way as we used in case of function pointers. as we used in case of function pointers.
As performance, there is always a trade-off between performance and code As performance, there is always a trade-off between performance and code
simplicity and organisation. Static dispatch gives faster performance, while simplicity and organisation. Static dispatch gives faster performance, while

@ -9,23 +9,25 @@ simple language.
Basically, for any kind of problems we define: Basically, for any kind of problems we define:
- A [domain specific language](https://en.wikipedia.org/wiki/Domain-specific_language), - A
[domain specific language](https://en.wikipedia.org/wiki/Domain-specific_language),
- A grammar for this language, - A grammar for this language,
- An interpreter that solves the problem instances. - An interpreter that solves the problem instances.
## Motivation ## Motivation
Our goal is to translate simple mathematical expressions into postfix expressions Our goal is to translate simple mathematical expressions into postfix
(or [Reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation)) expressions (or
[Reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation))
For simplicity, our expressions consist of ten digits `0`, ..., `9` and two For simplicity, our expressions consist of ten digits `0`, ..., `9` and two
operations `+`, `-`. For example, the expression `2 + 4` is translated into operations `+`, `-`. For example, the expression `2 + 4` is translated into
`2 4 +`. `2 4 +`.
## Context Free Grammar for our problem ## Context Free Grammar for our problem
Our task is translating infix expressions into postfix ones. Let's define a context Our task is translating infix expressions into postfix ones. Let's define a
free grammar for a set of infix expressions over `0`, ..., `9`, `+`, and `-`, context free grammar for a set of infix expressions over `0`, ..., `9`, `+`, and
where: `-`, where:
- Terminal symbols: `0`, `...`, `9`, `+`, `-` - Terminal symbols: `0`, `...`, `9`, `+`, `-`
- Non-terminal symbols: `exp`, `term` - Non-terminal symbols: `exp`, `term`
@ -39,9 +41,10 @@ exp -> term
term -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 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
to do with it. For example, we might need to remove left recursion. For more going to do with it. For example, we might need to remove left recursion. For
details please see [Compilers: Principles,Techniques, and Tools](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools) more details please see
[Compilers: Principles,Techniques, and Tools](https://en.wikipedia.org/wiki/Compilers:_Principles,_Techniques,_and_Tools)
(aka Dragon Book). (aka Dragon Book).
## Solution ## Solution
@ -102,18 +105,18 @@ pub fn main() {
## Discussion ## Discussion
There may be a wrong perception that the Interpreter design pattern is about design There may be a wrong perception that the Interpreter design pattern is about
grammars for formal languages and implementation of parsers for these grammars. design grammars for formal languages and implementation of parsers for these
In fact, this pattern is about expressing problem instances in a more specific grammars. In fact, this pattern is about expressing problem instances in a more
way and implementing functions/classes/structs that solve these problem instances. specific way and implementing functions/classes/structs that solve these problem
Rust language has `macro_rules!` that allow us to define special syntax and rules instances. Rust language has `macro_rules!` that allow us to define special
on how to expand this syntax into source code. syntax and rules on how to expand this syntax into source code.
In the following example we create a simple `macro_rules!` that computes In the following example we create a simple `macro_rules!` that computes
[Euclidean length](https://en.wikipedia.org/wiki/Euclidean_distance) of `n` [Euclidean length](https://en.wikipedia.org/wiki/Euclidean_distance) of `n`
dimensional vectors. Writing `norm!(x,1,2)` might be easier to express and more dimensional vectors. Writing `norm!(x,1,2)` might be easier to express and more
efficient than packing `x,1,2` into a `Vec` and calling a function computing efficient than packing `x,1,2` into a `Vec` and calling a function computing the
the length. length.
```rust ```rust
macro_rules! norm { macro_rules! norm {

@ -2,5 +2,5 @@
From [Wikipedia](https://en.wikipedia.org/wiki/Behavioral_pattern): From [Wikipedia](https://en.wikipedia.org/wiki/Behavioral_pattern):
> Design patterns that identify common communication patterns among objects. > Design patterns that identify common communication patterns among objects. By
> By doing so, these patterns increase flexibility in carrying out communication. > doing so, these patterns increase flexibility in carrying out communication.

@ -1,14 +1,14 @@
# Newtype # Newtype
What if in some cases we want a type to behave similar to another type or What if in some cases we want a type to behave similar to another type or
enforce some behaviour at compile time when using only type aliases would enforce some behaviour at compile time when using only type aliases would not be
not be enough? enough?
For example, if we want to create a custom `Display` implementation for `String` For example, if we want to create a custom `Display` implementation for `String`
due to security considerations (e.g. passwords). due to security considerations (e.g. passwords).
For such cases we could use the `Newtype` pattern to provide **type safety** For such cases we could use the `Newtype` pattern to provide **type safety** and
and **encapsulation**. **encapsulation**.
## Description ## Description
@ -66,7 +66,7 @@ field is private, which it is by default).
## Disadvantages ## Disadvantages
The downside of newtypes (especially compared with type aliases), is that there 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 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 wrapped type, and an impl for every trait you want to also be implemented for
the wrapper type. the wrapper type.
@ -76,7 +76,8 @@ the wrapper type.
Newtypes are very common in Rust code. Abstraction or representing units are the Newtypes are very common in Rust code. Abstraction or representing units are the
most common uses, but they can be used for other reasons: most common uses, but they can be used for other reasons:
- restricting functionality (reduce the functions exposed or traits implemented), - restricting functionality (reduce the functions exposed or traits
implemented),
- making a type with copy semantics have move semantics, - making a type with copy semantics have move semantics,
- abstraction by providing a more concrete type and thus hiding internal types, - abstraction by providing a more concrete type and thus hiding internal types,
e.g., e.g.,
@ -85,10 +86,10 @@ most common uses, but they can be used for other reasons:
pub struct Foo(Bar<T1, T2>); pub struct Foo(Bar<T1, T2>);
``` ```
Here, `Bar` might be some public, generic type and `T1` and `T2` are some internal Here, `Bar` might be some public, generic type and `T1` and `T2` are some
types. Users of our module shouldn't know that we implement `Foo` by using a `Bar`, internal types. Users of our module shouldn't know that we implement `Foo` by
but what we're really hiding here is the types `T1` and `T2`, and how they are used using a `Bar`, but what we're really hiding here is the types `T1` and `T2`, and
with `Bar`. how they are used with `Bar`.
## See also ## See also

@ -2,33 +2,36 @@
## Description ## Description
The [Strategy design pattern](https://en.wikipedia.org/wiki/Strategy_pattern) The [Strategy design pattern](https://en.wikipedia.org/wiki/Strategy_pattern) is
is a technique that enables separation of concerns. a technique that enables separation of concerns. It also allows to decouple
It also allows to decouple software modules through [Dependency Inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle). software modules through
[Dependency Inversion](https://en.wikipedia.org/wiki/Dependency_inversion_principle).
The basic idea behind the Strategy pattern is that, given an algorithm solving The basic idea behind the Strategy pattern is that, given an algorithm solving a
a particular problem, we define only the skeleton of the algorithm at an abstract particular problem, we define only the skeleton of the algorithm at an abstract
level, and we separate the specific algorithms implementation into different parts. level, and we separate the specific algorithms implementation into different
parts.
In this way, a client using the algorithm may choose a specific implementation, In this way, a client using the algorithm may choose a specific implementation,
while the general algorithm workflow remains the same. In other words, the abstract while the general algorithm workflow remains the same. In other words, the
specification of the class does not depend on the specific implementation of the abstract specification of the class does not depend on the specific
derived class, but specific implementation must adhere to the abstract specification. implementation of the derived class, but specific implementation must adhere to
This is why we call it "Dependency Inversion". the abstract specification. This is why we call it "Dependency Inversion".
## Motivation ## Motivation
Imagine we are working on a project that generates reports every month. Imagine we are working on a project that generates reports every month. We need
We need the reports to be generated in different formats (strategies), e.g., the reports to be generated in different formats (strategies), e.g., in `JSON`
in `JSON` or `Plain Text` formats. or `Plain Text` formats. But things vary over time, and we don't know what kind
But things vary over time, and we don't know what kind of requirement we may get of requirement we may get in the future. For example, we may need to generate
in the future. For example, we may need to generate our report in a completely new our report in a completely new format, or just modify one of the existing
format, or just modify one of the existing formats. formats.
## Example ## Example
In this example our invariants (or abstractions) are `Formatter` and `Report`, while `Text` and `Json` are our strategy structs. These strategies In this example our invariants (or abstractions) are `Formatter` and `Report`,
have to implement the `Formatter` trait. while `Text` and `Json` are our strategy structs. These strategies have to
implement the `Formatter` trait.
```rust ```rust
use std::collections::HashMap; use std::collections::HashMap;
@ -97,37 +100,38 @@ fn main() {
The main advantage is separation of concerns. For example, in this case `Report` The main advantage is separation of concerns. For example, in this case `Report`
does not know anything about specific implementations of `Json` and `Text`, does not know anything about specific implementations of `Json` and `Text`,
whereas the output implementations does not care about how data is preprocessed, whereas the output implementations does not care about how data is preprocessed,
stored, and fetched. The only thing they have to know is a specific stored, and fetched. The only thing they have to know is a specific trait to
trait to implement and its method defining the concrete algorithm implementation processing implement and its method defining the concrete algorithm implementation
the result, i.e., `Formatter` and `format(...)`. processing the result, i.e., `Formatter` and `format(...)`.
## Disadvantages ## Disadvantages
For each strategy there must be implemented at least one module, so number of modules For each strategy there must be implemented at least one module, so number of
increases with number of strategies. If there are many strategies to choose from modules increases with number of strategies. If there are many strategies to
then users have to know how strategies differ from one another. choose from then users have to know how strategies differ from one another.
## Discussion ## Discussion
In the previous example all strategies are implemented in a single file. In the previous example all strategies are implemented in a single file. Ways of
Ways of providing different strategies includes: providing different strategies includes:
- All in one file (as shown in this example, similar to being separated as modules) - All in one file (as shown in this example, similar to being separated as
modules)
- Separated as modules, E.g. `formatter::json` module, `formatter::text` module - Separated as modules, E.g. `formatter::json` module, `formatter::text` module
- Use compiler feature flags, E.g. `json` feature, `text` feature - Use compiler feature flags, E.g. `json` feature, `text` feature
- Separated as crates, E.g. `json` crate, `text` crate - Separated as crates, E.g. `json` crate, `text` crate
Serde crate is a good example of the `Strategy` pattern in action. Serde allows Serde crate is a good example of the `Strategy` pattern in action. Serde allows
[full customization](https://serde.rs/custom-serialization.html) of the serialization [full customization](https://serde.rs/custom-serialization.html) of the
behavior by manually implementing `Serialize` and `Deserialize` traits for our serialization behavior by manually implementing `Serialize` and `Deserialize`
type. For example, we could easily swap `serde_json` with `serde_cbor` since they traits for our type. For example, we could easily swap `serde_json` with
expose similar methods. Having this makes the helper crate `serde_transcode` much `serde_cbor` since they expose similar methods. Having this makes the helper
more useful and ergonomic. crate `serde_transcode` much more useful and ergonomic.
However, we don't need to use traits in order to design this pattern in Rust. However, we don't need to use traits in order to design this pattern in Rust.
The following toy example demonstrates the idea of the Strategy pattern using Rust The following toy example demonstrates the idea of the Strategy pattern using
`closures`: Rust `closures`:
```rust ```rust
struct Adder; struct Adder;

@ -7,8 +7,8 @@ collection of objects. It allows multiple different algorithms to be written
over the same data without having to modify the data (or their primary over the same data without having to modify the data (or their primary
behaviour). behaviour).
Furthermore, the visitor pattern allows separating the traversal of Furthermore, the visitor pattern allows separating the traversal of a collection
a collection of objects from the operations performed on each object. of objects from the operations performed on each object.
## Example ## Example
@ -72,9 +72,9 @@ to modify the AST data.
## Motivation ## Motivation
The visitor pattern is useful anywhere that you want to apply an algorithm to The visitor pattern is useful anywhere that you want to apply an algorithm to
heterogeneous data. If data is homogeneous, you can use an iterator-like pattern. heterogeneous data. If data is homogeneous, you can use an iterator-like
Using a visitor object (rather than a functional approach) allows the visitor to pattern. Using a visitor object (rather than a functional approach) allows the
be stateful and thus communicate information between nodes. visitor to be stateful and thus communicate information between nodes.
## Discussion ## Discussion
@ -109,5 +109,5 @@ The visitor pattern is a common pattern in most OO languages.
[Wikipedia article](https://en.wikipedia.org/wiki/Visitor_pattern) [Wikipedia article](https://en.wikipedia.org/wiki/Visitor_pattern)
The [fold](../creational/fold.md) pattern is similar to visitor but produces The [fold](../creational/fold.md) pattern is similar to visitor but produces a
a new version of the visited data structure. new version of the visited data structure.

@ -62,8 +62,8 @@ fn builder_test() {
## Motivation ## Motivation
Useful when you would otherwise require many constructors or where Useful when you would otherwise require many constructors or where construction
construction has side effects. has side effects.
## Advantages ## Advantages
@ -88,8 +88,9 @@ Rust than in C++, Java, or others.
This pattern is often used where the builder object is useful in its own right, This pattern is often used where the builder object is useful in its own right,
rather than being just a builder. For example, see rather than being just a builder. For example, see
[`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html) [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html)
is a builder for [`Child`](https://doc.rust-lang.org/std/process/struct.Child.html) is a builder for
(a process). In these cases, the `T` and `TBuilder` naming pattern is not used. [`Child`](https://doc.rust-lang.org/std/process/struct.Child.html) (a process).
In these cases, the `T` and `TBuilder` naming pattern is not used.
The example takes and returns the builder by value. It is often more ergonomic The example takes and returns the builder by value. It is often more ergonomic
(and more efficient) to take and return the builder as a mutable reference. The (and more efficient) to take and return the builder as a mutable reference. The
@ -108,8 +109,8 @@ as well as the `FooBuilder::new().a().b().build()` style.
## See also ## See also
- [Description in the style guide](https://web.archive.org/web/20210104103100/https://doc.rust-lang.org/1.12.0/style/ownership/builders.html) - [Description in the style guide](https://web.archive.org/web/20210104103100/https://doc.rust-lang.org/1.12.0/style/ownership/builders.html)
- [derive_builder](https://crates.io/crates/derive_builder), a crate for automatically - [derive_builder](https://crates.io/crates/derive_builder), a crate for
implementing this pattern while avoiding the boilerplate. automatically implementing this pattern while avoiding the boilerplate.
- [Constructor pattern](../../idioms/ctor.md) for when construction is simpler. - [Constructor pattern](../../idioms/ctor.md) for when construction is simpler.
- [Builder pattern (wikipedia)](https://en.wikipedia.org/wiki/Builder_pattern) - [Builder pattern (wikipedia)](https://en.wikipedia.org/wiki/Builder_pattern)
- [Construction of complex values](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder) - [Construction of complex values](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder)

@ -5,9 +5,9 @@
Run an algorithm over each item in a collection of data to create a new item, Run an algorithm over each item in a collection of data to create a new item,
thus creating a whole new collection. thus creating a whole new collection.
The etymology here is unclear to me. The terms 'fold' and 'folder' are used The etymology here is unclear to me. The terms 'fold' and 'folder' are used in
in the Rust compiler, although it appears to me to be more like a map than a the Rust compiler, although it appears to me to be more like a map than a fold
fold in the usual sense. See the discussion below for more details. in the usual sense. See the discussion below for more details.
## Example ## Example
@ -102,9 +102,9 @@ reused; however, a node must be cloned even if unchanged, which can be
expensive. expensive.
Using a reference counted pointer gives the best of both worlds - we can reuse Using a reference counted pointer gives the best of both worlds - we can reuse
the original data structure, and we don't need to clone unchanged nodes. However, the original data structure, and we don't need to clone unchanged nodes.
they are less ergonomic to use and mean that the data structures cannot be However, they are less ergonomic to use and mean that the data structures cannot
mutable. be mutable.
## See also ## See also

@ -2,7 +2,8 @@
From [Wikipedia](https://en.wikipedia.org/wiki/Creational_pattern): From [Wikipedia](https://en.wikipedia.org/wiki/Creational_pattern):
> Design patterns that deal with object creation mechanisms, trying to create objects > Design patterns that deal with object creation mechanisms, trying to create
> in a manner suitable to the situation. The basic form of object creation could > objects in a manner suitable to the situation. The basic form of object
> result in design problems or in added complexity to the design. Creational design > creation could result in design problems or in added complexity to the design.
> patterns solve this problem by somehow controlling this object creation. > Creational design patterns solve this problem by somehow controlling this
> object creation.

@ -5,46 +5,51 @@
When designing APIs in Rust which are exposed to other languages, there are some 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: important design principles which are contrary to normal Rust API design:
1. All Encapsulated types should be _owned_ by Rust, _managed_ by the user, 1. All Encapsulated types should be *owned* by Rust, *managed* by the user, and
and _opaque_. *opaque*.
2. All Transactional data types should be _owned_ by the user, and _transparent_. 2. All Transactional data types should be *owned* by the user, and
*transparent*.
3. All library behavior should be functions acting upon Encapsulated types. 3. All library behavior should be functions acting upon Encapsulated types.
4. All library behavior should be encapsulated into types not based on structure, 4. All library behavior should be encapsulated into types not based on
but _provenance/lifetime_. structure, but *provenance/lifetime*.
## Motivation ## Motivation
Rust has built-in FFI support to other languages. Rust has built-in FFI support to other languages. It does this by providing a
It does this by providing a way for crate authors to provide C-compatible APIs way for crate authors to provide C-compatible APIs through different ABIs
through different ABIs (though that is unimportant to this practice). (though that is unimportant to this practice).
Well-designed Rust FFI follows C API design principles, while compromising the Well-designed Rust FFI follows C API design principles, while compromising the
design in Rust as little as possible. There are three goals with any foreign API: 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. 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. 2. Avoid the API dictating internal unsafety on the Rust side as much as
3. Keep the potential for memory unsafety and Rust `undefined behaviour` as small possible.
as possible. 3. Keep the potential for memory unsafety and Rust `undefined behaviour` as
small as possible.
Rust code must trust the memory safety of the foreign language beyond a certain 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 point. However, every bit of `unsafe` code on the Rust side is an opportunity
bugs, or to exacerbate `undefined behaviour`. for bugs, or to exacerbate `undefined behaviour`.
For example, if a pointer provenance is wrong, that may be a segfault due to For example, if a pointer provenance is wrong, that may be a segfault due to
invalid memory access. But if it is manipulated by unsafe code, it could become invalid memory access. But if it is manipulated by unsafe code, it could become
full-blown heap corruption. full-blown heap corruption.
The Object-Based API design allows for writing shims that have good memory safety The Object-Based API design allows for writing shims that have good memory
characteristics, and a clean boundary of what is safe and what is `unsafe`. safety characteristics, and a clean boundary of what is safe and what is
`unsafe`.
## Code Example ## Code Example
The POSIX standard defines the API to access an on-file database, known as [DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/ndbm.h). The POSIX standard defines the API to access an on-file database, known as
[DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/ndbm.h).
It is an excellent example of an "object-based" API. It is an excellent example of an "object-based" API.
Here is the definition in C, which hopefully should be easy to read for those Here is the definition in C, which hopefully should be easy to read for those
involved in FFI. The commentary below should help explain it for those who involved in FFI. The commentary below should help explain it for those who miss
miss the subtleties. the subtleties.
```C ```C
struct DBM; struct DBM;
@ -63,50 +68,47 @@ int dbm_store(DBM *, datum, datum, int);
This API defines two types: `DBM` and `datum`. This API defines two types: `DBM` and `datum`.
The `DBM` type was called an "encapsulated" type above. The `DBM` type was called an "encapsulated" type above. It is designed to
It is designed to contain internal state, and acts as an entry point for the contain internal state, and acts as an entry point for the library's behavior.
library's behavior.
It is completely opaque to the user, who cannot create a `DBM` themselves since 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 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. This means all `DBM`s are "owned" by the library in a Rust sense. The internal
The internal state of unknown size is kept in memory controlled by the library, state of unknown size is kept in memory controlled by the library, not the user.
not the user. The user can only manage its life cycle with `open` and `close`, The user can only manage its life cycle with `open` and `close`, and perform
and perform operations on it with the other functions. operations on it with the other functions.
The `datum` type was called a "transactional" type above. The `datum` type was called a "transactional" type above. It is designed to
It is designed to facilitate the exchange of information between the library and facilitate the exchange of information between the library and its user.
its user.
The database is designed to store "unstructured data", with no pre-defined
The database is designed to store "unstructured data", with no pre-defined length length or meaning. As a result, the `datum` is the C equivalent of a Rust slice:
or meaning. As a result, the `datum` is the C equivalent of a Rust slice: a bunch a bunch of bytes, and a count of how many there are. The main difference is that
of bytes, and a count of how many there are. The main difference is that there is there is no type information, which is what `void` indicates.
no type information, which is what `void` indicates.
Keep in mind that this header is written from the library's point of view. The
Keep in mind that this header is written from the library's point of view. user likely has some type they are using, which has a known size. But the
The user likely has some type they are using, which has a known size. library does not care, and by the rules of C casting, any type behind a pointer
But the library does not care, and by the rules of C casting, any type behind a can be cast to `void`.
pointer can be cast to `void`.
As noted earlier, this type is *transparent* to the user. But also, this type is
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
_owned_ by the user. it. The question is, who owns the memory that pointer points to?
This has subtle ramifications, due to that pointer inside it.
The question is, who owns the memory that pointer points to? The answer for best memory safety is, "the user". But in cases such as
retrieving a value, the user does not know how to allocate it correctly (since
The answer for best memory safety is, "the user". they don't know how long the value is). In this case, the library code is
But in cases such as retrieving a value, the user does not know how to allocate expected to use the heap that the user has access to -- such as the C library
it correctly (since they don't know how long the value is). In this case, the library `malloc` and `free` -- and then *transfer ownership* in the Rust sense.
code is expected to use the heap that the user has access to -- such as the C library
`malloc` and `free` -- and then _transfer ownership_ in the Rust sense. This may all seem speculative, but this is what a pointer means in C. It means
the same thing as Rust: "user defined lifetime." The user of the library needs
This may all seem speculative, but this is what a pointer means in C. to read the documentation in order to use it correctly. That said, there are
It means the same thing as Rust: "user defined lifetime." some decisions that have fewer or greater consequences if users do it wrong.
The user of the library needs to read the documentation in order to use it correctly. Minimizing those are what this best practice is about, and the key is to
That said, there are some decisions that have fewer or greater consequences if users *transfer ownership of everything that is transparent*.
do it wrong. Minimizing those are what this best practice is about, and the key
is to _transfer ownership of everything that is transparent_.
## Advantages ## Advantages
@ -119,12 +121,12 @@ relatively small number:
3. The `dptr` on any `datum` must be `NULL`, or point to a valid slice of memory 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. In addition, it avoids a lot of pointer provenance issues. To understand why,
To understand why, let us consider an alternative in some depth: key iteration. let us consider an alternative in some depth: key iteration.
Rust is well known for its iterators. Rust is well known for its iterators. When implementing one, the programmer
When implementing one, the programmer makes a separate type with a bounded lifetime makes a separate type with a bounded lifetime to its owner, and implements the
to its owner, and implements the `Iterator` trait. `Iterator` trait.
Here is how iteration would be done in Rust for `DBM`: Here is how iteration would be done in Rust for `DBM`:
@ -144,8 +146,8 @@ struct DbmKeysIter<'it> {
impl<'it> Iterator for DbmKeysIter<'it> { ... } impl<'it> Iterator for DbmKeysIter<'it> { ... }
``` ```
This is clean, idiomatic, and safe. thanks to Rust's guarantees. This is clean, idiomatic, and safe. thanks to Rust's guarantees. However,
However, consider what a straightforward API translation would look like: consider what a straightforward API translation would look like:
```rust,ignore ```rust,ignore
#[no_mangle] #[no_mangle]
@ -166,9 +168,9 @@ pub extern "C" fn dbm_iter_del(*mut DbmKeysIter) {
``` ```
This API loses a key piece of information: the lifetime of the iterator must not This API loses a key piece of information: the lifetime of the iterator must not
exceed the lifetime of the `Dbm` object that owns it. A user of the library could exceed the lifetime of the `Dbm` object that owns it. A user of the library
use it in a way which causes the iterator to outlive the data it is iterating on, could use it in a way which causes the iterator to outlive the data it is
resulting in reading uninitialized memory. iterating on, resulting in reading uninitialized memory.
This example written in C contains a bug that will be explained afterwards: This example written in C contains a bug that will be explained afterwards:
@ -206,23 +208,23 @@ end-of-iteration marker:
2. The length is incremented, in this case by zero. 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 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. 4. The loop condition executes again, causing a `next` call on the closed
object.
The worst part about this bug? The worst part about this bug? If the Rust implementation was careful, this code
If the Rust implementation was careful, this code will work most of the time! will work most of the time! If the memory for the `Dbm` object is not
If the memory for the `Dbm` object is not immediately reused, an internal check immediately reused, an internal check will almost certainly fail, resulting in
will almost certainly fail, resulting in the iterator returning a `-1` indicating the iterator returning a `-1` indicating an error. But occasionally, it will
an error. But occasionally, it will cause a segmentation fault, or even worse, cause a segmentation fault, or even worse, nonsensical memory corruption!
nonsensical memory corruption!
None of this can be avoided by Rust. None of this can be avoided by Rust. From its perspective, it put those objects
From its perspective, it put those objects on its heap, returned pointers to them, on its heap, returned pointers to them, and gave up control of their lifetimes.
and gave up control of their lifetimes. The C code simply must "play nice". The C code simply must "play nice".
The programmer must read and understand the API documentation. The programmer must read and understand the API documentation. While some
While some consider that par for the course in C, a good API design can mitigate consider that par for the course in C, a good API design can mitigate this risk.
this risk. The POSIX API for `DBM` did this by _consolidating the ownership_ of The POSIX API for `DBM` did this by *consolidating the ownership* of the
the iterator with its parent: iterator with its parent:
```C ```C
datum dbm_firstkey(DBM *); datum dbm_firstkey(DBM *);
@ -236,25 +238,25 @@ Thus, all the lifetimes were bound together, and such unsafety was prevented.
However, this design choice also has a number of drawbacks, which should be However, this design choice also has a number of drawbacks, which should be
considered as well. considered as well.
First, the API itself becomes less expressive. First, the API itself becomes less expressive. With POSIX DBM, there is only one
With POSIX DBM, there is only one iterator per object, and every call changes iterator per object, and every call changes its state. This is much more
its state. This is much more restrictive than iterators in almost any language, restrictive than iterators in almost any language, even though it is safe.
even though it is safe. Perhaps with other related objects, whose lifetimes are Perhaps with other related objects, whose lifetimes are less hierarchical, this
less hierarchical, this limitation is more of a cost than the safety. limitation is more of a cost than the safety.
Second, depending on the relationships of the API's parts, significant design effort Second, depending on the relationships of the API's parts, significant design
may be involved. Many of the easier design points have other patterns associated effort may be involved. Many of the easier design points have other patterns
with them: associated with them:
- [Wrapper Type Consolidation](./wrappers.md) groups multiple Rust types together - [Wrapper Type Consolidation](./wrappers.md) groups multiple Rust types
into an opaque "object" together into an opaque "object"
- [FFI Error Passing](../../idioms/ffi/errors.md) explains error handling with integer - [FFI Error Passing](../../idioms/ffi/errors.md) explains error handling with
codes and sentinel return values (such as `NULL` pointers) integer codes and sentinel return values (such as `NULL` pointers)
- [Accepting Foreign Strings](../../idioms/ffi/accepting-strings.md) allows accepting - [Accepting Foreign Strings](../../idioms/ffi/accepting-strings.md) allows
strings with minimal unsafe code, and is easier to get right than accepting strings with minimal unsafe code, and is easier to get right than
[Passing Strings to FFI](../../idioms/ffi/passing-strings.md) [Passing Strings to FFI](../../idioms/ffi/passing-strings.md)
However, not every API can be done this way. However, not every API can be done this way. It is up to the best judgement of
It is up to the best judgement of the programmer as to who their audience is. the programmer as to who their audience is.

@ -1,13 +1,13 @@
# FFI Patterns # FFI Patterns
Writing FFI code is an entire course in itself. Writing FFI code is an entire course in itself. However, there are several
However, there are several idioms here that can act as pointers, and avoid traps idioms here that can act as pointers, and avoid traps for inexperienced users of
for inexperienced users of unsafe Rust. unsafe Rust.
This section contains design patterns that may be useful when doing FFI. 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, 1. [Object-Based API](./export.md) design that has good memory safety
and a clean boundary of what is safe and what is unsafe characteristics, and a clean boundary of what is safe and what is unsafe
2. [Type Consolidation into Wrappers](./wrappers.md) - group multiple Rust types 2. [Type Consolidation into Wrappers](./wrappers.md) - group multiple Rust types
together into an opaque "object" together into an opaque "object"

@ -5,33 +5,34 @@
This pattern is designed to allow gracefully handling multiple related types, This pattern is designed to allow gracefully handling multiple related types,
while minimizing the surface area for memory unsafety. while minimizing the surface area for memory unsafety.
One of the cornerstones of Rust's aliasing rules is lifetimes. One of the cornerstones of Rust's aliasing rules is lifetimes. This ensures that
This ensures that many patterns of access between types can be memory safe, many patterns of access between types can be memory safe, data race safety
data race safety included. included.
However, when Rust types are exported to other languages, they are usually transformed However, when Rust types are exported to other languages, they are usually
into pointers. In Rust, a pointer means "the user manages the lifetime of the pointee." transformed into pointers. In Rust, a pointer means "the user manages the
It is their responsibility to avoid memory unsafety. lifetime of the pointee." It is their responsibility to avoid memory unsafety.
Some level of trust in the user code is thus required, notably around use-after-free Some level of trust in the user code is thus required, notably around
which Rust can do nothing about. However, some API designs place higher burdens use-after-free which Rust can do nothing about. However, some API designs place
than others on the code written in the other language. higher burdens than others on the code written in the other language.
The lowest risk API is the "consolidated wrapper", where all possible interactions The lowest risk API is the "consolidated wrapper", where all possible
with an object are folded into a "wrapper type", while keeping the Rust API clean. interactions with an object are folded into a "wrapper type", while keeping the
Rust API clean.
## Code Example ## Code Example
To understand this, let us look at a classic example of an API to export: iteration To understand this, let us look at a classic example of an API to export:
through a collection. iteration through a collection.
That API looks like this: That API looks like this:
1. The iterator is initialized with `first_key`. 1. The iterator is initialized with `first_key`.
2. Each call to `next_key` will advance the iterator. 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. 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 4. As noted above, the iterator is "wrapped into" the collection (unlike the
Rust API). native Rust API).
If the iterator implements `nth()` efficiently, then it is possible to make it If the iterator implements `nth()` efficiently, then it is possible to make it
ephemeral to each function call: ephemeral to each function call:
@ -62,19 +63,19 @@ As a result, the wrapper is simple and contains no `unsafe` code.
## Advantages ## Advantages
This makes APIs safer to use, avoiding issues with lifetimes between types. This makes APIs safer to use, avoiding issues with lifetimes between types. See
See [Object-Based APIs](./export.md) for more on the advantages and pitfalls [Object-Based APIs](./export.md) for more on the advantages and pitfalls this
this avoids. avoids.
## Disadvantages ## Disadvantages
Often, wrapping types is quite difficult, and sometimes a Rust API compromise Often, wrapping types is quite difficult, and sometimes a Rust API compromise
would make things easier. would make things easier.
As an example, consider an iterator which does not efficiently implement `nth()`. As an example, consider an iterator which does not efficiently implement
It would definitely be worth putting in special logic to make the object handle `nth()`. It would definitely be worth putting in special logic to make the
iteration internally, or to support a different access pattern efficiently that object handle iteration internally, or to support a different access pattern
only the Foreign Function API will use. efficiently that only the Foreign Function API will use.
### Trying to Wrap Iterators (and Failing) ### Trying to Wrap Iterators (and Failing)
@ -82,9 +83,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, do what a C version of the code would do: erase the lifetime of the iterator,
and manage it manually. 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: A first version of `MySetWrapper` would look like this:
@ -97,14 +98,13 @@ struct MySetWrapper {
} }
``` ```
With `transmute` being used to extend a lifetime, and a pointer to hide it, With `transmute` being used to extend a lifetime, and a pointer to hide it, it's
it's ugly already. But it gets even worse: _any other operation can cause ugly already. But it gets even worse: *any other operation can cause Rust
Rust `undefined behaviour`_. `undefined behaviour`*.
Consider that the `MySet` in the wrapper could be manipulated by other Consider that the `MySet` in the wrapper could be manipulated by other functions
functions during iteration, such as storing a new value to the key it was during iteration, such as storing a new value to the key it was iterating over.
iterating over. The API doesn't discourage this, and in fact some similar C The API doesn't discourage this, and in fact some similar C libraries expect it.
libraries expect it.
A simple implementation of `myset_store` would be: A simple implementation of `myset_store` would be:
@ -134,29 +134,30 @@ pub mod unsafe_module {
} }
``` ```
If the iterator exists when this function is called, we have violated one of Rust's If the iterator exists when this function is called, we have violated one of
aliasing rules. According to Rust, the mutable reference in this block must have Rust's aliasing rules. According to Rust, the mutable reference in this block
_exclusive_ access to the object. If the iterator simply exists, it's not exclusive, must have *exclusive* access to the object. If the iterator simply exists, it's
so we have `undefined behaviour`! [^1] not exclusive, so we have `undefined behaviour`! [^1]
To avoid this, we must have a way of ensuring that mutable reference really is exclusive. To avoid this, we must have a way of ensuring that mutable reference really is
That basically means clearing out the iterator's shared reference while it exists, exclusive. That basically means clearing out the iterator's shared reference
and then reconstructing it. In most cases, that will still be less efficient than while it exists, and then reconstructing it. In most cases, that will still be
the C version. less efficient than the C version.
Some may ask: how can C do this more efficiently? Some may ask: how can C do this more efficiently? The answer is, it cheats.
The answer is, it cheats. Rust's aliasing rules are the problem, and C simply ignores Rust's aliasing rules are the problem, and C simply ignores them for its
them for its pointers. In exchange, it is common to see code that is declared pointers. In exchange, it is common to see code that is declared in the manual
in the manual as "not thread safe" under some or all circumstances. In fact, as "not thread safe" under some or all circumstances. In fact, the
the [GNU C library](https://manpages.debian.org/buster/manpages/attributes.7.en.html) [GNU C library](https://manpages.debian.org/buster/manpages/attributes.7.en.html)
has an entire lexicon dedicated to concurrent behavior! has an entire lexicon dedicated to concurrent behavior!
Rust would rather make everything memory safe all the time, for both safety and Rust would rather make everything memory safe all the time, for both safety and
optimizations that C code cannot attain. Being denied access to certain shortcuts optimizations that C code cannot attain. Being denied access to certain
is the price Rust programmers need to pay. shortcuts is the price Rust programmers need to pay.
[^1]: For the C programmers out there scratching their heads, the iterator need [^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 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 compiler optimizations which may cause inconsistent observations by the
shared reference (e.g. stack spills or reordering instructions for efficiency). iterator's shared reference (e.g. stack spills or reordering instructions for
These observations may happen _any time after_ the mutable reference is created. efficiency). These observations may happen *any time after* the mutable
reference is created.

@ -14,15 +14,17 @@ about a programming language.
## Design patterns in Rust ## Design patterns in Rust
Rust has many unique features. These features give us great benefit by removing Rust has many unique features. These features give us great benefit by removing
whole classes of problems. Some of them are also patterns that are _unique_ to Rust. whole classes of problems. Some of them are also patterns that are *unique* to
Rust.
## YAGNI ## YAGNI
YAGNI is an acronym that stands for `You Aren't Going to Need It`. YAGNI is an acronym that stands for `You Aren't Going to Need It`. It's a vital
It's a vital software design principle to apply as you write code. software design principle to apply as you write code.
> The best code I ever wrote was code I never wrote. > The best code I ever wrote was code I never wrote.
If we apply YAGNI to design patterns, we see that the features of Rust allow us to If we apply YAGNI to design patterns, we see that the features of Rust allow us
throw out many patterns. For instance, there is no need for the [strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern) to throw out many patterns. For instance, there is no need for the
in Rust because we can just use [traits](https://doc.rust-lang.org/book/traits.html). [strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern) in Rust
because we can just use [traits](https://doc.rust-lang.org/book/traits.html).

@ -2,5 +2,5 @@
From [Wikipedia](https://en.wikipedia.org/wiki/Structural_pattern): From [Wikipedia](https://en.wikipedia.org/wiki/Structural_pattern):
> Design patterns that ease the design by identifying a simple way to realize relationships > Design patterns that ease the design by identifying a simple way to realize
> among entities. > relationships among entities.

@ -5,27 +5,27 @@
Prefer small crates that do one thing well. Prefer small crates that do one thing well.
Cargo and crates.io make it easy to add third-party libraries, much more so than Cargo and crates.io make it easy to add third-party libraries, much more so than
in say C or C++. Moreover, since packages on crates.io cannot be edited or removed in say C or C++. Moreover, since packages on crates.io cannot be edited or
after publication, any build that works now should continue to work in the future. removed after publication, any build that works now should continue to work in
We should take advantage of this tooling, and use smaller, more fine-grained dependencies. the future. We should take advantage of this tooling, and use smaller, more
fine-grained dependencies.
## Advantages ## Advantages
- Small crates are easier to understand, and encourage more modular code. - Small crates are easier to understand, and encourage more modular code.
- Crates allow for re-using code between projects. - Crates allow for re-using code between projects. For example, the `url` crate
For example, the `url` crate was developed as part of the Servo browser engine, was developed as part of the Servo browser engine, but has since found wide
but has since found wide use outside the project. use outside the project.
- Since the compilation unit - Since the compilation unit of Rust is the crate, splitting a project into
of Rust is the crate, splitting a project into multiple crates can allow more of multiple crates can allow more of the code to be built in parallel.
the code to be built in parallel.
## Disadvantages ## 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
versions of a crate at the same time. For example, the `url` crate has both versions conflicting versions of a crate at the same time. For example, the `url` crate
1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url` from `url:0.5` are has both versions 1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url`
different types, an HTTP client that uses `url:0.5` would not accept `Url` values from `url:0.5` are different types, an HTTP client that uses `url:0.5` would
from a web scraper that uses `url:1.0`. 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. 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

@ -6,7 +6,8 @@ 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 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 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 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. that call directly into the unsafe code. Users may use this to gain speed
benefits.
## Advantages ## Advantages
@ -21,13 +22,13 @@ that call directly into the unsafe code. Users may use this to gain speed benefi
## Examples ## Examples
- The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe operations - The [`toolshed`](https://docs.rs/toolshed) crate contains its unsafe
in submodules, presenting a safe interface to users. operations in submodules, presenting a safe interface to users.
- `std`'s `String` class is a wrapper over `Vec<u8>` with the added invariant - `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 that the contents must be valid UTF-8. The operations on `String` ensure this
behavior. behavior. However, users have the option of using an `unsafe` method to create
However, users have the option of using an `unsafe` method to create a `String`, a `String`, in which case the onus is on them to guarantee the validity of the
in which case the onus is on them to guarantee the validity of the contents. contents.
## See also ## See also

@ -1,12 +1,13 @@
# Refactoring # Refactoring
Refactoring is very important in relation to these topics. Refactoring is very important in relation to these topics. Just as important as
Just as important as the other topics covered here, is how to take good code and the other topics covered here, is how to take good code and turn it into great
turn it into great code. code.
We can use [design patterns](../patterns/index.md) to [DRY] up code and generalize We can use [design patterns](../patterns/index.md) to [DRY] up code and
abstractions. We must avoid [anti-patterns](../anti_patterns/index.md) while we generalize abstractions. We must avoid
do this. While they may be tempting to employ, their costs outweigh their benefits. [anti-patterns](../anti_patterns/index.md) while we do this. While they may be
tempting to employ, their costs outweigh their benefits.
> Shortcuts make for long days. > Shortcuts make for long days.

@ -1,7 +1,9 @@
# Translations # Translations
We are utilizing [mdbook-i18n-helper](https://github.com/google/mdbook-i18n-helpers). We are utilizing
Please read up on how to _add_ and _update_ translations in [their repository](https://github.com/google/mdbook-i18n-helpers#creating-and-updating-translations) [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 ## External translations

@ -12,8 +12,8 @@ A short, prose description of the pattern.
``` ```
When writing examples, please try to make them compile. This allows us to test When writing examples, please try to make them compile. This allows us to test
them. If you fail to write an example that is both complete and readable, them. If you fail to write an example that is both complete and readable, please
please at least mark your example code with `ignore` as in here: at least mark your example code with `ignore` as in here:
```rust,ignore ```rust,ignore
// A non-runnable example of the pattern in action, should be mostly code, commented // A non-runnable example of the pattern in action, should be mostly code, commented

Loading…
Cancel
Save