Skip to content

Add introduction #117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Jan 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Rust Design Patterns

An open source repository of design patterns and idioms in the Rust programming
language.
An open source book about design patterns and idioms in the Rust programming
language that you can read [here](https://rust-unofficial.github.io/patterns/).


## Contents
Expand All @@ -28,6 +28,7 @@ language.
* TODO FFI usage (By being mindful of how to provide Rust libraries, and make use of existing libraries across the FFI, you can get more out of benefits Rust can bring)
* [Easy doc initialization](idioms/rustdoc-init.md)


### Design patterns

* [Builder](patterns/builder.md)
Expand All @@ -53,7 +54,6 @@ language.
* [Compose structs together for better borrowing](patterns/compose-structs.md)



### Anti-patterns

* TODO thread + catch_panic for exceptions
Expand Down Expand Up @@ -94,4 +94,4 @@ If you want to build it locally you can run one of these two commands in the roo
Serves the book at `http://localhost:3000` (port is changeable, take a look at the terminal output
to be sure) and reloads the browser when a change occurs.



3 changes: 3 additions & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Summary

- [Introduction](./intro.md)

- [Idioms](./idioms/README.md)
- [Concatenating Strings with `format!`](./idioms/concat-format.md)
- [Constructor](./idioms/ctor.md)
Expand Down Expand Up @@ -30,3 +31,5 @@
- [Anti-patterns](./anti_patterns/README.md)
- [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md)
- [Deref Polymorphism](./anti_patterns/deref.md)

- [Functional Programming](./functional/README.md)
5 changes: 4 additions & 1 deletion anti_patterns/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Anti-patterns

TODO: add description/explanation
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.
53 changes: 53 additions & 0 deletions functional/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Functional Usage of Rust

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.

## Imperative

```rust
let mut sum = 0;
for i in 1..11 {
sum += i;
}
println!("{}", sum);
```

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.

| `i` | `sum` |
| --- | ----- |
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
| 6 | 21 |
| 7 | 28 |
| 8 | 36 |
| 9 | 45 |
| 10 | 55 |

This is how most of us start out programming. We learn that a program is a set of steps.

## Declarative

```rust
println!("{}", (1..11).fold(0, |a, b| a + b));
```

Whoa! This is really different! What's going on here? Remember that with declarative programs we are describing __what__ to do, rather than __how__ to do it. `fold` is a function that [composes](https://en.wikipedia.org/wiki/Function_composition) 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. 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`.

| `a` | `b` | result |
| --- | --- | ------ |
| 0 | 1 | 1 |
| 1 | 2 | 3 |
| 3 | 3 | 6 |
| 6 | 4 | 10 |
| 10 | 5 | 15 |
| 15 | 6 | 21 |
| 21 | 7 | 28 |
| 28 | 8 | 36 |
| 36 | 9 | 45 |
| 45 | 10 | 55 |
8 changes: 7 additions & 1 deletion idioms/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Idioms

TODO: add description/explanation
[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?

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".
Copy link
Contributor

@pickfire pickfire Jan 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like go to me. I feel like rust is aiming to be "keep it simple but not simpler".

Go is able to be simple by having tons of footguns as a result, simplicity matters more than correctness, it just aims to be correct enough.

Rust aims more to be correct than simple (this is why rust became so complicated), one good thing is that the footguns are reduced to bare minimum, this is what I believe how the API in rust is designed.

Maybe be should still mention that correctness should be valued before simplicity?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I strongly agree with you.

We can say something like:
"even if the kiss principle is important rust tends to prefer correctness over simplicity."

If you want to do a PR where you explain this it would be nice I think (you don't have to report my words necessarily).


> Code is there for humans, not computers, to understand.
18 changes: 15 additions & 3 deletions intro.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
# Introduction

## Design patterns

What are design patterns? What are idioms? Anti-patterns.
When developing programs, we have to solve many problems. A program can be viewed as a solution to a problem. It can also be viewed as a collection of solutions to many different problems. All of these solutions work together to solve a bigger problem.

## Design patterns in Rust

Why Rust is a bit special - functional elements, type system - borrow checker
There are many problems that share the same form. Due to the fact that Rust is not object-oriented design patterns vary with respect to other object-oriented programming languages. While the details are different, since they have the same form they can be solved using the same fundamental methods.

[Design patterns](patterns/README.md) are methods to solve common problems when writing software.

[Anti-patterns](anti_patterns/README.md) are methods to solve these same common problems.
Comment on lines +10 to +12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels like the same wordings, maybe we should let readers know what are the differences between them? "Anti-patterns" is a tough english word, we should have more descriptions.


However, while design patterns give us benefits, anti-patterns create more problems. There are some problems that we don't need to solve because [Rust rocks](rust_rocks.md)!

[Idioms](idioms/README.md) are guidelines to follow when coding. They are social norms of the community.
You can break them, but if you do you should have a good reason for it.

[Refactoring](refactoring/README.md) is the process by which you convert code that works, but is hard to understand, into code that works and is easy to understand.

TODO: Mention why Rust is a bit special - functional elements, type system, borrow checker
20 changes: 19 additions & 1 deletion patterns/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# Design Patterns

TODO: add description/explanation
[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.

## Design patterns in Rust

Rust has many very unique features. These features give us great benefit by removing whole classes of problems. For more about this, read why [Rust rocks](/rust_rocks.md)! 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.

> 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).

TODO: Maybe include some code to illustrate the traits.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could remove this, I wonder if illustration is really needed here for understanding.

4 changes: 4 additions & 0 deletions patterns/newtype.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Newtype

Rust has strong static types. This can be very different than what you are used to if you are coming from a loosely-typed language. Don't worry, though. Once you get used to them, you'll find the types actually make your life easier. Why? Because you are making implicit assumptions explicit.

A really convenient application of the Rust type system is the Newtype pattern.
Copy link
Contributor

@pickfire pickfire Jan 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is Newtype pattern convenient? I think we should use a different word other than convenient or maybe we should explain why is it convenient. Or maybe we could just

An application of the Rust type system is the Newtype pattern.

Or maybe we should have a better question or a problem as the introduction?

What if in some cases we want to have a type to behave similarly but still different to another type?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if in some cases we want to have a type to behave similarly but still different to another type?

I like this :)


## Description

Use a tuple struct with a single field to make an opaque wrapper for a type.
Expand Down
15 changes: 15 additions & 0 deletions refactoring/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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.

We can use [design patterns](patterns/README.md) to DRY up code and generalize abstractions. We must avoid [anti-patterns](anti_patterns/README.md) while we do this. While they may be tempting to employ, their costs outweigh their benefits.

> Shortcuts make for long days.

We can also use [idioms](idioms/README.md) to structure our code in a way that is understandable.

## Tests

Tests are of vital importance during refactoring.

## Small changes