1. 变量与不可变性
1.1 基础知识
-
在Rust中,使用let关键字来声明变量
-
在Rust支持类型推导,但你也可以显示指定变量的类型:
- let x: i32 = 5 // 显示指定x的类型为i32
-
变量名蛇形命名法(Snake Case),而枚举和结构体命名使用帕斯卡命名法,如果变量没有用到可以前置下划线,消除警告
-
强制类型转换 Casting a Value to a Different Type
- let a = 3.1; let b = a as i32
-
打印变量 ({}与{:?}需要实现特质之后章节会介绍,基础类型默认实现)
- println!("val: {}", x)
- println!("val: {x}")
1.1 Rust中的变量是默认不可变的
不可变性是Rust实现其可靠性和安全性目标的关键
他迫使程序员更深入地思考程序状态的变化,并明确哪些部分的程序状态可能会发生变化的
不可变性有助于防止一类常见的错误,如数据竞争和并发问题
使用mut关键字进行可变声明:
-
如果你希望一个变量是可变的,你需要使用mut关键字进行明确声明
- let mut y = 10; // 可变变量
- y = 20; // 合法的修改
Shadowing Variables 并不是重新赋值
Rust允许你隐藏一个变量,这意味着你可以声明一个与现有变量同名的新变量,从而有效地隐藏前一个变量。
- 可以改变值
- 可以改变类型
- 可以改变可变性
fn main() {
// 不可变与命名
let nice_count = 100; // 自动推导
let nice_number: i64 = 54;
// nice_count = 23; // 变量不可变
// 声明可变
let mut count: i32 = 3;
count = 4;
// Shadowing
let x: i32 = 5;
{
// 命名空间
let x = 10;
println!("inner x: {}", x) // 10
} // 内部的x被销毁了
print!("outer x: {}", x); // 5
let x: &str = "hello"; // 在同一作用域下重新声明了x, 最终覆盖了之前的x
println!("New x: {x}"); // hello
let mut x = "this"; // 重新定义可变性
println!("x: {x}"); // this
x = "that";
println!("x: {x}"); // that
}
2. 常量const与静态变量static
2.1 常量
- 常量的值必须是在编译时已知的常量表达式,必须指定类型与值
- 与C语言的宏定义(宏替换)不同,Rust的const常量的值被直接嵌入到生成的底层机器代码中,而不是进行简单的字符替换
- 常量名与静态变量命名必须全部大写,单词之间加入下划线
- 常量的作用域是块级作用域,它们只在声明它们的作用域内可见
2.2 static静态变量
- 与const常量不同,static变量是在运行时分配内存的
- 并不是不可变的,可以使用unsafe修改
- 静态变量的生命周期为整个程序的运行时间
static MY_STATIC: i32 = 42;
static mut MY_MUT_STATIC: i32 = 42;
fn main() {
// const
const SECOND_HOUR: usize = 3_600; // 常量必须规定类型和值
const SECOND_DAY: usize = 24 * SECOND_HOUR; // 编译的时候就已经确定了值
{
const SE: usize = 1_1000;
println!("{SE}");
}
// println!("{SE}"); // 无法打印, const无法超出它的作用域
println!("{SECOND_DAY}");
println!("{MY_STATIC}");
unsafe {
MY_MUT_STATIC = 32;
println!("{MY_MUT_STATIC}");
}
// println!("{MY_MUT_STATIC}"); // 这里已经不能进行打印了, 只能在unsafe里面打印
}
3. Rust基础数据类型
-
Integer types默认推断为i32
- i8, i16, i32, i64, i128
-
Unsigned Integer Types
- u8, u16, u32, u64, u128
-
Platform-Specific Integer Type(由平台决定)
- usize
- isize
-
Float Types
- f32与f64
- 尽量用f64, 除非你清楚边界需要空间
-
Boolean Values
- true
- false
-
Character Types
- Rust支持Unicode字符
- 表示char类型使用单引号
fn main() {
// 进制的字面量
let a1 = -125; // 10进制
let a2 = 0xFF; // 16进制
let a3 = 0o13; // 8进制
let a4 = 0b10; // 2进制
println!("{a1} {a2} {a3} {a4}");
// Max Min
println!("u32 max: {}", u32::MAX); // 2^32 - 1
println!("u32 min: {}", u32::MIN); // 0
println!("isize is {} bytes", std::mem::size_of::<isize>()); // 8
println!("usize is {} bytes", std::mem::size_of::<usize>()); // 8
// float
let f1: f32 = 1.23234;
let f2: f64 = 9.88888;
println!("Float are {:.2} {:.2}", f1, f2); // 1.23 9.89
// bool
let is_ok = true;
let can_ok = false;
println!("{is_ok} {can_ok}"); // true false
// 与,或运算
println!("is_ok or can_ok: {}", is_ok || can_ok); // true
println!("is_ok and can_ok: {}", is_ok && can_ok); // false
// char
let char_c = 'c';
println!("{char_c}");
}
4. 元组与数组
4.1 相同点
- 元组和数组都是Compound Types(复合类型),而Vec和Map都是Collection Types(集合类型)
- 元组和数组长度都是固定的
4.2 不同点
- Tuples不同类型的数据类型
- Arrays同一类型的数据类型
4.3 数组
-
数组是固定长度的同构集合
-
创建方式:
- [a, b, c]
- [value; size]
-
获取元素: arr[index]
-
获取长度: arr.len()
fn main() {
// array
let mut arr = [11, 12, 34];
arr[0] = 999; // 修改数组元素
println!("arr len {}, first element is {}", arr.len(), arr[0]); // 3, 999
// 遍历
for i in arr {
println!("{}", i);
}
let arr2: [i32; 3] = [11; 3];
for i in arr2 {
println!("{}", i); // 3个11
}
}
4.4 元组
- 元组是固定长度的异构集合
- Empty tuple()
- 为函数返回默认值
- 元组获取元素
- tup.index
- 没有len()
fn main() {
// tuple
let tup = (0, "hi", 3.4);
println!("tup elements {} {} {}", tup.0, tup.1, tup.2); // 0 hi 3.4
let mut tup2 = (0, "hi", 3.4);
tup2.1 = "f"; // 加上mut后元组的元素就可以进行修改, 必须和修改前的元素是同一类型
println!("tup2 elements {} {} {}", tup2.0, tup2.1, tup2.2); // 0 f 3.4
// 空元组
let tup3 = ();
println!("tup3 {:?}", tup3); // ()
}
4.5 Ownership所有权机制
类型基础类型与数组、元组,它们和String数据类型的不同
- 基础类型与数组、元组:实现了copy的操作
- String、结构体:没有实现copy的特质,只能实现所有权(ownership)的转移, 也就是move操作
fn main() {
// ownership
let arr_item: [i32; 3] = [1, 2, 3];
let tup_item: (i32, &'static str) = (2, "ff");
println!("arr {:?}", arr_item); // [1, 2, 3]
println!("tup {:?}", tup_item); // (2, "ff")
let arr_ownership: [i32; 3] = arr_item;
let tup_ownership: (i32, &'static str) = tup_item;
println!("arr {:?}", arr_item); // [1, 2, 3]
println!("tup {:?}", tup_item); // (2, "ff")
let a = 3;
let b = a; // a在赋值给b后, a还是存在的(copy操作)
println!("{a}"); // 3
// move ownership
// 什么数据类型会执行move操作: String
let string_a = String::from("aaa");
println!("{string_a}"); // "aaa"
let string_b = string_a; // String类型就把Ownership进行move操作
// println!("{string_a}"); // borrow of moved value: 'string_a' value borrowed here after move
println!("{string_b}"); // "aaa"
}