介绍

Move 中局部变量使用let关键字定义,变量可以直接修改。

声明

  1. ##
  2. let x; // 声明一个变量
  3. x = 0; // 赋值
  4. ##
  5. let x = 1; // 声明一个变量并赋值
  6. let x = x + x;

赋值

变量必须在使用前进行赋值操作。

  1. let x;
  2. x + x; // ERROR
  3. let x;
  4. if (cond) x = 0;
  5. x + x; // ERROR
  6. let x;
  7. while (cond) x = 0;
  8. x + x; // ERROR

命名

变量名可以包含_a-Z0-9。但是变量名必须是以_或者字母a-z开头。

  1. // valid name
  2. let x = e;
  3. let _x = e;
  4. let _A = e;
  5. let x0 = e;
  6. let xA = e;
  7. let foo_11 = e;
  8. // invalid name
  9. let X = e; // ERROR
  10. let Foo = e; // ERROR

注释

通常情况下,Move 可以自动识别变量类型,不过变量类型显性表示出来,可以让代码更可读、更清晰:

  1. let x: T = e; // 变量x的类型为T,赋值为e
  1. address 0x1 {
  2. module Example {
  3. struct S { x: u64, y: u64 }
  4. fun annotated() {
  5. let u: u8 = 0;
  6. let b: vector<u8> = b"hello";
  7. let q: address = @0x0;
  8. let (x, y): (&u64, &mut u64) = (&0, &mut 1);
  9. let S { x, y: f2 }: S = S { x: 0, y: 1 };
  10. }
  11. }
  12. }

一些特殊情况下,Move 编译器无法识别变量类型,则必须显性表示:

  1. let _v1 = Vector::empty(); // ERROR!
  2. // ^^^^^^^^^^^^^^^ Could not infer this type. Try adding an annotation
  3. let v2: vector<u64> = Vector::empty(); // NO ERROR

下面情况下,Move 类型编译器野无法识别变量类型。returnabort函数能返回任何类型:

  1. let a: u8 = return ();
  2. let b: bool = abort 0;
  3. let c: signer = loop ();
  4. let x = return (); // ERROR!
  5. // ^ Could not infer this type. Try adding an annotation
  6. let y = abort 0; // ERROR!
  7. // ^ Could not infer this type. Try adding an annotation
  8. let z = loop (); // ERROR!
  9. // ^ Could not infer this type. Try adding an annotation

元组

  1. let () = ();
  2. let (x0, x1) = (0, 1);
  3. let (y0, y1, y2) = (0, 1, 2);
  4. let (z0, z1, z2, z3) = (0, 1, 2, 3);

结构

let在赋值struct类型时,可以一次创建多个局部变量,并初始化为此struct中的字段:

  1. ## example 1
  2. struct T { f1: u64, f2: u64 }
  3. let T { f1: local1, f2: local2 } = T { f1: 1, f2: 2 };// local1: u64,local2: u64
  4. ## example 2
  5. address 0x1 {
  6. module Example {
  7. struct X { f: u64 }
  8. struct Y { x1: X, x2: X }
  9. fun new_x(): X {
  10. X { f: 1 }
  11. }
  12. fun example() {
  13. let Y { x1: X { f }, x2 } = Y { x1: new_x(), x2: new_x() };
  14. assert!(f + x2.f == 2, 1);
  15. let Y { x1: X { f: f1 }, x2: X { f: f2 } = Y { x1: new_x(), x2: new_x() };
  16. assert!(f1 + f2 == 2, 1);
  17. }
  18. }
  19. }

struct有两个作用:识别绑定的字段和变量名称。

  1. let X { f } = e;
  2. let X { f: f } = e; // 这两种写法等价
  3. let Y { x1: x, x2: x } = e; // let 关键字在元组赋值时不能声明两个相同的名称 x

引用

在下面的struct事例中,使用let关键字后,绑定的值实际上是被移动了:销毁了struct和它绑定的值:

  1. ## let 之后,T { f1: 1, f2: 2 } 被销毁
  2. struct T { f1: u64, f2: u64 }
  3. let T { f1: local1, f2: local2 } = T { f1: 1, f2: 2 };

如果使用let关键字后不让他自动销毁,可以引用此struct,例如:

  1. let t = T { f1: 1, f2: 2 };
  2. let T { f1: local1, f2: local2 } = &t;

忽略某些值

使用let关键字,还可以做到忽略部分值。使用_作为前缀,不会引入新的变量:

  1. fun three(): (u64, u64, u64) {
  2. (0, 1, 2)
  3. }
  4. let (x1, _, z1) = three();
  5. let (x2, _y, z2) = three();
  6. assert!(x1 + z1 == x2 + z2)

语法解释

  1. let (x, y): (u64, u64) = (0, 1);
  2. // ^ local-variable
  3. // ^ pattern
  4. // ^ local-variable
  5. // ^ pattern
  6. // ^ pattern-list
  7. // ^^^^ pattern-list
  8. // ^^^^^^ pattern-or-list
  9. // ^^^^^^^^^^^^ type-annotation
  10. // ^^^^^^^^ initializer
  11. // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ let-binding
  12. let Foo { f, g: x } = Foo { f: 0, g: 1 };
  13. // ^^^ struct-type
  14. // ^ field
  15. // ^ field-binding
  16. // ^ field
  17. // ^ local-variable
  18. // ^ pattern
  19. // ^^^^ field-binding
  20. // ^^^^^^^ field-binding-list
  21. // ^^^^^^^^^^^^^^^ pattern
  22. // ^^^^^^^^^^^^^^^ pattern-or-list
  23. // ^^^^^^^^^^^^^^^^^^^^ initializer
  24. // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ let-binding