一、基本类型
Move 的基本数据类型包括: 整型 (u8, u64, u128)、布尔型 boolean 和地址 address。
Move 不支持字符串和浮点数。
整型
整型包括 u8、u64 和 u128,我们通过下面的例子来理解整型:
script {
fun main() {
// define empty variable, set value later
let a: u8;
a = 10;// define variable, set type let a: u64 = 10; // finally simple assignment let a = 10; // simple assignment with defined value type let a = 10u128; // in function calls or expressions you can use ints as constant values if (a < 10) {}; // or like this, with type if (a < 10u8) {}; // usually you don't need to specify type } }
运算符as
当需要比较值的大小或者当函数需要输入不同大小的整型参数时,你可以使用as运算符将一种整型转换成另外一种整型:
script {
fun main() {
let a: u8 = 10;
let b: u64 = 100;// we can only compare same size integers if (a == (b as u8)) abort 11; if ((a as u64) == b) abort 11; } }
布尔型
布尔类型就像编程语言那样,包含false和true两个值。
script {
fun main() {
// these are all the ways to do it
let b : bool; b = true;
let b : bool = true;
let b = true
let b = false; // here’s an example with false
} }
地址
地址是区块链中交易发送者的标识符,转账和导入模块这些基本操作都离不开地址。
script {
fun main() {
let addr: address; // type identifier// in this book I'll use {{sender}} notation; // always replace `{{sender}}` in examples with VM specific address!!! addr = {{sender}}; // in Diem's Move VM and Starcoin - 16-byte address in HEX addr = 0x...; // in dfinance's DVM - bech32 encoded address with `wallet1` prefix addr = wallet1....; } }
二、表达式和作用域
在编程语言中,表达式是具有返回值的代码单元。有返回值的函数调用是一个表达式,它有返回值;整型常数也是一个表达式,它返回整数;其它表达式依此类推。
表达式必须用分号";"隔开
空表达式
类似于 Rust,Move 中的空表达式用空括号表示:
script {
fun empty() {
() // this is an empty expression
} }
文字(Literal)表达式
下面的代码,每行包含一个以分号结尾的表达式。最后一行包含三个表达式,由分号隔开。
script {
fun main() {
10;
10 + 5;
true;
true != false;
0x1;
1; 2; 3
} }
现在我们已经知道了最简单的表达式。但是为什么我们需要它们?以及如何使用它们?这就需要介绍 let 关键字了。
变量和let关键字
关键字 let 用来将表达式的值存储在变量中,以便于将其传递到其它地方。我们曾经在基本类型章节中使用过 let,它用来创建一个新变量,该变量要么为空(未定义),要么为某表达式的值。
script {
fun main() {
let a;
let b = true;
let c = 10;
let d = 0x1;
a = c;
} }关键字 let 会在当前作用域内创建新变量,并可以选择初始化此变量。该表达式的语法是:let : ;或let = 。
创建和初始化变量后,就可以使用变量名来修改或访问它所代表的值了。在上面的示例中,变量 a 在函数末尾被初始化,并被分配了一个值 c。
等号"="是赋值运算符。它将右侧表达式赋值给左侧变量。示例:a = 10 表示将整数10赋值给变量a。
整型运算符
Move具有多种用于修改整数值的运算符:
下划线 “_” 表示未被使用
Move 中每个变量都必须被使用,否则代码编译不会通过, 因此我们不能初始化一个变量却不去使用它。但是你可以用下划线来告诉编译器,这个变量是故意不被使用的。
例如,下面的脚本在编译时会报错:
script {
fun main() {
let a = 1;
} }报错:
┌── /scripts/script.move:3:13 ───
│ 33 │ let a = 1;
│ ^ Unused assignment or binding for local ‘a’. Consider removing or replacing it with ‘_’
│
编译器给出明确提示:用下划线来代替变量名。
script {
fun main() {
let _ = 1;
} }
屏蔽
Move 允许两次定义同一个的变量,第一个变量将会被屏蔽。但有一个要求:我们仍然需要"使用"被屏蔽的变量。
script {
fun main() {
let a = 1;
let a = 2;
let _ = a;
} }
在上面的示例中,我们仅使用了第二个a。第一个a实际上未使用,因为a在下一行被重新定义了。所以,我们可以通过下面的修改使得这段代码正常运行。
script {
fun main() {
let a = 1;
let a = a + 2;
let _ = a;
} }
块表达式
块表达式用花括号"{}"表示。块可以包含其它表达式(和其它代码块)。函数体在某种意义上也是一个代码块。
script {
fun block() {
{ };
{ { }; };
true;
{
true;{ 10; }; }; { { { 10; }; }; }; } }
作用域
如 Wikipedia 中所述,作用域是绑定生效的代码区域。换句话说,变量存在于作用域中。Move 作用域是由花括号扩起来的代码块,它本质上是一个块。
定义一个代码块,实际上是定义一个作用域。
script {
fun scope_sample() {
// this is a function scope
{
// this is a block scope inside function scope
{
// and this is a scope inside scope
// inside functions scope… etc
};
};{ // this is another block inside function scope }; } }
从该示例可以看出,作用域是由代码块(或函数)定义的。它们可以嵌套,并且可以定义多个作用域,数量没有限制。
变量的生命周期和可见性
我们前面已经介绍过关键字 let 的作用,它可以用来定义变量。有一点需要强调的是,该变量仅存在于变量所处的作用域内。也就是说,它在作用域之外不可访问,并在作用域结束后立即消亡。
script {
fun let_scope_sample() {
let a = 1; // we’ve defined variable A inside function scope{ let b = 2; // variable B is inside block scope { // variables A and B are accessible inside // nested scopes let c = a + b; }; // in here C dies // we can't write this line // let d = c + b; // as variable C died with its scope // but we can define another C let c = b - 1; }; // variable C dies, so does C // this is impossible // let d = b + c; // we can define any variables we want // no name reservation happened let b = a + 1; let c = b + 1; } // function scope ended - a, b and c are dropped and no longer accessible }
变量仅存在于其作用域(或代码块)内,当作用域结束时变量随之消亡。
块返回值
上面我们了解到代码块是一个表达式,但是我们没有介绍为什么它是一个表达式以及代码块的返回值是什么。
代码块可以返回一个值,如果它后面没有分号,则返回值为代码块内最后一个表达式的值。
script {
fun block_ret_sample() {// since block is an expression, we can // assign it's value to variable with let let a = { let c = 10; c * 1000 // no semicolon! }; // scope ended, variable a got value 10000 let b = { a * 1000 // no semi! }; // variable b got value 10000000 { 10; // see semi! }; // this block does not return a value let _ = a + b; // both a and b get their values from blocks } }
代码块中的最后一个表达式(不带分号)是该块的返回值。
体验后总结:
1.每个表达式都必须以分号结尾,除非它是 block 的返回值。
2.关键字 let 使用值或表达式创建新变量,该变量的生命周期与其作用域相同。
3.代码块是一个可能具有也可能没有返回值的表达式。