My personal style guide for the D programming language. This document outlines best practices that I have found to help with D source code readability and cleanliness.
Take a moment to think about what you spend the most time doing when you are writing a program. Is it typing? Certainly not. Is it looking through documentation? Arguably. Is it reading the mess you've written and trying to understand it? Most of the time.
Code readability is very important. If no one (including you!) can read your code, you've done somthing wrong. The vast majority of our time as programmers is spent reading and understanding either our code or someone else's. The majority of this document focuses on code quality as code readability. The rules defined here are designed to make your code easier to read and easier to follow.
One thing this document does not claim to do is to make code easier to write. In fact, it does quite the opposite. Making code easy to read is inherently making it harder to write. Keep that in mind while reading.
This is the simplest rule in the book. Indentation should be consistent above all else. Functions, classes, and etc. should be indented tab length can be two or four spaces, but tab length should be consistent throughout a document. Soft tabs (all spaces) should be preferred to hard tabs (tab character).
Put a space after for
, foreach
, if
, and while
for (…) { … }
foreach (…) { … }
if (x) { … }
while (…) { … }
do { … } while (…);
Put a space between binary operators and assignment.
a + b
a = b
Comments are often heralded as being a good way to write readable code. This is partially true. Good comments are concise and add to the meaning of a line of code instead of duplicating it. For example:
//Add a and b
a = a + b;
Don't do that. Never use comments to repeat the meaning of a line of code. Use it to add meaning. I cannot repeat this enough times.
Comments should only be used when they are nessasary, which usually means when a piece of code is not self-documenting. Instead of:
//Get the score and save it in data
data = ballgame.getData();
Make your function names descriptive:
score = ballgame.getScore();
This shortens your code, and keeps it maintainable, because you do not have to update the comment when you update the line.
In any language, keeping your namespace clean can help make the structure of your program easier to understand. I find that when I'm trying to read someone else's source code, one of the hardest things is figuring out where they imported a class or function from, so that I can go look at its implementation. This is language agnostic. To solve this problem for users in D, I have a few best practices:
- Keep your imports formatted nicely. Instead of this:
import std.conv;
import myproject.randommodule;
import myproject.othermodule;
import std.stdio;
import std.algorithm.mutation;
Do this:
import std.stdio;
import std.conv;
import std.algorithm.mutation;
import myproject.randommodule;
import myproject.othermodule;
- Import specific functions, classes, and other symbols explicitly rather than importing the whole module (alternative below)! I can't stress this enough. This is the best thing for code clarity since goto went out of style. Instead of one of the above examples, do this:
import std.stdio : writeln, write;
import std.conv : to;
import std.algorithm.mutation : remove;
import myproject.randommodule : myRandFunc, myOtherRandFunc, MyClass;
import myproject.othermodule : myFunc, myStruct, MyMixin;
This allows you to easily see what module a function or class is being imported from, and can be especially helpful when your program includes a lot of modules.
- If you don't want to list all of your functions/classes/etc, you can use a static import. Static imports make you namepsace your function/Class/etc usages, similar to C++. Static imports are not compatible with selective imports.
static import std.stdio;
void main() {
std.stdio.writeln("My Stuff");
}
Type inferrence can be a convenient feature of a language. However, sometimes it makes for difficult readability, because it may not be immediately clear what type of variable you are working with. D allows type inferrence with a few keywords: auto
, immutable
, const
, and enum
.
Avoid it. In general, it promotes sloppier code.
Auto can be used for almost any variable or function declaration. However, when used for function declarations, where return values are almost always inferrable, it can sometimes lead to slip-ups. For example:
auto getSomeValue() {
/* Do a bunch of things that may
take up
a bunch
of
lines
of code
and forget your return statement */
}
Arguably the only code-readability improvement auto
can make is this situation:
auto myVar = new VeryVeryVeryVeryLongClassName();
In this situation, it is immediately clear based on the constructor what that variable is, and it reduces extreme redundancy.
Oftentimes it can be easy to forget your type when creating immutable ad const values. Be cognisant of when this happens, because it will often compile without any issues. This can be especially harmful when someone else is reading your code, because you may know exactly what that variable is doing, but it may not be immediately clear to a thrid party.
enum
arguably is the only place where type inferrence should be used by default. In many cases, an enum is simply a value whose value does not matter. For example:
enum TrafficLightState {
green,
yellow,
red
}
In this instance, the base type of the enum really does not matter, because the enum is only being used to pass around a state, not a value.
A C language feature that has carried over to D is the ability to use an if statement without brackets if only one line of code is being executed after the if statement. For example:
if (/* condition */)
/* statement */
This allows for an if/else chain without a ridiculous number of brackets (as detailed by Ali Çehreli in his book Programming in D). This is the only case where a bracketless if
should be used, because it can cause confusion if whitespace rules are not followed.
Please comment if you would challenge anything, or suggest improvements or additions! Also teach me how to use spellcheck in the gist editor...