fn main(){
  // rust里通过let关键字来声明变量
  let x = 1;
  let y:i64 = 2;
  let z = 3i32;
  let total = x + y;
  // println!("{},{}", x+y, x+z);
  // rust可以通过在变量名后加冒号type":type"的形式来声明一个变量
  // 很多时候type可以省略、rust会通过上下文类型推导来确定变量的类型
  // 比如上面注释的部分会报错，因为x+y会将x推导为i64，x+z会让一个i64类型和i32类型相加，这是不允许的
  println!("x+y = {}, z = {}", total, z);
  // 对于数字类型，也可以将类型放置在数字值后面，为了方便阅读，数字值之间可以通过下划线隔开
  let big = 100_000_000;
  println!("big is {}", big);
  // 变量也可以先声明，后赋值进行初始化，不过不允许使用未被初始化的变量
  let lazy_initial;
  /**
  *测试一：加入条件分支
  *虽然在代码逻辑上lazy_initial一定会被初始化
  *不过rust不允许这种情况出现。虽然rust的类型推导很智能，但还无法对这种条件的结果进行判断，所以分支条件必须是完备的。
  if true{
    lazy_initial = 3;
  }
  *测试二：条件分支完备
  *虽然以下条件分支完备，但lazy_initial的类型值却不一样，这也是无法通过的。初始化的数据类型必须一致。
  if true{
    lazy_initial = 3;
  }else{
    lazy_initial = "hello";
  }
  */
  // println!("lazy_initial = {}", lazy_initial); // 这个会报错，lazy_initial还没被初始化
  {
    // 初始化变量
    lazy_initial = 5;
    println!("init lazy");  
  }
  println!("lazy var:{}", lazy_initial);
  // 变量遮蔽
  // x和y已经声明过了，但我们可以再次声明x或y，在以下的部分，x和y的值被新的值所遮蔽
  let x;
  let y = "hello"; // y的类型是&str，而不是上面的i64了
  x = if true {
    true
  } else {
    false
  };
  println!("x = {}, y = {}", x, y);
  // z = 100;
  // 上面的 z = 100，前面没有let，这是对变量重新赋值，和变量遮蔽不一样，重新赋值是不能改变数据类型的。
  // 更重要的是rust里声明的变量默认是不可变的，所以上面的语句会报错。要声明一个变量可变，得加上mut关键字。
  let mut z = 1;
  println!("before:z = {}", z);
  z = 100;
  println!("after:z={}", z);
  // 再来看看下面的例子
  // 右边的值是一个元组，我们通过左侧同样使用元组的方式对数据进行了解构
  // 所以rust里let的作用并不是简单的声明变量，它提供了一种模式，将右侧的数据按照左侧的书写模式绑定到对应变量上。
  let (mut x, y) = (1, 2);
  x = 99;
  // y = 100; // y is immutable，y是不可变的
  println!("x = {}, y = {}", x, y);
  // 再看回去上面的x通过条件语句的方式完成了初始化
  // rust里所有的语句、代码块、表达式都具有返回值。
  // 比如上面的赋值语句，声明语句，循环里用到的break; continue;等，它们都会返回单元类型()。
  // 当像if这样的条件语句放在右侧时，其后必须加分号;，成为语句表达式，其内部可以通过将返回值绑定到左侧。
  // 如果一个表达式没有后面的分号;，这个值就会被当作返回值，如果在函数体内，它的作用和（return xxx;）等效；
  // 注意：在上面的if/match这类的语句表达式中，必须以这种方式返回值，如果使用return，它的作用是为当前语句所在的函数体返回值，而不是左侧变量。
  // let a = b = 1; // rust里是不支持这种连续赋值的，按照解析方式，会先解析右侧 b = 1;，相当于为b赋值1，而此时b还未被声明；另外b = 1;语句应该返回单元类型()，这样a的值就变成了()而不是1，显然这有违我们的本意。
  let z = x;
  println!("z被赋值成x的值:{}",z);
  // 因为x是i32类型，上面的赋值语句会将x的值直接拷贝一份绑定给z，这没有什么好疑惑的，但后面介绍到复杂类型和变量的所有权的时候，我们会发现赋值操作有时会发生所有权的转移，我们需要进行更深入的研究。
}