变量和可变性
Rust变量默认是不可改变的(immutable),一旦值被绑定一个名称上,你就不能改变这个值
let x = 5;
x = 6;
报错cannot assign twice to immutable variable
对不可变变量 x 赋第二个值,如果想要重新赋值,可以这样做:
let mut x = 5;
x = 6;
通过 mut ,允许把绑定到 x 的值从 5 改成 6
变量和常量的区别
不可变变量和通常意义的常量有一定的区别
常量(constants)类似于不可变变量, 常量是绑定到一个名称的不允许改变的值
(1)不允许对常量使用 mut 。常量不光默认不能变,它总是不能变。
(2)声明常量使用 const 关键字而不是 let ,并且 必须 注明值的类型。在下一部分,“数据类
型” 中会介绍类型和类型注解,现在无需关心这些细节,记住总是标注类型即可。
(3)常量可以在任何作用域中声明,包括全局作用域,这在一个值需要被很多部分的代码用到时很有用。
(3)最后一个区别是,常量只能被设置为常量表达式,而不能是函数调用的结果,或任何其他只能在运行时计算出的值。
const MAX_POINTS: u32 = 100_000;
常量在整个程序生命周期中都有效
隐藏(Shadowing)
let x = 5;
let x = x + 1;
let x = x * 2;
这个程序首先将 x 绑定到值 5 上。接着通过 let x = 隐藏 x ,获取初始值并加 1 ,这
样 x 的值就变成 6 了。第三个 let 语句也隐藏了 x ,将之前的值乘以 2 , x 最终的
值是 12 。
对默认不可变变量x绑定初值,但是可以通过let定义同名的x对其进行隐藏,去掉let就等同于对不可变变量重新赋值了,会报错
隐藏与将变量标记为 mut 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用
let 关键字,就会导致编译时错误。通过使用 let ,我们可以用这个值进行一些计算,不
过计算完之后变量仍然是不变的。
mut 与隐藏的另一个区别是,当再次使用 let 时,实际上创建了一个新变量,我们可以改
变值的类型,但复用这个名字。
let spaces = " ";
let spaces = spaces.len();
数据类型
Rust 是 静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的
类型
let guess: u32 = "42".parse().expect("Not a number!");
u32即类型注解,如果不加
let guess = "42".parse().expect("Not a number!");
则会报错。
标量类型
标量(scalar)类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔
类型和字符类型
长度 | 有符号 | 无符号 |
---|---|---|
8-bit16-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
isize 和 usize 类型依赖运行程序的计算机架构:64 位架构上它们是 64 位的, 32
位架构上它们是 32 位的。
Rust 中的整型字面值
数字字面值 | 例子 |
---|---|
Decimal | 98_222 |
Hex | 0xff |
Octal | 0o77 |
Binary | 0b1111_0000 |
Byte | ( u8 only) b'A' |
浮点型
f32 是单精度浮点数, f64 是双精度浮点数。
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
数值运算
fn main() {
// 加法
let sum = 5 + 10;
// 减法
let difference = 95.5 - 4.3;
// 乘法
let product = 4 * 30;
// 除法
let quotient = 56.7 / 32.2;
// 取余
let remainder = 43 % 5;
}
布尔型
fn main() {
let t = true;
let f: bool = false; // 显式指定类型注解
}
字符类型
Rust 的 char 类型是语言中最原生的字母类型
fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '';
}
Rust 的 char 类型的大小为四个字节(four bytes),并代表了一个 Unicode 标量值(Unicode
Scalar Value),这意味着它可以比 ASCII 表示更多内容。在 Rust 中,拼音字母(Accented
letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的
char 值。Unicode 标量值包含从 U+0000 到 U+D7FF 和 U+E000 到 U+10FFFF 在内的值。
不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的
char 并不符合。
复合类型
复合类型(Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:
元组(tuple)和数组(array)。
元组类型
fn main() {
#元组初始化
let tup: (i32, f64, u8) = (500, 6.4, 1);
#复制元组
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
#访问元组
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
数组类型
数组中的每个元素的类型必须相,Rust 中的数组是固定长度的:一旦
声明,它们的长度不能增长或缩小。
fn main() {
#数组初始化
let a = [1, 2, 3, 4, 5];
let a: [i32; 5] = [1, 2, 3, 4, 5];#类型为i32大小为5
let a = [3; 5];#大小为5,初始值都为3
#访问数组
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
vector 类型是标准库提供的一个 允许 增长和缩小长度的类似数组的集合类型。当
不确定是应该使用数组还是 vector 的时候,你可能应该使用 vector。第八章会详细讨论
vector。
函数
Rust 代码中的函数和变量名使用 snake case 规范风格。在 snake case 中,所有字母都是小
写并使用下划线分隔单词
Rust 不关心函数定义于何处,只要定义了就行
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
函数参数
fn main() {
another_function(5);
}
fn another_function(x: i32) {
println!("The value of x is: {}", x);
}
语句和表达式
函数体由一系列的语句和一个可选的结尾表达式构成
Rust 是一门基于表达式(expression-based)的语言, 这是一个需要理解的(不同于其他语言)重要区别.
-
语句(Statements)是执行一些操作但不返回值的指令。
-
表达式(Expressions)计算并产生一个值
let 关键字创建变量并绑定一个值是一个语句,
let x = (let y = 6); #将会报错
let y = 6 语句并不返回值,所以没有可以绑定到 x 上的值
函数定义也是语句
函数调用是一个表达式。
宏调用是一个表达式。
我们用来创建新作用域的大括号(代码块) {} ,也是一个表达式,
let y = 6; 中的 6 是一个表达式,它计算出的值是 6
let y = {
let x = 3;
x + 1
};
{}代码块是表达式, x + 1结尾没有分号
具有返回值的函数
fn five() -> i32 {
5
}
->指定函数的返回值。{5}代码块是一个表达式,返回值为5,5后面不能带分号。
注释
// hello, world #rust只有单行注释,如果要多行,每行前都加上//
Rust 还有另一种注释,称为文档注释,后面在看。
控制流
if 表达式
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
}
代码中的条件 必须 是 bool 值。如果条件不是 bool 值,我们将得到一个错误。
Rust 并不会尝试自动地将非布尔值转换为布尔值。必须总是显式地使用布尔值作为 if
的条件
if number != 0{
println!("number was something other than zero");
}
if number {
#错误, number非bool类型
}
使用 else if 处理多重条件
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
Rust 只会执行第一个条件为真的代码块,并且一旦它找到一个以后,甚至都不会检查剩下的条件了。
在 let 语句中使用 if
if 是一个表达式,我们可以在 let 语句的右侧使用它
fn main() {
let condition = true;
let number = if condition {
5
} else {
6
};
println!("The value of number is: {}", number);
}
if 表达式的返回值赋给一个变量, 代码块的值是其最后一个表达式的值,整个 if 表达式的值取决于哪个代码块被执行, 这意味着 if 的每个分支的可能的返回值都必须是相同类型
使用循环重复执行
fn main() {
loop {
println!("again!");
}
}
从循环返回
如果将返回值加入你用来停止循环的 break 表达式,它会被停止的循环返回
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; # break可以带参数跳出循环
}
};
println!("The result is {}", result);
}
while 条件循环
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number = number - 1;
}
println!("LIFTOFF!!!");
}
使用 for 遍历集合
fn main() {
let a = [10, 20, 30, 40, 50];
let mut index = 0;
while index < 5 {
println!("the value is: {}", a[index]);
index = index + 1;
}
}
这个过程很容易出错;如果索引index长度更新不正确会导致程序 panic。这也使程序更慢,因为编译器增加了运行时代码来对每次循环的每个元素进行条件检查。
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a.iter() {
println!("the value is: {}", element);
}
for number in (1..4).rev() {
println!("{}!", number);
}
println!("LIFTOFF!!!");
}
a.iter()是数组的迭代器方法,rev()是翻转,反序遍历,暂时先不管