{
"$type": "site.standard.document",
"canonicalUrl": "https://deterministic.space/how-to-order-rust-code.html",
"path": "/how-to-order-rust-code.html",
"publishedAt": "2019-01-17T00:00:00.000Z",
"site": "at://did:plc:x67qh7v3fd7znbdhauc45ng3/site.standard.publication/3mjcd2t6afe25",
"textContent": "Note:\nThis post is about how I arrange the code I write in Rust.\nIf you wanted to \"order\" Rust code\nin the \"hire someone to write code\" sense,\nyou should still keep on reading\nas this is excellent material for a job interview.\n(Not the opinion I present but having an opinion on the topic.)\n\nArrange code to be in the suggested reading order\n\nI try to order the functions/modules/items in my source files\ngoing from most high-level to most-concrete/small-scope.\nYou might call it fn main first.\n\nI do this so that when someone opens the file and starts reading\nthey can get the general idea of what this file is about very quickly\nand then, if needed, dive into the details they are looking for.\nI think this works especially well for the entry files in a project.\n\nThe opposite position would be to start with\nthe generic helpers, then introduce domain specific types and functions,\nand finally have a main function\nthat calls all the things you've defined above it.\nI tend to read these files by scrolling to the end\nand then moving upwards;\nso it just feels weird to someone used to reading European languages.\n\nThis is not necessarily the order I write code in\n\nI typically don't sit down,\nwrite code from top to bottom,\nand end up with a perfectly structured and arranged file.\nThat is not the goal at all:\nIt's easy enough to copy and paste parts of a file into another,\nor use editor/language plugin features to quickly navigate between sections of files\nwhen I'm looking for something specific.\nRemember:\nMy goal of arranging code in the way described above is\nfor when you read it for the first time.\n\nSome specifics about Rust code\n\nThe order of items usually doesn't matter in Rust\n(macros are a weird edge-case).\nThere are some things to decide though:\n\nHow to order type definitions (struct, enums) and their implementations?\n\nThere are two obvious choices:\n\n- Define all the types first and then list all the implementations?\n- Interleave the implementations with the types?\n\nI personally am fine with both,\nand tend to go with the latter.\nI might however split impl blocks up\nand define some methods (especially private ones)\nright next to the functions they are needed for.\n(This sometimes feels like ad-hoc single-instance traits.)\n\n[@matklad] had another interesting comment:\n\n> [I] love to read types upfront\n> (if you know the set of fields, you know all potential methods that can exists)\n\nWhere to put use statements?\n\nTo use an item (type, trait, function, etc.) that is not in scope\nyou can either refer to it by its full path\n(e.g., std::collections::HashMap)\nor import it using\nuse std::collections::HashMap;\nafter which you can refer it as just HashMap.\nThe issue is:\nWhere to put these use statements?\n\nOne typical approach is to put them all at the top of the file.\nThis \"wall of imports\"\nis what you also see in many other programming languages.\nThis is a good idea if the only location you can put import statements\nis at the root level\nand especially if the file contains one \"main item\"\n(e.g., if foo.java contains import statements followed by class Foo { … }).\nIn Rust, however,\nyou don't often have just one item at the root level.\nYou have a foo.rs\nthat contains a struct Foo { … },\nvarious impl Bar for Foo { … } blocks,\npossibly some free functions,\nand in many cases even unit tests.\nSo, we should rethink where to put these use lines!\n\nOne approach I've taken previously is\nto keep uses as close to the area they are needed as possible.\nIf I have a function that reads five files,\nI add a use std::fs::File; at the beginning _of that function._\nSadly,\nthis breaks down when you want to import a type\nto use it in a function's/method's signature or as a field type in a struct:\nIn that case,\nthe use use needs to be on the level _above_ the usage point\n(i.e., on the level of the function/trait/struct definition).\nAdditionally,\nif you have use std::sync::Arc above one struct,\nit becomes available in the general scope.\nSo, your next struct that uses Arc\ndoesn't need to have a second instance of that use line.\n\nThis all lead me to the point where\nI often just go back to collecting uses at the top of the file.\nI will however not write single use lines for all the items I want to use,\nbut instead\n\n- only import the module when I use various items but each of them only a few times (e.g. use std::sync; and refer to sync::Arc and sync::Mutex.)\n- make use of Rust's nested imports (e.g. use std::{error::Error, fs, io::{self, Read}};).\n\nSplit public and private interfaces\n\nWhen the entry file of a package or module[^1] gets to long,\nyou want to split it up.\nA solid approach is to move implementation details are in a separate file,\nwhich might end up being an \"helpers\" file\nor actually be most of the code split up in modules\nthat are not exposed to the outside.\n\n[^1]: \"Package or module\"? Yes, and also \"application\" and \"function\": This is a fractal property.\n\nIn languages that allow specifying the visibility of items on a very granular level\nyou can very precisely mark only parts of your code as \"public interface\".\nBut this means also means that there is a non-public interface:\nIndeed, most abstractions have two interfaces:\nA public, consumer-facing one,\nand an internal one, for \"producers\".\nConsciously separating the two by the layout of your code\nwill help create maintainable and comprehensible code bases.\n\nAbstractions on top of abstractions\n\nOften, you end up writing structures that are only used internally\nbut then get converted into other structures for the consumers of your package/module.\nI don't have a good recipe on how to deal with that,\nexcept that I would recommend trying to\nthe boilerplate/conversion part \"obvious\"/invisible and\nthus highlight the differentiating details.\n\n[@matklad]: https://github.com/matklad",
"title": "How to order Rust code",
"updatedAt": "2019-01-17T00:00:00.000Z"
}