变量与函数
变量
变量的值默认是不可变,是不是有点诧异?叫变量却不可变:
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");
}
复制代码
调用函数:
函数参数在技术文档中有两种区分: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
复制代码
评论