D言語コーディング規約メモ
空白
- 1行に1文
- タブではなくスペースを使用する
- インデントのレベル毎ニ、4つの空白文字
int a; // OK
int a; int b; // NG. Having two statements.
if (a) {
int a; // OK
int b; // NG. The indent should be 4 columns.
}
命名規約
一般
特筆ない限り、命名には camelCase を使用する。これは全ての変数を含む。命名は、それがprivateでない限り _
で始めるべきではない
int myFunc();
string myLocalVar
モジュール
モジュールとパッケージの命名には lowercase を使用し、[a..z][0..9][_]
のみを使用する。これは大文字小文字を区別しないファイルシステムに対応するためである。
import std.algorithm
Classes, Interfaces, Structs, Unions, Enums, Non-Eponymous Templates
ユーザ定義型の命名は PascalCase を使用する。
class Foo;
struct FooAndBar
※Non-Eponymous Templates は、後述の Eponymous Templates ではないという意味
Eponymous Templates
テンプレート内のシンボルと同名のテンプレートの場合、内部のシンボルと同じ命名になる。例えば、型は PascalCase で、変数は camelCase になる。
template GetSomeType(T) { alias GetSomeType = T; }
template isSomeType(T) { enum isSomeType = is(T == SomeType); }
template MyType(T) { struct MyType { ... } }
template map(fun...) { auto map(Range r) { ... } }
関数
関数の命名は camelCase を使用する。これはプロパティとメンバー関数を含む。
int done();
int doneProcessing();
定数
定数の命名は、通常の変数と同様に camelCase を使用する。
enum secondsPerMinute = 60;
immutable hexDigits = "0123456789ABCDEF";
Enum メンバー
enum のメンバーは camelCase を使用する。
enum Direction { bwd, fwd, both }
enum OpenRight { no, yes }
キーワード
もし命名がキーワードと被り、かつ他の名前を使うのが相応しくない場合、_
をサフィックスとして追加する。大文字を使うという回避はするべきではない。
enum Attribute { nothrow_, pure_, safe }
略語
シンボル名として頭文字による略語を使用する場合、略語の中の全ての文字の大文字小文字は統一される。つまり、最初の文字が小文字であれば全て小文字で、大文字であれば全て大文字となる。
class UTFException;
ubyte asciiChar
UDA
UDAの命名は camelCase を使用する。このルールは、これまで記述したルールよりも優先される。これはビルトインアトリビュートのルールと一致する。
struct Foo {} // This struct follows the regular naming conventions.
// This is only for UDA
struct name { string value; }
@name("bar") Foo foo;
型エイリアス
型エイリアスの記述の方法は2種類あるが、=
を使用する方法を使う。
alias size_t uint; // OK
alias uint size_t; // NG
以下のような意味のないエイリアスは使わない。
alias VOID = void;
alias INT = int;
alias pint = int*;
宣言のスタイル
宣言は左側に紐づくため、識別子は左に寄せる
int[] x, y; // makes it clear that x and y are the same type
int** p, q; // ditto
int []x, y; // confusing since y is also an int[]
int **p, q; // ditto
オペレータオーバーロード
オペレータオーバーロードは強力だが、コードを難解にするリスクがある。、既存のDオペレータは直感に反しない意味を持っている(+
は加算、<<
は左シフト)。+
に加算以外の意味を持たせるのは混乱を生むため避ける。
ハンガリアン記法
変数の型が何であるかを示すためにハンガリアン記法を使用するのは良くない。しかしながら、型に表れない目的を示すために注釈を入れるのはしばし上手くいく。
プロパティ記法
意図したユースケースでない限り、UFCS と 括弧の省略(関数を括弧なしで呼べる)は使用すべきではない。括弧の省略は、メンバーが変数か関数かを気にしなくて良い場合に有効である。例えば、range の front
など。また、 range 関数をコンパクトに繋ぐ場合にも有効である。しかし、単純な呼び出しが副作用を持つ関数になる場合、通常の関数呼び出しを行うべき。
import std.range, std.stdio;
void main()
{
// good
writeln();
writeln("hello");
iota(0, 10).dropOne.array.front.writeln;
// bad
writeln;
"hello".writeln;
writeln = "hello"; // This is a syntax sugar of writeln("hello")
}
ドキュメンテーション
全てのpublic 宣言は Ddocによるドキュメントを記述すべきである。少なくとも、Params
と Returns
セクションは必要。
Unit Tests
可能な限り、全ての関数はunittest
による確認を行うべきである。すべてのコードパスは少なくとも1回は実行されるべきである。これにはカバレッジを使用することができる。
Phobos 向けの追加の要求
このガイドラインは基本的にフォーマットガイドラインに関する推奨も要求も示していない。例外は最初に記述した空白に関するものだけである。しかし、Phobos や他の公式のDコードには追加の要求がある。
括弧
波括弧は独立した行に記述する。例外は lambda 関数などである。
void func(int param)
{
if (param < 0)
{
...
}
else
{
...
}
}
不要な括弧は避ける。
(a == b) ? "foo" : "bar"; // NO
a == b ? "foo" : "bar"; // OK
行の長さ
各行は、80文字のソフトリミットを持ち、120文字のハードリミットを持つ。つまり、通常は80文字の制限だが、必要に応じて超過することが可能。ただし、120文字を超えてはならない。
空白
-
for
,foreach
,if
,while
,version
の後には空白を入れる
for (…) { … }
foreach (…) { … }
static foreach (…) { … }
if (x) { … }
static if (x) { … }
while (…) { … }
do { … } while (…);
version (…) { … }
-
else if
,static if
,else version
のようなキーワードが連結している場合は同じ行に記述する。
if (...)
{
...
}
else if (...)
{
...
}
- 2項演算子、代入、キャスト、ラムダには空白を入れる。
a + b
a / b
a == b
a && b
arr[1 .. 2]
int a = 100;
b += 1;
short c = cast(short) a;
filter!(a => a == 42);
- 単項演算子、
assert
の後には空白を入れない。
a = !a && !(2 == -1);
bool b = ~a;
auto d = &c;
e++;
assert(*d == 42);
callMyFancyFunction("hello world");
Imports
- 局所的で選択的な import は全てを import するよりも望ましい
- 選択的な import では
:
の後にスペースを入れる (e.g.import std.range : zip;
) - import はソートされているべき。
返り値の型
- ドキュメントとコードを読み易くするために、返り値の型は可能な限り明示すること
- 関数内にネストされた構造体を返す(aka Voldemort types)のは、public struct よりも望ましい。
Attributes
- テンプレートではない関数は対応する attribute でアノテートされているべき(
@nogc
,@safe
,pure
,nothrow
) - テンプレート関数において、テンプレート引数が設定すべき attribute に影響を与える場合、コンパイラに推論させるべきで、 attribute を記述すべきではない。テンプレート引数が attribute に影響を与えない場合、テンプレートでない関数と同様に attribute を記述すべき。
- attribute はアルファベット順に並べるべき。(
@
は含まない) - テンプレート関数における attribute の存在を確認するために、 全ての unittest はアノテートされているべき。
テンプレート
- テンプレート内で
unittest
ブロックを記述するのは避けるべき。テンプレートと一緒にunittest
もそれぞれインスタンス化されるため。unittest
はテンプレートの外側で記述する。
宣言
- 宣言における制約条件は、各宣言と同じインデントレベルにすべき。
void foo(R)(R r)
if (R == 1)
- 事前条件、事後条件は対応する宣言と同じインデントレベルにすべき。1つのassert を持つ長い表現の文法と同じ意味を持つ式ベースの表現がある場合、式ベースの表現を使うべき。
in/out
の後には空白を入れる。
// Prefer:
T transmogrify(T)(T value)
if (isIntegral!T)
in (value % 7 == 0)
out (result; result % 11 == 0)
{
// ...
}
// over this:
T transmogrify(T)(T value)
if (isIntegral!T)
in { assert(value % 7 == 0); }
out (result) { assert(result % 11 == 0); }
do
{
// ...
}
- 不変条件(
invariant
) は、1つのassert を持つ長い表現と同じ意味の式ベースの表現を持つ時、式ベースを使用すべき。invariant
の後には空白を入れる。
struct S
{
int x;
invariant (x > 0);
}
Class/Struct の変数宣言
- 構造体及びクラスでは、型の後のスペースは1つのみにすべき。これは将来の変更によるdiffが大きくなるのを防ぐため。
class MyClass
{
// bad
int a;
double b;
// good
int x;
double y;
}
ドキュメンテーション
- 全てのpublicシンボルはドキュメンテーションされているべき。
- 全てのpublic 関数は Ddoc 形式の概要と、Params, Returns セクションを持つべき。
/**
Checks whether a number is positive.
`0` isn't considered as positive number.
Params:
number = number to be checked
Returns: `true` if the number is positive, `0` otherwise.
See_Also: $(LREF isNegative)
*/
bool isPositive(int number)
{
return number > 0;
}
- セクション内の文字は、それが2行以上に渡る時は次の行から、1レベルインデントされて記述されるべき。
- ブロックコメント(
/**
)か、ネスト可能なブロックコメント(/++
)を使用すべき。例外はditto
コメント (/// Ditto
)。 - ドキュメントコメントの各行は、先頭のアスタリスクを記述すべきではない。
- Example ブロックには 3つのダッシュ(
---
)のみを使用する。
リファレンス
vscode でのオプションの挙動
Align Switch Statements
true
import std.stdio;
void main()
{
int a = 0;
switch (a)
{
case 0:
writeln("hello");
break;
case 1: .. case 4:
writeln("world");
break;
default:
assert(0);
}
}
false
import std.stdio;
void main()
{
int a = 0;
switch (a)
{
case 0:
writeln("hello");
break;
case 1: .. case 4:
writeln("world");
break;
default:
assert(0);
}
}
Brace Style
vscode では、 editorconfig による設定はできない。
allman
import std.stdio;
void main()
{
int a = 0;
if (a == 0)
{
writeln("Hello");
}
else if (a == 1)
{
writeln("world");
}
}
otbs
import std.stdio;
void main() {
int a = 0;
if (a == 0) {
writeln("Hello");
} else if (a == 1) {
writeln("world");
}
}
strustrup
import std.stdio;
void main() {
int a = 0;
if (a == 0) {
writeln("Hello");
}
else if (a == 1) {
writeln("world");
}
}
Compact labeled Statements
true
import std.stdio;
void main()
{
foo: foreach (i; 0 .. 10)
{
writeln("hello");
}
}
false
import std.stdio;
void main()
{
foo:
foreach (i; 0 .. 10)
{
writeln("hello");
}
}
Keep Line Breaks
true
import std.stdio;
void main()
{
writeln(
"hello");
}
false
import std.stdio;
void main()
{
writeln("hello"); // ← formatted
}
Selective Import Space
true
import std.stdio : writeln;
false
import std.stdio: writeln;
Space After Cast
// true
auto a = cast(int) 0.0;
// false
auto a = cast(int)0.0;
Space Before AAColon
// true
auto a = ["a" : 0, "b" : 1];
// false
auto a = ["a": 0, "b": 1];
Space Before Function Parameters
// true
void main (string[] args)
// false
void main(string[] args)
Split Operator At Line End
?
Template Constraint Style
// conditional_newline_indent
template A(T) if (is(T == int))
{
alias Foo = T;
}
// conditional_newline
template A(T) if (is(T == int))
{
alias Foo = T;
}
// always_newline
template A(T)
if (is(T == int))
{
alias Foo = T;
}
// always_newline_indent
template A(T)
if (is(T == int))
{
alias Foo = T;
}
EditorConfig
# Phobos style
indent_style = space
indent_size = 4
dfmt_brace_style = allman
max_line_length = 120
dfmt_soft_max_line_length = 80
dfmt_align_switch_statements = unset
dfmt_outdent_attributes = unset
dfmt_split_operator_at_line_end = unset
dfmt_space_after_cast = true
dfmt_space_after_keywords = unset
dfmt_space_before_function_parameters = unset
dfmt_selective_import_space = true # according to the example
dfmt_compact_labeled_statements = unset
dfmt_template_constraint_style = always_newline
dfmt_single_template_constraint_indent = unset
dfmt_space_before_aa_colon = unset
dfmt_keep_line_breaks = unset
dfmt_single_indent = unset
vscode
{
"dfmt.braceStyle": "allman",
"dfmt.selectiveImportSpace": true,
"dfmt.spaceAfterCast": true,
"dfmt.templateConstraintStyle": "always_newline"
}