From 5f1425d7ed242f805593eb4e09e053c127694d4c Mon Sep 17 00:00:00 2001 From: Takashi Idobe Date: Tue, 5 Jan 2021 09:36:20 -0500 Subject: [PATCH] Apply markdownlint to project (#163) --- .markdownlint.yaml | 5 ++++ CONTRIBUTING.md | 10 +++---- README.md | 16 ++++-------- SUMMARY.md | 46 ++++++++++++++++----------------- anti_patterns/deref.md | 5 ---- anti_patterns/index.md | 2 +- idioms/coercion-arguments.md | 11 ++++---- idioms/concat-format.md | 2 -- idioms/ctor.md | 3 +-- idioms/deref.md | 5 ---- idioms/dtor-finally.md | 6 ----- idioms/mem-replace.md | 2 -- idioms/on-stack-dyn-dispatch.md | 2 +- idioms/pass-var-to-closure.md | 4 --- idioms/priv-extend.md | 1 - idioms/rustdoc-init.md | 9 ++++--- idioms/temporary-mutability.md | 4 --- intro.md | 9 ++++--- patterns/RAII.md | 7 +---- patterns/builder.md | 4 --- patterns/compose-structs.md | 5 ---- patterns/entry.md | 6 ----- patterns/fold.md | 3 --- patterns/index.md | 2 +- patterns/late-bounds.md | 6 ----- patterns/newtype.md | 12 +++------ patterns/visitor.md | 3 --- refactoring/index.md | 2 +- template.md | 6 ----- 29 files changed, 65 insertions(+), 133 deletions(-) create mode 100644 .markdownlint.yaml diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..b44e386 --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,5 @@ +--- +MD004: false +MD010: + code_blocks: false +MD013: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 26a2626..b9d0ed5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,24 +2,24 @@ ## Discussion board -If you have a question or an idea regarding certain content but you want to have feedback of fellow community members +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 appropriate to file an issue open a discussion in our [discussion board](https://github.com/rust-unofficial/patterns/discussions). ## Writing a new article -Before writing a new article please check our [issues](https://github.com/rust-unofficial/patterns/issues) and +Before writing a new article please check our [issues](https://github.com/rust-unofficial/patterns/issues) and the [Pull Requests](https://github.com/rust-unofficial/patterns/pulls) if there are existing issues or someone is working on that topic. 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) please open a new issue, so we can discuss about the ideas and future content of the article together and maybe -give some feedback/input on it. +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) 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. Consider writing your article in a way that has a low barrier of entry so also [Rustlings](https://github.com/rust-lang/rustlings) can follow -and understand the thought process behind it. So we can encourage people to use these patterns early on. +and understand the thought process behind it. So we can encourage people to use these patterns early on. We encourage you to write idiomatic Rust code that builds in the [playground](https://play.rust-lang.org/). @@ -35,7 +35,7 @@ Please make `Draft Pull requests` early so we can follow your progress and can g "Release early and often!" also applies to pull requests! Once your article has some visible work, create a `[WIP]` draft pull request and 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. +Early reviews of the community are not meant as an offense but to give feedback. A good principle: "Work together, share ideas, teach others." diff --git a/README.md b/README.md index 2000361..9956c87 100644 --- a/README.md +++ b/README.md @@ -3,35 +3,32 @@ 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/). - ## TODOs ### Idioms * TODO stability for extensibility -* 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) +* TODO trait to separate visibility of methods from visibility of data () * 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") * TODO interior mutability - UnsafeCell, Cell, RefCell * 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) - ### Design patterns * TODO iterators (to safely avoid bounds checks) * TODO closures and lifetimes (coupling to lifetime) -* TODO platform-specific sub-modules (https://github.com/rust-lang/rfcs/blob/master/text/0517-io-os-reform.md#platform-specific-opt-in) +* TODO platform-specific sub-modules () * 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) * [Entry API](patterns/entry.md) (Currently just a boilerplate) * TODO extension traits -* TODO destructor bombs (ensure linear typing dynamically, e.g., https://github.com/Munksgaard/session-types/commit/0f25ccb7c3bc9f65fa8eaf538233e8fe344a189a) -* 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) +* TODO destructor bombs (ensure linear typing dynamically, e.g., ) +* TODO convertible to Foo trait for more generic generics (e.g., ) * [Late bound bounds](patterns/late-bounds.md) (Currently just a boilerplate) * TODO 'shadow' borrowed version of struct - e.g., double buffering, Niko's parser generator * TODO composition of structs to please the borrow checker * TODO `Error` traits and `Result` forwarding * TODO graphs - ### Anti-patterns * TODO thread + catch_panic for exceptions @@ -41,7 +38,6 @@ language that you can read [here](https://rust-unofficial.github.io/patterns/). * TODO taking an enum rather than having multiple functions * TODO `unwrap()`ing every `Result` instead of forwarding it - ## Contributing You are missing content in this repository that can be helpful for others and you are eager to explain it? @@ -49,7 +45,6 @@ Awesome! We are always happy about new contributions (e.g. elaboration or correc We suggest reading our [Contribution guide](./CONTRIBUTING.md) to get more information on how it works. - ## Building with mdbook This book is built with [mdbook](https://rust-lang.github.io/mdBook/). You can install it by running `cargo install mdbook`. @@ -62,10 +57,9 @@ If you want to build it locally you can run one of these two commands in the roo - `mdbook serve` - Serves the book at `http://localhost:3000` (port is changeable, take a look at the terminal output + 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. - ## License This content of this repository is licensed under **MPL-2.0**; see [LICENSE](./LICENSE). diff --git a/SUMMARY.md b/SUMMARY.md index a0f4257..8429b73 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -3,33 +3,33 @@ - [Introduction](./intro.md) - [Idioms](./idioms/index.md) - - [Use borrowed types for arguments](./idioms/coercion-arguments.md) - - [Concatenating Strings with `format!`](./idioms/concat-format.md) - - [Constructor](./idioms/ctor.md) - - [The `Default` Trait](./idioms/default.md) - - [Collections Are Smart Pointers](./idioms/deref.md) - - [Finalisation in Destructors](./idioms/dtor-finally.md) - - [`mem::replace(_)`](./idioms/mem-replace.md) - - [On-Stack Dynamic Dispatch](./idioms/on-stack-dyn-dispatch.md) - - [Iterating over an `Option`](./idioms/option-iter.md) - - [Pass Variables to Closure](./idioms/pass-var-to-closure.md) - - [Privacy For Extensibility](./idioms/priv-extend.md) - - [Easy doc initialization](./idioms/rustdoc-init.md) - - [Temporary mutability](./idioms/temporary-mutability.md) + - [Use borrowed types for arguments](./idioms/coercion-arguments.md) + - [Concatenating Strings with `format!`](./idioms/concat-format.md) + - [Constructor](./idioms/ctor.md) + - [The `Default` Trait](./idioms/default.md) + - [Collections Are Smart Pointers](./idioms/deref.md) + - [Finalisation in Destructors](./idioms/dtor-finally.md) + - [`mem::replace(_)`](./idioms/mem-replace.md) + - [On-Stack Dynamic Dispatch](./idioms/on-stack-dyn-dispatch.md) + - [Iterating over an `Option`](./idioms/option-iter.md) + - [Pass Variables to Closure](./idioms/pass-var-to-closure.md) + - [Privacy For Extensibility](./idioms/priv-extend.md) + - [Easy doc initialization](./idioms/rustdoc-init.md) + - [Temporary mutability](./idioms/temporary-mutability.md) - [Design Patterns](./patterns/index.md) - - [Builder](./patterns/builder.md) - - [Compose Structs](./patterns/compose-structs.md) - - [Fold](./patterns/fold.md) - - [Newtype](./patterns/newtype.md) - - [RAII Guards](./patterns/RAII.md) - - [Prefer Small Crates](./patterns/small-crates.md) - - [Contain unsafety in small modules](./patterns/unsafe-mods.md) - - [Visitor](./patterns/visitor.md) + - [Builder](./patterns/builder.md) + - [Compose Structs](./patterns/compose-structs.md) + - [Fold](./patterns/fold.md) + - [Newtype](./patterns/newtype.md) + - [RAII Guards](./patterns/RAII.md) + - [Prefer Small Crates](./patterns/small-crates.md) + - [Contain unsafety in small modules](./patterns/unsafe-mods.md) + - [Visitor](./patterns/visitor.md) - [Anti-patterns](./anti_patterns/index.md) - - [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md) - - [Deref Polymorphism](./anti_patterns/deref.md) + - [`#[deny(warnings)]`](./anti_patterns/deny-warnings.md) + - [Deref Polymorphism](./anti_patterns/deref.md) - [Functional Programming](./functional/index.md) diff --git a/anti_patterns/deref.md b/anti_patterns/deref.md index f037c74..832d3fa 100644 --- a/anti_patterns/deref.md +++ b/anti_patterns/deref.md @@ -5,7 +5,6 @@ Abuse the `Deref` trait to emulate inheritance between structs, and thus reuse methods. - ## Example Sometimes we want to emulate the following common pattern from OO languages such @@ -68,7 +67,6 @@ have two unrelated types. However, since the dot operator does implicit dereferencing, it means that the method call will search for methods on `Foo` as well as `Bar`. - ## Advantages You save a little boilerplate, e.g., @@ -81,7 +79,6 @@ impl Bar { } ``` - ## Disadvantages Most importantly this is a surprising idiom - future programmers reading this in @@ -103,7 +100,6 @@ interfaces, class-based privacy, or other inheritance-related features. So, it gives an experience that will be subtly surprising to programmers used to Java inheritance, etc. - ## Discussion There is no one good alternative. Depending on the exact circumstances it might @@ -125,7 +121,6 @@ operator is a case where the ergonomics strongly favour an implicit mechanism, but the intention is that this is limited to degrees of indirection, not conversion between arbitrary types. - ## See also [Collections are smart pointers idiom](../idioms/deref.md). diff --git a/anti_patterns/index.md b/anti_patterns/index.md index 45b876a..fbae8f2 100644 --- a/anti_patterns/index.md +++ b/anti_patterns/index.md @@ -1,6 +1,6 @@ # 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. diff --git a/idioms/coercion-arguments.md b/idioms/coercion-arguments.md index a38015c..b9a18a6 100644 --- a/idioms/coercion-arguments.md +++ b/idioms/coercion-arguments.md @@ -8,7 +8,7 @@ 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`, or `&T` over `&Box`. 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. +We can avoid this by using `&str` instead, and letting `&String` coerce to a `&str` whenever the function is invoked. ## Example @@ -50,8 +50,8 @@ fn main() { ``` This works fine because we are passing a `&String` type as a parameter. -If we comment in the last two lines this example fails because a `&str` type will not coerce to a `&String` type. -We can fix this by simply modifying the type for our argument. +If we comment in the last two lines this example fails because a `&str` type will not coerce to a `&String` type. +We can fix this by simply modifying the type for our argument. For instance, if we change our function declaration to: @@ -66,7 +66,7 @@ Ferris: false 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 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`. @@ -109,9 +109,10 @@ Running this example using our function declared with an argument type `&str` wi curious has three consecutive vowels! ``` -However, this example will not run when our function is declared with an argument type `&String`. +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. ## 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. diff --git a/idioms/concat-format.md b/idioms/concat-format.md index e457336..372d86e 100644 --- a/idioms/concat-format.md +++ b/idioms/concat-format.md @@ -7,7 +7,6 @@ mutable `String`, or using its `+` operator. However, it is often more convenient to use `format!`, especially where there is a mix of literal and non-literal strings. - ## Example ```rust @@ -23,7 +22,6 @@ fn say_hello(name: &str) -> String { } ``` - ## Advantages Using `format!` is usually the most succinct and readable way to combine strings. diff --git a/idioms/ctor.md b/idioms/ctor.md index 17684b7..56ff212 100644 --- a/idioms/ctor.md +++ b/idioms/ctor.md @@ -5,7 +5,6 @@ Rust does not have constructors as a language construct. Instead, the convention is to use a static `new` method to create an object. - ## Example ```rust,ignore @@ -34,4 +33,4 @@ impl Vec { ## See also The [builder pattern](../patterns/builder.md) for constructing objects where there are multiple -configurations. +configurations. diff --git a/idioms/deref.md b/idioms/deref.md index 999e1c4..1815301 100644 --- a/idioms/deref.md +++ b/idioms/deref.md @@ -5,7 +5,6 @@ Use the `Deref` trait to treat collections like smart pointers, offering owning and borrowed views of data. - ## Example ```rust,ignore @@ -40,7 +39,6 @@ must account for these semantics properly in order to give a good user experience. When implementing a data structure which owns its data, offering a borrowed view of that data allows for more flexible APIs. - ## Advantages Most methods can be implemented only for the borrowed view, they are then @@ -48,14 +46,12 @@ implicitly available for the owning view. Gives clients a choice between borrowing or taking ownership of data. - ## Disadvantages Methods and traits only available via dereferencing are not taken into account when bounds checking, so generic programming with data structures using this pattern can get complex (see the `Borrow` and `AsRef` traits, etc.). - ## Discussion Smart pointers and collections are analogous: a smart pointer points to a single @@ -76,7 +72,6 @@ implement `Deref>` where `Bar` is a dynamically sized type and Commonly, ordered collections will implement `Index` for `Range`s to provide slicing syntax. The target will be the borrowed view. - ## See also [Deref polymorphism anti-pattern](../anti_patterns/deref.md). diff --git a/idioms/dtor-finally.md b/idioms/dtor-finally.md index 87fd1f9..95436fa 100644 --- a/idioms/dtor-finally.md +++ b/idioms/dtor-finally.md @@ -6,7 +6,6 @@ Rust does not provide the equivalent to `finally` blocks - code that will be executed no matter how a function is exited. Instead an object's destructor can be used to run code that must be run before exit. - ## Example ```rust,ignore @@ -30,7 +29,6 @@ fn bar() -> Result<(), ()> { } ``` - ## Motivation If a function has multiple return points, then executing code on exit becomes @@ -41,13 +39,11 @@ an exception handling mechanism, but unlike Java (which has `finally`), there is no way to schedule code to run in both the normal and exceptional cases. Panicking will also exit a function early. - ## Advantages Code in destructors will (nearly) always be run - copes with panics, early returns, etc. - ## Disadvantages It is not guaranteed that destructors will run. For example, if there is an @@ -62,7 +58,6 @@ debugging tricky. Requiring an object and `Drop` impl just for finalisation is heavy on boilerplate. - ## Discussion There is some subtlety about how exactly to store the object used as a @@ -90,7 +85,6 @@ that desctructors are not absolutely guaranteed to run. It also means that you must take extra care in your destructors not to panic, since it could leave resources in an unexpected state. - ## See also [RAII](../patterns/RAII.md). diff --git a/idioms/mem-replace.md b/idioms/mem-replace.md index e670ae7..5f3bd50 100644 --- a/idioms/mem-replace.md +++ b/idioms/mem-replace.md @@ -84,7 +84,6 @@ Note, however, that if we are using an `Option` and want to replace its value with a `None`, `Option`’s `take()` method provides a shorter and more idiomatic alternative. - ## Advantages Look ma, no allocation! Also you may feel like Indiana Jones while doing it. @@ -107,7 +106,6 @@ However, in Rust, we have to do a little more work to do this. An owned value may only have one owner, so to take it out, we need to put something back in – like Indiana Jones, replacing the artifact with a bag of sand. - ## See also This gets rid of the [Clone to satisfy the borrow checker] antipattern in a diff --git a/idioms/on-stack-dyn-dispatch.md b/idioms/on-stack-dyn-dispatch.md index 64155ec..c076458 100644 --- a/idioms/on-stack-dyn-dispatch.md +++ b/idioms/on-stack-dyn-dispatch.md @@ -83,7 +83,7 @@ Read` ## See also -* [Finalisation in destructors](dtor-finally.md) and +* [Finalisation in destructors](dtor-finally.md) and [RAII guards](../patterns/RAII.md) can benefit from tight control over lifetimes. * For conditionally filled `Option<&T>`s of (mutable) references, one can initialize an `Option` directly and use its [`.as_ref()`] method to get an diff --git a/idioms/pass-var-to-closure.md b/idioms/pass-var-to-closure.md index 4d82767..b9b3c32 100644 --- a/idioms/pass-var-to-closure.md +++ b/idioms/pass-var-to-closure.md @@ -8,7 +8,6 @@ give it copy of some data, pass it by reference, or perform some other transform Use variable rebinding in separate scope for that. - ## Example Use @@ -45,7 +44,6 @@ let closure = move || { }; ``` - ## Advantages Copied data are grouped together with closure definition, so their purpose is more clear @@ -53,8 +51,6 @@ and they will be dropped immediately even if they are not consumed by closure. Closure uses same variable names as surrounding code whether data are copied or moved. - ## Disadvantages Additional indentation of closure body. - diff --git a/idioms/priv-extend.md b/idioms/priv-extend.md index b8d05ac..a6c77fb 100644 --- a/idioms/priv-extend.md +++ b/idioms/priv-extend.md @@ -5,7 +5,6 @@ Use a private field to ensure that a struct is extensible without breaking stability guarantees. - ## Example ```rust,ignore diff --git a/idioms/rustdoc-init.md b/idioms/rustdoc-init.md index 6c69375..89b00f4 100644 --- a/idioms/rustdoc-init.md +++ b/idioms/rustdoc-init.md @@ -2,12 +2,13 @@ ## Description -If a struct takes significant effort to initialize, when writing docs, it can be quicker to wrap your example with a +If a struct takes significant effort to initialize, when writing docs, it can be quicker to wrap your example with a function which takes the struct as an argument. ## Motivation + Sometimes there is a struct with multiple or complicated parameters and several methods. -Each of these methods should have examples. +Each of these methods should have examples. For example: @@ -41,6 +42,7 @@ 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: ```rust,ignore @@ -64,6 +66,7 @@ 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. ## Advantages @@ -77,7 +80,7 @@ So this pattern is most useful when need `no_run`. With this, you do not need to ## Discussion -If assertions are not required this pattern works well. +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). Then this method can be called inside of rustdoc because it is part of the crate's public API. diff --git a/idioms/temporary-mutability.md b/idioms/temporary-mutability.md index 2b7bb21..d473a12 100644 --- a/idioms/temporary-mutability.md +++ b/idioms/temporary-mutability.md @@ -7,7 +7,6 @@ and never modified. The intention can be made explicit by redefining the mutable It can be done either by processing data within nested block or by redefining variable. - ## Example Say, vector must be sorted before usage. @@ -34,14 +33,11 @@ let data = data; // Here `data` is immutable. ``` - ## Advantages Compiler ensures that you don't accidentally mutate data after some point. - ## Disadvantages Nested block requires additional indentation of block body. One more line to return data from block or redefine variable. - diff --git a/intro.md b/intro.md index 59728b4..9a423b4 100644 --- a/intro.md +++ b/intro.md @@ -1,19 +1,20 @@ # Introduction ## Design 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 -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. +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/index.md) are methods to solve common problems when writing software. -[Anti-patterns](./anti_patterns/index.md) are methods to solve these same common problems. +[Anti-patterns](./anti_patterns/index.md) are methods to solve these same common problems. However, while design patterns give us benefits, anti-patterns create more problems. -[Idioms](./idioms/index.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. +[Idioms](./idioms/index.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. TODO: Mention why Rust is a bit special - functional elements, type system, borrow checker diff --git a/patterns/RAII.md b/patterns/RAII.md index 61875cc..b576a80 100644 --- a/patterns/RAII.md +++ b/patterns/RAII.md @@ -69,20 +69,17 @@ fn baz(x: Mutex) { } ``` - ## Motivation Where a resource must be finalised after use, RAII can be used to do this finalisation. If it is an error to access that resource after finalisation, then this pattern can be used to prevent such errors. - ## Advantages Prevents errors where a resource is not finalised and where a resource is used after finalisation. - ## Discussion RAII is a useful pattern for ensuring resources are properly deallocated or @@ -111,8 +108,6 @@ Note that implementing `Deref` is not a core part of this pattern, it only makes using the guard object more ergonomic. Implementing a `get` method on the guard works just as well. - - ## See also [Finalisation in destructors idiom](../idioms/dtor-finally.md) @@ -120,7 +115,7 @@ works just as well. RAII is a common pattern in C++: [cppreference.com](http://en.cppreference.com/w/cpp/language/raii), [wikipedia][wikipedia]. -[wikipedia]: https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization +[wikipedia]: https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization [Style guide entry](https://doc.rust-lang.org/1.0.0/style/ownership/raii.html) (currently just a placeholder). diff --git a/patterns/builder.md b/patterns/builder.md index b79725a..c3612a2 100644 --- a/patterns/builder.md +++ b/patterns/builder.md @@ -50,13 +50,11 @@ fn builder_test() { } ``` - ## Motivation Useful when you would otherwise require many different constructors or where construction has side effects. - ## Advantages Separates methods for building from other methods. @@ -65,13 +63,11 @@ Prevents proliferation of constructors Can be used for one-liner initialisation as well as more complex construction. - ## Disadvantages More complex than creating a struct object directly, or a simple constructor function. - ## Discussion This pattern is seen more frequently in Rust (and for simpler objects) than in diff --git a/patterns/compose-structs.md b/patterns/compose-structs.md index 232d001..47e93ea 100644 --- a/patterns/compose-structs.md +++ b/patterns/compose-structs.md @@ -14,7 +14,6 @@ behaviour. This will often lead to a better design in other ways: applying this design pattern often reveals smaller units of functionality. - ## Example Here is a contrived example of where the borrow checker foils us in our plan to @@ -68,19 +67,16 @@ fn baz(a: &mut A) { } ``` - ## Motivation TODO Why and where you should use the pattern - ## Advantages Lets you work around limitations in the borrow checker. Often produces a better design. - ## Disadvantages Leads to more verbose code. @@ -89,7 +85,6 @@ Sometimes, the smaller structs are not good abstractions, and so we end up with a worse design. That is probably a 'code smell', indicating that the program should be refactored in some way. - ## Discussion This pattern is not required in languages that don't have a borrow checker, so diff --git a/patterns/entry.md b/patterns/entry.md index 4e92437..b06a07e 100644 --- a/patterns/entry.md +++ b/patterns/entry.md @@ -4,7 +4,6 @@ A short, prose description of the pattern. - ## Example ```rust @@ -12,27 +11,22 @@ A short, prose description of the pattern. // liberally. ``` - ## Motivation Why and where you should use the pattern - ## Advantages Good things about this pattern. - ## Disadvantages Bad things about this pattern. Possible contraindications. - ## Discussion TODO vs insert_or_update etc. - ## See also [RFC](https://github.com/rust-lang/rfcs/blob/master/text/0216-collection-views.md) diff --git a/patterns/fold.md b/patterns/fold.md index 6b649b8..5b91bf7 100644 --- a/patterns/fold.md +++ b/patterns/fold.md @@ -70,7 +70,6 @@ A folder can also be defined to map one data structure to a different (but usually similar) data structure. For example, we could fold an AST into a HIR tree (HIR stands for high-level intermediate representation). - ## Motivation It is common to want to map a data structure by performing some operation on @@ -83,7 +82,6 @@ appropriate. Like the visitor pattern, the fold pattern allows us to separate traversal of a data structure from the operations performed to each node. - ## Discussion Mapping data structures in this fashion is common in functional languages. In OO @@ -108,7 +106,6 @@ the original data structure and we don't need to clone unchanged nodes. However, they are less ergonomic to use and mean that the data structures cannot be mutable. - ## See also Iterators have a `fold` method, however this folds a data structure into a diff --git a/patterns/index.md b/patterns/index.md index 324a6c3..eada5b8 100644 --- a/patterns/index.md +++ b/patterns/index.md @@ -1,6 +1,6 @@ # 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](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. diff --git a/patterns/late-bounds.md b/patterns/late-bounds.md index 8c91ef3..fe5ebe7 100644 --- a/patterns/late-bounds.md +++ b/patterns/late-bounds.md @@ -4,7 +4,6 @@ TODO late binding of bounds for better APIs (i.e., Mutex's don't require Send) - ## Example ```rust @@ -12,29 +11,24 @@ TODO late binding of bounds for better APIs (i.e., Mutex's don't require Send) // liberally. ``` - ## Motivation Why and where you should use the pattern - ## Advantages Good things about this pattern. - ## Disadvantages Bad things about this pattern. Possible contraindications. - ## Discussion A deeper discussion about this pattern. You might want to cover how this is done in other languages, alternative approaches, why this is particularly nice in Rust, etc. - ## See also Related patterns (link to the pattern file). Versions of this pattern in other diff --git a/patterns/newtype.md b/patterns/newtype.md index b71aa7e..7668222 100644 --- a/patterns/newtype.md +++ b/patterns/newtype.md @@ -1,10 +1,10 @@ # Newtype -What if in some cases we want a type to behave similar to another type or -enforce some behaviour at compile time where using only type aliases would -not be enough? +What if in some cases we want a type to behave similar to another type or +enforce some behaviour at compile time where using only type aliases would +not be 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). For such cases we could use the `Newtype` pattern to provide __type safety__ and __encapsulation__. @@ -52,7 +52,6 @@ fn main() { } ``` - ## Motivation The primary motivation for newtypes is abstraction. It allows you to share @@ -63,7 +62,6 @@ API, it allows you to change implementation backwards compatibly. Newtypes can be used for distinguishing units, e.g., wrapping `f64` to give distinguishable `Miles` and `Kms`. - ## Advantages The wrapped and wrapper types are not type compatible (as opposed to using @@ -83,7 +81,6 @@ You need a 'pass through' method for every method you want to expose on the wrapped type, and an impl for every trait you want to also be implemented for the wrapper type. - ## Discussion Newtypes are very common in Rust code. Abstraction or representing units are the @@ -99,7 +96,6 @@ pub struct Foo(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 - [Advanced Types in the book](https://doc.rust-lang.org/book/ch19-04-advanced-types.html?highlight=newtype#using-the-newtype-pattern-for-type-safety-and-abstraction) diff --git a/patterns/visitor.md b/patterns/visitor.md index 855a3e0..9f5de67 100644 --- a/patterns/visitor.md +++ b/patterns/visitor.md @@ -10,7 +10,6 @@ behaviour). Furthermore, the visitor pattern allows separating the traversal of a collection of objects from the operations performed on each object. - ## Example ```rust,ignore @@ -70,7 +69,6 @@ impl Visitor for Interpreter { One could implement further visitors, for example a type checker, without having to modify the AST data. - ## Motivation The visitor pattern is useful anywhere that you want to apply an algorithm to @@ -78,7 +76,6 @@ heterogeneous data. If data is homogeneous, you can use an iterator-like pattern Using a visitor object (rather than a functional approach) allows the visitor to be stateful and thus communicate information between nodes. - ## Discussion It is common for the `visit_*` methods to return void (as opposed to in the diff --git a/refactoring/index.md b/refactoring/index.md index 71f6b09..d549022 100644 --- a/refactoring/index.md +++ b/refactoring/index.md @@ -6,7 +6,7 @@ We can use [design patterns](../patterns/index.md) to [DRY] up code and generali > Shortcuts make for long days. -We can also use [idioms](../idioms/index.md) to structure our code in a way that is understandable. +We can also use [idioms](../idioms/index.md) to structure our code in a way that is understandable. ## Tests diff --git a/template.md b/template.md index f8e4ff3..f5f6501 100644 --- a/template.md +++ b/template.md @@ -4,7 +4,6 @@ A short, prose description of the pattern. - ## Example ```rust @@ -21,29 +20,24 @@ please at least mark your example code with `ignore` as in here: // liberally. ``` - ## Motivation Why and where you should use the pattern - ## Advantages Good things about this pattern. - ## Disadvantages Bad things about this pattern. Possible contraindications. - ## Discussion A deeper discussion about this pattern. You might want to cover how this is done in other languages, alternative approaches, why this is particularly nice in Rust, etc. - ## See also Related patterns (link to the pattern file). Versions of this pattern in other