├── README.md
├── function.move.md
├── hello_world.move.md
└── variable.move.md
/README.md:
--------------------------------------------------------------------------------
1 | # LearnMove
2 | 手把手写Move智能合约
3 |
--------------------------------------------------------------------------------
/function.move.md:
--------------------------------------------------------------------------------
1 | 最近比较忙,一直没时间更新专栏,抓住中秋假期的尾巴,写篇Move教程。
2 |
3 | 在我们的Move教程基础篇中,前面讲了如何搭建Move环境、Move的基本数据类型,今天我们要讲一讲Move的函数定义以及函数调用。对于所有的高级语言,函数定义其实都差不多,无非是一些细节上的不同。因为Move的智能合约语言,所以对比Solidity来讲(就事论事,没有喷Solidity的意思)。
4 |
5 |
6 |
7 | ## Move与Solidity的函数对比
8 |
9 | 首先需要了解一点,Move的代码跟状态是分离的。对比Solidity,Move的函数显得非常的干净:
10 |
11 | * 没有Library的概念
12 | * 没有继承的概念
13 | * 没有特殊函数(Solidity有fallback、receive等函数,都是为了处理特殊的状态)
14 | * 没有Solidity的calldata、memory、storage等变量(但是有mutable和immutable的引用变量)
15 | * 没有Modifier
16 | * 没有payable
17 |
18 | 在函数定义方面,对比Solidity,除了语法上的区别,还有几个重要的不同:
19 |
20 | * 函数可见性(Move有更丰富的函数可见性)
21 | * Move可以传值,也可以传引用(其他高级语言也是这样)
22 | * 返回结构体struct(这个在Move进阶课程中再介绍)和tuple元组(也就是多个值),还可以返回引用
23 |
24 | 在函数调用方面,Move和Solidity也有很大的不同:
25 |
26 | * Move没有Solidity的msg上下文了(比较难理解的特性,容易出现安全问题)
27 | * Move是静态调用(没法构造递归调用,也没有Solidity合约DelegateCall这些容易带来安全隐患的东西)
28 | * Move只能修改当前合约的数据,Solidity被调用合约可以修改调用合约的数据(反常识)
29 |
30 | Move是一门更“正常”的编程语言,除了acquires关键字之外,其他的都跟我们对高级语言的理解是一样的。Solidity因为代码和状态没有完全分离,所以添加了很多“为了解决某个状态问题而专门设计的特性”,例如payable和fallback函数等等,还有容易让人混淆的msg上下文等概念,有点缝缝补补的感觉。(这里暂时不涉及泛型对函数定义和调用的影响,在后面讲泛型的时候,专门介绍)。
31 |
32 |
33 |
34 | ## Move的函数签名
35 |
36 | 前面对比了Move跟Solidity的不同,接下来我们介绍一下Move的函数签名。
37 |
38 | 高级语言的函数签名都大同小异,一般包含以下部分:
39 |
40 | * 函数可见性
41 | * 函数名
42 | * 函数参数(包括传值和传引用)
43 | * 函数返回值
44 | * acquires(这是智能合约特有的关键字)
45 |
46 | Move的编程风格有点类似于Rust,以下是函数的定义:
47 |
48 | ~~~
49 | 函数可见性 fun 函数名(参数列表): 返回值列表 acquires 结构体列表 {
50 | 函数体
51 | return 结果
52 | }
53 | ~~~
54 |
55 | 这里有几个地方简单说明一下:
56 |
57 | * `fun`是函数定义的关键字(Rust是function,为了区分Move跟Rust,Move团队特意引入了一些小的区别)
58 | * Move 函数使用snake_case命名规则,小写字母以及下划线作为单词分隔符
59 | * `:`指定返回值,可以是列表,列表用`()`括起来,也支持struct类型的返回值(跟Solidity有区别),不指定表示没有返回值
60 | * `acquires`指定函数引用到的struct(这块在后面的进阶教程中,介绍Struct的时候再讲),不指定表示没有引用
61 | * `return` 在函数体的最后可以省略,同时,不需要`;`,否则编译会抛错
62 |
63 |
64 |
65 | ## 函数可见性
66 |
67 | 前面在讲函数签名的时候没有讲Move函数的可见性。Move的可见性非常有意思,值得单独拿一个小节来讲。
68 |
69 | 了解Solidity的人都知道,Solidity只有`public`和`private`两种可见性。对于开源的DeFi系统来说,函数要么是所有人可见,要么说有人都不可见,有点激进。不知道大家用过Java没有?Java有4种级别的函数可见性:`public`、`friendly(无关字)`、`protected`、`private`,其中,`protected`表示继承关系的可见性,`friendly`指定package级别的可见性。Move也设计了4种函数的可见性,分别是(关于什么是模块Module,暂时可以类比Solidity的Contract,在进阶课程会专门介绍):
70 |
71 | * private:当前Module可见(Move函数的默认可见性)
72 | * friend:当前address下被授权的Module可见(可以理解为address级别的可见性,类比Java的package级别的可行性。Solidity通常使用Modifier来控制调用权限,这依赖开发者的个人能力,容易引发安全隐患,被黑客绕过)
73 | * public:所有Module可见,但是用户不能直接作为交易使用(跟Solidity不一样,Solidity是调用public的合约函数发起链上交易)
74 | * entry或者script:交易的入口(可以理解为可编程的交易,在讲Module的时候再详细介绍)
75 |
76 | 以上4种可见性,可见范围逐渐放大。
77 |
78 | ### 1. public & private
79 |
80 | 我们先看看`public`和`private`的使用。`public`和`private`跟Solidity大同小异,没什么特别的地方。下面是在public函数中调用了一个private函数:
81 |
82 |
83 |
84 | 这里简单说明一下:
85 |
86 | * ① 是一个private的函数,③ 是一个public的函数,尤其要注意的是,Move默认是函数可见性是private的(这点跟Solidity不一样,Solidity默认是public的),能很好的避免函数的泄露
87 | * ② 是返回值,需要注意,这里不能有`;`,否则编译不通过
88 | * ④ 和 ⑤ 都是对当前合约的`test_private`函数的调用,Self表示当前Module
89 |
90 | ### 2. friend
91 |
92 | 接下来,我们再介绍一下`friend`可见性的使用。
93 |
94 | 可见性的本质是控制函数对外的访问权限。`friend`的作用是控制「相同address下,跨Module的函数调用」,这样精细化的访问控制,能更好的避免函数泄露的问题。下来是`friend`使用的例子。
95 |
96 |
97 |
98 | 我们定义了一个function1的模块,简单说明一下:
99 |
100 | * ② 通过`public(friend)`关键字定义了一个`friend`可见性的test_friend1的函数
101 | * ① 通过`friend`授权让std地址(本实例是0x2地址)的function2的模块可以调用
102 |
103 | 我们再定义一个function2的模块,对test_friend1进行调用:
104 |
105 |
106 |
107 | 简单说明一下(这块跟调用public函数一样,没有区别,注意一下跨Module调用以及use关键字):
108 |
109 | * ③ 是引入function1的模块
110 | * ④ 调用function1的test_friend1函数
111 |
112 | 以上是`friend`可见性函数的调用例,都是在std的address下(本例是0x2)。我们来看一下,`friend`的函数能不能授权给其他的address调用呢?
113 |
114 |
115 |
116 | 上图是假设授权给0x3这个地址的function3的模块使用,可以看到编译错误,所以`friend`可见性的函数是不可以跨地址使用的。
117 |
118 | ## 3. entry & script
119 |
120 | entry或者script可见性,也是Move很有意思的一个函数可见性,是用户跟链上交互的入口。
121 |
122 | 在EVM合约开发中,Solidity代码编写完之后,要将所有的代码部署到链上,然后,用户通过调用Contract的public方法发起交易。这么处理当然没问题,但是灵活性不足,因为Contract是部署在链上,并且部署之后不能修改(或者说修改很麻烦),而用户的需求往往会随着事情的发展而改变的。
123 |
124 | Move在合约设计上,将链上和链下区分开。也就是说,Move的交易是链下的,是可以编程的,拥有极好的灵活性,并不像Solidity那样通过函数名来发起交易。而entry或者script可见性正是用来代表交易的函数。
125 |
126 |
127 |
128 | Move最早叫script可见性,后面修改成entry可见性了,更直接,更好理解,也能跟模块化的script进行区分(讲模块化的时候会讲到)。不管叫什么,也不管关键字是什么,理解了它的应用场景最重要,使用起来很简单。下面是例子:
129 |
130 |
131 |
132 | 简单说明一下:
133 |
134 | * ① 是使用`public(script)`定义的函数
135 | * ② 使用`entry`关键字定义函数
136 | * ③ 编译的时候提示了`public(script)`将会弃用
137 |
138 |
139 |
140 | # 传值 & 传引用
141 |
142 | 前面对Move的4种函数可见性做了详细介绍。跟Solidity对比,除了上面这些区别,还有一个很大的区别,Move的参数既可以传值,也可以传引用(常见的高级语言也是这样,例如Java),并且区分`mutable ref`「可变引用」和`immutable ref`「不可变引用」,这是Rust的风格。
143 |
144 | 我们来理解一下`immutable ref`和`mutable ref`:
145 |
146 | * `immutable ref`可以类比只读锁,`mutable ref`可以类比独占的写锁。
147 | * 同一个结构体的实例,同时可以有多个`rimmutable ef`
148 | * 同一个结构体的实例,同时只能有一个`mutable ref`
149 | * 同一个结构体的实例,`mutable ref`和`immutable ref`不能同时存在,但是`mutable ref`可以当`immutable ref`使用
150 |
151 | 所以我们在定义函数的时候一定要注意,引用参数应该是「可变」还是「不可变」。「可变」意味着函数逻辑可以修改当前结构体instance的数据,而「不可变」意味着只读。下面是例子:
152 |
153 |
154 |
155 | 简单说明一下:
156 |
157 | * ① 定义了一个strut(这个在进阶课程会详细介绍)
158 | * 对immutable ref的定义是`&`,而mutable ref的定义是`&mut`
159 | * ② 定义了一个immutable ref参数的函数,只读
160 | * ③ 定义了一个mutable ref参数的函数,可以修改
161 |
162 |
163 |
164 | ## 返回Struct & 引用 & 元组
165 |
166 | 对比Solidity,Move函数的返回值也很有特点。
167 |
168 | Solidity的函数最早只能返回基本数据类型,后面增加了返回结构体的支持。但是,Move函数的返回值更加自由,能返回任何形式的数据,例如`immutable ref`、`mutable ref`、tuple元组。
169 |
170 |
171 |
172 | 上面是Move函数返回各种类型的数据的例子,简单说明一下:
173 |
174 | * ① 定义了一个strut
175 | * ② 函数返回了一个struct
176 | * ③ 函数返回了一个immutable的引用,以为着数据只读
177 | * ④ 函数返回了一个immutable的引用,意味着数据可以被修改
178 | * ⑤ 函数返回了一个tuple类型,调用了 ② 的函数
179 |
180 |
181 |
182 | ## 跟我学Move & 完整代码
183 |
184 | 当前教程详细介绍了Move函数如何定义和调用,重点对比Solidity、介绍了Move函数的可见性、如何传引用、如何返回数据类型。完整的代码如下(Module和Struct、acquires以及函数的泛型在后面的进阶课程再介绍):
185 |
186 | ~~~
187 | module std::function1 {
188 |
189 | fun test_private() :bool {
190 | true
191 | }
192 |
193 | public fun test_public() : bool {
194 | Self::test_private();
195 | test_private();
196 | false
197 | }
198 |
199 | friend std::function2;
200 | // friend 0x3::function3;
201 |
202 | public(friend) fun test_friend1() : u64 {
203 | 1
204 | }
205 |
206 | public(script) fun test_script() {}
207 |
208 | public entry fun test_entry() {}
209 |
210 | struct Test{
211 | flag:bool
212 | }
213 |
214 | fun test_immutable(test: &Test):bool {
215 | return test.flag
216 | }
217 |
218 | fun test_mutable(test:&mut Test) {
219 | test.flag = true;
220 | }
221 |
222 | fun test_struct_return() : Test {
223 | Test{
224 | flag: true
225 | }
226 | }
227 |
228 | fun test_im_return(test: &Test) : &Test {
229 | test
230 | }
231 |
232 | fun test_mut_return(test: &mut Test) : &mut Test {
233 | test
234 | }
235 |
236 | fun test_tuple_return() : (Test, bool) {
237 | (test_struct_return(),false)
238 | }
239 | }
240 | ~~~
241 |
242 | 对比Solidity的函数,Move的函数确实显得更简洁、丰富、自由。
243 |
244 | 我是@MoveContract(twittter),也可以关注@Move小王子 的知乎专栏“手把手写Move智能合约”,跟我一起学习Move。下期是Move基础课程的最后一讲,主要讲Move经常用的到一些类库,例如vector、table等等。
245 |
--------------------------------------------------------------------------------
/hello_world.move.md:
--------------------------------------------------------------------------------
1 | ## Move真的时薪$1200吗?
2 |
3 | 最近一则Move时薪的新闻刷爆了朋友圈。对比往日的门可罗雀,很多朋友私下开始咨询我有关Move的事情。这种突入其来的转变,作为Move的先行者(布道师),深受震撼。了解我的人都知道,因为各种机缘,我从Move白皮书发布就一直致力于Move的研究,从纯技术学习的角度,布道Move也已经有一年多了。很坦率的说,我对Move是由衷的热爱(这里需要说明的是,Web3与区块链并不是必然的联系,但是Web3离开不了安全。法律是最底层的安全,所有的研究都基于法律允许的框架内)。如果你跟我一样,那么欢迎加入Move的大家庭。我们相互学习,一起期待有一天Move强大起来,成为Web3的重要技术。
4 |
5 | 也有很多人对Move充满了疑惑。
6 |
7 | 1)Move要挑战Ethereum?
8 |
9 | 2)除了安全,Move看上去没啥嘛。
10 |
11 | 3)Over engineering(过度设计)
12 |
13 | 4)Move没有生态,起不来
14 |
15 | 5)Move真的有那么高的时薪吗?
16 |
17 | 抱有各种各样的质疑,这些我也解答不了,交给时间去验证吧(没关系,这都是我非常理解的,毕竟Move穷志短。相信我,我在布道Move的一年多时间里,听过更难听的评论,但是并不耽误我对Move的热情)。我相信一点,技多不压身,与其漫无目的的浪费精力,不如静下心学习一下Move,万一有用呢?用李笑来的话说,你并不孤独。只是学习Move不要功利性太强,就当玩游戏吧。
18 |
19 |
20 |
21 |
22 |
23 | ## Move vs Solidity
24 |
25 | Move语言说难也难,说简单特别的简单。受限于智能合约的场景,Move不可能复杂。比起高级语言,例如并发编程、网络调用等等,智能合约语言是一个完全隔离的环境,完全串型执行,使用特别的简单(很期待哪天能设计出一种并发执行智能合约的模型出来)。所以,如果你会任何一种高级语言,完全不用担心学不会Move,实在是太简单了。
26 |
27 | 跟Soidity对比,最核心的区别,Solidity是动态语言,Move纯静态语言。在扩展性方面,Solidity依靠的是动态调用,而Move因为是静态语言,所以为了保证扩展性,Move选择了泛型编程的范式。但是正是这种底层大方向的不同,导致了安全性的巨大差异(需要说明的是,Move是从Solidity的真实安全漏洞中吸取了经验教训,才选择一种截然不同的路线)。
28 |
29 | (从学习的角度来说,我并没有贬低Solidity的意思,所以不要以为我在搞语言之争,放平心态。)
30 |
31 | Move最核心的两个特性是:
32 |
33 | * 面向资源编程
34 | * 面向泛型编程
35 |
36 | 但是上面的两个核心特性,远不足以概况Move的优点。Move有很多的安全特性,很多都对开发者完全透明,例如算数溢出、默认可见性导致的权限泄露等等(合约开发者不需要花太多的心思在安全上,Move语言已经帮你做了很多的安全处理,尤其是很多低级的安全隐患)。可以说,Move从语言层面,大幅降低了开发者的安全门槛,开发者只需要专注业务模型上的设计与实现。除此之外,Move在可扩展性的基础上,工程上也有很强的实力,能够非常轻松地设计出复杂的工程,我认为这一点也是不容忽视的。
37 |
38 |
39 |
40 |
41 |
42 | ## Move & Aptos
43 |
44 | 不管出于什么目的,我们已经开始行动,学习Move了,希望对你来说这是一个愉快的过程。让我们开始吧。
45 |
46 | 对于很多刚入门的朋友来说,经常搞不清楚Move和Aptos、Sui、Starcoin等公链的关系,尤其是这些公链都有各自的IDE和Framework,有点蒙圈。对于初学者来说,到底应该怎么入手呢?
47 |
48 | Move是一门编程语言,本身是中立的,这里是Move官方的[Github仓库]()。那为什么还会有各种IDE和Framework呢?
49 |
50 | 各个公链在发展的过程中,可能会有一些特殊的需求,需要用到Move暂时没有的一些功能,所以通常会从Move的Github中fork一份代码,自己稍加修改(别担心,通常不会是大的修改,或者以后会有兼容方案),以满足自身的发展。在自有版本的Move之上,因为需要对自己的链结合,例如Account模型上可能一些差别,或者方便开发者基于自己的链快速开发Dapp,通常会封装一层Framework。
51 |
52 | 对于我们在学习Move语言基础的时候,建议使用Move官方的版本(以后基于某个公链实战,再考虑选择Framework的问题)。
53 |
54 |
55 |
56 |
57 |
58 | ## 搭建Move环境
59 |
60 | 我们这里以Move官方标准库为基础学习Move,先搭建Move的开发环境。
61 |
62 | 截止目前,Move还没有一个很好的开发环境。好消息是,比起我学Move的时候,已经非常的方便了(2019年的时候,Move本身还不稳定,完全没有可用的IDE,靠纯文本编辑器写代码,而且错误提示经常不对)。由于Move官方没有可以直接下载的二进制Release版本,我们需要自己手动编译,需要先安装一些依赖。
63 |
64 |
65 |
66 | ## ① 安装和学习Git
67 |
68 | 这里不介绍了,开源系统,基本都通过Git协作,不会的请先查看Git教程(当然也可以绕过Git,直接下载源码到本地)。
69 |
70 |
71 |
72 | ### ② 安装Rust环境
73 |
74 | Move官方的库是用Rust实现的,并且没有可直接下载的二进制包,所以我们先搭建Rust的编译环境(这块教程很多,自己去网上找一下,这里不详细说)。这里需要注意的是,并不要求对Rust了解,只需要借助Rust完成Move环境的搭建。
75 |
76 | 通过下面命令确定Rust环境是否安装成功:
77 |
78 | ~~~
79 | $ rustc -V
80 | ~~~
81 |
82 |
83 |
84 | ### ③ 编译安装Move环境
85 |
86 | 先通过Git下载[Move](https://github.com/move-language/move)代码(假设Git已经安装好):
87 |
88 | ~~~
89 | $ git clone https://github.com/move-language/move
90 | ~~~
91 |
92 |
93 |
94 | 下载完成之后,编译和安装Move-cli:
95 |
96 | ```
97 | $ cargo install --path move/language/tools/move-cli
98 | ```
99 |
100 |
101 |
102 | 这种方式安装的Move会在 cargo home里,例如~/.cargo/bin。查看安装结果:
103 |
104 | ~~~
105 | $ move -h
106 | ~~~
107 |
108 | 如果安装正确,会显示move的帮助信息。
109 |
110 |
111 |
112 | 这时候,Move环境已经搭建好了。
113 |
114 |
115 |
116 | ## Move插件和IDE
117 |
118 | 前面搭建了Move的编译环境,如果想要快速开发Move程序,还需要选择合适的IDE(主要是高亮和自动提示等,帮助开发)。我习惯使用Idea开发,大家也可以选择VS Code等IDE。
119 |
120 |
121 |
122 | 怎么理解Move环境和Move插件(IDE)呢?
123 |
124 | Move的IDE会做一些语法提示、检查以及高亮显示,帮助我们快速开发Move。但是代码能否运行,需要依赖Move环境,编译不出错,则说明代码没有语法上面的问题。所以,Move环境和Move的IDE对我们来说,都很重要。
125 |
126 |
127 |
128 | ## 第一个Move程序:HelloWorld
129 |
130 | 前期的准备完成,我们可以愉快的学习Move了。按照行业惯例,我们写第一个HelloWorld例子。
131 |
132 | ### ① Move命令使用
133 |
134 | 首先,通过前面安装的Move命令,生成一个hello_world的项目:
135 |
136 | ~~~
137 | $ move new hello_world
138 | ~~~
139 |
140 | 结果如图所示:
141 |
142 |
143 |
144 | 生成了Move.toml文件和source目录,这里先不介绍各自的作用。
145 |
146 |
147 |
148 | ### ② 编写HelloWorld程序
149 |
150 | 这里可以使用IDE引入刚创建的hello_world项目(以Idea为例)。在source文件夹上点击右键,选择`Move File`。生成一个叫hello_world的Script(以后会详细介绍Script的作用):
151 |
152 |
153 |
154 | 生成一个hello_world.move的文件,如下图所示:
155 |
156 |
157 |
158 | 在编写代码之前,我们需要打开Move.toml文件设置依赖(整个风格跟Rust有点像):
159 |
160 | ~~~
161 | [addresses]
162 | std = "0x1"
163 |
164 | [dependencies]
165 | MoveNursery = { git = "https://github.com/move-language/move.git", subdir = "language/move-stdlib/nursery", rev = "main" }
166 | ~~~
167 |
168 | 其中dependencies配置依赖的代码,这里指定到Move官方的Github仓库。addresses需要格外注意,要跟Move官方的stdlib中的地址变量保持一致。由于Move代码里面是std,所以我们这里也用std(大小写也要一致)。
169 |
170 |
171 |
172 | 终于到编写Move代码这一步了。打开hello_world.move文件,输入代码:
173 |
174 | ~~~
175 | script {
176 | use std::debug;
177 | fun main() {
178 | debug::print(&b"hello world");
179 | }
180 | }
181 | ~~~
182 |
183 | HelloWorld的代码就完成了。也是最关键的一步,编译代码:
184 |
185 | ~~~
186 | move build
187 | ~~~
188 |
189 | 编译成功,说明代码没有问题。
190 |
191 |
192 |
193 | 最后一步,验证结果:
194 |
195 | ~~~
196 | move sandbox run sources/hello_world.move
197 | ~~~
198 |
199 | 打印结果出来了。
200 |
201 |
202 |
203 |
204 |
205 | ## 跟我学Move
206 |
207 | 今天主要是教大家如何搭建Move的开发环境,并写了第一个Move程序,顺便解答了一些有关Move的疑惑。
208 |
209 | 欢迎关注tw账号@MoveContract,跟我一起学Move(或者关注知乎"Move小王子"订阅“手把手写Move智能合约”专栏)。下期我们将会介绍Move的基础语法。
210 |
211 |
--------------------------------------------------------------------------------
/variable.move.md:
--------------------------------------------------------------------------------
1 | 今天听到一个很好的观点,对于普通人来说,对世界最大的贡献是让自己快乐。学习使我快乐。作为Move布道师,帮助别人学习也很快乐。
2 |
3 | 希望大家不要被面向资源编程这个概念给吓跑了(只是一只纸老虎),坚持学习。当然也不要太急功近利,技多不压身,学的技能多了,就会由量变产生质变。当学不动的时候,想想$1200美刀的时薪还没到手,有什么理由不学呢(开个玩笑)。
4 |
5 | 在前期的基础课程中,我将继续为大家一些Move的基础知识(后面会有进阶课程,但愿我能坚持下来,我也需要大家的鼓励,才会分泌更多的多巴胺)。前面我给大家介绍了如何安装Move环境,并且用一种简单的方式实现了一个最简版的HelloWorld,你入门了吗?本期课程主要介绍Move的基本数据类型以及如何定义Move变量。
6 |
7 |
8 |
9 | ## Move基本数据类型和数组
10 |
11 | 语言嘛,其实都差不多。如果有其他高级语言的编程经验,比如Python、Java、Golang、Rust等等,那么你可以跨过本期内容了。真的没啥可讲的。
12 |
13 | Move主要有3类基本数据类型:
14 |
15 | * 整型,其中整型包括 (`u8`, `u64`, `u128`)
16 | * 布尔型 `boolean`
17 | * 地址 `address`
18 |
19 | 跟其他语言对比,Move的基本数据类型缺少两种类型:
20 |
21 | * Move没有字符串类型(更正一下,最新Move已经支持string类型。从定义中我们可以看到存了一个UTF-8的二进制数组)
22 |
23 |
24 |
25 | * Move没有浮点型(Solidity也没有浮点型,这个其实简单,用整数来代替就可以了),所以在各大Move公链的Framework或者Stdlib中都有一个FixedPoint32.move的模块
26 |
27 | 另外,Move的基本数据类型还有3个点需要注意的地方:
28 |
29 | * 没有负数(这个其实也简单,用一个`bool`型表示符号就可以了)
30 | * 没有`u256`类型(这个在DeFi里面很重要,记得当时Starcoin跟Libra的Move社区讨论过很久,一直没有通过,最后选择在`u128`上面进行封装)
31 | * address虽然是专门的地址类型,但是本质上是整型,所以address能跟整型相互转换(Solidity也是这样的)
32 |
33 | 这是Move的基本数据类型跟其他高级语言不同的地方。
34 |
35 | 除了上面提到的基本数据类型,数组也是最常用的数据类型,所以这里把数组也一起讲了。Move的数组类型是vector。关于vector类型有3个需要说明的地方:
36 |
37 | * vector是泛型类型(以后再讲什么是泛型),例如`vector`或者`vector`等等
38 | * vector是类型,而std::vector是一个Move模块(不是Move语言本身的),std::vector是一些用来操作vector类型数据的函数。所以一定要注意,vector和std::vector虽然有关联,但不是一个东西
39 | * 在Move官方版本中,操作vector类型的是std::vector,而在一些公链的Stdlib中则是std::Vector,这个也需要格外注意一下
40 |
41 |
42 |
43 | ## 定义Move变量和常量
44 |
45 | 我们这里以Move官方版本为准,使用上面介绍的这些基本数据类型和数组定义Move变量。
46 |
47 | Move是mini版的Rust(开玩笑),Move借鉴了很多Rust的思想。Move在定义变量的时候,也跟Rust一样,使用了let关键字。
48 |
49 | 1. 整型变量
50 |
51 | ~~~
52 | //变量定义
53 | let _a:u8 = 1;
54 | let _b: u8;
55 | _b = 10;
56 | let c = 2u64;
57 |
58 | // 类型强转
59 | let _d = c as u128;
60 | let _e = c as u8;
61 | ~~~
62 |
63 | 上面既介绍了整型变量的定义方式,也介绍了如何类型强转。
64 |
65 | 2. 布尔型变量
66 |
67 | ~~~
68 | let _flag = true;
69 | ~~~
70 |
71 | 3. 地址变量
72 |
73 | ~~~
74 | let _addr1: address;
75 | _addr1 = @test_addr;
76 |
77 | let _addr2 = 0x1;// 确定的地址
78 | ~~~
79 |
80 | 这里的地址类型需要注意一下:test_address是在Move.toml文件里面定义好的地址,通过`@`引用。
81 |
82 |
83 |
84 | 4. 数组类型
85 |
86 | ~~~
87 | let _hello_world = b"hello_world";// 字符串会根据ASCII转换成u8数组
88 | let _boxes: vector = std::vector::empty();// 通过std::vector定义vector类型的数组
89 | ~~~
90 |
91 | 注意一下,数组类型的两个例子很有代表性。
92 |
93 | 5. 常量
94 |
95 | ~~~
96 | const ADDR: address = @test_addr;
97 | const FASLE: bool = false;
98 | ~~~
99 |
100 | 常量是通过const关键字定义的,必须放在方法外面定义。
101 |
102 |
103 |
104 | ## 完整代码
105 |
106 | 我们看一下上面这些例子的完整代码:
107 |
108 | ~~~
109 | script {
110 | const ADDR: address = @test_addr;
111 | const FASLE: bool = false;
112 |
113 | fun main() {
114 | let _a:u8 = 1;
115 | let _b: u8;
116 | _b = 10;
117 | let c = 2u64;
118 |
119 | let _d = (c as u128);
120 | let _e = (c as u8);
121 |
122 | let _flag = true;
123 |
124 | let _addr1: address;
125 | _addr1 = @test_addr;
126 |
127 | let _addr2 = 0x1;
128 |
129 | let _hello_world = b"hello_world";
130 | let _boxes: vector = std::vector::empty();
131 | }
132 | }
133 | ~~~
134 |
135 | 这里注意一下变量前面的下划线`_`,如果定义的变量在后面没有被使用到,编译的时候会抛错,使用`_`表示你知道后面没被使用,在内存中可以快速回收掉,编译器在编译的时候不会报错。这也是Rust的风格。
136 |
137 | 最后,编译一下代码:
138 |
139 | ~~~
140 | move build
141 | ~~~
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | ## 跟我学Move
150 |
151 | 好了,今天主要是教大家Move的基础数据类型,并且通过基础数据类型定义变量和常量。这里划重点,Move跟其他语言的不同,以及很多Move编程需要注意的细节。
152 |
153 | 我是@MoveContract(twittter),也可以关注@Move小王子的知乎专栏“手把手写Move智能合约”,跟我一起学习Move。下期我们将会介绍Move的函数定义。
154 |
--------------------------------------------------------------------------------