Skip to content

Commit b61bee8

Browse files
authored
Merge branch 'master' into catch_panic_anti_pattern
2 parents 8871ccb + a0e7651 commit b61bee8

13 files changed

+157
-74
lines changed

.github/workflows/ci.yml

+1-6
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,10 @@ jobs:
4242

4343
- run: mdbook test
4444

45-
lint:
45+
markdown-lint:
4646
runs-on: ubuntu-20.04
4747
steps:
4848
- uses: actions/checkout@v2
49-
- name: Lint all files in current directory
50-
uses: avto-dev/markdown-lint@v1
51-
with:
52-
config: '.markdownlint.yaml'
53-
args: '*.md'
5449
- name: Lint all files recursively
5550
uses: avto-dev/markdown-lint@v1
5651
with:

.markdownlint.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
MD004: false
32
MD010:
43
code_blocks: false
5-
MD013: false
4+
MD013:
5+
line_length: 300

CONTRIBUTING.md

+30-9
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ and you think it may not be appropriate to file an issue open a discussion in ou
1717

1818
## Writing a new article
1919

20-
Before writing a new article please check our [issues](https://github.com/rust-unofficial/patterns/issues) and
21-
the [Pull Requests](https://github.com/rust-unofficial/patterns/pulls) if there are existing issues or someone
22-
is working on that topic.
20+
Before writing a new article please check in one of the following resources if there is an existing discussion or if someone is already working on that topic:
21+
22+
- [Umbrella issue](https://github.com/rust-unofficial/patterns/issues/116),
23+
- [All issues](https://github.com/rust-unofficial/patterns/issues),
24+
- [Pull Requests](https://github.com/rust-unofficial/patterns/pulls)
2325

2426
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)
2527
please open a new issue, so we can discuss about the ideas and future content of the article together and maybe
@@ -40,6 +42,30 @@ Don't forget to add your new article to the `SUMMARY.md` to let it be rendered t
4042

4143
Please make `Draft Pull requests` early so we can follow your progress and can give early feedback (see the following section).
4244

45+
## Check the article locally
46+
47+
Before submitting the PR launch the commands `mdbook build` to make sure that the book builds and `mdbook test` to make sure that
48+
code examples are correct.
49+
50+
### Markdown lint
51+
52+
To make sure the files comply with our Markdown style we use [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli).
53+
To spare you some manual work to get through the CI test you can use the following commands to automatically fix most of the emerging problems when writing Markdown files.
54+
55+
- Install:
56+
57+
```sh
58+
npm install -g markdownlint-cli
59+
```
60+
61+
- Check all markdown files:
62+
- unix: `markdownlint '**/*.md'`
63+
- windows: `markdownlint **/*.md`
64+
65+
- Automatically fix basic errors:
66+
- unix: `markdownlint -f '**/*.md'`
67+
- windows: `markdownlint -f **/*.md`
68+
4369
## Creating a Pull Request
4470

4571
"Release early and often!" also applies to pull requests!
@@ -49,13 +75,8 @@ Early reviews of the community are not meant as an offense but to give feedback.
4975

5076
A good principle: "Work together, share ideas, teach others."
5177

52-
### Test the book locally before submitting
53-
54-
Before submitting the PR launch the commands `mdbook build` to make sure that the book builds and `mdbook test` to make sure that
55-
code examples are correct.
56-
5778
### Important Note
5879

59-
Please **don't force push** your branch to keep commit history and make it easier of us to see changes between reviews.
80+
Please **don't force push** commits in your branch, in order to keep commit history and make it easier for us to see changes between reviews.
6081

6182
Make sure to `Allow edits of maintainers` (under the text box) in the PR so people can actually collaborate on things or fix smaller issues themselves.

README.md

+3-35
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,14 @@
33
An open source book about design patterns and idioms in the Rust programming
44
language that you can read [here](https://rust-unofficial.github.io/patterns/).
55

6-
## TODOs
7-
8-
### Idioms
9-
10-
* TODO stability for extensibility
11-
* TODO trait to separate visibility of methods from visibility of data (<https://github.com/sfackler/rust-postgres/blob/v0.9.6/src/lib.rs#L1400>)
12-
* TODO leak amplification ("Vec::drain sets the Vec's len to 0 prematurely so that mem::forgetting Drain "only" mem::forgets more stuff. instead of exposing uninitialized memory or having to update the len on every iteration")
13-
* TODO interior mutability - UnsafeCell, Cell, RefCell
14-
15-
### Design patterns
16-
17-
* TODO iterators (to safely avoid bounds checks)
18-
* TODO closures and lifetimes (coupling to lifetime)
19-
* TODO platform-specific sub-modules (<https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md#platform-specific-opt-in>)
20-
* TODO Module organisation (by looking at examples such as Rusts `libstd`, and how it integrated into the Rusts source code, lessons can be learned about ergonomic project management and API design. Closely assosciated with platform-specific sub-modules)
21-
* [Entry API](patterns/entry.md) (Currently just a boilerplate)
22-
* TODO extension traits
23-
* TODO destructor bombs (ensure linear typing dynamically, e.g., <https://github.com/Munksgaard/session-types/commit/0f25ccb7c3bc9f65fa8eaf538233e8fe344a189a>)
24-
* TODO convertible to Foo trait for more generic generics (e.g., <http://static.rust-lang.org/doc/master/std/fs/struct.File.html#method.open>)
25-
* [Late bound bounds](patterns/late-bounds.md) (Currently just a boilerplate)
26-
* TODO 'shadow' borrowed version of struct - e.g., double buffering, Niko's parser generator
27-
* TODO composition of structs to please the borrow checker
28-
* TODO `Error` traits and `Result` forwarding
29-
* TODO graphs
30-
31-
### Anti-patterns
32-
33-
* [catch_unwind for exceptions](anti_patterns/catch_panic.md)
34-
* TODO Clone to satisfy the borrow checker
35-
* TODO Matching all fields of a struct (back compat)
36-
* TODO wildcard matches
37-
* TODO taking an enum rather than having multiple functions
38-
* TODO `unwrap()`ing every `Result` instead of forwarding it
39-
406
## Contributing
417

428
You are missing content in this repository that can be helpful for others and you are eager to explain it?
439
Awesome! We are always happy about new contributions (e.g. elaboration or corrections on certain topics) to this project.
4410

45-
We suggest reading our [Contribution guide](./CONTRIBUTING.md) to get more information on how it works.
11+
You can check the [Umbrella issue](https://github.com/rust-unofficial/patterns/issues/116) for all the patterns, anti-patterns, and idioms that could be added.
12+
13+
We suggest reading our [Contribution guide](./CONTRIBUTING.md) to get more information on how contributing to this repository works.
4614

4715
## Building with mdbook
4816

SUMMARY.md

+2
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,5 @@
4343
- [Functional Programming](./functional/index.md)
4444

4545
- [Additional Resources](./additional_resources.md)
46+
47+
- [Design principles](./design-principles.md)

design-principles.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Design principles
2+
3+
## A brief overview over common design principles
4+
5+
---
6+
7+
## [SOLID](https://en.wikipedia.org/wiki/SOLID)
8+
9+
- [Single Responsibility Principle (SRP)](https://en.wikipedia.org/wiki/Single-responsibility_principle):
10+
A class should only have a single responsibility, that is, only changes to one part of the software's
11+
specification should be able to affect the specification of the class.
12+
- [Open/Closed Principle (OCP)](https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle):
13+
"Software entities ... should be open for extension, but closed for modification."
14+
- [Liscov Substitution Principle (LSP)](https://en.wikipedia.org/wiki/Liskov_substitution_principle):
15+
"Objects in a program should be replaceable with instances of their subtypes without altering the correctness
16+
of that program."
17+
- [Interface Segregation Principle (ISP)](https://en.wikipedia.org/wiki/Interface_segregation_principle):
18+
"Many client-specific interfaces are better than one general-purpose interface."
19+
- [Dependency Inversion Principle (DIP)](https://en.wikipedia.org/wiki/Dependency_inversion_principle):
20+
One should "depend upon abstractions, [not] concretions."
21+
22+
## [DRY (Don’t Repeat Yourself)](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself)
23+
24+
"Every piece of knowledge must have a single, unambiguous, authoritative representation within a system"
25+
26+
## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle)
27+
28+
most systems work best if they are kept simple rather than made complicated; therefore, simplicity should be a key goal in design, and unnecessary complexity should be avoided
29+
30+
## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter)
31+
32+
a given object should assume as little as possible about the structure or properties of anything else (including its subcomponents), in accordance with the principle of "information hiding"
33+
34+
## [Design by contract (DbC)](https://en.wikipedia.org/wiki/Design_by_contract)
35+
36+
software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants
37+
38+
## [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))
39+
40+
bundling of data with the methods that operate on that data, or the restricting of direct access to some of an object's components. Encapsulation is used to hide the values or state of a structured data object inside a class, preventing unauthorized parties' direct access to them.
41+
42+
## [Command-Query-Separation(CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation)
43+
44+
“Functions should not produce abstract side effects...only commands (procedures) will be permitted to produce side effects.” - Bertrand Meyer: Object Oriented Software Construction
45+
46+
## [Principle of least astonishment (POLA)](https://en.wikipedia.org/wiki/Principle_of_least_astonishment)
47+
48+
a component of a system should behave in a way that most users will expect it to behave. The behavior should not astonish or surprise users
49+
50+
## Linguistic-Modular-Units
51+
52+
“Modules must correspond to syntactic units in the language used.” - Bertrand Meyer: Object Oriented Software Construction
53+
54+
## Self-Documentation
55+
56+
“The designer of a module should strive to make all information about the module part of the module itself.” - Bertrand Meyer: Object Oriented Software Construction
57+
58+
## Uniform-Access
59+
60+
“All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.” - Bertrand Meyer: Object Oriented Software Construction
61+
62+
## Single-Choice
63+
64+
“Whenever a software system must support a set of alternatives, one and only one module in the system should know their exhaustive list.” - Bertrand Meyer: Object Oriented Software Construction
65+
66+
## Persistence-Closure
67+
68+
“Whenever a storage mechanism stores an object, it must store with it the dependents of that object. Whenever a retrieval mechanism retrieves a previously stored object, it must also retrieve any dependent of that object that has not yet been retrieved.” - Bertrand Meyer: Object Oriented Software Construction

functional/index.md

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
# Functional Usage of Rust
22

3-
Rust is an imperative language, but it follows many functional programming paradigms. One of the biggest hurdles to understanding functional programs when coming from an imperative background is the shift in thinking. Imperative programs describe __how__ to do something, whereas declarative programs describe __what__ to do. Let's sum the numbers from 1 to 10 to show this.
3+
Rust is an imperative language, but it follows many functional programming paradigms.
4+
One of the biggest hurdles to understanding functional programs when coming from an imperative background is the shift in thinking.
5+
Imperative programs describe __how__ to do something, whereas declarative programs describe __what__ to do.
6+
Let's sum the numbers from 1 to 10 to show this.
47

58
## Imperative
69

710
```rust
811
let mut sum = 0;
912
for i in 1..11 {
10-
sum += i;
13+
sum += i;
1114
}
1215
println!("{}", sum);
1316
```
1417

15-
With imperative programs, we have to play compiler to see what is happening. Here, we start with a `sum` of `0`. Next, we iterate through the range from 1 to 10. Each time through the loop, we add the corresponding value in the range. Then we print it out.
18+
With imperative programs, we have to play compiler to see what is happening.
19+
Here, we start with a `sum` of `0`.
20+
Next, we iterate through the range from 1 to 10.
21+
Each time through the loop, we add the corresponding value in the range.
22+
Then we print it out.
1623

1724
| `i` | `sum` |
1825
|:---:|:-----:|
@@ -35,9 +42,14 @@ This is how most of us start out programming. We learn that a program is a set o
3542
println!("{}", (1..11).fold(0, |a, b| a + b));
3643
```
3744

38-
Whoa! This is really different! What's going on here? Remember that with declarative programs we are describing __what__ to do, rather than __how__ to do it. `fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition) functions. The name is a convention from Haskell.
45+
Whoa! This is really different! What's going on here? Remember that with declarative programs we are describing __what__ to do, rather than __how__ to do it.
46+
`fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition) functions. The name is a convention from Haskell.
3947

40-
Here, we are composing functions of addition (this closure: `|a, b| a + b)`) with a range from 1 to 10. The `0` is the starting point, so `a` is `0` at first. `b` is the first element of the range, `1`. `0 + 1 = 1` is the result. So now we `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the next result. This process continues until we get to the last element in the range, `10`.
48+
Here, we are composing functions of addition (this closure: `|a, b| a + b)`) with a range from 1 to 10.
49+
The `0` is the starting point, so `a` is `0` at first.
50+
`b` is the first element of the range, `1`. `0 + 1 = 1` is the result.
51+
So now we `fold` again, with `a = 1`, `b = 2` and so `1 + 2 = 3` is the next result.
52+
This process continues until we get to the last element in the range, `10`.
4153

4254
| `a` | `b` | result |
4355
|:---:|:---:|:------:|

idioms/pass-var-to-closure.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ let num3 = Rc::new(3);
4040
let num2_cloned = num2.clone();
4141
let num3_borrowed = num3.as_ref();
4242
let closure = move || {
43-
*num1 + *num2_cloned + *num3_borrowed;
43+
*num1 + *num2_cloned + *num3_borrowed;
4444
};
4545
```
4646

idioms/priv-extend.md

+12-5
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,15 @@ fn main(s: a::S) {
2727

2828
## Discussion
2929

30-
Adding a field to a struct is a mostly backwards compatible change. However, if a client uses a pattern to deconstruct a struct instance, they might name all the fields in the struct and adding a new one would break that pattern. The client could name some of the fields and use `..` in the pattern, in which case adding another field is backwards compatible. Making at least one of the struct's fields private forces clients to use the latter form of patterns, ensuring that the struct is future-proof.
31-
32-
The downside of this approach is that you might need to add an otherwise unneeded field to the struct. You can use the `()` type so that there is no runtime overhead and prepend `_` to the field name to avoid the unused field warning.
33-
34-
If Rust allowed private variants of enums, we could use the same trick to make adding a variant to an enum backwards compatible. The problem there is exhaustive match expressions. A private variant would force clients to have a `_` wildcard pattern.
30+
Adding a field to a struct is a mostly backwards compatible change.
31+
However, if a client uses a pattern to deconstruct a struct instance, they might name all the fields in the struct and adding a new one would break that pattern.
32+
The client could name some of the fields and use `..` in the pattern, in which case adding another field is backwards compatible.
33+
Making at least one of the struct's fields private forces clients to use the latter form of patterns, ensuring that the struct is future-proof.
34+
35+
The downside of this approach is that you might need to add an otherwise unneeded field to the struct.
36+
You can use the `()` type so that there is no runtime overhead and prepend `_` to the field name to avoid the unused field warning.
37+
38+
If Rust allowed private variants of enums, we could use the same trick to make adding a variant to an enum backwards compatible.
39+
The problem there is exhaustive match expressions.
40+
A private variant would force clients to have a `_` wildcard pattern.
41+
A common way to implement this instead is using the [#[non_exhaustive]](https://doc.rust-lang.org/reference/attributes/type_system.html) attribute.

idioms/temporary-mutability.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ Using nested block:
1515

1616
```rust,ignore
1717
let data = {
18-
let mut data = get_vec();
19-
data.sort();
20-
data
18+
let mut data = get_vec();
19+
data.sort();
20+
data
2121
};
2222
2323
// Here `data` is immutable.

patterns/newtype.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@ the wrapper type.
8686
Newtypes are very common in Rust code. Abstraction or representing units are the
8787
most common uses, but they can be used for other reasons:
8888

89-
* restricting functionality (reduce the functions exposed or traits implemented),
90-
* making a type with copy semantics have move semantics,
91-
* abstraction by providing a more concrete type and thus hiding internal types, e.g.,
89+
- restricting functionality (reduce the functions exposed or traits implemented),
90+
- making a type with copy semantics have move semantics,
91+
- abstraction by providing a more concrete type and thus hiding internal types, e.g.,
9292

9393
```rust,ignore
9494
pub struct Foo(Bar<T1, T2>);

patterns/small-crates.md

+8-3
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@
44

55
Prefer small crates that do one thing well.
66

7-
Cargo and crates.io make it easy to add third-party libraries, much more so than in say C or C++. Moreover, since packages on crates.io cannot be edited or removed after publication, any build that works now should continue to work in the future. We should take advantage of this tooling, and use smaller, more fine-grained dependencies.
7+
Cargo and crates.io make it easy to add third-party libraries, much more so than in say C or C++.
8+
Moreover, since packages on crates.io cannot be edited or removed after publication, any build that works now should continue to work in the future.
9+
We should take advantage of this tooling, and use smaller, more fine-grained dependencies.
810

911
## Advantages
1012

1113
* Small crates are easier to understand, and encourage more modular code.
12-
* Crates allow for re-using code between projects. For example, the `url` crate was developed as part of the Servo browser engine, but has since found wide use outside the project.
14+
* Crates allow for re-using code between projects.
15+
For example, the `url` crate was developed as part of the Servo browser engine, but has since found wide use outside the project.
1316
* Since the compilation unit of Rust is the crate, splitting a project into multiple crates can allow more of the code to be built in parallel.
1417

1518
## Disadvantages
1619

17-
* This can lead to "dependency hell", when a project depends on multiple conflicting versions of a crate at the same time. For example, the `url` crate has both versions 1.0 and 0.5. Since the `Url` from `url:1.0` and the `Url` from `url:0.5` are different types, an HTTP client that uses `url:0.5` would not accept `Url` values from a web scraper that uses `url:1.0`.
20+
* This can lead to "dependency hell", when a project depends on multiple conflicting versions of a crate at the same time.
21+
For example, the `url` crate has both versions 1.0 and 0.5.
22+
Since the `Url` from `url:1.0` and the `Url` from `url:0.5` are different types, an HTTP client that uses `url:0.5` would not accept `Url` values from a web scraper that uses `url:1.0`.
1823
* Packages on crates.io are not curated. A crate may be poorly written, have unhelpful documentation, or be outright malicious.
1924
* Two small crates may be less optimized than one large one, since the compiler does not perform link-time optimization (LTO) by default.
2025

0 commit comments

Comments
 (0)