标准库常用类型
Box
在 Rust 中,默认情况下,所有的值都是栈分配的。通过创建 Box<T>
,可以将值分配在堆上。Box 是指向堆上分配的 T
类型值的智能指针。
可以使用 *
运算符对已经装箱(Boxed)的值进行解引用。
// 定义一个结构体
struct Person {
name: String,
age: u32,
}
// 创建一个装箱的 Person 的函数
fn create_person(name: &str, age: u32) -> Box<Person> {
let person = Person {
name: String::from(name),
age,
};
// 将 person 装箱并返回 Box
Box::new(person)
}
fn main() {
let boxed_person = create_person("Alice", 30);
// 通过解引用访问字段
println!("Name: {}, Age: {}", boxed_person.name, (*boxed_person).age);
// Name: Alice, Age: 30
// The memory for the person is automatically deallocated when the box goes out of scope
}
Vectors 向量
向量是可调整大小的数组。与切片一样,它们的大小在编译时是未知的,不同的是它们可以随时增长或缩小。
fn main() {
// 创建一个带有初始值的向量
let mut numbers: Vec<i32> = vec![1, 2, 3, 4, 5];
// 访问元素
println!("Original Vector: {:?}", numbers);
// 向向量添加一个元素
numbers.push(6);
println!("After pushing 6: {:?}", numbers);
// 移除最后一个元素
let popped = numbers.pop();
println!("Popped element: {:?}", popped);
println!("After popping: {:?}", numbers);
// 迭代向量
println!("Doubling each element:");
for num in &mut numbers {
*num *= 2;
println!("{}", num);
}
// 创建一个新的向量并将其与原向量拼接起来
let more_numbers = vec![7, 8, 9];
numbers.extend(more_numbers);
println!("After extending with more_numbers: {:?}", numbers);
// 使用 `contains` 方法
let contains_8 = numbers.contains(&8);
println!("Does the vector contain 8? {}", contains_8);
// 使用 `map` 方法创建平方的向量
let squares: Vec<i32> = numbers.iter().map(|&x| x * x).collect();
println!("Vector of squares: {:?}", squares);
}
Strings 字符串
在Rust中有两种类型的字符串:String
和 &str
。
String
存储为字节向量 (Vec<u8>
),它在堆上分配,可增长且可变。
fn main() {
// 创建一个新的空字符串
let mut my_string = String::new();
// 添加字面字符串
my_string.push_str("Hello, ");
// 添加另一个字符串
let name = "Alice";
my_string.push_str(name);
// 显示结果
println!("String: {}", my_string);
// 将 String 转换为 &str 进行借用
let borrowed_str: &str = &my_string;
println!("Borrowed &str: {}", borrowed_str);
}
&str
是一个切片 (&[u8]
),始终指向有效的 UTF-8 序列,它可以引用 String
的一部分,或者字符串字面值,甚至另一个 &str
。
fn main() {
// Creating a string literal
let hello_str: &str = "Hello, ";
// Creating another string literal
let name_str: &str = "Bob";
// Concatenating string literals to form a new &str
let greeting: &str = hello_str.to_owned() + name_str;
// Displaying the result
println!("Concatenated &str: {}", greeting);
}
字符串转义
有多种方法可以在字符串文字中编写带有特殊字符的字符串。通常情况下,特殊字符会用反斜杠字符 \
转义。
fn main() {
let orig_str = "Escapes work here: \x3F \u{211D}";
println!("{}", orig_str);
let raw_str = r"Escapes don't work here: \x3F \u{211D}";
println!("{}", raw_str);
// 如果需要在原始字符串中包含引号,添加一对 #
let quotes = r#"He said: "It's impossible!""#;
println!("{}", quotes);
// 如果需要在字符串中包含 "#,只需在两边使用更多的 #
let longer_delimiter = r###"It's a hashtag "#rust in it. And even "##!"###;
println!("{}", longer_delimiter);
}
Option
Option<T>
枚举有两个变体:
Some(value)
: 具有类型T
的value
的元组结构体None
: 没有值
fn find_even_number(numbers: &[u32]) -> Option<u32> {
for &num in numbers {
if num % 2 == 0 {
return Some(num);
}
}
None
}
fn main() {
let numbers = vec![1, 3, 5, 2, 8, 9];
match find_even_number(&numbers) {
Some(even_num) => println!("Found even number: {}", even_num),
None => println!("No even number found."),
}
}
Result
Result<T, E>
枚举也有两个变体:
Ok(value)
: 具有类型T
的value
Err(reason)
: 含有类型E
的失败原因reason
// 基于条件返回 Result<u32, String> 的函数
fn divide(a: u32, b: u32) -> Result<u32, String> {
if b == 0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(a / b)
}
}
fn main() {
let result2 = divide(8, 0);
match result2 {
Ok(quotient) => println!("Result: Quotient is {}", quotient),
Err(error) => println!("Result: Error - {}", error),
}
}
?
运算符是在 Rust 中报错传播的一种简写。它可以在返回 Result
的函数中使用,使得错误处理更为简洁。
use std::fs::File;
use std::io::{self, Read};
fn read_file_contents(file_path: &str) -> Result<String, io::Error> {
// 尝试打开文件
let mut file = File::open(file_path)?;
// 将文件内容读入字符串
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
panic!
在 Rust 中,panic!
宏用于在遇到不可恢复的错误时展开堆栈并终止程序,以防止发生危险的未定义行为。
fn main() {
let divisor = 0;
if divisor == 0 {
// 尝试除以零时引发 panic
panic!("Attempted to divide by zero!");
}
// 如果发生 panic,将不会执行到达此处
let result = 42 / divisor;
println!("Result: {}", result);
}
HashMap
在 Rust 中,可以使用 std::collections
模块中的 HashMap
数据结构来存储键值对。
use std::collections::HashMap;
fn main() {
// 创建一个空的 HashMap
let mut my_map = HashMap::new();
// 将键值对插入 HashMap
my_map.insert("one", 1);
my_map.insert("two", 2);
my_map.insert("three", 3);
my_map.insert("literank", 58);
// 通过键访问值
match my_map.get("two") {
Some(&value) => println!("The value of 'two' is: {}", value),
None => println!("Key 'two' not found."),
}
// 更新值
my_map.insert("three", 30);
// 遍历 HashMap
println!("Iterating over the HashMap:");
for (key, value) in &my_map {
println!("Key: {}, Value: {}", key, value);
}
// 移除键值对
my_map.remove("literank");
// 检查键是否存在
if my_map.contains_key("literank") {
println!("Key 'literank' is present.");
} else {
println!("Key 'literank' is not present.");
}
}
HashSet
HashSet
数据结构也由 std::collections
模块提供,允许存储一组唯一值。
use std::collections::HashSet;
fn main() {
// 创建一个空的 HashSet
let mut my_set = HashSet::new();
// 将元素插入 HashSet
my_set.insert("apple");
my_set.insert("banana");
my_set.insert("orange");
// 检查元素是否在 HashSet 中
if my_set.contains("banana") {
println!("The set contains 'banana'.");
} else {
println!("The set does not contain 'banana'.");
}
// 从 HashSet 中移除元素
my_set.remove("apple");
// 遍历 HashSet
println!("Iterating over the HashSet:");
for fruit in &my_set {
println!("Fruit: {}", fruit);
}
// 清空 HashSet
my_set.clear();
// 检查 HashSet 是否为空
if my_set.is_empty() {
println!("The HashSet is empty.");
} else {
println!("The HashSet is not empty.");
}
}
Rc(引用计数)
Rc
(引用计数)是 std::rc
模块中的类型,通过引用计数提供对值的共享所有权。
use std::rc::Rc;
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
fn main() {
// 创建包含 Person 的 Rc
let person = Rc::new(Person {
name: String::from("Alice"),
age: 30,
});
// 创建对 Rc 的多个引用
let reference1 = Rc::clone(&person);
let reference2 = Rc::clone(&person);
// 打印引用计数
println!("Reference Count after creation: {}", Rc::strong_count(&person));
// 通过引用访问打印数据
println!("Person: {:?}", *reference1);
println!("Person: {:?}", *reference2);
// 析构引用
drop(reference1);
println!("Reference Count after dropping one reference: {}", Rc::strong_count(&person));
// 析构最后的引用
drop(reference2);
// 引用都没有了之后,Rc 被自动销毁
// 引用都销毁后尝试访问数据会触发报错
}
Arc(原子引用计数)
一个线程安全的引用计数指针。类型 Arc<T>
提供了对在堆中分配的类型为 T
的值的共享所有权。
当你需要在多个线程之间共享所有权并确保线程安全时,Arc
很有用。它使用原子操作进行引用计数,使其适用于并发编程。
例如:
use std::sync::Arc;
use std::thread;
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
fn main() {
// 创建包含 Person 的 Arc
let person = Arc::new(Person {
name: String::from("Bob"),
age: 25,
});
// 在新线程中创建对 Arc 的引用
let thread_person = Arc::clone(&person);
// 生成一个新线程
let handle = thread::spawn(move || {
// 通过引用在新线程中访问并打印数据
println!("Person in the new thread: {:?}", *thread_person);
});
// 在主线程中访问并打印数据
println!("Person in the main thread: {:?}", *person);
// 等待新线程完成
handle.join().unwrap();
// 在线程完成后,当最后一个引用被析构时,Arc 会自动释放
}
代码挑战
修改编辑器中的代码,尝试获取 Alice 的成绩。