├── 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 | move_fun_1 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 | move_friend_1 97 | 98 | 我们定义了一个function1的模块,简单说明一下: 99 | 100 | * ② 通过`public(friend)`关键字定义了一个`friend`可见性的test_friend1的函数 101 | * ① 通过`friend`授权让std地址(本实例是0x2地址)的function2的模块可以调用 102 | 103 | 我们再定义一个function2的模块,对test_friend1进行调用: 104 | 105 | move_friend_2 106 | 107 | 简单说明一下(这块跟调用public函数一样,没有区别,注意一下跨Module调用以及use关键字): 108 | 109 | * ③ 是引入function1的模块 110 | * ④ 调用function1的test_friend1函数 111 | 112 | 以上是`friend`可见性函数的调用例,都是在std的address下(本例是0x2)。我们来看一下,`friend`的函数能不能授权给其他的address调用呢? 113 | 114 | move_friend_3 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 | image-20220917112202477 127 | 128 | Move最早叫script可见性,后面修改成entry可见性了,更直接,更好理解,也能跟模块化的script进行区分(讲模块化的时候会讲到)。不管叫什么,也不管关键字是什么,理解了它的应用场景最重要,使用起来很简单。下面是例子: 129 | 130 | move_entry 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 | move_mut_ref 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 | move_return 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 | move时薪 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 | info&resouurce 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 | Move_Framework 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 | move1 93 | 94 | 下载完成之后,编译和安装Move-cli: 95 | 96 | ``` 97 | $ cargo install --path move/language/tools/move-cli 98 | ``` 99 | 100 | move3 101 | 102 | 这种方式安装的Move会在 cargo home里,例如~/.cargo/bin。查看安装结果: 103 | 104 | ~~~ 105 | $ move -h 106 | ~~~ 107 | 108 | 如果安装正确,会显示move的帮助信息。 109 | 110 | move2 111 | 112 | 这时候,Move环境已经搭建好了。 113 | 114 | 115 | 116 | ## Move插件和IDE 117 | 118 | 前面搭建了Move的编译环境,如果想要快速开发Move程序,还需要选择合适的IDE(主要是高亮和自动提示等,帮助开发)。我习惯使用Idea开发,大家也可以选择VS Code等IDE。 119 | 120 | move4 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 | move5 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 | move6 153 | 154 | 生成一个hello_world.move的文件,如下图所示: 155 | 156 | move8 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 | move10 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 | move7 192 | 193 | 最后一步,验证结果: 194 | 195 | ~~~ 196 | move sandbox run sources/hello_world.move 197 | ~~~ 198 | 199 | 打印结果出来了。 200 | 201 | move11 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 | move_string 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 | image-20220821100344880 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 | move_variable 146 | 147 | 148 | 149 | ## 跟我学Move 150 | 151 | 好了,今天主要是教大家Move的基础数据类型,并且通过基础数据类型定义变量和常量。这里划重点,Move跟其他语言的不同,以及很多Move编程需要注意的细节。 152 | 153 | 我是@MoveContract(twittter),也可以关注@Move小王子的知乎专栏“手把手写Move智能合约”,跟我一起学习Move。下期我们将会介绍Move的函数定义。 154 | --------------------------------------------------------------------------------