翻译原文
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
,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的结束括号同一行。对呗其他区块结构的规则,这是一个例外。
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_
这个约定用于自定义变量和内建变量或保留名称有命名冲突的时候。