Fix lines to 150 line length for better tracking of future changes (#189)

pull/193/head
simonsan 3 years ago committed by GitHub
parent 868cd2f3f4
commit f61f2f40f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,7 +5,7 @@ MD003:
# Set maximum line length
MD013:
line_length: 300
line_length: 150
# Use `---` for horizontal rule
MD035:

@ -17,7 +17,8 @@ and you think it may not be appropriate to file an issue open a discussion in ou
## Writing a new article
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:
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:
- [Umbrella issue](https://github.com/rust-unofficial/patterns/issues/116),
- [All issues](https://github.com/rust-unofficial/patterns/issues),
@ -50,7 +51,8 @@ code examples are correct.
### Markdown lint
To make sure the files comply with our Markdown style we use [markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli).
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.
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.
- Install:

@ -8,7 +8,8 @@ language that you can read [here](https://rust-unofficial.github.io/patterns/).
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 (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) for all the patterns, anti-patterns, and idioms that could be added.
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.
We suggest reading our [Contribution guide](./CONTRIBUTING.md) to get more information on how contributing to this repository works.

@ -25,44 +25,62 @@
## [KISS principle](https://en.wikipedia.org/wiki/KISS_principle)
most systems work best if they are kept simple rather than made complicated; therefore, simplicity should be a key goal in design, and unnecessary complexity should be avoided
most systems work best if they are kept simple rather than made complicated; therefore,
simplicity should be a key goal in design, and unnecessary complexity should be avoided
## [Law of Demeter (LoD)](https://en.wikipedia.org/wiki/Law_of_Demeter)
a given object should assume as little as possible about the structure or properties of anything else (including its subcomponents), in accordance with the principle of "information hiding"
a given object should assume as little as possible about the structure or properties of
anything else (including its subcomponents), in accordance with the principle of "information hiding"
## [Design by contract (DbC)](https://en.wikipedia.org/wiki/Design_by_contract)
software designers should define formal, precise and verifiable interface specifications for software components, which extend the ordinary definition of abstract data types with preconditions, postconditions and invariants
software designers should define formal, precise and verifiable interface specifications
for software components, which extend the ordinary definition of abstract data types with
preconditions, postconditions and invariants
## [Encapsulation](https://en.wikipedia.org/wiki/Encapsulation_(computer_programming))
bundling of data with the methods that operate on that data, or the restricting of direct access to some of an object's components. Encapsulation is used to hide the values or state of a structured data object inside a class, preventing unauthorized parties' direct access to them.
bundling of data with the methods that operate on that data, or the restricting of
direct access to some of an object's components. Encapsulation is used to hide the
values or state of a structured data object inside a class, preventing unauthorized
parties' direct access to them.
## [Command-Query-Separation(CQS)](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation)
“Functions should not produce abstract side effects...only commands (procedures) will be permitted to produce side effects.” - Bertrand Meyer: Object Oriented Software Construction
“Functions should not produce abstract side effects...only commands (procedures) will
be permitted to produce side effects.” - Bertrand Meyer: Object Oriented Software Construction
## [Principle of least astonishment (POLA)](https://en.wikipedia.org/wiki/Principle_of_least_astonishment)
a component of a system should behave in a way that most users will expect it to behave. The behavior should not astonish or surprise users
a component of a system should behave in a way that most users will expect it to behave.
The behavior should not astonish or surprise users
## Linguistic-Modular-Units
“Modules must correspond to syntactic units in the language used.” - Bertrand Meyer: Object Oriented Software Construction
“Modules must correspond to syntactic units in the language used.” - Bertrand Meyer:
Object Oriented Software Construction
## Self-Documentation
“The designer of a module should strive to make all information about the module part of the module itself.” - Bertrand Meyer: Object Oriented Software Construction
“The designer of a module should strive to make all information about the module part of
the module itself.” - Bertrand Meyer: Object Oriented Software Construction
## Uniform-Access
“All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation.” - Bertrand Meyer: Object Oriented Software Construction
“All services offered by a module should be available through a uniform notation,
which does not betray whether they are implemented through storage or through computation.” -
Bertrand Meyer: Object Oriented Software Construction
## Single-Choice
“Whenever a software system must support a set of alternatives, one and only one module in the system should know their exhaustive list.” - Bertrand Meyer: Object Oriented Software Construction
“Whenever a software system must support a set of alternatives, one and only one module
in the system should know their exhaustive list.” - Bertrand Meyer: Object Oriented
Software Construction
## Persistence-Closure
“Whenever a storage mechanism stores an object, it must store with it the dependents of that object. Whenever a retrieval mechanism retrieves a previously stored object, it must also retrieve any dependent of that object that has not yet been retrieved.” - Bertrand Meyer: Object Oriented Software Construction
“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

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

@ -42,7 +42,8 @@ This is how most of us start out programming. We learn that a program is a set o
println!("{}", (1..11).fold(0, |a, b| a + b));
```
Whoa! This is really different! What's going on here? Remember that with declarative programs we are describing __what__ to do, rather than __how__ to do it.
Whoa! This is really different! What's going on here? Remember that with declarative programs
we are describing __what__ to do, rather than __how__ to do it.
`fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition) functions. The name is a convention from Haskell.
Here, we are composing functions of addition (this closure: `|a, b| a + b)`) with a range from 1 to 10.

@ -5,14 +5,18 @@
Using a target of a deref coercion can increase the flexibility of your code when you are deciding which argument type to use for a function argument.
In this way, the function will accept more input types.
This is not limited to slice-able or fat pointer types. In fact you should always prefer using the __borrowed type__ over __borrowing the owned type__. E.g., `&str` over `&String`, `&[T]` over `&Vec<T>`, or `&T` over `&Box<T>`.
This is not limited to slice-able or fat pointer types.
In fact you should always prefer using the __borrowed type__ over __borrowing the owned type__.
Such as `&str` over `&String`, `&[T]` over `&Vec<T>`, or `&T` over `&Box<T>`.
Using borrowed types you can avoid layers of indirection for those instances where the owned type already provides a layer of indirection. For instance, a `String` has a layer of indirection, so a `&String` will have two layers of indrection.
Using borrowed types you can avoid layers of indirection for those instances where the owned type already provides a layer of indirection.
For instance, a `String` has a layer of indirection, so a `&String` will have two layers of indrection.
We can avoid this by using `&str` instead, and letting `&String` coerce to a `&str` whenever the function is invoked.
## Example
For this example, we will illustrate some differences for using `&String` as a function argument versus using a `&str`, but the ideas apply as well to using `&Vec<T>` versus using a `&[T]` or using a `&T` versus a `&Box<T>`.
For this example, we will illustrate some differences for using `&String` as a function argument versus using a `&str`,
but the ideas apply as well to using `&Vec<T>` versus using a `&[T]` or using a `&T` versus a `&Box<T>`.
Consider an example where we wish to determine if a word contains three consecutive vowels.
We don't need to own the string to determine this, so we will take a reference.
@ -70,7 +74,8 @@ But wait, that's not all! There is more to this story.
It's likely that you may say to yourself: that doesn't matter, I will never be using a `&'static str` as an input anways (as we did when we used `"Ferris"`).
Even ignoring this special example, you may still find that using `&str` will give you more flexibility than using a `&String`.
Let's now take an example where someone gives us a sentence, and we want to determine if any of the words in the sentence has a word that contains three consecutive vowels.
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 has a word that contains three consecutive vowels.
We probably should make use of the function we have already defined and simply feed in each word from the sentence.
An example of this could look like this:
@ -110,9 +115,12 @@ curious has three consecutive vowels!
```
However, this example will not run when our function is declared with an argument type `&String`.
This is because string slices are a `&str` and not a `&String` which would require an allocation to be converted to `&String` which is not implicit, whereas converting from `String` to `&str` is cheap and implicit.
This is because string slices are a `&str` and not a `&String` which would require an allocation to be
converted to `&String` which is not implicit, whereas converting from `String` to `&str` is cheap and implicit.
## See also
- [Rust Language Reference on Type Coercions](https://doc.rust-lang.org/reference/type-coercions.html)
- For more discussion on how to handle `String` and `&str` see [this blog series (2015)](https://web.archive.org/web/20201112023149/https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html) by Herman J. Radtke III.
- For more discussion on how to handle `String` and `&str` see
[this blog series (2015)](https://web.archive.org/web/20201112023149/https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html)
by Herman J. Radtke III

@ -102,11 +102,14 @@ This code in inferior to the original in two respects:
The bug here is a simple mistake in pointer arithmetic: the string was copied, all `msg_len` bytes of it.
However, the `NUL` terminator at the end was not.
The Vector then had its size *set* to the length of the *zero padded string* -- rather than *resized* to it, which could have added a zero at the end. As a result, the last byte in the Vector is uninitialized memory.
The Vector then had its size *set* to the length of the *zero padded string* --
rather than *resized* to it, which could have added a zero at the end.
As a result, the last byte in the Vector is uninitialized memory.
When the `CString` is created at the bottom of the block, its read of the Vector will cause `undefined behaviour`!
Like many such issues, this would be difficult issue to track down.
Sometimes it would panic because the string was not `UTF-8`, sometimes it would put a weird character at the end of the string, sometimes it would just completely crash.
Sometimes it would panic because the string was not `UTF-8`, sometimes it would put a weird character at the end of the string,
sometimes it would just completely crash.
## Disadvantages

@ -85,10 +85,12 @@ pub mod unsafe_module {
}
```
This code will result in a dangling pointer, because the lifetime of the `CString` is not extended by the pointer creation, unlike if a reference were created.
This code will result in a dangling pointer, because the lifetime of the `CString` is not extended
by the pointer creation, unlike if a reference were created.
Another issue frequently raised is that the initialization of a 1k vector of zeroes is "slow".
However, recent versions of Rust actually optimize that particular macro to a call to `zmalloc`, meaning it is as fast as the operating system's ability to return zeroed memory (which is quite fast).
However, recent versions of Rust actually optimize that particular macro to a call to `zmalloc`,
meaning it is as fast as the operating system's ability to return zeroed memory (which is quite fast).
## Disadvantages

@ -1,9 +1,15 @@
# Idioms
[Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly used styles and patterns largely agreed upon by a community. They are guidelines. Writing idiomatic code allows other developers to understand what is happening because they are familiar with the form that it has.
[Idioms](https://en.wikipedia.org/wiki/Programming_idiom) are commonly used styles and patterns largely agreed upon by a community.
They are guidelines.
Writing idiomatic code allows other developers to understand what is happening because they are familiar with the form that it has.
The computer understands the machine code that is generated by the compiler. The language is therefore mostly beneficial to the developer. So, since we have this abstraction layer, why not put it to good use and make it simple?
The computer understands the machine code that is generated by the compiler.
The language is therefore mostly beneficial to the developer.
So, since we have this abstraction layer, why not put it to good use and make it simple?
Remember the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle): "Keep It Simple, Stupid". It claims that "most systems work best if they are kept simple rather than made complicated; therefore, simplicity should be a key goal in design, and unnecessary complexity should be avoided".
Remember the [KISS principle](https://en.wikipedia.org/wiki/KISS_principle): "Keep It Simple, Stupid".
It claims that "most systems work best if they are kept simple rather than made complicated;
therefore, simplicity should be a key goal in design, and unnecessary complexity should be avoided".
> Code is there for humans, not computers, to understand.

@ -2,7 +2,8 @@
## Description
`Option` can be viewed as a container that contains either zero or one elements. In particular, it implements the `IntoIterator` trait, and as such can be used with generic code that needs such a type.
`Option` can be viewed as a container that contains either zero or one elements.
In particular, it implements the `IntoIterator` trait, and as such can be used with generic code that needs such a type.
## Examples
@ -31,15 +32,20 @@ for logician in logicians.iter().chain(turing.iter()) {
}
```
Note that if the `Option` is always `Some`, then it is more idiomatic to use [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) on the element instead.
Note that if the `Option` is always `Some`, then it is more idiomatic to use [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html)
on the element instead.
Also, since `Option` implements `IntoIterator`, it's possible to iterate over it using a `for` loop. This is equivalent to matching it with `if let Some(..)`, and in most cases you should prefer the latter.
Also, since `Option` implements `IntoIterator`, it's possible to iterate over it using a `for` loop.
This is equivalent to matching it with `if let Some(..)`, and in most cases you should prefer the latter.
## See also
* [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is an iterator which yields exactly one element. It's a more readable alternative to `Some(foo).into_iter()`.
* [`std::iter::once`](https://doc.rust-lang.org/std/iter/fn.once.html) is an iterator which yields exactly one element.
It's a more readable alternative to `Some(foo).into_iter()`.
* [`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) is a version of [`Iterator::flat_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.flat_map), specialized to mapping functions which return `Option`.
* [`Iterator::filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) is a version of
[`Iterator::flat_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.flat_map), specialized to mapping functions which
return `Option`.
* The [`ref_slice`](https://crates.io/crates/ref_slice) crate provides functions for converting an `Option` to a zero- or one-element slice.

@ -28,7 +28,8 @@ fn main(s: a::S) {
## Discussion
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.
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.

@ -43,7 +43,8 @@ impl Connection {
## Example
Instead of typing all of this boiler plate to create an `Connection` and `Request` it is easier to just create a wrapping dummy function which takes them as arguments:
Instead of typing all of this boiler plate to create an `Connection` and `Request` it is easier to just
create a wrapping dummy function which takes them as arguments:
```rust,ignore
struct Connection {
@ -67,7 +68,8 @@ impl Connection {
}
```
**Note** in the above example the line `assert!(response.is_ok());` will not actually run while testing because it is inside of a function which is never invoked.
**Note** in the above example the line `assert!(response.is_ok());` will not actually run while testing
because it is inside of a function which is never invoked.
## Advantages
@ -82,5 +84,6 @@ So this pattern is most useful when need `no_run`. With this, you do not need to
If assertions are not required this pattern works well.
If they are, an alternative can be to create a public method to create a dummy instance which is annotated with `#[doc(hidden)]` (so that users won't see it).
If they are, an alternative can be to create a public method to create a dummy instance which is annotated
with `#[doc(hidden)]` (so that users won't see it).
Then this method can be called inside of rustdoc because it is part of the crate's public API.

@ -102,4 +102,4 @@ as well as the `FooBuilder::new().a().b().build()` style.
- [derive_builder](https://crates.io/crates/derive_builder), a crate for automatically implementing this pattern while avoiding the boilerplate.
- [Constructor pattern](../idioms/ctor.md) for when construction is simpler.
- [Builder pattern (wikipedia)](https://en.wikipedia.org/wiki/Builder_pattern)
- [Builders enable construction of complex values (C-BUILDER)](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder) from the Rust API guidelines
- [Construction of complex values](https://web.archive.org/web/20210104103000/https://rust-lang.github.io/api-guidelines/type-safety.html#c-builder)

@ -14,7 +14,8 @@ When designing APIs in Rust which are exposed to other languages, there are some
Rust has built-in FFI support to other languages.
It does this by providing a way for crate authors to provide C-compatible APIs through different ABIs (though that is unimportant to this practice).
Well-designed Rust FFI follows C API design principles, while compromising the design in Rust as little as possible. There are three goals with any foreign API:
Well-designed Rust FFI follows C API design principles, while compromising the design in Rust as little as possible.
There are three goals with any foreign API:
1. Make it easy to use in the target language.
2. Avoid the API dictating internal unsafety on the Rust side as much as possible.
@ -30,7 +31,8 @@ The Object-Based API design allows for writing shims that have good memory safet
## Code Example
The POSIX standard defines the API to access an on-file database, known as [DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/ndbm.h). It is an excellent example of an "object-based" API.
The POSIX standard defines the API to access an on-file database, known as [DBM](https://web.archive.org/web/20210105035602/https://www.mankier.com/0p/ndbm.h).
It is an excellent example of an "object-based" API.
Here is the definition in C, which hopefully should be easy to read for those involved in FFI.
The commentary below should help explaining it for those who miss the subtleties.
@ -58,10 +60,12 @@ It is designed to contain internal state, and acts as an entry point for the lib
It is completely opaque to the user, who cannot create a `DBM` themselves since they don't know its size or layout.
Instead, they must call `dbm_open`, and that only gives them *a pointer to one*.
This means all `DBM`s are "owned" by the library in a Rust sense. The internal state of unknown size is kept in memory controlled by the library, not the user.
This means all `DBM`s are "owned" by the library in a Rust sense.
The internal state of unknown size is kept in memory controlled by the library, not the user.
The user can only manage its life cycle with `open` and `close`, and perform operations on it with the other functions.
The `datum` type was called a "transactional" type above. It is designed to facilitate the exchange of information between the library and its user.
The `datum` type was called a "transactional" type above.
It is designed to facilitate the exchange of information between the library and its user.
The database is designed to store "unstructured data", with no pre-defined length or meaning.
As a result, the `datum` is the C equivalent of a Rust slice: a bunch of bytes, and a count of how many there are.
@ -77,7 +81,8 @@ The question is, who owns the memory that pointer points to?
The answer for best memory safety is, "the user".
But in cases such as retrieving a value, the user does not know how to allocate it correctly (since they don't know how long the value is).
In this case, the library code is expected to use the heap that the user has access to -- such as the C library `malloc` and `free` -- and then *transfer ownership* in the Rust sense.
In this case, the library code is expected to use the heap that the user has access to --
such as the C library `malloc` and `free` -- and then *transfer ownership* in the Rust sense.
This may all seem speculative, but this is what a pointer means in C.
It means the same thing as Rust: "user defined lifetime."
@ -176,11 +181,13 @@ This bug is a classic. Here's what happens when the iterator returns the end-of-
The worst part about this bug?
If the Rust implementation was careful, this code will work most of the time!
If the memory for the `Dbm` object is not immediately reused, an internal check will almost certainly fail, resulting in the iterator returning a `-1` indicating an error.
If the memory for the `Dbm` object is not immediately reused, an internal check will almost certainly fail,
resulting in the iterator returning a `-1` indicating an error.
But occasionally, it will cause a segmentation fault, or even worse, nonsensical memory corruption!
None of this can be avoided by Rust.
From its perspective, it put those objects on its heap, returned pointers to them, and gave up control of their lifetimes. The C code simply must "play nice".
From its perspective, it put those objects on its heap, returned pointers to them, and gave up control of their lifetimes.
The C code simply must "play nice".
The programmer must read and understand the API documentation.
While some consider that par for the course in C, a good API design can mitigate this risk.
@ -209,7 +216,8 @@ Many of the easier design points have other patterns associated with them:
- [FFI Error Passing](../idioms/ffi-errors.md) explains error handling with integer codes and sentinel return values (such as `NULL` pointers)
- [Accepting Foreign Strings](../idioms/ffi-accepting-strings.md) allows accepting strings with minimal unsafe code, and is easier to get right than [Passing Strings to FFI](../idioms/ffi-passing-strings.md)
- [Accepting Foreign Strings](../idioms/ffi-accepting-strings.md) allows accepting strings with minimal unsafe code,
and is easier to get right than [Passing Strings to FFI](../idioms/ffi-passing-strings.md)
However, not every API can be done this way.
It is up to the best judgement of the programmer as to who their audience is.

@ -13,7 +13,8 @@ In Rust, a pointer means "the user manages the lifetime of the pointee." It is t
Some level of trust in the user code is thus required, notably around use-after-free which Rust can do nothing about.
However, some API designs place higher burdens than others on the code written in the other language.
The lowest risk API is the "consolidated wrapper", where all possible interactions with an object are folded into a "wrapper type", while keeping the Rust API clean.
The lowest risk API is the "consolidated wrapper", where all possible interactions with an object
are folded into a "wrapper type", while keeping the Rust API clean.
## Code Example
@ -62,11 +63,13 @@ See [Object-Based APIs](./ffi-export.md) for more on the advantages and pitfalls
Often, wrapping types is quite difficult, and sometimes a Rust API compromise would make things easier.
As an example, consider an iterator which does not efficiently implement `nth()`.
It would definitely be worth putting in special logic to make the object handle iteration internally, or to support a different access pattern efficiently that only the Foreign Function API will use.
It would definitely be worth putting in special logic to make the object handle iteration internally,
or to support a different access pattern efficiently that only the Foreign Function API will use.
### Trying to Wrap Iterators (and Failing)
To wrap any type of iterator into the API correctly, the wrapper would need to do what a C version of the code would do: erase the lifetime of the iterator, and manage it manually.
To wrap any type of iterator into the API correctly, the wrapper would need to do what a C version of
the code would do: erase the lifetime of the iterator, and manage it manually.
Suffice it to say, this is *incredibly* difficult.
@ -86,7 +89,8 @@ struct MySetWrapper {
With `transmute` being used to extend a lifetime, and a pointer to hide it, it's ugly already.
But it gets even worse: *any other operation can cause Rust `undefined behaviour`*.
Consider that the `MySet` in the wrapper could be manipulated by other functions during iteration, such as storing a new value to the key it was iterating over.
Consider that the `MySet` in the wrapper could be manipulated by other functions during iteration,
such as storing a new value to the key it was iterating over.
The API doesn't discourage this, and in fact some similar C libraries expect it.
A simple implementation of `myset_store` would be:
@ -134,5 +138,6 @@ Rust would rather make everything memory safe all the time, for both safety and
Being denied access to certain shortcuts is the price Rust programmers need to pay.
[^1]: For the C programmers out there scratching their heads, the iterator need not be read *during* this code cause the UB.
The exclusivity rule also enables compiler optimizations which may cause inconsistent observations by the iterator's shared reference (e.g. stack spills or reordering instructions for efficiency).
These observations may happen *any time after* the mutable reference is created.
The exclusivity rule also enables compiler optimizations which may cause inconsistent observations by the iterator's
shared reference (e.g. stack spills or reordering instructions for efficiency).
These observations may happen *any time after* the mutable reference is created.

@ -1,21 +1,29 @@
# Design Patterns
[Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) are "general reusable solutions to a commonly occurring problem within a given context in software design".
Design patterns are a great way to describe some of the culture and 'tribal knowledge' of programming in a language.
Design patterns are very language-specific - what is a pattern in one language may be unnecessary in another due to a language feature, or impossible to express due to a missing feature.
[Design patterns](https://en.wikipedia.org/wiki/Software_design_pattern) are "general reusable
solutions to a commonly occurring problem within a given context in software design".
Design patterns are a great way to describe some of the culture and 'tribal knowledge'
of programming in a language.
Design patterns are very language-specific - what is a pattern in one language may be
unnecessary in another due to a language feature, or impossible to express due to a missing feature.
If overused, design patterns can add unnecessary complexity to programs. However, they are a great way to share intermediate and advanced level knowledge about a programming language.
If overused, design patterns can add unnecessary complexity to programs.
However, they are a great way to share intermediate and advanced level knowledge about a programming language.
## Design patterns in Rust
Rust has many very 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.
Rust has many very unique features. These features give us great benefit by removing whole classes of problems.
Some of them are also patterns that are _unique_ to Rust.
## YAGNI
If you're not familiar with it, YAGNI is an acronym that stands for `You Aren't Going to Need It`. It's an important software design principle to apply as you write code.
If you're not familiar with it, YAGNI is an acronym that stands for `You Aren't Going to Need It`.
It's an important software design principle to apply as you write code.
> The best code I ever wrote was code I never wrote.
If we apply YAGNI to design patterns, we see that the features of Rust allow us to throw out many patterns. For instance, there is no need for the [strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern) in Rust because we can just use [traits](https://doc.rust-lang.org/book/traits.html).
If we apply YAGNI to design patterns, we see that the features of Rust allow us to throw out many patterns.
For instance, there is no need for the [strategy pattern](https://en.wikipedia.org/wiki/Strategy_pattern) in Rust
because we can just use [traits](https://doc.rust-lang.org/book/traits.html).
TODO: Maybe include some code to illustrate the traits.

@ -94,7 +94,9 @@ most common uses, but they can be used for other reasons:
pub struct Foo(Bar<T1, T2>);
```
Here, `Bar` might be some public, generic type and `T1` and `T2` are some internal types. Users of our module shouldn't know that we implement `Foo` by using a `Bar`, but what we're really hiding here is the types `T1` and `T2`, and how they are used with `Bar`.
Here, `Bar` might be some public, generic type and `T1` and `T2` are some internal types.
Users of our module shouldn't know that we implement `Foo` by using a `Bar`, but what we're
really hiding here is the types `T1` and `T2`, and how they are used with `Bar`.
## See also

@ -19,7 +19,8 @@ We should take advantage of this tooling, and use smaller, more fine-grained dep
* 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`.
Since the `Url` from `url:1.0` and the `Url` from `url:0.5` are different types,
an HTTP client that uses `url:0.5` would not accept `Url` values from a web scraper that uses `url:1.0`.
* Packages on crates.io are not curated. A crate may be poorly written, have unhelpful documentation, or be outright malicious.
* Two small crates may be less optimized than one large one, since the compiler does not perform link-time optimization (LTO) by default.

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

Loading…
Cancel
Save