变量和可变性
变量是用来存储数据的标识符,在程序执行过程中其对应的值是可以被改变的。在 Rust 中,对于变量的命名使用 snake_case 的形式。变量分为可变变量和不可变变量, 默认情况下声明的变量是不可变的。
通过控制变量的可以变性,来避免程序开发中的数据竞争和其他并发问题。在变量声明的时候,需要显性的考虑清楚变量的可变性。使用不可变变量可保证程序的安全性,但是会丧失一定的灵活性。
fn main() {
// 默认声明不可变变量
let x = 9;
println!("{}", x);
// x = 10; cannot assign twice to immutable variable `x`
println!("{}", x);
// 可变变量
let mut y = 9;
y = 10;
println!("{}", y);
//变量遮蔽; 复用变量名 y, 此时的 y 已经和上面的 y 没有任何关系; 可以避免起新的变量名
let y = "zjl";
println!("{}", y);
// 使用 _ 开头,声明一个不使用的变量,常见用项目设计阶段
let _y = 9;
// 变量解构
let (a, mut b): (bool, bool) = (true, false);
println!("{}:{}", a, b)
}
常量
常量一般用于声明程序中一些固定值,通常伴随整个生命周期且不会更改,命名方式使用 SNAKE_CASE 的形式。相较于不可变变量,常量使用 const 进行声明,还需要声明具体的类型。
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
fn main() {
println!("{}", THREE_HOURS_IN_SECONDS);
}
基本数据类型
Rust 是静态类型的语言,所有的值在编译时期就需要知道其具体的数据类型。当然,有些时候开发者无需指定具体类型,编译器会进行类型推断出具体类型。在 Rust 中可以将数据分为标量类型和复合类型。标量(scalar) 类型表示单个值。在 Rust 中有四个基本的标量类型:整型、浮点型、布尔和字符。
整型:对于 isize 和 usize 类型,其长度由程序运行时的计算机体系结构确定。使用 64 位架构系统则为 64 位,若使用 i32 位架构系统则为 32 位。
浮点型:是带有小数点的数字,在 Rust 中浮点类型(简称浮点型)数字也有两种基本类型。Rust 的浮点型是 f32 和 f64,它们的大小分别为 32 位和 64 位。默认浮点类型是 f64,因为在现代的 CPU 中它的速度与 f32 的几乎相同,但精度更高。所有浮点型都是有符号的。
布尔类型: Rust 中的布尔类型也有两个可能的值:true 和 false。布尔值的大小为 1 个字节。
字符类型: Rust 的 char(字符)类型是该语言最基本的字符类型。
fn main() {
let c = '😊';
println!("{}", std::mem::size_of_val(&c)); // 4 byte
let (a, b) = (1, 1.0);
println!("{}: {}", a + 1, b + 1.0);
}
函数
函数是一段具有特定功能的代码块,它可以接受输入(称为参数或变量),执行特定的操作,然后可能返回一个结果。函数的主要目的是封装一些代码,使其可以被重复使用,提高代码的可读性和可维护性。 在 Rust 中,函数命名使用 snake_case。
函数由一些列的语句组成,也可以选择表达式结尾,作为函数的返回结果。
fn main() {
// 语句:执行一些操作但是不返回值
let (a, b) = (5, 6);
let b = {
let y = a + b;
y // y 是一个表达式,即返回具体的值; 后面加 ';' 就是一个语句
};
println!("{}", b)
}
// 函数永不返回
fn f() -> ! {
loop {
println!("{}", 0);
}
}
fn add(a: i32, b: i32) -> i32 {
return a + b;
}
fn add_v1(a: i32, b: i32) -> i32 {
a + b //直接使用表达式, 无需使用语句:return a+b
}
注释
有人认为好的代码不需要注释,代码是可以自解释的。我并不完全认同这个观点,毕竟代码是写给计算机用来执行的,不像自然语言一样可以表达含义。通过注释可以直接表述复杂的代码逻辑。
在 Rust 中,通常使用双斜杠开头,直到行尾结束。对于超出单行的注释,需要在每行的行首增加//。此外,还有一种 /// 的注释,即文档注释,会生成对应的 html 文档,同时还支持嵌入代码,使用 cargo test 会被执行。此外,还有一种 //!的注释,用于注释说明整个 crate 的功能, 它下面可以不声明任何代码。
// 使用 cargo test 会执行 # Examples 下的代码,从而保证示例代码的准确性;
/// Adds one to the number given.
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = chapter14::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
// 加一运算
x + 1
}
控制流
控制流是一种描述程序执行顺序的机制,它决定了程序中语句或表达式的执行顺序。和其他语言一样,Rust 的控制流可以使用 if else, loop,for, while,contine, break 实现。
fn main() {
let a = 3;
// if 后面的表达式必须是一个布尔类型,否则将无法编译。
// if a {
// //expected `bool`, found integer
// println!("hello world");
// }
// 使用 else if 处理多重判断条件
if a % 2 == 0 {
println!("x2")
} else if a % 3 == 0 {
println!("x3")
} else if a % 4 == 0 {
println!("x4")
} else {
println!("null")
}
// if 本质上是一个表达式
let r = if a > 0 { true } else { false };
println!("{}", r);
}
contine 和 break 通常和 loop,for, while 一起使用。continue: 跳过这个循环阶段的剩余代码,开始下一个循环。break: 直接跳出循环。如果存在多层循环,continue 和 break 只能作用与内层循环,如果要跳出外层循环需要使用 loop label。
fn main() {
let (mut count, mut k) = (0, 10);
'loop_label: loop {
loop {
if k == 5 {
break 'loop_label; // 跳出外层循环
}
k = k - 1;
break;
}
count += 1;
}
println!("count: {}", count);
// 使用 break 返回 loop 表达式的值
let count = loop {
if count < 10 {
count += 1
} else {
break count;
}
};
println!("count: {}", count);
// for 循环表达式
for i in 1..4 {
println!("{}", i);
}
}