author: kwhao
date: 2021-01-12 Thur.
变量(Variables)与可变性 (Mutability)
- 变量默认是不可改变的(immutable)。
声明变量
let x = 5;
// 注意这样定义的变量不可改变(immutable)
声明可变(Mutable)变量:在变量名之前加
mut
来使其可变。fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
运行结果:
$ cargo run
Compiling variables v0.1.0 (file:///projects/variables)
Finished dev [unoptimized + debuginfo] target(s) in 0.30s
Running `target/debug/variables`
The value of x is: 5
The value of x is: 6
常量(constants)
- 不允许对常量使用
mut
。 - 声明常量使用
const
关键字:const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
隐藏(Shadowing)
- 可以定义一个与之前变量同名的新变量,则称第一个变量被第二个隐藏了。
可以用相同变量名称来隐藏一个变量,以及重复使用
let
关键字来多次隐藏:fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("The value of x in the inner scope is: {}", x);
}
println!("The value of x is: {}", x);
}
运行结果:
The value of x in the inner scope is: 12
The value of x is: 6
mut
versus 隐藏(Shadowing):隐藏的数据类型可以不同,mut
数据类型必须相同:// 下例隐藏可以运行
let spaces = " ";
let spaces = spaces.len();
// 下例mut不可运行
let mut spaces = " ";
spaces = spaces.len();
数据类型(Data Types)
- Rust 是静态类型(statically typed)语言,即在编译时就必须知道所有变量的类型。
- 类型转换示例:
let guess: u32 = "42".parse().expect("Not a number!");
标量类型(Scalar Types)
标量(scalar)类型代表一个单独的值:整型、浮点型、布尔类型和字符类型。
整型(Integer Types)
| 长度 | 有符号 | 无符号 | | —- | —- | —- | | 8-bit | i8 | u8 | | 16-bit | i16 | u16 | | 32-bit | i32 | u32 | | 64-bit | i64 | u64 | | 128-bit | i128 | u128 | | arch | isize | usize |
多种数字类型的数字字面值允许使用类型后缀,例如
57u8
来指定类型,同时也允许使用_
做为分隔符以方便读数,例如1_000
,它的值与你指定的1000
相同。- | 数字字面值 | 例子 | | —- | —- | | Decimal (十进制) | 98_222 | | Hex (十六进制) | 0xff | | Octal (八进制) | 0o77 | | Binary (二进制) | 0b1111_0000 | | Byte (单字节字符)(仅限于u8) | b’A’ |
整型溢出:
debug
模式会panic
,release
模式会wrapping
。
标准库中有一个类型显式提供此功能,Wrapping
浮点型(Floating-Point Types)
- Rust 的浮点数类型是
f32
和f64
,分别占32
位和64
位。默认类型是f64
,因为在现代 CPU 中,它与f32
速度几乎一样,不过精度更高。 所有浮点型都是有符号的。
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
浮点数采用 IEEE-754 标准表示。
f32
是单精度(single-precision)浮点数,f64
是双精度(double precision)浮点数。
数值运算(Numeric Operations)
加法(addition)、减法(subtraction)、乘法(multiplication)、除法(division)和取余(remainder)。
fn main() {
// 加法
let sum = 5 + 10;
// 减法
let difference = 95.5 - 4.3;
// 乘法
let product = 4 * 30;
// 除法
let quotient = 56.7 / 32.2;
let floored = 2 / 3; // 结果为 0
// 取余
let remainder = 43 % 5;
}
附录 B | Appendix B 包含 Rust 提供的所有运算符的列表。
布尔型(The Boolean Type)
- Rust 中的布尔类型有两个可能的值:
true
和false
。 - Rust 中的布尔类型使用 bool 表示。例如:
fn main() {
let t = true;
let f: bool = false; // 显式指定类型注解
}
字符类型(The Character Type)
- Rust的
char
类型是语言中最原生的字母类型。 - 大小为四个字节(four bytes),并代表了一个 Unicode 标量值(Unicode Scalar Value)。
- 拼音字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的
char
值。 - 包含从
U+0000
到U+D7FF
和U+E000
到U+10FFFF
在内的值。
e.g.fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
}
复合类型(Compound Types)
复合类型(Compound types)可以将多个值组合成一个类型,Rust 有两个原生的复合类型:元组(tuple
)和数组(array
)。
元组类型(The Tuple Type)
- 元组:一个将多个其他类型的值组合进一个复合类型的主要方式。
- 元组长度固定:一旦声明,其长度不会增大或缩小。
元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的:
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
从元组中获取单个值,可以使用模式匹配(pattern matching)来解构(destructure)元组值:
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {}", y);
}
也可以使用点号(.)后跟值的索引来直接访问:
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
元组的第一个索引值是 0。
- 没有任何值的元组
()
是一种特殊的类型,只有一个值,也写成()
。该类型被称为 单元类型(unit type),而该值被称为单元值(unit value)。 - 如果表达式(Expressions)不返回任何其他值,则会隐式返回单元值。
数组类型(The Array Type)
- 数组(array)中的每个元素的类型必须相同。
数组的值写成在方括号内,用逗号分隔:
let a = [1, 2, 3, 4, 5];
想要在栈(stack)而不是在堆(heap)上为数据分配空间,或者是想要确保总是有固定数量的元素时,数组非常有用。
- 但是数组并不如
vector
类型灵活。vector
类型是标准库提供的一个允许增长和缩小长度的类似数组的集合类型。当不确定是应该使用数组还是vector
的时候,那么很可能应该使用vector
。 确定元素个数不会改变时,数组会更有用:
let months = ["January", "February", "March", "April", "May", "June", "July","August", "September", "October", "November", "December"];
在方括号中包含每个元素的类型,后跟分号,再后跟数组元素的数量:
let a: [i32; 5] = [1, 2, 3, 4, 5];
let a = [3; 5]; // 等同于 let a = [3, 3, 3, 3, 3];
访问数组元素(Accessing Array Elements)
使用索引来访问数组的元素:
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
无效的数组元素访问(Invalid Array Element Access)
use std::io;
fn main() {
let a = [1, 2, 3, 4, 5];
println!("Please enter an array index.");
let mut index = String::new();
io::stdin()
.read_line(&mut index)
.expect("Failed to read line");
let index: usize = index
.trim()
.parse()
.expect("Index entered was not a number");
let element = a[index];
println!(
"The value of the element at index {} is: {}",
index, element
);
}
此代码编译成功,若输入10
:
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrac
程序在索引操作中使用一个无效的值时导致运行时错误。
程序带着错误信息退出,并且没有执行最后的println!
语句。
函数(Functions)
fn
关键字,用来声明新函数。- Rust 代码中的函数和变量名使用snake case规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词
- Rust 不关心函数定义于何处,只要定义了就行。
fn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
参数(Function Parameters)
- 参数类型:形参(parameters)、实参(arguments),交流时统称参数。
- 函数签名中,必须声明每个参数的类型。
- 定义多个参数时,使用逗号分隔。
fn main() {
print_labeled_measurement(5, 'h');
}
fn print_labeled_measurement(value: i32, unit_label: char) {
println!("The measurement is: {}{}", value, unit_label);
}
语句和表达式(Function Bodies Contain Statements and Expressions)
- Rust 是一门基于表达式(expression-based)的语言,这是一个需要理解的(不同于其他语言)重要区别。
- 语句(Statements)是执行一些操作但不返回值的指令。
- 表达式(Expressions)计算并产生一个值。
语句不返回值。
let x = (let y = 6); // 不接受,因为let语句无返回值,不能被接受
函数调用是一个表达式,宏调用是一个表达式,用大括号创建的一个新的块作用域也是一个表达式:
fn main() {
let x = 5;
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {}", y);
}
本例中: 是一个表达式,返回值为
x+1
。{
let x = 3;
x + 1
}
表达式的结尾没有分号。如果在表达式的结尾加上分号,它就变成了语句,而语句不会返回值。
具有返回值的函数(Functions with Return Values)
- 函数可以向调用它的代码返回值,不对返回值命名,但要在箭头(->)后声明它的类型。
- 函数的返回值等同于函数体最后一个表达式的值。
- 使用 return 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式。
fn five() -> i32 {
5
}
fn main() {
let a = five();
println!("The value of a is: {}", a);
let x = plus_one(5);
println!("The value of x is: {}", x);
}
fn plus_one(x: i32) -> i32 {
x + 1
}
注释(comments)
示例:
// 这是注释
// 这还是注释
控制流(Control Flow)
if
表达式(if
Expressions)
使用 else if
处理多重条件(Handling Multiple Conditions with 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");
}
}
在let
语句中使用if
(Using if
in a let
Statement)
- 因为
if
是一个表达式,我们可以在let
语句的右侧使用它 - 注意这样使用时,
if
和else
的返回值均要保证同一数据类型。
示例:
let number = if condition {
5
} else {
6
};
使用循环重复执行(Repetition with Loops)
- Rust 有三种循环:
loop
、while
和for
。
使用 loop
重复执行代码(Repeating Code with loop
)
- 支持
break
和continue
。 可以选择在一个循环上指定一个循环标签(loop label),然后将标签与
break
或continue
一起使用,使这些关键字应用于已标记的循环而不是最内层的循环:fn main() {
let mut count = 0;
'counting_up: loop {
println!("count = {}", count);
let mut remaining = 10;
loop {
println!("remaining = {}", remaining);
if remaining == 9 {
break;
}
if count == 2 {
break 'counting_up;
}
remaining -= 1;
}
count += 1;
}
println!("End count = {}", count);
}
从循环返回(Returning Values from Loops)
示例:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("The result is {}", result);
}
while
条件循环(Conditional Loops with while
)
当条件为真,执行循环。当条件不再为真,调用 break 停止循环。
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number = number - 1;
}
println!("LIFTOFF!!!");
}
使用 for
遍历集合(Looping Through a Collection with for
)
示例:
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!!!");
}