变量使用 let
定义
不可变变量 当变量不可变时,一旦值被绑定一个名称上,就不能改变这个值 Rust 鼓励利用不可变性
Rust 编译器保证,如果声明一个值不会变,它就真的不会变,所以你不必自己跟踪它。这意味着你的代码更易于推导。
可变变量
在变量名前添加 mut
来使其可变
常量使用 const
定义
mut
rustconst THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
rustfn 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}");
}
当 let
一个同名变量时,编译器将看到第二个变量。
实际上,第二个变量“遮蔽”了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域结束。
可以用相同变量名称来隐藏一个变量,以及重复使用 let
关键字来多次隐藏。
隐藏和 mut
的区别:
当不小心尝试对变量重新赋值时,如果没有使用 let
关键字,就会导致编译时错误。通过使用 let
,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不可变的。
当再次使用 let
时,实际上创建了一个新变量,我们可以改变值的类型,并且复用这个名字。例如:
rustlet spaces = " ";
let spaces = spaces.len();
若使用 mut
修改变量的值,则不能改变变量的类型。
在 Rust 中,每一个值都属于某一个 数据类型(data type)。
Rust 是 静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型。
编译器通常可以推断出我们想要用的类型,但当多种类型均有可能时(比如使用 parse
将 String
转换为数字时),必须增加类型注解。(有点类似ts)
rustlet guess: u32 = "42".parse().expect("Not a number!");
// 不加会报错
标量(scalar)类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。
Rust 数字类型默认是 i32
,isize
或 usize
主要作为某些集合的索引。
长度 | 有符号 | 无符号 |
---|---|---|
8-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 位的。
数字字面值 | 例子 |
---|---|
Decimal (十进制) | 98_222 |
Hex (十六进制) | 0xff |
Octal (八进制) | 0o77 |
Binary (二进制) | 0b1111_0000 |
Byte (单字节字符)(仅限于u8 ) | b'A' |
可以以上表任意一种形式编写数字字面值
可以是多种数字类型的数字字面值允许使用类型后缀,例如 57u8
来指定类型,同时也允许使用 _
做为分隔符以方便读数,例如1_000
,它的值与你指定的 1000
相同。
整型溢出
--debug
:Rust检查这类问题并使程序 panic--release
:Rust不会**检测会导致 panic 的整型溢出,而是进行二进制补码wrapping(比此类型能容纳最大值还大的值会回绕到最小值,值256
变成0
,值257
变成1
)- 依赖整型溢出 wrapping 的行为被认为是一种错误。
为了显式地处理溢出的可能性,可以使用这几类标准库提供的原始数字类型方法:
- 所有模式下都可以使用
wrapping_*
方法进行 wrapping,如wrapping_add
- 如果
checked_*
方法出现溢出,则返回None
值- 用
overflowing_*
方法返回值和一个布尔值,表示是否出现溢出- 用
saturating_*
方法在值的最小值或最大值处进行饱和处理
Rust 的浮点数类型是 f32
和 f64
,分别占 32 位和 64 位。默认类型是 f64
。
rustfn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
加法、减法、乘法、除法和取余
整数除法:向零舍入到最接近的整数(正整数向下取整,负整数向上取整)
rustfn main() {
// addition
let sum = 5 + 10;
// subtraction
let difference = 95.5 - 4.3;
// multiplication
let product = 4 * 30;
// division
let quotient = 56.7 / 32.2;
let truncated = -5 / 3; // 结果为 -1
// remainder
let remainder = 43 % 5;
}
使用 bool
表示,值为 true
或 false
使用 char
表示,用单引号声明,是最原生的字母类型,大小为四个字节。
rustlet c = 'z';
let z: char = 'ℤ'; // with explicit type annotation
let heart_eyed_cat = '😻';
在 Rust 中,带变音符号的字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的
char
值。
复合类型(Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。
元组长度固定,一旦声明,其长度不会增大或缩小。元组中的每一个位置都有一个类型,不必相同。
元组可以用解构或索引取值。
rustlet tup = (500, 6.4, 1);
// 解构
let (x, y, z) = tup;
println!("{x}, {y}, {z}");
let t: (i32, f64, u8) = (500, 6.4, 1);
let num1 = t.0;
let num2 = t.1;
let num3 = t.2;
println!("num1: {num1}, num2: {num2}, num3: {num3}");
不带任何值的元组有个特殊的名称,叫做 单元(unit) 元组。这种值以及对应的类型都写作 ()
,表示空值或空的返回类型。如果表达式不返回任何其他值,则会隐式返回单元值。
数组中元素类型必须相同,数组在栈 (stack) 上为数据分配空间。
数组的长度是固定的,如果希望伸缩长度,应该使用 vector
。
rustlet months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
let a: [i32; 5] = [1, 2, 3, 4, 5];
// 数组包含 5 个元素,元素的值为 3
let a = [3; 5];
数组的元素访问使用方括号:a[0]
,如果进行无效的元素访问(例如超出数组长度),Rust会立即退出而不是允许内存访问并继续执行。
Rust 代码中的函数和变量名使用 snake case 规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。
rustfn main() {
println!("Hello, world!");
another_function();
}
fn another_function() {
println!("Another function.");
}
函数可以定义在任何位置,只要其在可被调用的作用域内即可,像这样也可以:
rustpub fn tuples() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("{x}, {y}, {z}");
ab();
fn ab() {
println!("hello");
}
}
必须声明每个参数的类型,参数之间使用逗号分隔。
rustfn show_number(num: i32) {
println!("The number is: {num}");
}
语句(Statements)是执行一些操作但不返回值的指令。 表达式(Expressions)计算并产生一个值。
let y = 6
是一个语句,其中 6
是表达式,表达式可以是语句的一部分。
大部分 Rust 代码都是由表达式组成的,例如数学运算 5*6
是一个表达式,函数调用是一个表达式,宏调用是一个表达式,用大括号创建一个新的块作用域也是一个表达式。例如:
rust{
let x = 3;
x + 1
}
这个代码块是一个表达式,值为4。
表达式的结尾**没有分号**。如果在表达式的结尾加上分号,它就变成了语句,而语句不会返回值。
具有返回值的函数,应当用 ->
声明返回值的类型。
rustpub fn get_five() -> i32 {
5
}
在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。
rustfn plus_one(x: i32) -> i32 {
x + 1
}
可以独立一行存在,也可以在代码行的末尾
rust// 这是一行注释
文档注释使用三斜杠 ///
而不是双斜杠以支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前。
rust/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = my_crate::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
x + 1
}
常见的文档注释标题:
cargo test
。panic!
的场景。并不希望程序崩溃的函数调用者应该确保他们不会在这些情况下调用此函数。Result
,此部分描述可能会出现何种错误以及什么情况会造成这些错误,这有助于调用者编写代码来采用不同的方式处理不同的错误。unsafe
代码(这会在第十九章讨论),这一部分应该会涉及到期望函数调用者支持的确保 unsafe
块中代码正常工作的不变条件(invariants)。文档注释风格 //!
为包含注释的项,而不是位于注释之后的项增加文档。这通常用于 crate 根文件(通常是 src/lib.rs)或模块的根文件为 crate 或模块整体提供文档。
作为一个例子,为了增加描述包含 add_one
函数的 my_crate
crate 目的的文档,可以在 src/lib.rs 开头增加以 //!
开头的注释。注意 //!
的最后一行之后没有任何代码。
rust//! # My Crate
//!
//! `my_crate` is a collection of utilities to make performing certain
//! calculations more convenient.
/// Adds one to the number given.
// --snip--
根据条件是否为真来决定是否执行某些代码,以及根据条件是否为真来重复运行一段代码的能力是大部分编程语言的基本组成部分。
if
表达式if
表达式代码中的条件必须是 bool
值,如果不是,Rust将会抛出错误:Rust 并不会尝试自动地将非布尔值转换为布尔值,必须总是显式地使用布尔值作为 if
的条件。
rustfn main() {
let number = 3;
if number < 5 {
println!("条件为真");
} else {
println!("条件为假");
}
}
else if
处理多重条件rustfn main() {
let number: i32 = 56;
if number % 4 == 0 {
println!("number能被4整除");
} else if number % 3 == 0 {
println!("number能被3整除");
} else if number % 2 == 0 {
println!("number能被2整除");
} else {
println!("其他情况")
}
}
使用过多的 else if
表达式会使代码显得杂乱无章,所以如果有多于一个 else if
表达式,最好重构代码(例如使用 match
)。
let
语句中使用 if
因为 if
是一个表达式,可以在 let
语句中,将 if
表达式的返回值赋给一个变量,表达式的返回值必须是相同类型
rustfn main() {
let condition = true;
let number = if condition { 5 } else { 6 };
println!("符合条件的数字是:{number}");
}
注意
代码块的值是其最后一个表达式的值,而数字本身就是一个表达式。
loop
重复执行代码loop
关键字告诉 Rust 一遍又一遍地执行一段代码直到你明确要求停止。
rustfn main() {
loop {
println!("again")
}
}
loop
的一个用例是重试可能会失败的操作,比如检查线程是否完成了任务。然而你可能会需要将操作的结果传递给其它的代码。如果将返回值加入你用来停止循环的 break
表达式,它会被停止的循环返回:
rustfn cou() {
let mut counter = 0;
let result = loop {
counter += 1;
if(counter == 10) {
// 使用break关键字返回值 counter * 2
break counter * 2;
}
};
println!("结果是:{result}")
}
如果存在嵌套循环,这时 break
和 continue
作用于最内层循环,可以为一个循环指定一个循环标签,然后将标签与 break
或 continue
一起使用,使这些关键字作用于打了标签的循环
rustfn 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!("最终count的值是{count}")
}
该段代码的执行结果为:
while
条件循环rustfn main() {
let mut number: i32 = 3;
while number > 0 {
println!("number: {number}");
number -= 1;
}
println!("循环结束");
}
for
遍历集合使用 while
也可以通过不断递增索引值来遍历集合,但是如果索引长度或测试条件不正确会导致程序panic(例如更改了数组长度却忘记更新条件)
rustfn main() {
let a: [i32; 5] = [10, 20, 30, 40, 50];
for element in a {
println!("{element}");
}
}
for
中使用 Range
rustfn main() {
// rev 方法用来反转 range
for number in(1..4).rev() {
println!("{number}");
}
println!("循环结束");
}
rustuse std::io;
fn main() {
loop {
println!("请选择要输入的温度类型:");
println!("1.摄氏温度");
println!("2.华氏温度");
println!("其他数字:退出");
let mut convert_type: String = String::new();
let mut temperature: String = String::new();
io::stdin().read_line(&mut convert_type).expect("读入失败");
let convert_type: u32 = match convert_type.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
if convert_type != 1 && convert_type != 2 {
break;
}
println!("请输入温度:");
io::stdin().read_line(&mut temperature).expect("读入失败");
let temperature: f64 = match temperature.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
if convert_type == 1 {
let fahrenheit = 32.0 + temperature * 1.8;
println!("华氏温度为:{fahrenheit}°F");
} else if convert_type == 2 {
let celsius = (temperature - 32.0) / 1.8;
println!("摄氏温度为:{celsius}°C");
} else {
break;
}
}
}
rustuse std::io;
fn main() {
let mut n: String = String::new();
io::stdin().read_line(&mut n).expect("读入失败");
let mut n: u32 = match n.trim().parse() {
Ok(num) => num,
Err(_) => return,
};
let mut prev_number: u32 = 1;
let mut next_number: u32 = 1;
let mut result: u32 = 0;
if n == 1 {
result = prev_number;
} else if n == 2 {
result = next_number;
} else {
while n > 2 {
result = prev_number + next_number;
prev_number = next_number;
next_number = result;
n -= 1;
}
}
println!("第{n}个斐波那契数是{result}");
}
本文作者:Morales
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 License 许可协议。转载请注明出处!