Appearance
变量
变量
变量默认不可变,添加 mut 使其可变。
rust
let x = 1;
let mut y = 123;常量
常量永远不可变。声明时要用 const 关键字,并必须标注类型。
常量命名约定是全大写。作用域内永远可用。
习惯将硬编码提取为常量。
rust
const LIGHT_SPEED = 300000000;遮蔽
可以重复使用同一个变量名来遮蔽一个变量,遮蔽的持续时间是:1. 再次被遮蔽; 2. 作用域结束。
和 mut 的区别:
- 不用
mut而修改变量会报错,而用let遮蔽变量则可以对值做一些变换,变换完成后还能回到之前的值。
rust
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}");
}shell
$ cargo run
x in scope: 12
outer x : 6- 遮蔽实际上是创建新的变量,所以可以改变值的类型。
rust
let spaces = " ";
let spaces = spaces.len();类型
Rust 是静态类型,编译时就必须知道所有的类型。
整形
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| 架构相关 | isize | usize |
有符号的可以表示 -2(^(n-1)) 到 2^(n-1) - 1 之间的数字,无符号的可以表示 0 到 2^n - 1 之间的数字。u8 能存 0 到 2^8 - 1 = 255。
可以使用字面值来表示整数
| 数字字面值 | 例子 |
|---|---|
| Decimal(十进制) | 98_222 |
| Hex(十六进制) | 0xff |
| Octal(八进制) | 0o77 |
| Binary(二进制) | 0b1111_0000 |
| Byte(字节字面值,仅限 u8) | b'A' |
也可以显示指定类型:57u8。数字字面值可以使用 _ 作为视觉分隔符:1_000 和 1000 完全相同。
浮点型
f32 和 f64,默认是 f64,浮点型都是有符号的。
数值运算
加减乘除取余。
整数除法会向零舍入到最接近的整数。
rust
let res = 56.7 / 32.2
// => -1布尔类型
true 和 false。
字符
char,使用单引号表示一个字符,占 4 个字节。
rust
let z: char = 'Z';复合类型:元组和数组
元组
元组每个位置都可以是一个类型,每个位置的类型不必相同。
rust
let tup: (i32, f64, u8) = (500, 6.4, 1);元组可以解构
rust
let (x, y, z) = tup;也可以用点 . 和索引直接访问。
rust
let x = tup.0;没有任何元素的元组叫 单元,写作 ()。
数组
Rust 的数组长度是固定的。
数组会在栈上分配一整块、大小已知且固定的内存。
数组的定义方式:
rust
let arr: [i32; 5] = [1, 2, 3, 4, 5];当数组元素全都一样时,这样写更简洁:
rust
let arr = [3; 5];
// 等价于
let arr = [3, 3, 3, 3, 3];函数
fn 开头,() 参数,{} 函数体。
Rust 不关心函数的位置,只要调用时在可见作用域内就行。
参数
语句和表达式
语句是执行一些操作但不返回值,表达式计算并产生一个值。
函数定义是语句,函数调用是表达式。
语句不返回值,所以
rust
fn main() {
let x = (let y = 6);
}会报错。
下面代码:
rust
fn main() {
let y = {
let x = 3;
x + 1
};
println!("The value of y is: {y}");
}中,表达式:
rust
{
let x = 3;
x + 1
}是一个代码块。
表达式的结尾没有分号,表达式加分号就变成了语句,而语句不会返回值。
x = 3 中,3 是一个表达式,赋值给了 x。而 let x = 3; 是一条语句,不返回值。
具有返回值的函数
函数会把返回值返回给调用它的代码,必须在箭头(->)后面声明返回值类型。函数的返回值是函数体中最后一个表达式的值,或者用 return 指定一个值。
rust
fn five() -> i32 {
5
}
fn main() {
let x = five();
println!("five is : {x}");
}控制流
if
rust
if condition {}
else {}if 是表达式,if 的 {} 中的代码块也是表达式,所以 if 可以用来赋值。
rust
let x = if input < 10 { "small" } else { "big" };循环
loop, while, for
循环还能用来返回值(Rust 真几把牛逼)。
rust
let mut counter = 2;
let x = loop {
counter += 1;
if counter == 10 {
break counter * 2
}
};
println!("x is {x}");循环标签
可以使用循环标签来标注具体某个循环,告诉 break 和 continue 要处理哪个循环。
rust
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}");
}while 和 for
rust
while condition {
}rust
for number in (1..5) {
}rust
let elements = [1, 4, 6, 7, 8];
for item in elements {
println!("curr item is {item}");
}所有权
字符串会在栈上保存内容指针、长度、容量,而内容会保存在堆上。
当把字符串赋值给另一个变量时,会创建一个拷贝,把栈上的数据拷贝过去,此时两个指针指向堆上的同一个内容。
赋值后,先前的值将不再有效。
rust
let s1 = String::from("hello");
let s2 = s1;
println!("{s1}{s2}");这个会出现报错:
sh
$ cargo run
Compiling ownership v0.1.0 (/Volumes/workspace/rustlearn/ownership)
error[E0382]: borrow of moved value: `s1`
--> src/main.rs:5:20
|
3 | let s1 = String::from("hello");
| -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
4 | let s2 = s1;
| -- value moved here
5 | println!("{s1}{s2}");
| ^^ value borrowed here after move
|
help: consider cloning the value if the performance cost is acceptable
|
4 | let s2 = s1.clone();
| ++++++++
For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error exit:101这个过程如下图所示

如果需要复制堆上的数据,可以使用 clone。下面的代码就可以运行了。
rust
let s1 = String::from("hello");
let s2 = s1.clone();
println!("{s1}{s2}");像整形这种存储在栈上的类型,它们实现了 Copy trait,不会在复制为 y 之后使 x 失效。
rust
let x = 5;
let y = x;
println!("{x}, {y}");任何不需要分配内存或某种形式资源的类型都可以实现 Copy 。如下是一些 Copy 的类型:
- 所有整数类型,比如 u32。
- 布尔类型,bool,它的值是 true 和 false。
- 所有浮点数类型,比如 f64。
- 字符类型,char。
- 元组,当且仅当其包含的类型也都实现 Copy 的时候。比如,(i32, i32) 实现了 Copy,但 (i32, String) 就没有。
将值传递给函数时也符合这种原理,例如:
rust
fn main() {
let s = String::from("hello");
takes_ownership(s);
println!("{s}");
let x = 5;
makes_copy(x);
println!("{x}")
}
fn takes_ownership(some_string: String) {
println!("{some_string}");
}
fn makes_copy(some_int: i32) {
println!("{some_int}");
}这会出现报错:
sh
cargo run
Compiling ownership v0.1.0 (/Volumes/workspace/rustlearn/ownership)
error[E0382]: borrow of moved value: `s`
--> src/main.rs:13:20
|
11 | let s = String::from("hello");
| - move occurs because `s` has type `String`, which does not implement the `Copy` trait
12 | takes_ownership(s);
| - value moved here
13 | println!("{s}");
| ^ value borrowed here after move
|
note: consider changing this parameter type in function `takes_ownership` to borrow instead if owning the value isn't necessary
--> src/main.rs:21:33
|
21 | fn takes_ownership(some_string: String) {
| --------------- ^^^^^^ this parameter takes ownership of the value
| |
| in this function
help: consider cloning the value if the performance cost is acceptable
|
12 | takes_ownership(s.clone());
| ++++++++
For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error exit:101因为在 takes_ownership 结束后,some_string 移出作用域并且被 drop 了。
返回值也会如此。
rust
fn main() {
let s1 = gives_ownership(); // gives_ownership 将它的返回值传递给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被传入 takes_and_gives_back,
// 它的返回值又传递给 s3
} // 此处,s3 移出作用域并被丢弃。s2 被 move,所以无事发生
// s1 移出作用域并被丢弃
fn gives_ownership() -> String { // gives_ownership 将会把返回值传入
// 调用它的函数
let some_string = String::from("yours"); // some_string 进入作用域
some_string // 返回 some_string 并将其移至调用函数
}
// 该函数将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String {
// a_string 进入作用域
a_string // 返回 a_string 并移出给调用的函数
}