美文网首页solidity
代码风格指导

代码风格指导

作者: gaoer1938 | 来源:发表于2017-08-02 17:55 被阅读28次

    翻译原文

    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_

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

    相关文章

      网友评论

        本文标题:代码风格指导

        本文链接:https://www.haomeiwen.com/subject/gzsylxtx.html