A catalogue of Rust design patterns, anti-patterns and idioms

Overview

Rust Design Patterns

An open source book about design patterns and idioms in the Rust programming language that you can read here.

Contributing

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 for all the patterns, anti-patterns, and idioms that could be added.

We suggest reading our Contribution guide to get more information on how contributing to this repository works.

Building with mdbook

This book is built with mdbook. You can install it by running cargo install mdbook.

If you want to build it locally you can run one of these two commands in the root directory of the repository:

  • mdbook build

    Builds static html pages as output and place them in the /book directory by default.

  • mdbook serve

    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.

Comments
  • Add Functional Programming - Generics as Type Classes

    Add Functional Programming - Generics as Type Classes

    I put this under functional languages, since it is really an example of the concept of "type classes" in Rust. If this specific example is more of a design pattern, I would be happy to move it.

    If no one beats me to it, I should be able to fix the two FIXMEs at the bottom sometime next week.

    addition pattern functional 
    opened by jhwgh1968 25
  • Formalize language style

    Formalize language style

    With more contributors, more differences in writing style will become apparent. This issue is a proposal to start a guideline to be able to refer back to for consistent writing.

    Most words if not all, seem to use the British style (e.g. -ise endings such as 'finalise'; also used in Australia and New Zealand), to name just one example. One may out of habit (or stubbornness) use US-style. Using US-style could be discussed, but it's important to have agreement upon consistency.

    A good example of inconsistent language usage is the usage of "we" and "you":

    We can avoid this by using &str instead, and letting &String coerce to a &str whenever the function is invoked.

    Source: idioms/coercion-arguments

    This pattern is only of interest in Rust. In GC'd languages, you'd take the reference to the value by default (and the GC would keep track of refs), and in other low-level languages like C you'd simply alias the pointer and fix things later.

    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

    Source: idioms/mem-replace

    One could argue that this is done on purpose in this specific instance, but it felt out of place when I read it. But there's also the usage of "me":

    The etymology here is unclear to me. The terms 'fold' and 'folder' are used in the Rust compiler, although it appears to me to be more like a map than a fold in the usual sense. See the discussion below for more details.

    Source: patterns/fold

    Most of the text is also written informally (e.g. 'anyways' over 'anyway') and with abbreviations (in this instance of 'GC', even without explanation).

    I am not necessarily suggesting a change and lots of things could be considered gray area (grey area?), but it would be good if there is some writing guideline to be able to refer to when in doubt.

    enhancement needed-discussion 
    opened by pedzed 18
  • New Section: Lenses and Prisms

    New Section: Lenses and Prisms

    As the intro says, this is a pure functional concept that Rust doesn't often use directly, but can be used to help understand some very "functional" looking APIs, such as Serde.

    It is a rather difficult concept, even for me, so the wording might not be the best. I'm open to any feedback and improvements.

    addition functional 
    opened by jhwgh1968 14
  • Fixing code examples, statement about working tests on code examples

    Fixing code examples, statement about working tests on code examples

    This doctests all code examples. There is some fallout to either ignore examples (if I deemed them too far from runnable) or change them to work, or at least compile.

    ~~I also added a .travis.yml so we can use CI to check PRs.~~

    fixes #49

    enhancement waiting-for zombie 
    opened by llogiq 14
  • Several FFI Sections: Strings, Errors, and API Design

    Several FFI Sections: Strings, Errors, and API Design

    This is still in draft because it's early, but I wanted to open this PR to claim it and show where I'm going. Let me know if this is the right approach, or if I missed conversation occurring elsewhere and jumped the gun.

    Looking through the PRs and the Issues, it looked like no one was tackling FFI. Well, I volunteer!

    It's a big subject, of course, so I'm only offering one small part: exporting Rust type APIs to other languages, using object-based API design.

    The example may seem rather obscure, but I think it's a good illustration. It is also one I am very familiar with. I learned Rust by trying to port the QDBM library from C (before finding something off-the-shelf). I got about half way done, and learned a lot about C bindings and memory safety! :smile: I figure this is the best way to capture a lot of that knowledge, and share it with the world.

    addition LGTM 
    opened by jhwgh1968 13
  • Prepare the book to be included as rust book

    Prepare the book to be included as rust book

    Should we try preparing the book to be included as part of the rustup install or maybe we shouldn't do it? Rust API Guidelines isn't there as well but I wonder if it would be good to have both taken in at the same time? This allows people to read it offline and while having it bundled with the official docs, but of course the quality needs to be there first (maybe the team needs to review). Or maybe we could take the first them by creating a rustup component?

    addition needed-discussion zombie 
    opened by pickfire 11
  • Is the `Builder` pattern an anti-pattern in Rust due to the `Default` trait?

    Is the `Builder` pattern an anti-pattern in Rust due to the `Default` trait?

    I claim that the builder pattern is more or less an anti-pattern and that you should use the Default trait instead. Here's why:

    Let's say we have a struct:

    pub struct Window {
        pub title: &'static str,
        pub width: usize,
        pub height: usize,
    }
    
    1. The builder pattern produces way too much code on the creators side while not having a significant amount of code reduction on the users side. This is especially visible if the struct has more fields:

    Creator:

    // Builder pattern
    pub struct WindowBuilder {
        __title: &'static str,
        __width: usize,
        __height: usize,
    }
    
    impl WindowBuilder {
        pub fn new() -> Self {
            Self {
                __title: "Default title",
                __width: 800,
                __title: 600,
            }
        }
    
        pub fn with_title(self, title: &'static str) -> Self {
            Self {
                __title: title,
                __width: self.width,
                __title: self.height,
            }
        }
        
        pub fn with_dimensions(self, width: usize, height: usize) -> Self {
            Self {
                __title: self.title,
                __width: width,
                __title: height,
            }
        }
    
        pub fn build(self) -> Window {
            Window {
                title: self.title,
                width: self.width,
                height: self.height,
            }
        }
    }
    
    // Default pattern: much less code!
    impl Default for Window {
        fn default() -> Self {
            Self {
               title: "Default title",
               width: 800,
               height: 600,
            }
        }
    }
    

    See how much code we need to construct a window in comparison to the Default trait?

    User:

    // Default pattern
    let window = Window {
         title: "Original title",
        .. Default::default()
    };
    
    // Builder pattern: not a significant reduction of usage code!
    let window = WindowBuilder::new()
                         .with_title("Original title")
                         .build();
    
    1. The builder pattern doesn't protect against double-initialization:
    let window = WindowBuilder::new()
                         .with_title("Original title")
                         .with_dimensions(800, 600)
                         .with_title("Oops, overwritten title!")
                         .build();
    

    The Default trait protects against that, because you can't initialize the same field twice. The builder pattern simply overwrites the field and you don't get any warning.

    1. The Default trait eliminates the need for the SomethingBuilder struct. The SomethingBuilder struct is an intermediate struct that provides a certain kind of type safety so that you have to call SomethingBuilder.build() to construct a Something out of a SomethingBuilder. All of this is unnecessary if you use the Default trait - less code with essentially the same outcome. The SomethingBuilder has one appropriate use, in my opinion: When you need something to happen in the .build() function and it needs to happen once (although you can implement this in a default() function, too). For example, you need to tell the OS to create a window. This is where it's appropriate to use a builder. However, I've seen the builder pattern to be completely overused, which is why I'm writing this.

    Often times when I'm having a struct with many fields that can have default values, it is easier to implement a default trait than to write ten or twenty builder functions. And that is why I claim that the builder pattern is actually an anti-pattern and that you should use the Default trait instead, wherever possible. It should at least be included somewhere in this repository.

    move-to-discussions 
    opened by fschutt 10
  • Inspiration for more patterns

    Inspiration for more patterns

    I recently started collecting some API design patterns here. I'm not sure if these match what this repo is all about (or which ones you already cover), but I just wanted to say: Feel free to take anything you like from that post! :)

    • Public type aliases
    • Use conversion traits
    • Laziness: Use Iterators
    • Laziness: Take closures
    • Custom traits for input parameters
    • Extension traits
    • Builder pattern
    • Session types

    (The markdown source of that post is here and I hereby relicense the content as MPL2 (in addition to CC-BY) and allow you to use it in this repo.)

    move-to-discussions 
    opened by killercup 10
  • Adding 'clone to satisfy the borrow checker' anti-pattern

    Adding 'clone to satisfy the borrow checker' anti-pattern

    Due to inactivity of the author and maintainers not being able to edit the PR I'll move #23 to a new branch in this repository.

    Future fixes to the PR will be made on this branch.

    addition anti-pattern 
    opened by simonsan 9
  • Maintainership/help proposal

    Maintainership/help proposal

    Hi @lambda-fairy,

    you stated here that this repo needs some help, how do you imagine helping with this repository? MarcoIeni & me would like to help. We could start reviewing all past issues and merging/closing or give some feedback to all pending PRs.

    Long-term, we would also like to turn this repo into a rust book that can be served with GitHub pages to the community.

    Cheers, Simonsan & MarcoIeni

    opened by simonsan 9
  • Discussion: Functions with many parameters

    Discussion: Functions with many parameters

    Requesting a design pattern for functions that take lots of parameters. For example this way:

    fn foo(parameter_0: type, parameter_1: type, parameter_2: type, parameter_3: type, parameter_4: type, parameter_5: type, parameter_6: type) -> type {
        // function body
    }
    

    is a way that I think should be an anti-pattern because it's too wide. I prefer things to be less than 80 chars wide.

    move-to-discussions 
    opened by DanteFalzone0 9
  • Clarify paragraph in 5.2

    Clarify paragraph in 5.2

    The section 5.2. Generics as Type Classes contains the following paragraph:

    In object-oriented languages, classes can inherit behavior from their parents. However, this allows the attachment of not only additional behavior to particular members of a type class, but extra behavior as well.

    which I find fairly confusing. The second sentence contains repetitive words and does not clarify the first statement enough. It would help if this paragraph was rewritten and/or expanded to add meaning and additional information. Thanks!

    clarification 
    opened by alensiljak 0
  • Borrow Checker Patterns

    Borrow Checker Patterns

    Inspired by the talk Rust in 2024 by Niko Matsakis (at 37m20s) I was thinking we may be able to create some more learning resources about that here to help to reduce accidental complexity for people being new to Rust.

    We have an article about interacting with the borrow checker (Anti-Pattern/Clone to satisfy the borrow checker), but I feel that more a don't than how to interact usually with the borrow checker.

    We may also revisit https://github.com/rust-unofficial/patterns/pull/323 to make these things overall more visible, I even thought about a new category for "Borrow Checker Pattterns" because they are quite exclusive to Rust (and may change within each edition).

    pattern 
    opened by simonsan 1
  • Feedback on 'Lenses and Prisms' article

    Feedback on 'Lenses and Prisms' article

    tmarsh1024 1 hr. ago

    I love seeing more discussion and presentations of optics such as lenses and prisms. I do find their treatment in this document, however, more than a little confusing.

    A lot of the discussion of lenses is focused on achieving structural typing (accessing a field by name if it exists on the struct without knowing the nominal type in advance). I've never witnessed optics in the context of structural or duck typing before and certainly isn't the usually touted benefit. The lenses section goes on to mention that the power in rust really comes from composition but stops there and doesn't show any examples.

    The section on prisms is mistaken in its characterization that prisms represent families of optics. This is no more true than it is true for the lens - both prisms and lenses can be generated from the simpler iso optic. In some representations of optics prisms can be seen as dual to lenses, another way lenses and prisms are peer level.

    I didn't understand the visitor example and the discussion of type erasure, which all seemed to me very far removed from anything to do with prisms.

    Maybe I'm missing something here, and I admit to not reading too deeply when I got confused, but I would definitely recommend the audience and the explanation be reevaluated. Like I said, I would love for these topics to be more broadly presented!

    CC: @jhwgh1968

    I asked the person to leave some feedback here as well, so we can improve on the article. But we could start discussing given feedback on top.

    opened by simonsan 6
  • Add double panic mentioning for RAII guards

    Add double panic mentioning for RAII guards

    I think it would be nice to mention that one should be careful when writing drop implementations on guards relatively to the double panic possibility when they find out about this pattern for the first time or are unaware of double panicking generally.

    It might help someone inexperienced I suppose.

    opened by nandesu-utils 1
  • Floating Point Related Idioms

    Floating Point Related Idioms

    Floating point numbers are known to be an imperfect model of real numbers, causing rounding errors in calculations and difficulty for equality comparison.

    For example, the Euclidean length example in the Interpreter chapter using sqrt has an average error of 2.9 bits for 4-dimension and 13.9 bits for 2-dimension, with more and bigger errors (over 56-bit errors) happening at points very close to the axes. This can be improved by using hypot ("hypotenus") instead. (Herbie page for 4D Euclid length) (Herbie page for 2D Euclid length)

    Similarly, taking the angle between two 2-dimentional vectors has an average error of 34.3 bits, again with more and bigger errors (64-bit errors) closer to the axes, can be improved with hypot and mul_add (aka fma, "fused multiply add") to cut the average error down to 18.9 bits. (Herbie page for angle between vectors)

    (The error figures above are taken from the results of Herbie project.)

    A related problem is that nominally equal floating point expressions often do not compare equal due to rounding errors. This necessitates something like the crate float-cmp.

    These are not Rust specific idioms per se, but I feel like this is a good place to raise awareness of these errors and list Rust specific solutions.

    enhancement addition idiom 
    opened by louy2 1
Owner
Catch-all organization for unofficial Rust projects which have become orphaned or otherwise need community maintainership
null
Elemental System Designs is an open source project to document system architecture design of popular apps and open source projects that we want to study

Elemental System Designs is an open source project to document system architecture design of popular apps and open source projects that we want to study

Jason Shin 9 Apr 10, 2022
A Domain Driven Design example application in Rust.

Rust Domain Driven Design Example rust-ddd Rust Domain-Driven-Design (DDD) Summery This repository is used to present how I find implementing DDD in R

Behrouz R.Farsi 6 Nov 15, 2022
Parks-McClellan Remez FIR design algorithm

pm-remez: Parks-McClellan Remez FIR design algorithm pm-remez is a modern Rust implementation of the Parks-McClellan Remez exchange algorithm. It can

Maia SDR 10 Apr 14, 2024
Generic abstractions for combining and nesting reduction patterns for iterables.

reductor Generic abstractions for combining and nesting reduction patterns for iterables. Docs: https//docs.rs/reductor Before & After: Before fn proc

Yotam Ofek 2 Jul 9, 2022
Tools to feature more lenient Polonius-based borrow-checker patterns in stable Rust

Though this be madness, yet there is method in 't. More context Hamlet: For yourself, sir, shall grow old as I am – if, like a crab, you could go back

Daniel Henry-Mantilla 52 Dec 26, 2022
In this repository you can find modules with code and comments that explain rust syntax and all about Rust lang.

Learn Rust What is this? In this repository you can find modules with code and comments that explain rust syntax and all about Rust lang. This is usef

Domagoj Ratko 5 Nov 5, 2022
A comprehensive and FREE Online Rust hacking tutorial utilizing the x64, ARM64 and ARM32 architectures going step-by-step into the world of reverse engineering Rust from scratch.

FREE Reverse Engineering Self-Study Course HERE Hacking Rust A comprehensive and FREE Online Rust hacking tutorial utilizing the x64, ARM64 and ARM32

Kevin Thomas 98 Jun 21, 2023
An API for getting questions from http://either.io implemented fully in Rust, using reqwest and some regex magic. Provides asynchronous and blocking clients respectively.

eithers_rust An API for getting questions from http://either.io implemented fully in Rust, using reqwest and some regex magic. Provides asynchronous a

null 2 Oct 24, 2021
Fast and simple datetime, date, time and duration parsing for rust.

speedate Fast and simple datetime, date, time and duration parsing for rust. speedate is a lax† RFC 3339 date and time parser, in other words, it pars

Samuel Colvin 43 Nov 25, 2022
A simpler and 5x faster alternative to HashMap in Rust, which doesn't use hashing and doesn't use heap

At least 5x faster alternative of HashMap, for very small maps. It is also faster than FxHashMap, hashbrown, ArrayMap, and nohash-hasher. The smaller

Yegor Bugayenko 12 Apr 19, 2023
Safe, efficient, and ergonomic bindings to Wolfram LibraryLink and the Wolfram Language

wolfram-library-link Bindings to the Wolfram LibraryLink interface, making it possible to call Rust code from the Wolfram Language. This library is us

Wolfram Research, Inc. 28 Dec 6, 2022
This blog provides detailed status updates and useful information about Theseus OS and its development

The Theseus OS Blog This blog provides detailed status updates and useful information about Theseus OS and its development. Attribution This blog was

Theseus OS 1 Apr 14, 2022
Omeglib, a portmanteau of "omegle" and "library", is a crate for interacting with omegle, simply and asynchronously

Omeglib, a portmanteau of "omegle" and "library", is a crate for interacting with omegle, simply and asynchronously. It is intended to suit one's every requirement regarding chat on omegle.

null 1 May 25, 2022
A tool and library to losslessly join multiple .mp4 files shot with same camera and settings

mp4-merge A tool and library to losslessly join multiple .mp4 files shot with same camera and settings. This is useful to merge multiple files that ar

Gyroflow 7 Jan 2, 2023
A tray application for Windows that gives you push notifications and instant downloads of new posts, messages and stories posted by models you subscribe to on Onlyfans.

OF-notifier A tray application for Windows that gives you push notifications and instant downloads of new posts, messages and stories posted by models

Gentlemen Mercenary 10 Dec 20, 2022
Leetcode Solutions in Rust, Advent of Code Solutions in Rust and more

RUST GYM Rust Solutions Leetcode Solutions in Rust AdventOfCode Solutions in Rust This project demostrates how to create Data Structures and to implem

Larry Fantasy 635 Jan 3, 2023
Rust 核心库和标准库的源码级中文翻译,可作为 IDE 工具的智能提示 (Rust core library and standard library translation. can be used as IntelliSense for IDE tools)

Rust 标准库中文版 这是翻译 Rust 库 的地方, 相关源代码来自于 https://github.com/rust-lang/rust。 如果您不会说英语,那么拥有使用中文的文档至关重要,即使您会说英语,使用母语也仍然能让您感到愉快。Rust 标准库是高质量的,不管是新手还是老手,都可以从中

wtklbm 493 Jan 4, 2023
Integra8 rust integration test framework Rust with a focus on productivity, extensibility, and speed.

integra8 Integra8 rust integration test framework Rust with a focus on productivity, extensibility, and speed. | This repo is in a "work in progress"

exceptional 3 Sep 26, 2022