Solidity文档(中文版)连载四:Solidity 源文件结构

序言
本文是 Solidity 文档(以太坊官方 Solidity 开发手册)中文版连载的第四部分。这个连载的前三部分是智能合约概述安装 Solidity 编译器结合实例学习 Solidity
这份文档的英文原文可以在以太坊官网的最下方 Solidity 链接中找到。官方的英文版本文档中有中译版链接,即是本连载内容的出处。这个连载将按照英文文档的先后顺序进行。
Solidity 是以太坊官方的智能合约开发高级语言。这份中文译本是由 Hiblock 社区组织贡献的,官方 Github:https://github.com/etherchina/solidity-doc-cn
我本人于 3 月初加入本项目,目前作为管理员、贡献者和校订人利用业余时间参与日常工作;截至到 4 月底,翻译工作已完成大半。有兴趣的朋友请直接在以太坊官网的链接中查看最新中文版本状态,或者关注上述中文译本的 Github repository。
出于单独阅读的需要,我在连载中会删除原文里的 rst 控制标签、文内链接和部分外部链接。
本文是在具体介绍 Solidity 语法、语言特性之前的铺垫,介绍了 Solidity 源文件中除具体合约代码以外的其他要素。

Solidity 源文件结构

源文件中可以包含任意多个合约定义、导入指令和杂注指令。

版本杂注

为了避免未来被可能引入不兼容变更的编译器所编译,源文件可以(也应该)被所谓的版本杂注所注解。
我们力图把这类变更做到尽可能小,特别是,我们需要以一种当修改语义时必须同步修改语法的方式引入变更,当然这有时候也难以做到。
因此,至少对含重大变更的版本,通读变更日志永远是好办法。
这些版本的版本号始终是 0.x.0 或者 x.0.0 的形式。

版本杂注使用如下

pragma solidity ^0.4.0;

这样,源文件将既不允许低于 0.4.0 版本的编译器编译,
也不允许高于(包含)0.5.0 版本的编译器编译(第二个条件因使用 ^ 被添加)。
这种做法的考虑是,编译器在 0.5.0 版本之前不会有重大变更,所以可确保源代码始终按预期被编译。
上面例子中不固定编译器的具体版本号,因此编译器的补丁版也可以使用。

可以使用更复杂的规则来指定编译器的版本,表达式遵循 npm 版本语义

Pragma 是 pragmatic information 的简称,微软 Visual C++ 文档 中译为杂注。
Solidity 中沿用 C ,C++ 等中的编译指令概念,用于告知编译器 如何 编译。
——译者注

导入其他源文件

语法与语义

虽然 Solidity 不知道“default export”为何物,
但是 Solidity 所支持的导入语句,其语法同 JavaScript(从 ES6 起)非常类似。

ES6 即 ECMAScript 6.0,ES6是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布。
——译者注

在全局层面上,可使用如下格式的导入语句:

import "filename";

此语句将从“filename”中导入所有的全局符号到当前全局作用域中(不同于 ES6,Solidity 是向后兼容的)。

import * as symbolName from "filename";

...创建一个新的全局符号 symbolName,其成员均来自 "filename" 中全局符号。

import {symbol1 as alias, symbol2} from "filename";

...创建新的全局符号 aliassymbol2,分别从 "filename" 引用 symbol1symbol2

另一种语法不属于 ES6,但或许更简便:

import "filename" as symbolName;

这条语句等同于 import * as symbolName from "filename";

路径

上文中的 filename 总是会按路径来处理,以 / 作为目录分割符、以 . 标示当前目录、以 .. 表示父目录。
... 后面跟随的字符是 / 时,它们才能被当做当前目录或父目录。
只有路径以当前目录 . 或父目录 .. 开头时,才能被视为相对路径。

import "./x" as x; 语句导入当前源文件同目录下的文件 x
如果用 import "x" as x; 代替,可能会引入不同的文件(在全局 include directory 中)。

最终导入哪个文件取决于编译器(见下文)到底是怎样解析路径的。
通常,目录层次不必严格映射到本地文件系统,
它也可以映射到能通过诸如 ipfs,http 或者 git 发现的资源。

在实际的编译器中使用

当运行编译器时,它不仅能指定如何发现路径的第一个元素,还可指定路径前缀重映射。
例如,github.com/ethereum/dapp-bin/library 会被重映射到 /usr/local/dapp-bin/library
此时编译器将从重映射位置读取文件。如果重映射到多个路径,优先尝试重映射路径最长的一个。
这允许将比如 "" 被映射到 "/usr/local/include/solidity" 来进行“回退重映射”。
同时,这些重映射可取决于上下文,允许你配置要导入的包,比如同一个库的不同版本。

solc:

对于 solc(命令行编译器),这些重映射以 context:prefix=target 形式的参数提供。
其中,context:=target 部分是可选的(此时 target 默认为 prefix )。
所有重映射的值都是被编译过的常规文件(包括他们的依赖),这个机制完全是向后兼容的(只要文件名不包含 = 或 : ),
因此这不是一个破坏性修改。
content 目录或其子目录中的源码文件中,所有导入语句里以 prefix 开头的导入文件都将被用 target 替换 prefix 来重定向。

举个例子,如果你已克隆 github.com/ethereum/dapp-bin/ 到本地 /usr/local/dapp-bin
可在源文件中使用:

import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;

然后运行编译器:

solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol

举个更复杂的例子,假设你依赖了一些使用了非常旧版本的 dapp-bin 的模块。
旧版本的 dapp-bin 已经被 checkout 到 /usr/local/dapp-bin_old ,此时你可使用:

solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \
     module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \
     source.sol

这样, module2 中的所有导入都指向旧版本,而 module1 中的导入则获取新版本。

注意, solc 只允许包含来自特定目录的文件:它们必须位于显式地指定的源文件目录(或子目录)中,或者重映射的目标目录(或子目录)中。
如果你想直接用绝对路径来包含文件,只需添加重映射 =/

如果有多个重映射指向一个有效文件,那么具有最长公共前缀的重映射会被选用。

Remix:

Remix 提供一个为 github 源代码平台的自动重映射,它将通过网络自动获取文件:
比如,你可以使用 import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping; 导入一个 map 迭代器。

未来,Remix 可能支持其他源代码平台。

注释

可以使用单行注释(//)和多行注释(/*...*/

// 这是一个单行注释。

/*
这是一个
多行注释。
*/

此外,有另一种注释称为 natspec 注释,其文档还尚未编写。
它们是用三个反斜杠(///)或双星号开头的块(/** ... */)书写,它们应该直接在函数声明或语句上使用。
可在注释中使用 Doxygen 样式的标签来文档化函数、
标注形式校验通过的条件,和提供一个当用户试图调用一个函数时显示给用户的 确认文本

在下面的例子中,我们记录了合约的标题、两个入参和两个返回值的说明:

pragma solidity ^0.4.0;

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

推荐阅读更多精彩内容