|
| 1 | +--- |
| 2 | +title: How to order Rust code |
| 3 | +categories: |
| 4 | +- rust |
| 5 | +--- |
| 6 | +**Note:** |
| 7 | +This post is about how I arrange the code I write in Rust. |
| 8 | +If you wanted to "order" Rust code |
| 9 | +in the "hire someone to write code" sense, |
| 10 | +you should still keep on reading |
| 11 | +as this is excellent material for a job interview. |
| 12 | +(Not the opinion I present but having an opinion on the topic.) |
| 13 | + |
| 14 | +## Arrange code to be in the suggested reading order |
| 15 | + |
| 16 | +I try to order the functions/modules/items in my source files |
| 17 | +going from most high-level to most-concrete/small-scope. |
| 18 | +You might call it **`fn main` first**. |
| 19 | + |
| 20 | +I do this so that when someone opens the file and starts reading |
| 21 | +they can get the general idea of what this file is about very quickly |
| 22 | +and then, if needed, dive into the details they are looking for. |
| 23 | +I think this works especially well for the entry files in a project. |
| 24 | + |
| 25 | +The opposite position would be to start with |
| 26 | +the generic helpers, then introduce domain specific types and functions, |
| 27 | +and finally have a `main` function |
| 28 | +that calls all the things you've defined above it. |
| 29 | +I tend to read these files by scrolling to the end |
| 30 | +and then moving upwards; |
| 31 | +so it just feels weird to someone used to reading European languages. |
| 32 | + |
| 33 | +## This is not necessarily the order I write code in |
| 34 | + |
| 35 | +I typically don't sit down, |
| 36 | +write code from top to bottom, |
| 37 | +and end up with a perfectly structured and arranged file. |
| 38 | +That is not the goal at all: |
| 39 | +It's easy enough to copy and paste parts of a file into another, |
| 40 | +or use editor/language plugin features to quickly navigate between sections of files |
| 41 | +when I'm looking for something specific. |
| 42 | +Remember: |
| 43 | +My goal of arranging code in the way described above is |
| 44 | +for when you read it for the first time. |
| 45 | + |
| 46 | +## Some specifics about Rust code |
| 47 | + |
| 48 | +The order of items usually doesn't matter in Rust |
| 49 | +(macros are a weird edge-case). |
| 50 | +There are some things to decide though: |
| 51 | + |
| 52 | +### How to order type definitions (struct, enums) and their implementations? |
| 53 | + |
| 54 | +There are two obvious choices: |
| 55 | + |
| 56 | +- Define all the types first and then list all the implementations? |
| 57 | +- Interleave the implementations with the types? |
| 58 | + |
| 59 | +I personally am fine with both, |
| 60 | +and tend to go with the latter. |
| 61 | +I might however split `impl` blocks up |
| 62 | +and define some methods (especially private ones) |
| 63 | +right next to the functions they are needed for. |
| 64 | +(This sometimes feels like ad-hoc single-instance traits.) |
| 65 | + |
| 66 | +[@matklad] had another interesting comment: |
| 67 | + |
| 68 | +> [I] love to read types upfront |
| 69 | +> (if you know the set of fields, you know all potential methods that can exists) |
| 70 | +
|
| 71 | +### Split public and private interfaces |
| 72 | + |
| 73 | +When the entry file of a package or module[^1] gets to long, |
| 74 | +you want to split it up. |
| 75 | +A solid approach is to move implementation details are in a separate file, |
| 76 | +which might end up being an "helpers" file |
| 77 | +or actually be most of the code split up in modules |
| 78 | +that are not exposed to the outside. |
| 79 | + |
| 80 | +[^1]: "Package or module"? Yes, and also "application" and "function": This is a fractal property. |
| 81 | + |
| 82 | +In languages that allow specifying the visibility of items on a very granular level |
| 83 | +you can very precisely mark only parts of your code as "public interface". |
| 84 | +But this means also means that there is a non-public interface: |
| 85 | +Indeed, most abstractions have two interfaces: |
| 86 | +A public, consumer-facing one, |
| 87 | +and an internal one, for "producers". |
| 88 | +Consciously separating the two by the layout of your code |
| 89 | +will help create maintainable and comprehensible code bases. |
| 90 | + |
| 91 | +### Abstractions on top of abstractions |
| 92 | + |
| 93 | +Often, you end up writing structures that are only used internally |
| 94 | +but then get converted into other structures for the consumers of your package/module. |
| 95 | +I don't have a good recipe on how to deal with that, |
| 96 | +except that I would recommend trying to |
| 97 | +the boilerplate/conversion part "obvious"/invisible and |
| 98 | +thus highlight the differentiating details. |
| 99 | + |
| 100 | +[@matklad]: https://github.com/matklad |
0 commit comments