代码风格指导

翻译原文

date:20170802

介绍

本指导提供代码约定,来规范Solidity的代码。本指导会不断变化,新的,有用的约定会不断添加进来,旧的约定会被淘汰。
很多项目都想自己实现各自的风格指导。作为事件冲突,项目指定的风格要优先考虑。
本指导中的结构和很多约定都是遵从Python的pep8代码风格指导
本指导的目标不是要规定写solidity代码的正确方法,或者最好的方法。目标是统一化。引用pep8的这个概念:

风格指导手册是对统一化的规定。根据手册,统一化编码是很重要的。同个项目的代码风格保持一致更加重要。在模块中统一化,或者函数中,是最重要的。但是最重要的:需要知道什么时候要统一化——有时候不需要代码风格指导。当有疑问的时候,要用你的判断力。查看其它例子,然后决定哪种看起来会最好。不要犹豫提问题,请教别人。

代码层级

缩进

每层缩进用4个空格。

Tabs和空格

空格是更期望的缩进方式。
tabs和空格混合是要避免的。

空行

solidity源码的顶层描述要有两个空行。
Yes:

contract A {
    ...
}


contract B {
    ...
}


contract C {
    ...
}

No:

contract A {
    ...
}
contract B {
    ...
}

contract C {
    ...
}

在合约中,函数用一行空行隔开。
相关的只有一行的代码,空行可以省略(例如虚拟合约的未实现的函数)。

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 {
    ...
}

函数的顺序

排序有助于读者可以方便的知道哪些函数是可以调用的,且可以快速的找到构造函数和回调函数的定义。
函数应该按照他们的可见性排序:

  • constructor
  • fallback 函数(如果有)
  • external
  • public
  • internal
  • private

constant函数放置分组的最后。

Yes:

contract A {
    function A() {
        ...
    }

    function() {
        ...
    }

    // External 函数
    // ...

    // External 的constant函数
    // ...

    // Public 函数
    // ...

    // Internal 函数
    // ...

    // Private 函数
    // ...
}

No:

contract A {

    // External 函数
    // ...

    // Private 函数
    // ...

    // Public 函数
    // ...

    function A() {
        ...
    }

    function() {
        ...
    }

    // Internal 函数
    // ...
}
表达式中的空格

在下列情况下应该避免额外的空格:

在圆括号,中括号或者大括号之间没有空格,除了单行的函数描述。
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;

在回调函数中不要包含空格:
Yes:

function() {
    ...
}

No:

function () {
    ...
}
控制结构

大括号包围的是合约的主体,库,函数和结构体,应该:

  • 在同一行中开始
  • 结束标签要新建一行,并在他们各自的同个缩进层级的位置
  • 开始括号应该用单个空格隔开

Yes:

contract Coin {
    struct Bank {
        address owner;
        uint balance;
    }
}

No:

contract Coin
{
    struct Bank {
        address owner;
        uint balance;
    }
}

该建议同样适用于控制体if,else,whilefor

另外,在if,whilefor和代表条件的括号块之间要有单个空格,条件和开始括号之间也有空格:

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 ifif块,else应该和if的结束括号同一行。对呗其他区块结构的规则,这是一个例外。
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;
}
函数声明

对于简短的函数的声明,推荐函数声明和开始括号在同一行。
函数的闭合括号应该与函数声明行有相同的缩进。

开始括号应该和函数声明在同一行。

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;}

函数的可见性,应该在任意自定义的修改器之前。
Yes:

function kill() public onlyowner {
    selfdestruct(owner);
}

No:

function kill() onlyowner public {
    selfdestruct(owner);
}

对于长的函数声明,推荐将每个参数都各自一行,并且缩进和函数体一样。参数关闭括号和函数体开始括号在同一行,缩进与函数声明一致。

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();
}

如果长的函数声明有修改器,那么将修改器放置在单独的行。
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();
}

对于构造函数,如果父合约的构造函数需要参数,函数声明比较长或者难于阅读,应该将父类构造函数和修改器一样,写在新的行。
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
    }
}

当声明短函数中只有一个句表述,可以写在一行中。

允许的:

function shortFunction() { doSomething(); }

这个函数声明的指导手册是想要提高可读性。作者应该有自己的判断,手册不会覆盖函数声明的所有情况。

映射

TODO

变量声明

数组的声明,类型和方括号之间不能有空格。

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;
  • 比其他操作符优先级高的操作符要去掉空格。这是为了提高复杂表达式的可读性,你在操作符两端,应该总是使用相同数量的空格符。

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;

命名规范

当代码广泛使用,命名约定是很重要的。不同约定的使用会传递重要的元信息,让人觉得代码不能马上使用。
这里给出的命名推荐试图提高代码可读性,但是他们不是规定,而是指导手册,让名字能够传递更多的信息。
最后,代码库的统一化约定都是要优先于本文列出的约定。

命名风格

为了避免产生困惑,下面的命名会使用不同的命名风格:

  • b(单个小写字符)
  • B(单个大写字符)
  • lowercase
  • lower_case_with_underscores
  • UPPERCASE
  • UPPER_CASE_WITH_UNDERSCORES
  • CapitalizedWords(或者CapWords)
  • mixedCase(和CapitalizedWords不同的是第一个字母是小写字母)
  • Capitalized_Words_With_Underscores

注意:当对缩略语用CapWords规则命名的时候,所有缩略字母都要大写。所以HTTPServerError会比HttPServerError更好。

应该避免的名字
  • l - 小写字母 el
  • O - 大写字母 oh
  • I - i的大写字母

不要使用这些单个字母的命名。因为他们经常会分不清是字符还是数字。

合约和库的名称

合约和库的名称应该使用CapWords风格。

事件

事件应该使用CapWords风格。

函数名字

函数命应该使用mixedCase风格。

函数参数

当写库函数来操作一个自定义的结构体的时候,结构体应该是第一个参数,并且总是命名为self

局部变量和状态变量

使用mixedCase风格。

静态变量

静态变量命名应该使用大写字母,并且单词之间要用下划线隔开。(例如,MAX_BLOCKS

修改器

使用mixedCase风格。

避免冲突
  • single_trailing_underscore_

这个约定用于自定义变量和内建变量或保留名称有命名冲突的时候。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • iOS编程规范0规范 0.1前言 为􏰀高产品代码质量,指导广大软件开发人员编写出简洁、可维护、可靠、可 测试、高效...
    iOS行者阅读 4,374评论 21 35
  • 推荐文章:禅与 Objective-C 编程艺 前言 为􏰀高产品代码质量,指导广大软件开发人员编写出简洁、可维护、...
    WolfTin阅读 2,685评论 0 1
  • 代码格式 使用空格而不是制表符 Tab 不要在工程里使用 Tab 键,使用空格来进行缩进。在 Xcode > Pr...
    small_Sun阅读 1,313评论 1 3
  • 示例 下面是一个示例头文件,演示了@interface声明的正确注释和间隔 一个示例源文件,演示了一个接口的@ i...
    我是Damo阅读 1,949评论 2 5
  • 《留春令》 满眼楼高,影单踟蹰。 难见西窗烛。 哪写紫毫书? 唱清风、无限事。 月月愧领五斗米, 心却行万里。 楼...
    黑星水生阅读 197评论 0 0