防盗网站人做清洁网络公司规章制度范本

当前位置: 首页 > news >正文

防盗网站人做清洁,网络公司规章制度范本,小程序搭建方案,建网站那个网最好1.Rust 语法总结 数值类型 有符号整数: i8, i16, i32, i64无符号整数: u8, u16, u32, u64 变量声明 声明变量: let i 0; // 类型推断let n: i32 1; // 显式类型声明 可变变量: let mut n 0; n n 1;
字符串 注意#xff0c;let s: str Hello world;…1.Rust 语法总结 数值类型 有符号整数: i8, i16, i32, i64无符号整数: u8, u16, u32, u64 变量声明 声明变量: let i 0; // 类型推断let n: i32 1; // 显式类型声明 可变变量: let mut n 0; n n 1;
字符串  注意let s: str Hello world; 是不正确的因为 str 类型不能单独使用。它必须通过引用str来使用。  集合 动态数组向量: let mut v: Veci32 Vec::new(); v.push(1); v.push(0); 固定大小数组: 在 Rust 中所有变量在使用之前必须初始化。这是为了防止未初始化变量引起的未定义行为。因此您不能声明一个未初始化的数组或变量。 // 创建一个可变数组 arr包含4个 i32 类型的元素将所有元素初始化为0 let mut arr: [i32; 4] [0; 4];// 或者可以逐个初始化每个元素 let mut arr: [i32; 4] [0, 0, 0, 0];// 修改数组的元素 arr[0] 0; arr[1] 1; arr[2] 2; arr[3] 3;循环 迭代器循环: // 使用 for 循环迭代向量中的元素for i in v.iter() {println!({}, i); // 打印每个元素} while 循环: while i 9 {i 1;println!(i {}, i); // 打印每次递增后的值} 无限循环: fn main() {let mut i 0; // 初始化一个可变变量 i初始值为 0loop {i 1; // 每次循环迭代将 i 的值增加 1if i 10 { // 检查 i 是否大于 10break; // 如果 i 大于 10则退出循环}}println!(Final value of i: {}, i); // 打印 i 的最终值 }函数 fn sum(a: i32, b: i32) - i32 {a b } 声明函数: fn sum(a: i32, b: i32) - i32 {a b }
表达式 三元表达式: let x if someBool { 2 } else { 4 }
输入输出 (1)确保所有缓冲区中的数据都被写入到标准输出通常是终端或控制台中 io::stdout().flush().unwrap(); (2)read_line 方法从标准输入读取用户输入并将其存储到 guess 中。如果读取失败程序会崩溃并显示错误信息 读取输入失败.。 let mut guess String::new(); io::stdin().read_line(mut guess).expect(读取输入失败.);操作符 1? 操作符 当一个函数返回 Result 或 Option 类型时可以使用 ? 操作符来自动处理这些结果。如果结果是 Ok则返回其中的值如果是 Err则返回错误并退出当前函数。 fn read_file_lines(filename: str) - ResultVecString, io::Error {// 尝试打开文件let file File::open(filename)?;// 如果成功打开文件继续执行如果失败返回错误并退出函数 } 定义结构体 在 Rust 中定义结构体类型时我们声明了结构体的字段及其类型而不是创建具体的实例。因此不需要使用 let 或 let mut 这样的关键字。let 和 let mut关键字用于创建变量而不是定义类型。 在这段代码中我们定义了一个名为 Node 的泛型结构体类型它包含三个字段 elem类型为 T表示节点存储的值。next类型为 LinkT表示下一个节点的引用。prev类型为 LinkT表示前一个节点的引用。 这个定义仅仅是声明了 Node 结构体的形状并没有创建任何实际的 Node 实例。 impl 块 Rust 中的 impl 块类似于其他编程语言中的 class 定义但有一些关键的不同之处。 类似点 方法定义 在 impl 块中定义的方法类似于在类中定义的方法。你可以定义实例方法和静态方法Rust 中称为关联函数。 封装 Rust 的 impl 块可以用于封装数据和行为类似于类。 不同点 数据和行为的分离 在 Rust 中数据通过结构体或枚举和行为通过 impl 块是分开的。在类中数据和行为通常是在一个定义中。 没有继承 Rust 没有类的继承。相反它使用特性traits来实现多态性。类系统通常有继承和多态性机制。 所有权和借用 Rust 强调所有权和借用确保内存安全。类系统通常使用垃圾回收如 Java或手动内存管理如 C。 struct Rectangle {width: u32,height: u32, }impl Rectangle {fn new(width: u32, height: u32) - Rectangle {Rectangle { width, height }}fn area(self) - u32 {self.width * self.height} }fn main() {let rect Rectangle::new(30, 50);println!(The area of the rectangle is {} square pixels., rect.area()); }2.进阶用法 闭包 闭包是一种匿名函数它允许捕获调用者作用域中的值可以赋值给变量也可以作为参数传递给其他函数。闭包在许多现代编程语言中作为核心特性被广泛使用。 示例代码 fn main() {let x 1;let sum |y| x y;assert_eq!(3, sum(2)); }闭包 sum 捕获了变量 x 并对其进行了操作。 使用闭包简化代码 传统函数实现 fn muuuuu(intensity: u32) - u32 {println!(muuuu…..);thread::sleep(Duration::from_secs(2));intensity }fn workout(intensity: u32, random_number: u32) {// 根据 intensity 调整健身动作 }函数变量实现 fn muuuuu(intensity: u32) - u32 {println!(muuuu…..);thread::sleep(Duration::from_secs(2));intensity }fn workout(intensity: u32, random_number: u32) {let action muuuuu;// 根据 intensity 调整健身动作 }闭包实现 fn workout(intensity: u32, random_number: u32) {let action || {println!(muuuu…..);thread::sleep(Duration::from_secs(2));intensity};// 根据 intensity 调整健身动作 }通过闭包可以简化代码并捕获外部变量使得代码更具灵活性。 闭包的语法和类型推导 闭包的定义形式如下 |param1, param2| {语句1;语句2;返回表达式 }类型推导示例 let sum |x: i32, y: i32| - i32 {x y };不标注类型的闭包声明更简洁 let sum |x, y| x y;结构体中的闭包 在 Rust 中实现一个简易缓存的设计可以通过使用结构体和闭包来实现。闭包应该作为一个变量传递并且使用泛型和特征约束来指定它的类型。 struct CacherT whereT: Fn(u32) - u32, {query: T,value: Optionu32, }核心概念 闭包与特征约束: T: Fn(u32) - u32 表示 T 是一个实现了 Fn(u32) - u32 特征的类型这意味着 query 是一个闭包或函数接受一个 u32 类型的参数并返回一个 u32 类型的值。每个闭包都有其唯一的类型即使它们的签名相同。 结构体设计: query 字段是一个闭包用于获取值。value 字段用于存储缓存的值初始为 None。 实现方法 为 Cacher 结构体实现方法 implT CacherT whereT: Fn(u32) - u32, {fn new(query: T) - CacherT {Cacher {query,value: None,}}fn value(mut self, arg: u32) - u32 {match self.value {Some(v) v,None {let v (self.query)(arg);self.value Some(v);v}}} }主要步骤 创建缓存实例: 使用 Cacher::new 创建新的缓存实例传入一个闭包或函数作为 query。 查询缓存值: value 方法首先检查 self.value 是否已有缓存值。如果没有调用 query 获取新值并将其存储在 self.value 中。 泛型扩展 为了支持其他类型如 str可以将 u32 替换为泛型类型 E。 struct CacherT, E whereT: Fn(E) - E, {query: T,value: OptionE, }implT, E CacherT, E whereT: Fn(E) - E,E: Copy, {fn new(query: T) - CacherT, E {Cacher {query,value: None,}}fn value(mut self, arg: E) - E {match self.value {Some(v) v,None {let v (self.query)(arg);self.value Some(v);v}}} }闭包的特征重要

  1. FnOnce转移所有权 语法move || { … }解释move 关键字用于指示闭包获取其捕获变量的所有权。示例 fn main() {let x String::from(Hello);let consume_x move || {println!({}, x);// x 被转移到闭包中闭包执行后 x 的所有权已被消耗};consume_x(); // 第一次调用成功// consume_x(); // 再次调用会报错因为 x 的所有权已被消耗 }2. FnMut可变借用 语法|mut param| { … }解释通过 mut 关键字在参数中表示可变借用允许闭包内部修改捕获的变量。示例 fn main() {let mut x 0;let mut add_to_x |y| {x y;};add_to_x(5); // x 现在是 5add_to_x(3); // x 现在是 8 }3. Fn不可变借用 语法|| { … }解释不需要特殊标记默认情况下闭包捕获变量是不可变借用。示例 fn main() {let x 10;let print_x || {println!({}, x);};print_x(); // 打印 10print_x(); // 可以多次调用因为 x 只是被不可变地借用 }闭包的生命周期 捕获变量的生命周期 闭包捕获的变量在闭包的生命周期内必须是有效的。如果闭包捕获的是借用则闭包的生命周期不能超过被借用变量的生命周期。 闭包的生命周期 闭包的生命周期不能超过其捕获变量的生命周期。使用 move 关键字可以将变量的所有权移动到闭包中使得变量的生命周期延长到与闭包相同。 生命周期标注 在复杂情况下可以显式地标注闭包和捕获变量的生命周期以确保它们之间的关系是有效的。 示例总结 以下是一个示例展示了闭包捕获变量并在闭包生命周期内使用 fn main() {let closure create_closure();println!(Closure result: {}, closure(5)); }fn create_closure() - impl Fn(i32) - bool {let x 10;move |z| z x }x 被闭包捕获并使用通过 move 关键字将其所有权移动到闭包中。闭包的生命周期因此延长可以在 create_closure 函数结束后继续有效。 (2)all 方法 fn allF(mut self, f: F) - bool whereF: FnMut(Self::Item) - bool,它接受一个闭包 f 作为参数并对迭代器中的每个元素应用这个闭包。all 方法会返回一个布尔值 如果所有元素都满足闭包 f 的条件则返回 true。如果任何一个元素不满足闭包 f 的条件则返回 false。 (3)迭代器 let chars_left vec![false, true, false, true];原理 创建迭代器 let iter chars_left.iter();迭代示例 let first iter.next();  // Some(false) let second iter.next(); // Some(true) let third iter.next();  // Some(false) let fourth iter.next(); // Some(true) let none iter.next();   // None 用法 创建迭代器通过调用集合的 iter、iter_mut 或 into_iter 方法创建迭代器。遍历使用 for 循环或 while let 语句。常用方法 转换map、filter、enumerate、zip收集collect、fold检查all、any链式调用将多个迭代器方法链式调用以实现复杂的数据处理。 (1)转换 map()对每个元素应用一个函数返回一个新的迭代器。filter()过滤符合条件的元素返回一个新的迭代器。enumerate()为迭代器中的每个元素生成一个索引返回 (索引, 元素) 对。zip()将两个迭代器合并为一个新的迭代器生成 (元素1, 元素2) 对。 (2)收集 collect()将迭代器的所有元素收集到一个集合类型中通常是一个向量VecT。fold()将迭代器的所有元素通过一个累积函数聚合为一个值。 (3)检查 all()检查是否所有元素都满足一个条件。any()检查是否有任意元素满足一个条件。 (4)链式调用 将多个迭代器方法链式调用以实现复杂的数据处理。例如过滤、映射和收集的组合。 例子: fn main() {let vec vec![1, 2, 3, 4, 5];// 使用迭代器遍历元素for val in vec.iter() {println!({}, val);}// 使用链式调用过滤和映射元素然后收集结果let processed: Veci32 vec.iter().filter(|x| x % 2 0) // 过滤出偶数.map(|x| x * 2) // 将每个偶数乘以 2.collect(); // 收集结果到一个向量println!({:?}, processed); // 输出: [4, 8] }(4)枚举 同一化类型 实际项目简化片段 在实际项目中通常需要处理多种类型的长连接。例如我们有一个 WEB 服务需要接受用户的长连接这些连接可能是 TcpStream 或 TlsStream。为了用同一个函数处理这两种连接我们可以使用枚举来简化代码。 假设我们有以下代码 fn new(stream: TcpStream) {let mut s stream;if tls {s negotiate_tls(stream);}// websocket 是一个 WebSocketTcpStream 或者 WebSocketnative_tls::TlsStreamTcpStream 类型websocket WebSocket::from_raw_socket(s, …); }使用枚举类型来简化处理 通过使用枚举类型我们可以将 TcpStream 和 TlsStream 统一化处理 enum WebSocketStream {Tcp(TcpStream),Tls(native_tls::TlsStreamTcpStream), }fn new(stream: WebSocketStream) {match stream {WebSocketStream::Tcp(tcp_stream) {// 处理 TcpStreamlet websocket WebSocket::from_raw_socket(tcp_stream, …);}WebSocketStream::Tls(tls_stream) {// 处理 TlsStreamlet websocket WebSocket::from_raw_socket(tls_stream, …);}} }通过这种方式我们可以将 TcpStream 和 TlsStream 封装在一个枚举类型 WebSocketStream 中并在同一个函数 new 中处理它们简化了代码逻辑。 枚举类型 1Option类型 在 Rust 中Option 类型是一种枚举用于表示一个值可能存在Some或者不存在None fn main() {let numbers vec![1, 2, 3];let empty: Veci32 Vec::new();match get_first_element(numbers) {Some(value) println!(第一个元素是: {}, value),None println!(数组为空),}match get_first_element(empty) {Some(value) println!(第一个元素是: {}, value),None println!(数组为空),} } 2Result类型 ResultT, E: 用于表示一个操作的成功或失败。 在 Rust 中Result 枚举类型需要两个类型参数 ResultT, E表示操作的结果。 Ok(T)表示操作成功包含类型 T 的值。Err(E)表示操作失败包含类型 E 的错误信息。 fn read_filelines(filename: str) - ResultVecString, io::Error {let file File::open(filename)?;let reader BufReader::new(file);let mut lines Vec::new();for line in reader.lines() {let line line?;lines.push(line);}Ok(lines) }使用 Ok 包装一个值时你实际上是在创建一个 Result 类型的实例表示操作成功并返回该值作为 Result 的成功变体。 模式匹配 (5)match 表达式 match 表达式是 Rust 中用于模式匹配的强大工具。它可以根据不同的模式执行不同的代码分支。 match value {pattern1 expr1,pattern2 expr2, expr3, // 通配模式匹配所有其他情况 }(6)读取文件  在 Rust 中读取文件的流程通常包括以下步骤 导入必要的模块包括文件系统和 I/O 操作的模块。打开文件使用 std::fs::File::open 方法打开文件并处理可能的错误。创建缓冲读取器可选如果逐行读取文件内容可以使用 std::io::BufReader 创建一个缓冲读取器。读取文件内容根据需要选择读取文件内容的方法例如逐行读取、一次性读取到字符串、一次性读取到字节数组等。处理文件内容对读取到的文件内容进行处理。错误处理在读取和处理文件内容的过程中处理可能的错误。 方法一逐行读取文件内容 关键点使用 BufReader 和 lines 方法逐行读取文件 use std::fs::File; use std::io::{self, BufRead, BufReader};fn read_file_lines(filename: str) - ResultVecString, io::Error {let file File::open(filename)?; // 打开文件let reader BufReader::new(file); // 创建缓冲读取器reader.lines().collect() // 逐行读取并收集结果 }fn main() {match read_file_lines(example.txt) {Ok(lines) lines.iter().for_each(|line| println!({}, line)),Err(e) eprintln!(Error reading file: {}, e),} }方法二一次性读取整个文件内容到字符串 关键点使用 read_to_string 方法一次性读取整个文件内容 use std::fs::File; use std::io::{self, Read};fn read_file_to_string(filename: str) - ResultString, io::Error {let mut file File::open(filename)?; // 打开文件let mut contents String::new();file.read_to_string(mut contents)?; // 读取文件内容到字符串Ok(contents) }fn main() {match read_file_to_string(example.txt) {Ok(contents) println!({}, contents),Err(e) eprintln!(Error reading file: {}, e),} }方法三一次性读取整个文件内容到字节数组 关键点使用 read_to_end 方法一次性读取整个文件内容到字节数组 use std::fs::File; use std::io::{self, Read};fn read_file_to_bytes(filename: str) - ResultVecu8, io::Error {let mut file File::open(filename)?; // 打开文件let mut contents Vec::new();file.read_to_end(mut contents)?; // 读取文件内容到字节数组Ok(contents) }fn main() {match read_file_to_bytes(example.txt) {Ok(contents) println!({:?}, contents),Err(e) eprintln!(Error reading file: {}, e),} }方法四使用 std::fs::read_to_string 直接读取整个文件到字符串 关键点使用 fs::read_to_string 直接读取文件内容到字符串 use std::fs;fn read_file_to_string(filename: str) - ResultString, std::io::Error {fs::read_to_string(filename) // 直接读取文件内容到字符串 }fn main() {match read_file_to_string(example.txt) {Ok(contents) println!({}, contents),Err(e) eprintln!(Error reading file: {}, e),} }方法五使用 std::fs::read 直接读取整个文件到字节数组 关键点使用 fs::read 直接读取文件内容到字节数组 use std::fs;fn read_file_to_bytes(filename: str) - ResultVecu8, std::io::Error {fs::read(filename) // 直接读取文件内容到字节数组 }fn main() {match read_file_to_bytes(example.txt) {Ok(contents) println!({:?}, contents),Err(e) eprintln!(Error reading file: {}, e),} }总结 逐行读取文件内容使用 BufReader 和 lines 方法。一次性读取整个文件内容到字符串使用 read_to_string 方法。一次性读取整个文件内容到字节数组使用 read_to_end 方法。直接读取整个文件到字符串使用 fs::read_to_string 方法。直接读取整个文件到字节数组使用 fs::read 方法。 RefCell 概括 RefCell 是 Rust 提供的一种类型用于在不可变的上下文中实现内部可变性。它允许你在运行时执行借用检查以确保安全地修改数据。这在某些数据结构如链表和特定场景如闭包或异步编程中非常有用。 核心特点 内部可变性 允许在不可变的上下文中修改数据。使用 borrow() 获取不可变引用。使用 borrow_mut() 获取可变引用。 运行时借用检查 在借用时进行运行时检查确保借用规则不被违反。如果在借用过程中违反规则会导致运行时错误。 典型用法 适用于实现复杂数据结构如链表、图等需要相互引用的结构。适用于跨越函数的借用尤其在闭包和异步编程中。 示例代码 use std::cell::RefCell;let x RefCell::new(5); {let y x.borrow();println!(y: {}, *y); // 输出: y: 5 } {let mut z x.borrow_mut();*z 10;println!(x: {}, x.borrow()); // 输出: x: 10 }Rc 的核心作用概括 RcReference Counted是 Rust 提供的一种智能指针允许多个所有者共享同一个数据。 核心特点 共享所有权 允许多个变量同时拥有同一个数据。适用于需要在多个地方访问和使用同一个数据的场景。 自动管理内存 通过引用计数管理数据的生命周期。当最后一个引用被删除时数据会自动释放。 单线程环境 只能在单线程环境中使用。如果需要在多线程环境中共享数据使用 ArcAtomic Reference Counted。 使用场景 数据共享 例如在树或图数据结构中多个节点可以共享同一个子节点。 不可变数据 通常用于共享不可变数据因为 Rc 默认不允许多个可变引用。如果需要修改数据可以结合 RefCell 使用。 示例代码 use std::rc::Rc;fn main() {let data Rc::new(5); // 创建一个 Rc 指针包含数据 5let data1 Rc::clone(data); // 创建 data 的克隆引用let data2 Rc::clone(data); // 创建 data 的另一个克隆引用println!(Reference count: {}, Rc::strong_count(data)); // 输出: 3println!(data: {}, data);println!(data1: {}, data1);println!(data2: {}, data2); }Rc 的销毁时机 对于 RcReference Counted智能指针当一个 Rc 实例超出其作用域时引用计数会自动减少。如果引用计数减少到零Rc 管理的数据将被释放。 use std::cell::RefCell; use std::rc::Rc;fn main() {{let data Rc::new(RefCell::new(5)); // 创建一个包含 RefCell 的 Rc 指针println!(Initial reference count: {}, Rc::strong_count(data)); // 输出: 1{let data1 Rc::clone(data); // 克隆 Rc 指针引用计数增加到 2println!(Reference count after creating data1: {}, Rc::strong_count(data)); // 输出: 2{let data2 Rc::clone(data); // 再次克隆 Rc 指针引用计数增加到 3println!(Reference count after creating data2: {}, Rc::strong_count(data)); // 输出: 3*data2.borrow_mut() 10; // 修改数据println!(Modified data through data2: {}, data.borrow()); // 输出: 10} // data2 超出作用域引用计数减少到 2println!(Reference count after data2 goes out of scope: {}, Rc::strong_count(data)); // 输出: 2} // data1 超出作用域引用计数减少到 1println!(Reference count after data1 goes out of scope: {}, Rc::strong_count(data)); // 输出: 1} // data 超出作用域引用计数减少到 0数据被释放// 由于 data 已经被释放不能再访问它 }字符串操作 字符串替换
  2. replace 适用类型: String 和 str参数: 第一个参数是要被替换的字符串。第二个参数是新的字符串。功能: 替换所有匹配的字符串返回一个新的字符串。 示例代码: fn main() {let string_replace String::from(I like rust. Learning rust is my favorite!);let new_string_replace string_replace.replace(rust, RUST);dbg!(new_string_replace); }运行结果: new_string_replace I like RUST. Learning RUST is my favorite!2. replacen 适用类型: String 和 str参数: 前两个参数与 replace 方法相同。第三个参数表示替换的次数。功能: 替换指定次数的匹配字符串返回一个新的字符串。 示例代码: fn main() {let string_replace I like rust. Learning rust is my favorite!;let new_string_replacen string_replace.replacen(rust, RUST, 1);dbg!(new_string_replacen); }运行结果: new_string_replacen I like RUST. Learning rust is my favorite!3. replace_range 适用类型: 仅适用于 String参数: 第一个参数是要替换的字符串范围Range。第二个参数是新的字符串。功能: 直接在原字符串上替换指定范围内的内容不返回新的字符串。 示例代码: fn main() {let mut string_replace_range String::from(I like rust!);string_replace_range.replace_range(7..8, R);dbg!(string_replace_range); }运行结果: string_replace_range I like Rust!字符串删除方法
  3. truncate 功能: 从指定位置开始删除字符串中从该位置到结尾的全部字符。特性: 直接操作原字符串无返回值。如果指定位置不在字符边界上则会发生错误。 示例代码: fn main() {let mut string_truncate String::from(测试truncate);string_truncate.truncate(3);dbg!(string_truncate); }运行结果: string_truncate 测2. clear 功能: 清空字符串删除字符串中的所有字符。特性: 直接操作原字符串相当于 truncate() 方法参数为 0。 示例代码: fn main() {let mut string_clear String::from(string clear);string_clear.clear();dbg!(string_clear); }运行结果: string_clear 字符串连接 
  4. 使用 或 操作符 要求: 右边的参数必须为字符串切片引用str。调用 操作符相当于调用了标准库中的 add 方法。 特性: 返回一个新的字符串。变量声明可以不需要 mut 关键字修饰。左边的字符串所有权会被转移。 示例代码: fn main() {let string_append String::from(hello );let string_rust String::from(rust);let result string_append string_rust; // string_append 的所有权被转移let mut result result !; // result ! 中的 result 是不可变的result !!!;println!(连接字符串 - {}, result); }运行结果: 连接字符串 - hello rust!!!!所有权转移示例: fn main() {let s1 String::from(hello,);let s2 String::from(world!);let s3 s1 s2; // s1 的所有权被转移assert_eq!(s3, hello,world!);// println!({}, s1); // 这行代码会报错因为 s1 的所有权已被转移 }连续连接示例: let s1 String::from(tic); let s2 String::from(tac); let s3 String::from(toe);// String String str str str str let s s1 - s2 - s3;s1 这个变量通过调用 add() 方法后所有权被转移到 add() 方法里面 add() 方法调用后就被释放了同时 s1 也被释放了。再使用 s1 就会发生错误。 
  5. 使用 format! 宏 适用: String 和 str特性: 类似于 print! 的用法生成一个新的字符串。 示例代码: fn main() {let s1 hello;let s2 String::from(rust);let s format!({} {}!, s1, s2);println!({}, s); }运行结果: hello rust!3.Rust特性 1所有权 所有权机制是Rust用来管理内存的一种系统它确保了内存安全性并防止了许多常见的编程错误。以下是所有权机制的核心概念和规则
  6. 所有权规则 每个值在Rust中都有一个所有者 所有者是一个变量只有一个变量可以是某个值的所有者。 值在任一时刻只能有一个所有者 当所有者变量超出作用域时该值将被自动清理。 当所有者离开作用域时该值将被丢弃 Rust在所有者超出作用域时自动调用drop函数来释放内存。
  7. 所有权转移Move 1非 Copy trait 的类型赋值 let s String::from(hello); let s1 s; 在这个例子中s的栈上的数据所有权被转移给 s1堆上数据仍然不变移动语义因此在之后使用s1会导致编译错误。 2self 作为参数
    方法或函数以 self 作为参数时会获取调用者的所有权调用后原变量失效。 3 非引用参数
    类似地函数以非引用类型参数接收变量时也会获取其所有权。 4克隆
    let s String::from(hello); let s1 s.clone(); 在Rust中使用clone方法可以进行深拷贝。深拷贝会复制堆上的数据并在栈上创建一个新的所有权指向这块堆内存。结果是栈上和堆上都有独立的拷贝因此两个变量互不影响。  5 Copy trait 的类型 整数类型 (i32, u32, 等)浮点数类型 (f32, f64)布尔类型 (bool)字符类型 (char)元组如果元组内的所有元素都实现了 Copy trait 6无法copy的类型 非Copy类型 复杂类型如String不实现Copy特性因为它们涉及更复杂的内存管理。不能对不实现Copy的类型进行直接赋值拷贝。 解决方案 使用引用对于无法实现Copy的类型可以通过引用来解决所有权冲突。 fn main() {let x (1, 2, (), hello.to_string());let y (x.0, x.1, x.2, x.3);println!({:?}, {:?}, x, y); }3. 借用Borrowing 引用的可变性决定了你是否可以通过引用来修改所引用的值。 1不可变借用 可以有多个不可变引用但不能同时有可变引用。let s String::from(hello); let r1 s; // 不可变引用 r1 let r2 s; // 不可变引用 r2println!(r1: {}, r2: {}, r1, r2); // 可以同时使用多个不可变引用2可变借用 同一时间只能有一个可变引用且不能同时存在不可变引用。let mut s String::from(hello); let r1 mut s; // 可变引用 r1r1.push_str(, world);println!({}, r1); // r1 修改了 s 的内容唯一的可变引用在任何给定的时间点一个变量只能有一个可变引用mut。不可变引用与可变引用互斥在有可变引用存在时不允许同时存在不可变引用。反之在存在不可变引用时不允许存在可变引用。 这些规则确保了在访问和修改数据时不会出现竞争条件。 3注意 1.不允许在存在不可变引用时修改原始变量  fn main() {let mut s String::from(hello); // 可变变量 s 被创建let ref1 s; // 创建对 s 的不可变引用 ref1s String::from(goodbye); // 尝试修改 s 的值println!({}, ref3.to_uppercase()); // 使用 ref3 打印 s 的值 }一种修复方法是将 println! 语句移动到修改 s 之前确保在修改 s 之前所有的不可变引用都已经被使用完毕。例如 fn main() {let mut s String::from(hello);let ref1 s;let ref2 ref1;let ref3 ref2;println!({}, ref3.to_uppercase()); // 在修改 s 之前使用 ref3s String::from(goodbye); // 现在可以安全地修改 s }2. 悬垂引用 返回的是一个局部变量的引用函数作用域结束后变量销毁 fn drip_drop() - String {let s String::from(hello world!);return s; }修改方法直接返回所有权 fn drip_drop() - String {let s String::from(hello world!);return s; }3.借用检查器错误 v[0]返回一个引用试图将向量中元素的引用赋值给一个所有权变量  fn main() {let s1 String::from(hello);let mut v Vec::new();v.push(s1);let s2: String v[0]; // 试图移动元素的所有权println!({}, s2); }解决方法仅读取 fn main() {let s1 String::from(hello);let mut v Vec::new();v.push(s1);let s2: String v[0];println!({}, s2); }4.切片 切片slice是Rust中对数组、字符串等集合部分数据的引用。它具有以下核心特性 引用类型不拥有数据所有权只是借用数据的一部分。不可变和可变支持不可变切片[T]和可变切片mut [T]。高效避免数据拷贝直接引用原数据。安全编译时和运行时边界检查防止越界访问和数据竞争。 1不可变切片 fn main() {let arr [1, 2, 3, 4, 5];let slice arr[1..4]; // 引用数组的部分数据println!({:?}, slice); // 输出 [2, 3, 4] }2可变切片 fn main() {let mut arr [1, 2, 3, 4, 5];let slice mut arr[1..3]; // 可变切片引用数组的部分数据slice[0] 10;println!({:?}, arr); // 输出 [1, 10, 3, 4, 5] }3字符串切片 fn main() {let s String::from(hello, world);let hello s[0..5]; // 引用字符串的部分数据println!({}, hello); // 输出 hello }4切片操作 fn main() {let arr [1, 2, 3, 4, 5];let slice arr[1..4];println!(Length: {}, slice.len()); // 输出 Length: 3println!(First element: {:?}, slice.first()); // 输出 First element: Some(2) }5. 数据竞争的避免 Rust的借用检查器在编译时强制执行借用规则以确保在任何给定时间只有一个可变引用或多个不可变引用从而避免数据竞争。
  8. 生命周期 Rust通过生命周期标注来确保引用的有效性防止悬空引用。 2显式使用引用操作符 常见情况 创建引用 当你需要创建一个变量的引用时需要显式地使用 。let x 5; let y x; // 创建对 x 的不可变引用 let z mut x; // 创建对 x 的可变引用需要 x 是可变的函数参数传递引用 当你定义一个函数并希望它接收一个引用作为参数时需要显式地使用 。fn print_value(value: i32) {println!({}, value); }let x 10; printvalue(x); // 传递 x 的引用解引用 当你需要从一个引用中获取实际值时需要显式地使用 *。let x 5; let y x; println!({}, *y); // 解引用 y 获取 x 的值注意和习惯 基本类型 为什么要手动设置变量可变性 Rust支持可变和不可变变量提供了灵活性和安全性性能优化将无需改变的变量声明为不可变可以提升运行性能避免多余的运行时检查。 变量命名 使用下划线开头的变量名可以忽略未使用变量的警告。let表达式可以用于变量解构从复杂变量中匹配出部分内容。 整形溢出处理 Rust提供了多种方法显式处理整型溢出如wrapping、checked_、overflowing*和saturating。 wrapping_ 方法 描述当发生溢出时值会按照二进制补码环绕wrap around。这意味着溢出后的结果将从最低有效位开始重新计算。用法wrapping_add、wrapping_sub、wrapping_mul等。示例 let x: u8 255; let y x.wrappingadd(1); // y 0核心溢出后环绕继续计算不会引发程序错误。 checked* 方法 描述当发生溢出时返回一个None否则返回Some(结果)。适合需要检测并处理溢出的情况。用法checked_add、checked_sub、checked_mul等。示例 let x: u8 255; if let Some(y) x.checkedadd(1) {// 不会执行 } else {println!(溢出检测到); }核心通过返回Option类型来检测和处理溢出。 overflowing* 方法 描述返回一个包含计算结果和布尔值的元组布尔值指示是否发生溢出。用法overflowing_add、overflowing_sub、overflowing_mul等。示例 let x: u8 255; let (y, overflowed) x.overflowingadd(1); // y 0, overflowed true核心提供溢出后的结果并显式指示溢出是否发生。 saturating* 方法 描述当发生溢出时值会被夹紧到类型的最大或最小值。适合需要确保结果在一定范围内的情况。用法saturating_add、saturating_sub、saturating_mul等。示例 let x: u8 255; let y x.saturating_add(1); // y 255核心溢出后结果被限制在合法范围内最大或最小值。 数字字面量下划线 在Rust中数字字面量中的下划线_可以用于增加可读性它们不会影响数值的实际值。 示例1_000.000_1 表示 1000.0001。 Rust字符 Rust 的字符不仅仅是 ASCII所有的 Unicode 值都可以作为 Rust 字符包括单个的中文、日文、韩文、emoji 表情符号等等都是合法的字符类型。Unicode 值的范围从 U0000 ~ UD7FF 和 UE000 ~ U10FFFF。 由于 Unicode 都是 4 个字节编码因此字符类型也是占用 4 个字节 单元类型 在Rust中单元类型 () 表示空值或空元组通常用于函数不返回任何值的情况。尽管逻辑上是空的但它在内存中占用的大小为0字节。使用 std::mem::size_of_val 可以确认这一点例如 assert!(size_of_val(unit) 0);这保证了 unit 的内存占用为0体现了Rust中零大小类型ZST的概念和用途。 所有权 永远不会返回的函数发散函数 发散函数返回类型为!表示函数永远不会正常返回控制权。实现方法 无限循环使用loop {}创建一个永不退出的循环。panic!触发一个恐慌使程序中止。std::process::exit立即终止程序并返回指定的状态码。 // 方法一使用无限循环 fn never_return_fn() - ! {loop {// 无限循环永远不会返回} }// 方法二调用panic! fn never_return_fn() - ! {panic!(This function never returns!); }// 方法三使用std::process::exit use std::process;fn never_return_fn() - ! {process::exit(1); // 退出程序并返回状态码1 } 当所有权转移时可变性也可以随之改变。 let x 5; // 不可变变量 let mut y x; // 所有权转移y 变为可变 y 1; // 修改 y 的值部分移动 部分移动 解构时可以同时移动变量的一部分并借用另一部分。被移动部分的所有权转移原变量不能再使用该部分。被借用部分仍然可以通过引用使用。 原变量状态 整体不能再使用因为部分所有权已转移。未转移所有权的部分仍可通过引用使用。 fn main() {let t (String::from(hello), String::from(world));let _s t.0;// 仅修改下面这行代码且不要使用 _sprintln!({:?}, t.1); } Ref和的区别 使用场景 是直接引用用于创建一个指向某个值的引用适用于任何需要引用的地方。ref 主要在模式匹配中使用用于方便地在模式匹配过程中获取某个值的引用。 代码简洁性和可读性 使用 创建引用时代码逻辑清晰直接指向某个值易于理解。使用 ref 在模式匹配中创建引用可以使模式匹配的代码更加简洁和直观避免了在模式匹配外部手动创建引用的繁琐。 当你在模式匹配中需要创建多个嵌套值的引用时ref 可以大大简化代码的编写和阅读。例如 struct Point {x: i32,y: i32, }let p Point { x: 10, y: 20 };match p {Point { ref x, ref y } {// 在这里 x 和 y 都是引用println!(x: {}, y: {}, x, y);} }在这个例子中使用 ref 可以直接在模式匹配中创建 x 和 y 的引用。如果不使用 ref你需要手动创建引用这样会使代码变得更复杂 struct Point {x: i32,y: i32, }let p Point { x: 10, y: 20 };let Point { x, y } p; let x_ref x; let y_ref y;println!(x: {}, y: {}, x_ref, y_ref);借用规则 不可变引用与可变引用Rust 不允许同时存在不可变引用和可变引用以确保内存安全。生命周期管理在可变借用之前必须完成所有对数据的不可变引用。 错误原因 调用 s.clear() 时存在对 s 的不可变引用 ch违反借用规则。 修正代码 确保在修改原数据前处理完所有对数据的引用 fn main() {let mut s String::from(hello world);// 获取第一个字符的不可变引用let ch first_character(s);println!(the first character is: {}, ch);// 清空字符串在使用完不可变引用之后s.clear(); }fn first_character(s: str) - str {s[..1] }字符串  进行切片时需要注意字符边界 UTF-8 编码的字符可能占用多个字节切片操作必须在字符边界上进行否则程序会崩溃。 核心要点 UTF-8 编码字符可能占用1至4个字节汉字通常占3个字节。字符边界切片索引必须对齐到字符边界。避免崩溃切片时需确保索引在字符边界否则会导致程序崩溃。使用工具可以使用 .char_indices() 方法获取字符边界确保切片安全。 示例 let s 中国人; let a s[0..3]; // 正确的切片取第一个汉字 中 println!({}, a); // 输出 中String 与 str 的转换 从 str 到 String: String::from(hello, world)hello, world.to_string() 从 String 到 str: ss[..]s.as_str() 这背后的原理是 Rust 的 Deref 隐式强制转换。 不允许直接通过索引访问字符串中的字符 底层存储Rust 字符串底层是字节数组 [u8]。UTF-8 编码字符占用不同字节数索引必须对齐字符边界。多种表示方式Rust 提供多种字符串表示方式适合不同需求。索引限制为了安全和性能考虑Rust 不允许直接索引字符串。遍历字符串使用 chars 和 bytes 方法遍历字符串确保正确处理字符和字节。 字符串转义与原样字符串 Rust 提供了多种方法来处理字符串中的转义字符和原样字符串。以下是详细说明及核心概括。
  9. 转义字符 ASCII 转义: 使用 \x 后跟两个十六进制数来表示 ASCII 字符。Unicode 转义: 使用 \u 后跟花括号中的 Unicode 码点来表示 Unicode 字符。 示例代码: fn main() {// 通过 \ 字符的十六进制表示转义输出一个字符let byte_escape Im writing \x52\x75\x73\x74!;println!(What are you doing\x3F (\x3F means ?) {}, byte_escape);// \u 可以输出一个 unicode 字符let unicode_codepoint \u{211D};let character_name \DOUBLE-STRUCK CAPITAL R\;println!(Unicode character {} (U211D) is called {},unicode_codepoint, character_name);// 换行了也会保持之前的字符串格式// 使用\忽略换行符let long_string String literalscan span multiple lines.The linebreak and indentation here -- can be escaped too!;println!({}, long_string); }运行结果: What are you doing? (\x3F means ?) Im writing Rust! Unicode character ℝ (U211D) is called DOUBLE-STRUCK CAPITAL R String literalscan span multiple lines.The linebreak and indentation here – can be escaped too!2. 原样字符串 原样字符串: 使用 r# 和 # 包围字符串忽略转义字符。带有双引号的字符串: 使用多个 # 包围字符串以处理双引号和其他复杂情况。 示例代码: fn main() {// 保持字符串的原样输出println!({}, hello \x52\x75\x73\x74);// 原样字符串let raw_str rEscapes dont work here: \x3F \u{211D};println!({}, raw_str);// 包含双引号的原样字符串let quotes r#And then I said: There is no escape!#;println!({}, quotes);// 使用多个 # 处理复杂情况let longer_delimiter r###A string with # in it. And even ##!###;println!({}, longer_delimiter); }运行结果: hello \x52\x75\x73\x74 Escapes dont work here: \x3F \u{211D} And then I said: There is no escape! A string with # in it. And even ##!操作 UTF-8 字符串
  10. 遍历字符 如果你想以 Unicode 字符的方式遍历字符串可以使用 chars 方法 示例代码: fn main() {for c in 中国人.chars() {println!({}, c);} }输出: 中 国 人2. 遍历字节 如果你想查看字符串的底层字节数组可以使用 bytes 方法 示例代码: fn main() {for b in 中国人.bytes() {println!({}, b);} }输出: 228 184 173 229 155 189 228 186
  11. 获取子串 由于 UTF-8 字符串的变长特性从中获取子串相对复杂。Rust 标准库无法直接支持按字符边界提取子串。需要使用第三方库如 utf8_slice来实现。 示例代码: fn main() {let s holla中国人नमस्ते;// 使用第三方库 utf8_slice 提取子串let sub utf8_slice::slice(s, 5, 8);println!({}, sub); // 输出: 中国 }str和str 字符串字面量类型: 字符串字面量的类型是 str。 str 类型的使用: str 类型表示字符串的不可变视图通常无法直接使用。可以使用 str 来处理字符串的引用。 Boxstr 和 str 的转换: 使用 Box 将 str 类型存储在堆上通过引用 将 Boxstr 转换为 str。 数组和元组 元组过长错误 问题Rust 默认只支持最多12个元素的元组超过会导致编译错误。 解决方案 使用数组适用于元素类型相同的情况。使用结构体适用于元素类型不同的情况。分解元组将长元组分解成多个短元组。 非基础类型数组的所有权问题 基础类型与复杂类型区别 基础类型如 i32支持 Copy 特性可以通过 [value; n] 语法初始化。复杂类型如 String不支持 Copy 特性需要逐个创建元素。 解决方法使用 std::array::from_fn 函数通过闭包生成每个元素避免所有权问题。
    切片大小 切片是对数组部分的引用包含一个指针和长度。64 位系统指针和长度各占 8 字节因此切片总大小为 16 字节。 错误示例修正 原代码错误是将切片大小误认为 8 字节。应将 assert! 中的值修改为 16。 修正代码 fn main() {let arr: [char; 3] [中, 国, 人];let slice arr[..2];// 修改数字 8 让代码工作assert!(std::mem::size_of_val(slice) 16); }结构体 结构体更新语法 结构体更新语法允许基于现有结构体实例创建新实例只需指定改变的字段其余字段自动从现有实例中获取。所有权转移结构体更新语法会转移所有权涉及 Copy 特征的字段会被拷贝不涉及 Copy 特征的字段会发生所有权转移。 示例代码 传统方式 let user2 User {active: user1.active,username: user1.username,email: String::from(anotherexample.com),sign_in_count: user1.sign_in_count, };结构体更新语法 let user2 User {email: String::from(anotherexample.com),..user1 };所有权转移与 Copy 特征 Copy 特征bool 和 u64 等实现了 Copy 特征的类型在赋值时会被拷贝不会转移所有权。非 Copy 类型如 String 等在赋值时会转移所有权导致原结构体实例中的对应字段不能再被使用。 例子 struct User {active: bool,username: String,email: String,sign_in_count: u64, }fn main() {let user1 User {email: String::from(someoneexample.com),username: String::from(someusername123),active: true,sign_in_count: 1,};let user2 User {email: String::from(anotherexample.com),..user1};// 使用了 Copy 特征的字段可以继续使用println!({}, user1.active); // 依然有效// 使用了所有权转移的字段将导致错误// println!({:?}, user1); // 错误user1 的 username 字段所有权已被转移 }结构体内存布局 struct File {name: String,data: Vecu8,} 从图中可以清晰地看出 File 结构体两个字段 name 和 data 分别拥有底层两个 [u8] 数组的所有权(String 类型的底层也是 [u8] 数组)通过 ptr 指针指向底层数组的内存地址这里你可以把 ptr 指针理解为 Rust 中的引用类型。 该图片也侧面印证了把结构体中具有所有权的字段转移出去后将无法再访问该字段但是可以正常访问其它的字段。 Rust 元组结构体 定义 语法 struct 结构体名(字段类型1, 字段类型2, …);示例 struct Color(i32, i32, i32); struct Point(i32, i32, i32);使用场景 当需要一个整体名称但不关心内部字段的名称时。适用于明确且简单的字段组合如 3D 坐标点 (x, y, z)。 #[derive(Debug)] 打印结构体 核心概念 #[derive(Debug)] 注解 使结构体或枚举自动实现 Debug 特征从而能够使用调试格式进行打印。 实现步骤 为结构体派生 Debug 特征 #[derive(Debug)] struct Rectangle {width: u32,height: u32, }调试打印 使用 dbg! 宏打印调试信息到标准错误输出 stderr let rect1 Rectangle {width: dbg!(30 * scale),height: 50, }; dbg!(rect1);使用 println! 宏在标准输出 stdout 中打印调试信息 println!({:?}, rect1);枚举  枚举类型定义 枚举类型用于定义一组相关的值。不能在枚举中使用浮点数作为值。 枚举值比较 不同枚举类型中的值不能直接比较。使用 as 操作符将枚举值转换为整数类型后进行比较。 // 修复错误 enum Number {Zero,One,Two, }enum Number1 {Zero 0,One,Two, }// C语言风格的枚举定义 enum Number2 {Zero 0,One 1,Two 2, }fn main() {// 通过 as 可以将枚举值强转为整数类型assert_eq!(Number::One as i32, Number1::One as i32);assert_eq!(Number1::One as i32, Number2::One as i32); }
    初始化枚举 enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(i32, i32, i32), } 使用字段名初始化 初始化结构体样式的枚举成员时必须使用字段名。let msg1 Message::Move { x: 1, y: 2 };确保类型正确 对于包含数据的枚举成员确保数据类型正确。let msg2 Message::Write(String::from(hello, world!));Option 类型处理 OptionT 是 Rust 中用来表示可能为空的值的类型。通过 unwrap_or 方法可以为 Option 提供一个默认值从而避免类型不匹配的问题。 模式匹配与解构 unwrap_or 用于从 Option 中提取值如果 Option 为 None则返回提供的默认值。示例 let value some_option.unwrap_or(0); unwrap_or_else 类似于 unwrap_or但接受一个闭包在 Option 为 None 时执行该闭包并返回结果。示例 let value some_option.unwrap_or_else(|| {println!(No value found, using default);0 });expect 用于从 Option 中提取值如果 Option 为 None则程序 panic 并输出自定义错误消息。示例 let value some_option.expect(Expected a value but found None);模式匹配match 使用 match 表达式优雅地处理 Option 的所有可能情况避免直接使用 unwrap 可能带来的 panic 问题。示例 match some_option {Some(value) println!(Value is: {}, value),None println!(No value found), }高级  函数式编程 闭包捕获作用域中的值 闭包特性闭包可以捕获并使用定义时作用域中的变量而函数则不能。使用场景在需要访问定义时作用域内的变量时闭包比函数更灵活。编译器提示Rust 编译器会友好地提示将函数替换为闭包以便捕获动态环境中的值。 闭包对内存的影响 闭包的内存分配 捕获环境变量闭包会捕获其定义时环境中的变量。内存分配捕获的变量需要额外的内存来存储。性能影响在某些场景中这种内存分配可能成为一种负担。 函数的内存管理 不捕获环境变量函数不会捕获其定义时环境中的变量。无额外内存分配因此函数不会为环境变量分配额外的内存。性能优势避免了因内存分配带来的负担。 Fnonce转移所有权易错 move 关键字move 关键字将变量的所有权从其原始作用域移动到闭包的作用域中。捕获行为对于 Copy 类型如 i32闭包捕获的是变量的一个副本而不是引用。副本的生命周期闭包持有的副本在整个闭包生命周期内是唯一的不会每次调用闭包时重新复制原始值。闭包内的修改每次调用闭包时闭包内部的 count 副本都会递增而外部的 count 保持不变。 示例代码 fn main() {let mut count 0;// 闭包 inc 使用 move 捕获了 count 的所有权// 由于 count 是一个 i32 类型实现了 Copy trait// 因此捕获的是 count 的一个副本但这个副本在闭包的整个生命周期内是唯一的let mut inc move || {count 1; // 修改的是闭包内的 count 副本每次调用闭包时这个副本都会递增println!(count: {}, count); // 打印副本的值};inc(); // 调用闭包闭包内的 count 变为 1// 尝试不可变借用原始的 count// 因为闭包捕获的是副本原始的 count 仍然存在且未被借用let _reborrow count;inc(); // 再次调用闭包闭包内的 count 副本变为 2// 尝试可变借用原始的 count// 由于闭包捕获的是副本原始的 count 仍然存在且未被借用let _count_reborrowed mut count;// 断言外部的 count 仍然为 0// 因为闭包内部修改的是副本外部的 count 未被修改assert_eq!(count, 0); }