5分钟速读之Rust权威指南(三)

547 阅读4分钟

变量与函数

变量

变量的值默认是不可变,是不是有点诧异?叫变量却不可变:

let x = 5;
println!("{}", x); // 5
x = 6; // 报错,x是不可变的

使用mut关键字来让变量可变:

let mut y = 5;
println!("{}", y); // 5
y = 6; // 加了mut之后就可以改变了
println!("{}", y); // 6

变量允许重复定义,将上一次的定义覆盖:

let z = 5;
println!("{}", z); // 5
let z = 6; // 重复定义
println!("{}", z); // 6

// 允许改变上次的类型
let spaces = "123";
let spaces = spaces.len(); // 从&str变成usize
println!("{}", spaces); // 3

// 使用mut只能变变量值,不能改变类型
let mut spaces2 = "123";
spaces2 = spaces2.len() // 报错,预期一个&str,却得到一个usize

常量使用const关键字来定义,名称全部大写,并显式标明数据类型:

const YEAR: u32 = 365;
println!("{}", YEAR); // 365

使用解构定义变量

从数组中解构成员:

let arr = [1, 2, 3];
let [a, b, c] = arr;
println!("{}-{}-{}", a, b, c); // 1-2-3

// 只解构部分成员将报错
let [a,b] = arr; // 报错,预期解构3个成员

// 使用..来省略不需要的成员
let [a, b, ..] = arr;

从元组中解构成员:

let tuple = (1, "2", 3.5);
let (a, b, c) = tuple;
println!("{}-{}-{}", a, b, c);

// 元组中也必须结构全部成员
let (a, b) = tuple; // 报错,预期解构3个成员

// 使用..来省略不需要的成员
let (a, b, ..) = tuple;

从结构体中解构属性,可以先将结构体理解成JS中的对象:

// 声明一个User类型的结构体
struct User {
  name: String,
  age: u8,
}

// 定义一个xiao_ming变量,是User结构体类型
let xiao_ming = User {
	name: "xiaoming".to_string(),
  age: 18
};

// 全部解构
let User { name, age } = xiao_ming;
println!("name:{} age:{}", name, age); // name:xiaoming age:18

// 解构部分,仍需要保证左模式与有结构相符合
let User { age } = xiao_ming; // 报错,左边的模式与右边的结构不匹配,缺少age

// 使用..来省略不需要的成员
let User { age, .. } = xiao_ming;
println!("age:{}", age); // 18

从枚举中结构成员,可以先简单认识下枚举,后面会有章节介绍:

// 定义一个枚举类型:Ip
enum Ip {
  // 包含一个String类型的“变体”:V4
	V4(String)
}

// 定义一个Ip::V4类型的变量(双冒号可以理解为“.”),包含的值是"127.0.0.1"
let localhost = Ip::V4(String::from("127.0.0.1"));

// 当枚举只有一个类型的时候我们可以直接进行解构,let后面是模式,括号中的ip是解构出来的变量
let Ip::V4(ip) = localhost;
println!("{}", ip); // 127.0.0.1

// 当枚举有多个类型时直接解构将报错
enum Ip {
	V4(String),
	V6(String)
}
let Ip::V4(ip) = localhost; // 报错, 因为localhost有可能是V6类型的

// 使用if let来对多个类型进行覆盖
if let Ip::V4(ip) = localhost {
    println!("V4: {}", ip); // 127.0.0.1    
} else if let Ip::V6(ip) = localhost {
    println!("V6: {}", ip);
} else {
    println!("other");
}

函数

函数使用关键字fn来定义,命名使用snake case命名方式,用下划线分割单词名称:

fn fn_name() {
  println!("fn_name called");
}

调用函数:

foo();

函数参数在技术文档中有两种区分:parameter表示形参,argument表示实参,但是我们一般不会区分,函数参数的类型必须显式注明:

foo(100); // 函数是允许调用在定义之前的
fn foo(num: i32) {
  println!("foo called with: {}", num); // foo called with: 100
}

函数的返回值的类型也需要显式注明,使用→来标注返回值类型:

// 标明返回i32类型
fn add_one(num: i32) -> i32  {
  return num + 1
}

// return关键字可以省略
fn add_two(num: i32) -> i32  {
  // 由于num+1后没有其他的调用,则会被为是函数的返回值
	// 但是末尾不能加分号结尾
  num + 2
}

函数的返回值其实是元组:

// 相当于返回了空元组:()
fn foo() {}

// 相当于返回了具有一个成员的元组:(1)
fn foo2() -> i32 {
  return 1
}
let num = foo2();
println!("{}", num); // 1

// 所以可以返回多个成员的元组
fn foo4() -> (i32, bool) {
  return (1, true)
}
let res = foo4();
// 打印元组或者数组需要使用:?占位符
println!("{:?}", res); // (1, true)

// 可以解构返回值
let (num, boo) = foo4();
println!("{}-{}", num, boo); // 1, true

// 解构返回值仍然需要解构所有成员
let (num) = foo4(); // 报错,返回值的元祖中包含两个成员

// 使用..来省略不需要的成员
let (num, ..) = foo4();

// 由于返回值是一个元组,所以用“.”可以取返回值的成员
let tup = foo4()
println!("{}", tup.0); // 1
println!("{}", tup.1); // true

函数体中的语句和表达式

语句指那些执行操作但没有返回值的指令:

// 使用let关键字创建变量并绑定值时使用的指令是一条语句
let x = 2;

// let z = 5是语句,没有返回值,所以rust不允许这样做
let y = (let z = 5); // 报错

表达式则是指会进行计算并产生一个值作为结果的指令:

// 数学运算是表达式
1 + 2

// 作用域块也是表达式
let x = {
  let y = 1;
  // 假如在表达式的末尾加上了分号,就变为了语句而不会返回任何值
  y + 2
};
println!("{}", x) // 3