date:20170802
介紹
本指導提供代碼約定,來規(guī)范Solidity的代碼瘩欺。本指導會不斷變化必盖,新的,有用的約定會不斷添加進來俱饿,舊的約定會被淘汰歌粥。
很多項目都想自己實現(xiàn)各自的風格指導。作為事件沖突拍埠,項目指定的風格要優(yōu)先考慮失驶。
本指導中的結(jié)構和很多約定都是遵從Python的pep8代碼風格指導。
本指導的目標不是要規(guī)定寫solidity代碼的正確方法枣购,或者最好的方法突勇。目標是統(tǒng)一化。引用pep8的這個概念:
風格指導手冊是對統(tǒng)一化的規(guī)定坷虑。根據(jù)手冊,統(tǒng)一化編碼是很重要的埂奈。同個項目的代碼風格保持一致更加重要迄损。在模塊中統(tǒng)一化,或者函數(shù)中账磺,是最重要的芹敌。但是最重要的:需要知道什么時候要統(tǒng)一化——有時候不需要代碼風格指導。當有疑問的時候垮抗,要用你的判斷力氏捞。查看其它例子,然后決定哪種看起來會最好冒版。不要猶豫提問題液茎,請教別人。
代碼層級
縮進
每層縮進用4個空格。
Tabs和空格
空格是更期望的縮進方式捆等。
tabs和空格混合是要避免的滞造。
空行
solidity源碼的頂層描述要有兩個空行。
Yes:
contract A {
...
}
contract B {
...
}
contract C {
...
}
No:
contract A {
...
}
contract B {
...
}
contract C {
...
}
在合約中栋烤,函數(shù)用一行空行隔開谒养。
相關的只有一行的代碼,空行可以省略(例如虛擬合約的未實現(xiàn)的函數(shù))明郭。
Yes:
contract A {
function spam();
function ham();
}
contract B is A {
function spam() {
...
}
function ham() {
...
}
}
No:
contract A {
function spam() {
...
}
function ham() {
...
}
}
源碼編碼
更傾向于UTF-8 或者 ASCII編碼买窟。
imports
導入表達式應該總是被放置在文件的頂部。
Yes:
import "owned";
contract A {
...
}
contract B is owned {
...
}
No:
contract A {
...
}
import "owned";
contract B is owned {
...
}
函數(shù)的順序
排序有助于讀者可以方便的知道哪些函數(shù)是可以調(diào)用的薯定,且可以快速的找到構造函數(shù)和回調(diào)函數(shù)的定義始绍。
函數(shù)應該按照他們的可見性排序:
- constructor
- fallback 函數(shù)(如果有)
- external
- public
- internal
- private
constant
函數(shù)放置分組的最后。
Yes:
contract A {
function A() {
...
}
function() {
...
}
// External 函數(shù)
// ...
// External 的constant函數(shù)
// ...
// Public 函數(shù)
// ...
// Internal 函數(shù)
// ...
// Private 函數(shù)
// ...
}
No:
contract A {
// External 函數(shù)
// ...
// Private 函數(shù)
// ...
// Public 函數(shù)
// ...
function A() {
...
}
function() {
...
}
// Internal 函數(shù)
// ...
}
表達式中的空格
在下列情況下應該避免額外的空格:
在圓括號沉唠,中括號或者大括號之間沒有空格疆虚,除了單行的函數(shù)描述。
Yes:
spam(ham[1], Coin({name: "ham"}));
No:
spam( ham[ 1 ], Coin( { name: "ham" } ) );
例外:
function singleLine() { spam(); }
在逗號满葛,分號之前径簿,沒有空格:
Yes:
function spam(uint i, Coin coin);
No:
function spam(uint i , Coin coin) ;
賦值應該要多于一個空格,或者和其他操作對齊
Yes:
x = 1;
y = 2;
long_variable = 3;
No:
x = 1;
y = 2;
long_variable = 3;
在回調(diào)函數(shù)中不要包含空格:
Yes:
function() {
...
}
No:
function () {
...
}
控制結(jié)構
大括號包圍的是合約的主體嘀韧,庫篇亭,函數(shù)和結(jié)構體,應該:
- 在同一行中開始
- 結(jié)束標簽要新建一行锄贷,并在他們各自的同個縮進層級的位置
- 開始括號應該用單個空格隔開
Yes:
contract Coin {
struct Bank {
address owner;
uint balance;
}
}
No:
contract Coin
{
struct Bank {
address owner;
uint balance;
}
}
該建議同樣適用于控制體if
,else
,while
和for
译蒂。
另外,在if
,while
谊却,for
和代表條件的括號塊之間要有單個空格柔昼,條件和開始括號之間也有空格:
Yes:
if (...) {
...
}
for (...) {
...
}
NO:
if (...)
{
...
}
while(...){
}
for (...) {
...;}
對于只有一條語句的控制體,且都寫在一行中炎辨,可以省略花括號捕透。
Yes:
if (x < 10)
x += 1;
No:
if (x < 10)
someArray.push(Coin({
name: 'spam',
value: 42
}));
對于有else
或者else if
的if
塊,else應該和if的結(jié)束括號同一行碴萧。對唄其他區(qū)塊結(jié)構的規(guī)則乙嘀,這是一個例外。
Yes:
if (x < 3) {
x += 1;
} else if (x > 7) {
x -= 1;
} else {
x = 5;
}
if (x < 3)
x += 1;
else
x -= 1;
No:
if (x < 3) {
x += 1;
}
else {
x -= 1;
}
函數(shù)聲明
對于簡短的函數(shù)的聲明破喻,推薦函數(shù)聲明和開始括號在同一行虎谢。
函數(shù)的閉合括號應該與函數(shù)聲明行有相同的縮進。
開始括號應該和函數(shù)聲明在同一行曹质。
Yes:
function increment(uint x) returns (uint) {
return x + 1;
}
function increment(uint x) public onlyowner returns (uint) {
return x + 1;
}
No:
function increment(uint x) returns (uint)
{
return x + 1;
}
function increment(uint x) returns (uint){
return x + 1;
}
function increment(uint x) returns (uint) {
return x + 1;
}
function increment(uint x) returns (uint) {
return x + 1;}
函數(shù)的可見性婴噩,應該在任意自定義的修改器之前擎场。
Yes:
function kill() public onlyowner {
selfdestruct(owner);
}
No:
function kill() onlyowner public {
selfdestruct(owner);
}
對于長的函數(shù)聲明,推薦將每個參數(shù)都各自一行讳推,并且縮進和函數(shù)體一樣顶籽。參數(shù)關閉括號和函數(shù)體開始括號在同一行,縮進與函數(shù)聲明一致银觅。
Yes:
function thisFunctionHasLotsOfArguments(
address a,
address b,
address c,
address d,
address e,
address f
) {
doSomething();
}
No:
function thisFunctionHasLotsOfArguments(address a, address b, address c,
address d, address e, address f) {
doSomething();
}
function thisFunctionHasLotsOfArguments(address a,
address b,
address c,
address d,
address e,
address f) {
doSomething();
}
function thisFunctionHasLotsOfArguments(
address a,
address b,
address c,
address d,
address e,
address f) {
doSomething();
}
如果長的函數(shù)聲明有修改器礼饱,那么將修改器放置在單獨的行。
Yes:
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyowner
priced
returns (address)
{
doSomething();
}
function thisFunctionNameIsReallyLong(
address x,
address y,
address z,
)
public
onlyowner
priced
returns (address)
{
doSomething();
}
No:
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyowner
priced
returns (address) {
doSomething();
}
function thisFunctionNameIsReallyLong(address x, address y, address z)
public onlyowner priced returns (address)
{
doSomething();
}
function thisFunctionNameIsReallyLong(address x, address y, address z)
public
onlyowner
priced
returns (address) {
doSomething();
}
對于構造函數(shù)究驴,如果父合約的構造函數(shù)需要參數(shù)镊绪,函數(shù)聲明比較長或者難于閱讀,應該將父類構造函數(shù)和修改器一樣洒忧,寫在新的行蝴韭。
Yes:
contract A is B, C, D {
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
{
// do something with param5
}
}
No:
contract A is B, C, D {
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
{
// do something with param5
}
}
contract A is B, C, D {
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4) {
// do something with param5
}
}
當聲明短函數(shù)中只有一個句表述,可以寫在一行中熙侍。
允許的:
function shortFunction() { doSomething(); }
這個函數(shù)聲明的指導手冊是想要提高可讀性榄鉴。作者應該有自己的判斷,手冊不會覆蓋函數(shù)聲明的所有情況蛉抓。
映射
TODO
變量聲明
數(shù)組的聲明庆尘,類型和方括號之間不能有空格。
Yes:
uint[] x;
No:
uint [] x;
其他建議
- string應該用雙引號包圍巷送,而不是單引號驶忌。
Yes:
str = "foo";
str = "Hamlet says, 'To be or not to be...'";
No:
str = 'bar';
str = '"Be yourself; everyone else is already taken." -Oscar Wilde';
- 操作符兩端應該用單個空格隔開。
Yes:
x = 3;
x = 100 / 10;
x += 3 + 4;
x |= y && z;
No:
x=3;
x = 100/10;
x += 3+4;
x |= y&&z;
- 比其他操作符優(yōu)先級高的操作符要去掉空格笑跛。這是為了提高復雜表達式的可讀性付魔,你在操作符兩端,應該總是使用相同數(shù)量的空格符飞蹂。
Yes:
x = 2**3 + 5;
x = 2*y + 3*z;
x = (a+b) * (a-b);
No:
x = 2** 3 + 5;
x = y+z;
x +=1;
命名規(guī)范
當代碼廣泛使用几苍,命名約定是很重要的。不同約定的使用會傳遞重要的元信息陈哑,讓人覺得代碼不能馬上使用擦剑。
這里給出的命名推薦試圖提高代碼可讀性,但是他們不是規(guī)定芥颈,而是指導手冊,讓名字能夠傳遞更多的信息赚抡。
最后爬坑,代碼庫的統(tǒng)一化約定都是要優(yōu)先于本文列出的約定。
命名風格
為了避免產(chǎn)生困惑涂臣,下面的命名會使用不同的命名風格:
-
b
(單個小寫字符) -
B
(單個大寫字符) lowercase
lower_case_with_underscores
UPPERCASE
UPPER_CASE_WITH_UNDERSCORES
-
CapitalizedWords
(或者CapWords) -
mixedCase
(和CapitalizedWords不同的是第一個字母是小寫字母) Capitalized_Words_With_Underscores
注意:當對縮略語用CapWords規(guī)則命名的時候盾计,所有縮略字母都要大寫售担。所以HTTPServerError會比HttPServerError更好。
應該避免的名字
-
l
- 小寫字母 el -
O
- 大寫字母 oh -
I
- i的大寫字母
不要使用這些單個字母的命名署辉。因為他們經(jīng)常會分不清是字符還是數(shù)字族铆。
合約和庫的名稱
合約和庫的名稱應該使用CapWords風格。
事件
事件應該使用CapWords風格哭尝。
函數(shù)名字
函數(shù)命應該使用mixedCase風格哥攘。
函數(shù)參數(shù)
當寫庫函數(shù)來操作一個自定義的結(jié)構體的時候,結(jié)構體應該是第一個參數(shù)材鹦,并且總是命名為self
逝淹。
局部變量和狀態(tài)變量
使用mixedCase風格。
靜態(tài)變量
靜態(tài)變量命名應該使用大寫字母桶唐,并且單詞之間要用下劃線隔開栅葡。(例如,MAX_BLOCKS
)
修改器
使用mixedCase風格尤泽。
避免沖突
single_trailing_underscore_
這個約定用于自定義變量和內(nèi)建變量或保留名稱有命名沖突的時候欣簇。