Style guide
In general, this guide tries to follow the recommended D style and Phobos conventions. However, there are a number of differences, such as comment style and brace placement inside of functions.
If you want to use a formatting utility, vibe.d's .editorconfig file contains dfmt (included with the compiler) directives that result in a file formatted pretty closely to these guidelines with regards to brace placement and spacing. It doesn't cover all rules and doesn't produce optimal formatting, but provides a decent starting point.
Naming conventions
Modules and packages
Rules | Rationale |
---|---|
Module and package names are always written lower case. This applies both, to the module declaration, and to the file/directory name. Names usually do not contain any non-alphanumeric characters. An exception to this is when a module name equals that of a D keyword such as A library or application should have all of its source code contained below its own, single root package. |
The naming conventions follows the D style conventions and putting everything in one root package avoids conflicting modules and allows to esily map a module to a certain library/application. |
module mylib.system.process; module mylib.compiler.function_;
Type names
Rules | Rationale |
---|---|
Type names are always written in CamelCase. Acronyms are written all-caps, except if they form a de-facto word that is pronouncible. Types never have a prefix that characterizes their type family (e.g. interfaces do not have the common I-prefix). |
Follows the D/Phobos style conventions. |
class Test {} class HTMLRenderer {} // HTML is an unpronouncible acronym class JsonProcessor {} // Json is a de-facto word struct MySimpleStruct {} interface MyInterface {} // not IMyInterface! alias int MyInteger;
Functions and methods
Rules | Rationale |
---|---|
All functions and methods follow a pascalStyle naming scheme. They always begin with a verb. |
Follows the D/Phobos style conventions. |
int performSomething() {} struct MyStruct { void computeMe() {} } void performTemplateComputation(T)(T arg) {}
Variables
Variables have different naming conventions according to their place of definition and according to their level of protection. This is to make it obvious with which kind of variable is dealt with in a certain piece of code, so that the code is easy to understand and looking up or changing the variable declaration is made simple. Variables never begin with a verb to make them distinguishable from functions and methods.
Global variables
Rules | Rationale |
---|---|
The naming scheme of global variables depends on their shared state. Their basic naming scheme is pascalCase. Global thread-local variables get a |
The semantic prefix makes it possible to immediately distinguish the scope of different variables, as well as their need for synchronization. Encountering use of a shared variable with no synchronization/atomic operation in place can be directly spotted. |
private int g_moduleLevelVariable; package int g_packageLevelVariable; public int g_publicVariable; public int publicVariable; // possible, but @property recommended shared int gs_globalSharedVariable; __gshared int gs_globalSharedVariable2;
Function parameters
Rules | Rationale |
---|---|
Parameters use underscore_naming_convention. They should have meaningful names without abbrevations, if practical. Remember that these will show up in the API documentation later on. |
The use of a lower-case naming convention enhances the visual separation of function name and signature and allows to distinguish local variables from fields or global variables immediately. |
void myFunction(int first_parameter, int second_parameter, int next) {}
Local variables
Rules | Rationale |
---|---|
Local variable names should stay short (use abbrevations) and contain neither underscores, nor upper case letters. The abbrevations must stay clear and one-letter variable names such as |
Using this convention makes local variables distinguishable from parameters and public fields in most cases. Since local variables are expected to show up very frequently in the code, the short nature of their name helps to keep the code concise and readable. |
void func() { int idx = 0; int maxvalue = 0; char[] outbuf; // abbrevated to keep the name short foreach (i; 0 .. 10) maxvalue += i; }
Fields and properties
Rules | Rationale |
---|---|
Struct/class variables and properties in general follow the pascalCase naming convention. Private and protected class/struct member variables have an |
The prefix makes member variables immediately recognizable as fields and this avoids the need to use `this.` as a prefix to distinguish them from other variables optically. Public fieds still require that prefix so they can be distinguished from parameters and local variables without searching the declaration. |
class MyClass { private { int m_myField; } protected { int m_myField2; } int somePublicField; // public fields have no prefix @property int myField() const { return m_myField; } void doSomething() { this.somePublicField = m_myField; m_myField2 = this.myField; } }
Punctiation
Braces
Rules | Rationale |
---|---|
There are three kinds of brace placement styles used:
Consecutive control statements of the same logical chain are appended directly to the same line as the preceeding closing brace. |
Cases 2 and 3 are supposed to increase vertical conciseness in places where it usually benefits readability. Case 1 on the other hand sets apart the function signature from the body to improve the visual distinction of declaration and implementation when skimming over the source code. |
private { int s_globalCounter; } class C { private { int m_field; } @property int field() const { return m_field; } void doSomething() { try { foreach (i; 0 .. 10) { // ... } } catch (Exception e) { // ... } finally { // ... } if (1 == 2) { // ... } else { // ... } } }
Clamps
Rules | Rationale |
---|---|
In general, the inside of clamps tightly fits its contents. For function declarations and function calls, there is also no space before the opening clamp. Control statements have a single space before the opening clamp. |
Tight clamps make the code more concise and help grouping related content together visually. Since control statements often contain large expressions with logical operators inside, a space is added to visually separate the expression from the rest of the line, resulting in better visual cues about it. |
int doSomething(int a, int b, int c) { assert (b > 0); // maybe assert should be an exception? if (a > 0) doSomething(a - b, a * (c + a), c); switch (b) { default: return b+c; case 1: return 0; case 2: return -1; } }
Operators
Rules | Rationale |
---|---|
Binary operators are usually surrounded by spaces, except in cases where it enhances readability to leave them off. Unary operators are always directly preceeding or following their argument. The ".." range token is always surrounded by spaces. |
int count = 0; foreach (i; 0 .. 10) { if (i / 2 > 3) doSomething(count++); else doSomething(sum(array[i .. i+1])); }
Order of declaration
The general intention for the declaration order as specified here is to be as helpful as possible in reading/scanning the source code as a user of the code and also, as a secondary goal, as the developer of the code.
Module level
Rules | Rationale |
---|---|
The general order of declaration is sorted by protection level: Inside of these sections it is recommended to put functions first, followed by type definitions. The declaration order then generally goes from high level to low level (i.e. a function which uses some other functions comes before the functions that it depends on). |
Class/struct level
Rules | Rationale |
---|---|
The following order must be adhered to:
Other combinations (e.g. |
This order results from the following goals:
|
Function level
Inside of functions, there is generall no restriction on the order of declarations. However, it is recommended to place inner functions and types, as well as static variables at the top of the function. Variable declarations should occur just before their first use or at the latest possible earlier point.
White space
Rules | Rationale |
---|---|
Basic indentation always uses tab characters and no spaces. Indentation is always semantic, meaning that each nesting level gets exactly one tabulator of indentation. When braking up a statement or a declaration into multiple lines, indent all but the first line by one additional tab. Everything else, and in particular aligning comments on different lines to the same column, is done with spaces and no tabs. Function declarations are separated by one or two blank line and inside of functions a single blank line should be used to separate different functional/logical blocks (e.g. a loop from other statements). |
Intendation is probably the topic that is mostly argued about. The rule used here is pragmatic in that it allows different people to set different tab widths, but still guanrantees that manual alignment works correctly. |
enum MyEnum {↓ → someMember,······/// Aligned comment↓ → someOtherMember··/// Another aligned comment↓ }↓ ↓ foreach (i; 0 .. 10) {↓ → makeSomethingThatCausesAnOverlongLine(i, i+1,↓ → → i, i*2 + 10, "some param");↓ }↓
Doc comments
Rules | Rationale |
---|---|
Documentation comments come in three basic forms (see example). Usually the same style should be used throughout all documented members of an entity, but exceptions may be made in the benefit of readability. In general, very short documentation strings (e.g. for The module header must always contain the "Authors", "Copyright" and "License" sections, as given in the example. |
/** This is the short description for the module. This is the long description of the module that gives an introduction to the module as a whole. This is optional. Copyright: © 2020 Sönke Ludwig License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. Authors: Some Name, Some Other Name */ module vibe.some.module; /** This is a class. */ class Class { /** This function does something. Params: param1 = Maybe this parameter has an effect param2 = Maybe this one has also an effect Returns: The result of doing something is returned. */ int doSomething(int param1, int param2); /// ditto int doSomething(); }