代码风格指导

翻译原文

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_

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

你可能感兴趣的:(代码风格指导)