【rust学习】rust基础知识清单详情(全)

模        块问题答案
安装 如何安装rust1.linux  mac安装rust
    1. curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    2. 执行  rustc  -v  查看是否安装成功 和 版本
环境搭建 1.如何用命令新建项目
  cargo new  project
2.如何构建项目
  1.cargo build 
  2.cargo run  
cargo教程cargo的作用 1.项目管理:通过cargo 构建 运行 测试项目
  1.允许开发者配置构建选项 如编译器选项,特性 和 目标平台
  2.通过 cargo new命令快速创建项目
  3.通过 cargo test 命令来运行项目的单元测试
  4.通过 cargo publish 将库发布到 crates.io 上
  5.只从跨平台构建
2.包管理:通过在项目中引入第三方依赖项
  1.通过cargo.toml 文件管理依赖项
  2.允许开发者搜索 添加 管理 第三方库 
输出到命令行println!()用法 1.println!("a is {}",a);   {}是占位符 
2.如果想把a输出两遍,如何输出 
   println!("a is {0}, a again is {0}",a )
基础语法 如何声明变量1.rust是强类型语言
2.通过let 关键字 声明    let a = 123;
3.声明后 a的值不可改变 ,如果想a的值可以改变需要用 let mut a = 123;a=456
常量与不可变变量的区别1.let a = 123;
   let a = 456;
  以上程序合法
2.const a : i32 =123;
   let a = 456;
  以上程序不合法
重影 是什么变量的名称可以被重新使用   
  let x= 5
  let x = x + 1
  let x = x * 2   
  x = 12 
重影和赋值的区别 1.重影:用同一个名字重新代表另一个变量实体 类型,可变属性,值都可以改变
2.可变变量赋值:仅仅值能改变 
  let mut s = "123"
  s = s.len()
  以上程序会报错,因为s是字符传类型的,不能赋值给整型
rust的数据类型rust的整型数据类型有哪些i8  u8
i16 u16
i32  u32
i64  u64
i128  u128
isize  usize
isize和usize的解释及用法1.解释
  1.这两个数据类型可以存储数据的长度取决于所处平台。如32位架构的
处理器则存储32位的整型  
2.用法
  1.数组索引
     1.数组索引通常用usize类型 
        let array: [i32; 5] = [1,2,3,4,5]
  2.指针运算
  3.集合大小
  4.内存地址
浮点类型 1.f32 表示32位浮点数,  f64表示64位浮点数
2.现代计算器对两个浮点类型的处理速度基本相同,但是64位浮点数精度更高
数学运算 let sum = 5 + 10; // 加
 let difference = 95.5 - 4.3; // 减
 let product = 4 * 30; // 乘
 let quotient = 56.7 / 32.2; // 除
 let remainder = 43 % 5; // 求余
字符型 1.字符类型用 char 表示
2.char类型大小位4字节(单个char类型变量占用4字节,不是字符串)
   如果一个字符 串是 hello, 那么它包含5个字符,因此会占用 5 * 4 = 20个字节空间
3.用法
  1.声明类型(用单引号)  
     let letter = 'A'
  2.char类型和字符串类型是不一样的
    1.单个字符  let c:char = 'a' //单个字符
    2.字符串切片 let s: &str = "hello" //字符串切片
    3.可变字符串  let string: String = String::from("world") //可变字符串
  3.char转换为整数类型
     let char = 'A'
     let int_vlaue = ch as u32 
复合类型 1.元组用()包含一组数据 
  1.定义元组  let  tuple_example = (10, "hello", 3.14)
  2.访问元组中元素  let first = tuple_example.0
  3.解构元组
     1.let (x,y,z) = tumple
        println !("{} {} {}",x,y,z)
2.数组用[]包含相同数据
   1.定义数组 let a =[1,2,3,4,5]
   2.访问数组元素 let first = a[0]
   3.定义可变数组 let mut a = [1,2,3]
rust注释  代码注释
  1. /**/ 
  2.//
文档注释
  ///  
函数定义函数1.定义函数使用fn关键字。 
2.函数可以有参数 也可以没有
3.函数可以有返回值 也可以不返回值

fn function_name(parameters) -> return_type {
    //函数体
    //最后一个表达式通常是返回值(如果没有分号
}
不带参数的函数fn main() {
      say_hello()
}

fn say_hello() {
     println!("hello,world");
}
带参数的函数fn main() {
    let name = "Alice";
    greet(name);
}

fn greet(name: &str) {
    println!("Hello, {}!", name);
}
返回值的函数fn main() {
    let result = add(5, 3);
    println!("The result is {}", result);
}

fn add(x: i32, y: i32) -> i32 {
    x + y // 这里没有分号,所以这是返回值
}
函数中的表达式1.表达式是什么
    1.表达式是计算某个值的代码片段 
    2.表达式不以分号(;)结束
    3.如  x+1 就是表达式

2.举例:
fn calculate(x: i32, y: i32) -> i32 {
    let z = x * y;
    z + 10  // 这里是返回值
}
控制流 1.函数中可以使用 if  loop for  while等循环
fn main() {
    let number = 6;

    if number % 4 == 0 {
        println!("number is divisible by 4");
    } else if number % 3 == 0 {
        println!("number is divisible by 3");
    } else if number % 2 == 0 {
        println!("number is divisible by 2");
    } else {
        println!("number is not divisible by 4, 3, or 2");
    }
}   
函数中使用闭包fn main() {
    let plus_one = |x: i32| -> i32 { x + 1 };
    let result = plus_one(5);
    println!("The result is {}", result);
}
条件语句1.if语句
if number < 5 {
} else {

}

rust中条件表达式必须是bool类型
  let number = 3;
  if number {}    会报错

if <condition> {block 1} else { block 2 }  block1和block2是函数体表达式
let number = if a > 3 { 0 } else { 1 }

2.if let语句:用于处理模式匹配,它允许你将一个特定的模式与一个值进行比较,如果匹配,则执行对应的代码块
let some_value = Some(3);

if let Some(3) = some_value {
    println!("three");
} else {
    println!("not three");
}
循环rust循环 1.while循环 
   1.while number != 4 {}
   2. while i < 10 {
          i += 1;
      }

2.for循环
   let a = [10,20,30,40]
   for i in a.iter(){
      println !("值为:{}",i)
    }

   //通过下标访问数组
   for i in 0..5 {
     println!("a[{}] = {}",i,a[i])
   }

3. loop循环 
  1.无限循环  
  2.loop {}  会一直执行 直到遇到break 
    let mut counter = 0;

    loop {
        counter += 1;
        println!("Counter: {}", counter);

        if counter == 10 {
            break;
        }
    }
迭代器迭代器1.高效便利数据的方式
2.不需要管理或者循环索引
3.迭代过程中不能修改元素
如何创建迭代器1.创建借用迭代器
     let  vec = vec![1,2,3.4]
     let  iter = vec.iter();
2.创建可变借用迭代器(允许在遍历过程中修改集合元素 )
     let mut vec = vec![1,2,3,4,5]
     let iter_mut = vec.iter_mut();
     for num in iter_mut {
        *num *= 2; //修改每个元素
     }
3.创建所有权迭代器
    let vec = vec![1,2,3,4.5]
    let into_iter = vec.into_iter();
    for num in iter {
        println!("{}",num);
    }
    // 下面的代码会导致编译错误,因为 `vec` 已经被 `into_iter` 消耗了
    // println!("{:?}", vec); // 编译错误:borrow of moved value
常见的迭代器方法 1.使用map和filter
  fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 创建一个迭代器,对每个元素乘以 2
    let doubled: Vec<i32> = numbers.iter().map(|x| x * 2).collect();  //collect:将迭代器中的元素收集到一个集合中

    // 创建一个迭代器,筛选出偶数
    let even_numbers: Vec<i32> = numbers.into_iter().filter(|x| x % 2 == 0).collect();

    println!("Doubled: {:?}", doubled);
    println!("Even numbers: {:?}", even_numbers);
  }

2.使用fold计算总和
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 使用 fold 计算总和
    let sum: i32 = numbers.iter().fold(0, |acc, &x| acc + x);  //fold:将迭代器中的元素累积成一个单一的值

    println!("Sum: {}", sum);
}

3.迭代器的链式调用
fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 链式调用:筛选出偶数,然后对每个元素乘以 2,最后收集到一个 Vec 中
    let result: Vec<i32> = numbers.into_iter()
                                  .filter(|x| x % 2 == 0)
                                  .map(|x| x * 2)
                                  .collect();

    println!("Result: {:?}", result);
}
闭包闭包定义 声明 使用1.闭包是什么
    1.闭包允许在其定义的作用域之外访问变量 
2.闭包如何声明
    let a = | 传递给闭包的参数 | -> 闭包返回的类型 {
        闭包的实现
    }
3.如何调用闭包
   像调用函数一样被调用 let result = 函数名(参数1,参数2)
4.闭包可以使用闭包外的环境变量
    let x = 5;
    let square = |num| num * x
    println !("{}",square(3))  //输出15
5.获取所有权:获取到外部变量的使用权后,闭包可以访问该变量,外部不能访问
  let s = String::from("hello")
  let print_s = move || println !("{}",s )  //move:获取变量的所有权 而不是借用它们
  print_s()
  //prilntln !("{}",s ) //这行代码会报错,因为s的所有权已经转到了闭包

 
闭包作为参数6.闭包如何作为参数
  1.定义了函数,它可以接收一个泛型参数,这个泛型参数可以是任何类型
  2.但不能使用这个参数,因为它还不是一个具体类型
  3.需要给泛型 取一个名字 比如F
  4.rust编译器不知道F是什么类型,它需要一些帮助来解释F是什么 这就是泛型约束
 
 fn call_fn<F>(f: F) where F: Fn() {  //<F>:泛型参数的声明    where F:Fn()   F必须实现Fn() trait
    f();F
}

 
闭包和错误处理
7.闭包和错误处理
  1.错误处理 
     fn divide(a: i32, b: i32) -> Result<i32, String> {
         if b == 0 {
            return Err("Divisionby zero".to_string());
         }
        Ok(a / b)
     }
  2.闭包的错误处理
       .filter(|x| x % 2 === 0 )
       .collect::<Result<Vec<_>,String>>()?
  解释
      .collect::<Result<Vec<_>,String>>()
      .collect:Vec类型的一个方法,将迭代器中的元素收集到一个向量中
      ::作用域解析运算符 访问结构体 枚举 模块 和关联函数的成员
     <>  指定泛型参数的类型约束
     Result<>  result类型,包含两个参数
     Vec:可变动态数组
     <_>: 定义泛型类型的参数 
     _:类型参数占位符
闭包和多线程8.闭包和多线程
    1.在不同线程中使用共享数据和执行任务
    如何在多线程中使用闭包
       1.使用move关键字
            fn main(){
                let s = String::from("hello")
                let handle = thread::spawn(move || {
                    println!("{}",s);
                })
                handle.join().unwrap();
            }
闭包和生命周期 生命周期:生命周期用于确保引用始终有效
1.借用环境中的变量 
   let x = 5;
   let print_x = || println!("x: {}",x);
   print_x();
2.可变借用环境中的变量
   let mut y = 10;
   let mut add_to_y = || y+=1
   add_to_y();
   println!("y:{}",y);
3.所有权转移
  let z = String::from("hello")
  let move_z = move || println!("z:{}",z);
  move_z();
闭包的类型1.Fn:表示闭包可以多次被调用 每次调用都会借用环境中的变量
2.FnMut:闭包可以多次被调用 但每次可变地借用环境中地变量
3.FnOnce:闭包只能被调用一次  因为调用时会消耗环境中的变量。这些变量的所有权会被移动到闭包中,因此在闭包外部不能再使用这些变量
所有权 所有权是什么  1为内存安全和并发提供了强大的保障  
2.同时避免了垃圾回收带来的性能开销
所有权的规则 1.rust中的每个值都有一个变量,称其为所有者 
2.一次只能有一个所有者    
3.所有者不在程序运行范围时,该值直接被删除
变量范围  变量范围是变量的一个属性,其代表变量的可行域
内存和分配 栈:已知且固定大小的数据,分配到栈上
堆:  大小未知或者可能变化的数据,比如string类型,rust分配到堆上
变量与数据交互的方式1.移动:想象下传球,你把球传给另一个人 你就没有这个球的了
  let a = String::from();   //a给了b  a无效了
  let b = a;
2.克隆:你得到和别人一摸一样的球
  let c = Photo::new("我的")
  let d = phtot.clone()
3.拷贝:有些东西很轻 你可以轻松复制很多份
  let  e = 123456
  let f = e  // e 和 f都有效
4.不可变引用
  let g = String::from("a")
  let h = &g   h可以查看g 但不能修改
5.可变引用
  let i = String::from("a")
  let j = &mut i;   j和i都可以查看和修改
函数返回值的所有权机制1.函数返回值的所有权机制
     1.当一个函数返回一个东西时,如果这个东西是小的(比如数字,布尔),那么函数就像在复印一张纸,给你一张复印件,它还留着原来那张纸
     2.但是如果这个东西比较大(比如一段文件,一个列表),那么函数就直接把东西给了你了,自己不留着,你获取了这个东西的所有权

2.举例  
 fn give_away_list() -> Vec<i32> {
           let my_list = vec![1,2,3]
           my_list  //返回my list
          //  println!("Inside function: {:?}", my_list);  // 错误:使用已移动的值
       }

 fn main(){
     let my_list = give_away_list();  //上面函数中的my_list所有权已经转移到了这里的my_list 所以上面函数打印my list时会报错
     println!("Revicewd list:{:?}",my_list)
 } 
 在give_away_list函数内部再次访问 my_list时会报错,因为my_list的所有权已经给了my_list
   
引用与租借引用
  引用的概念
      像一个标签或指针,指向某个数据在内存中的位置,但并不拥有那个数据
  引用的方式
      通过&符号创建   let a = &value   
租借
     租借的概念
          就像在图书馆看书,你可以租借书阅读,但是这本书不属于你,你看完后还得还书
     租借的两种方式
        1.不可变租借:以只读模式修改借过来的书,不能在书上做任何标记或修改
           1.通过 & 进行不可变租借。如: let a = &b  
        2.可变租借:可以在借过来的书上做标记什么的,但是书最后还要还给图书馆
           1.通过 &mut 进行可变租借。 例: let a = &mut b 
  
垂悬引用垂悬引用:你有个朋友的地址,你打算去找他,但是你的朋友已经搬家了。你手里的地址(指针)还在,
但它指向的房子(数据)已经不再是你的朋友的家了。这时候,你手里的地址就是一个垂悬引用。

举例:
fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String {
    let s = String::from("hello");
    &s
}

很显然,伴随着 dangle 函数的结束,其局部变量的值本身没有被当作返回值,被释放了。但它的引用却被返回,这个引用所指向的值已经不能确定的存在,故不允许其出现。
切片切片的概念引用集合中一段连续的元素 
切片的类型 1.字符串切片:对String类型的引用  如 &str
2.数组切片:对数组或向量的引用 如 &[i32]
如何创建切片1.let array = [1,2,3,4,5]
  let slice = &array[1..4]
切片的工作方式 1.切片不拥有数据 只是对数据的引用
2.切片长度是固定的,一旦创建,其长度不变(和golang不同)
3.可以通过 for 循环遍历切片 
切片的性能 1.使用切片比复制整个数据结构高效
2.切片让函数能接受不同长度的数据序列
    就像一个万能的容器,不管你放进去几个苹果,它都能装得下。不需要为每个苹果数量准备一个专门的容器。  
    举例
       fn print_number(slice: &[i32]) {  
          for number in slice {
              println !("{}", number )
           }
       }
切片的常用方法 len()   返回切片的长度
is_empty()   检查切片是否为空
first()    返回切片第一个元素的引用
iter()  返回切片的迭代器
   let numbers = vec ![1,2,3,4]
   let slice = &number[..]
   let mut iterator = slice.iter()  //获取迭代器
   where let Some(number) = iterator.next(){
     print!("{}",number )
   }
结构体结构体的定义的三种方式1. struct StructName {
      field1: Type1
      field2: Type2
    }

2.元组结构体 (元组:可以包含多个不同类型元组的集合)
  1.字段只有类型,没有名称
  2.因为有些地方用结构体的时候不需要名称 
  3.如何定义
     struct StructName(i32, i32, i32)

3.单元结构体
  1.单元结构体没有字段,类似于空元组(())
  2.单元结构体用于实现某些trait 或作为标记
  struct AlwaysEqual;
  fn main() {
      let subject = AlwaysEqual;
      // 可以在这里实现某些 trait 或使用 subject 作为标记
  }
如何实例化结构体 1.实例化普通结构体
  let point = {x:10, y:20}

2.假如你有一个现有结构体,并且想创建一个新的实例,但只需要更改其中一部分,就可以使用结构体更新语法
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

fn main() {
    let user1 = User {
        username: String::from("john_doe"),
        email: String::from("john@example.com"),
        sign_in_count: 1,
        active: true,
    };

    // 使用结构体更新语法创建新的实例
    let user2 = User {
        username: String::from("jane_doe"),
        email: String::from("jane@example.com"),
        ..user1 // 复制 user1 的其他字段
    };

    println!("User2: {}", user2.username);
}

3.使用new函数实例化
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

impl User {
    // 构造函数
    fn new(username: String, email: String, sign_in_count: u64, active: bool) -> User {
        User {
            username,
            email,
            sign_in_count,
            active,
        }
    }
}

fn main() {
    // 使用构造函数创建结构体实例
    let user1 = User::new(
        String::from("john_doe"),
        String::from("john@example.com"),
        1,
        true,
    );

    println!("User: {}", user1.username);
}

3.元组结构体实例化
struct Color(u8, u8, u8); // 代表 RGB 颜色
struct Point3D(f64, f64, f64);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point3D(0.0, 0.0, 0.0);

    println!("Black color: {}, {}, {}", black.0, black.1, black.2);
    println!("Origin point: {}, {}, {}", origin.0, origin.1, origin.2);
}

4.单元结构体实例化(直接使用结构体名称即可)
struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;

    // 可以在这里实现某些 trait 或使用 subject 作为标记
    println!("Instance of AlwaysEqual created.");
}
结构体的所有权结构体所有权的规则
  1.创建结构体时,就相当于书架上上了新书,这些新书(字段)属于这个书架
     let bookshelf = bookshelf {
        book1:"书1",
        book2:"书2"
     }

  2.赋值和转移, 当你把这个书架(结构体实例)给了别人,别人获得了这个书架和这个书架
上的书,这些东西都不属于你了
     let friendshelf = myshelf   //书架给了你朋友你 不属于你了

  3.使用引用:如果你只想让别人看书架和书,不想给给人,可以用&来借用结构体字段
    fn show_books(bookshelf: &Bookshelf) {
        println!("书架上有:{} 和 {}", bookshelf.book1, bookshelf.book2);
     }
     show_books(&my_bookshelf); // 借用书架上的书来展示,`my_bookshelf` 依然有效
    
如何输出结构体如何输出结构体
 1.在最开头写    #[derive(Debug)]
 2.输出的时候用 {:?}这种格式  println!("rect1 is {:?}", rect1);
结构体方法1.结构体方法写在impl中
2.结构体方法的第一个参数是self 可以是self   &self   &mut self
  &self:借用结构体实例 不允许修改
    &mut self:可变引用 允许修改结构体实例
    self: 移动结构体实例 方法结束后实例不再有效
示例
  1.struct Rectangle {
       width:u32,
       height:u32,
    }
  2.impl Rectangle{
      fn area($self) -> u32 {
          self.width * self.height
      }
    }
结构体关联函数 1.在impl中 却没有 &self参数
2.不需要示例(实例化后的结构体),但需要声明在哪个impl中
#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn create(width: u32, height: u32) -> Rectangle {
        Rectangle { width, height }
    }
}

fn main() {
    let rect = Rectangle::create(30, 50);
    println!("{:?}", rect);
}
枚举类 如何定义使用枚举类1.定义
enum Book{
    Parpery(u32),
    Electronic(String),
}
2.实例化
let book = Book::Parpery(1001)
let ebook = Book::Electronic(String::from("url://..."))
match语法 match action {
  Action::Move {x, y} => println!("移动到({}, {})",x,y),
  Action::Attack => println!("执行攻击"),
  Action::Rest => println!("休息一下"),
}
忽略某些枚举  

不用匹配的情况用 _ 表示 
match action {
    Action::Move {..} => println!("移动到某个位置")
    _ => println!("执行其他动作"),
}

组织管理 箱是什么 箱是编译器编译得最小单位,可以是库或可执行文件
箱中的种类1.库箱:可以被其他项目引用的箱
2.二进制箱:生成可执行问价你的箱
包是什么1.一个包是一个或多个箱的集合。
2.每个包包含一个cargo.toml文件,定义了包的元数据和依赖项
模块是什么 组织代码的工具 将相关功能,类型,宏 分组到一起,并且可以控制他们的可见性
如何使用模块1.定义:mod my_module {}
2.使用定义好的模块  my_module::public_function 
难以发现的模块1.文件名称使用.rs命名时,可以不写 mod xx {}
2.别的文件想引用这个模块的直接 直接 mod xx;
引用标准库 1.rust官方标准库:List of all items in this crate
2.导入标准库使用函数
    use std::f64::consts::PI;

    fn main() {
       println!("{}", (PI / 2.0).sin());
    }
错误处理 1.Result<T, E>(结果)
2.Option<T>(选项)
3.panic!(恐慌)
错误处理的方法fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err("Division by zero is not allowed!".to_string())
    } else {
        Ok(a / b)
    }
}

1.match
   fn main() {
       let result = divide(10, 2);

        match result {
           Ok(answer) => println!("The result is: {}", answer),
           Err(e) => println!("Error: {}", e),
       }
   }

2.if let
   fn main() {
        let result = divide(10, 0);

        if let Ok(answer) = result {
            println!("The result is: {}", answer);
        }
        // 如果是 Err,我们不打印任何东西
    }
3.unwarp 和 expect
  fn main() {
    let answer = divide(10, 2).unwrap(); // 如果是 Err,这里会 panic
    println!("The result is: {}", answer);

    // 使用 expect 提供更明确的错误信息
    let answer = divide(10, 0).expect("Cannot divide by zero!");
    println!("The result is: {}", answer); // 这行不会执行,因为上面会 panic
}

4. 使用?运算符传播错误
fn main() -> Result<(), String> {
    let answer = divide(10, 2)?;
    println!("The result is: {}", answer);
    Ok(())
}

 
kind 分类不同的错误类型
use std::io;
use std::io::Read;
use std::fs::File;

fn read_text_from_file(path: &str) -> Result<String, io::Error> {
    let mut f = File::open(path)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

fn main() {
    let str_file = read_text_from_file("hello.txt");
    match str_file {
        
Ok(s) => println!("{}", s),
        
Err(e) => {
            match e.kind() {
                io::ErrorKind::NotFound => {
                    println!("No such file");
                },
                _ => {
                    println!("Cannot read the file");
                }
            }
        }
    }
}
rust泛型 泛型是什么就像是占位符,写代码的时候用什么类型,先泛型占用下。
等到真的使用时,再根据需要把占位符换成具体类型
为什么需要泛型  比如所 你想写一个函数,两个数相加,参数只能是某个具体类型 。 但如果用泛型,参数可以是任意类型  
泛型怎么用1.泛型函数
  fn print_value<T>(value: T){
    println !("Value is  :{:?}",value);
  }

2.泛型结构体  
  struct Wrapper<T> {
    value: T,
  }

3.泛型枚举 
  enum Option<T> {
    Some(T),
    None,
  }
特性特性是什么 比如说你开了一家餐厅,你需要厨师,但是不是随便一个人能当的。你要求厨师会做饭,
这个 会做饭 就是一种技能 ,就是我们说的
特性
特性怎么用1.定义一个特性:先定义一个技能标准,比如 会做饭 的厨师得会炒菜, 在rust中就会定义一个trait
     例如:定义一个特性 会做饭 
           trait CanCook{
              fn cook(&self, recipe: &str) -> String;
           }
2.让类型实现特性:根据你定义的特性,找到会炒菜的人。让一个类型实现trait
    例如: 让厨师
实现会做饭这个技能中要求的所有方法
       1. 定义一个厨师
            struct Chef {  
               name: String,
             }
       2. 让厨师实现会做饭这个方法
            impl CanCook for Chef {
               fn cook(&self, recipe: &str) ->String {
                 format("{} is Cooking {}", 
self.name, recipe)
               }
            }
特性有啥特别的地方1.自带技能:你这个技能证书里不光有要求,还附赠一些基本技能,比如每个厨师都得会开火
   1.写技能(特性)中的默认方法(默认会的动作)
     trait CanCook {
           //声明一个动作  要求会这个技能的人必须会的动作
           fn cook(&self, recipe:&str) -> String;

          //默认动作 会这个技能的人默认会的动作
          fn light_fire(&self) -> String {
              "Turning on the stove...".to_string()
          }    
     }
   2. 会这个技能的厨师不用不用学也会light_fire动作,所以厨师学习这个技能的时候,不用学习light_fire动作 
      impl CanCook for Chef {
         //厨师需要学习这个动作
         fn cook(&self, recipe: &str) -> String {
              format!("{} is cooking {}", self.name, recipe)
         }
        //厨师不需要学习light_fire这个动作,也能直接用它
      }
    3. 厨师会的动作
     fn main() {
        let chef = Chet {
           name: "Alice".to_string().
        }

        //厨师会cook
        println!("{}", chef.cook("spaghetti"));
       //厨师会 light_fire
        println!("{}", chef.light_fire());
     }
2.一身多技;一个人可以学会很多技能,一个类型也可以实现很多特性。如,一个厨师不光会炒菜,还会烤面包。
  1.有一个烤面包的特性 
   trait CanBake {
      fn Bake(&self, item: &str) -> string 
  }

  2.厨师同时可以继承上面的会炒菜,和 烤面包的特性
   impl CanBake for Chef {
      fn  Bake(&self, item: &str) -> String {
           format!("{} is baking {}", self.name, item)
      }
      
   }
特性在函数里的应用1.特性当参数:如果你要办一个厨师大赛,你不会管厨师是哪里人,长什么样,你只关心他们会不会炒菜。在 Rust 函数里,这就是用 impl Trait 当参数,只要你会炒菜,就能参加比赛。
 1.定义一个厨师大赛的函数 参加这个大赛的人都得是实现CanCook这个特性
    fn chef_competition(chef: impl CanCook){
       println !("{}", chef.cook("gourmet meal"))
    }

2.实现CanCook的厨师参数厨师大赛
  fn main(){
     let chef =Chef {
       name: "Bob".to_string(),
     }

    //假设chef已经实现了CanCook特性
   //chef参加厨师大赛
   chef_competition(chef);
  }
2.特性当返回值:如果你说,我要派一个厨师去参加比赛,这个厨师得会炒菜。这就是一个函数返回一个实现了特定特性的类型,但是你只能派一个厨师,不能一会儿派一个,一会儿又换一个。

  1.定义一个厨师大赛的函数,该函数返回一个会炒饭的厨师 
     fn send_chef_to_competition() -> impl CanCook {
          Chef {
              name: "Alice".to_string(),
          }
      }
  2.调用该函数,得到一个会炒饭的厨师,可以使用该厨师会的动作
     let chef = send_chef_to_competition();
     // 厨师开始烹饪
     println!("{}", chef.cook("souffle")); 
生命周期生命周期的概念生命周期注释:给钥匙上贴了一个标签,写着只能周一到周五使用
  fn main() {
      let message = String::from("hello world") //message的生命周期开始
      let message_ref = &message   

       print_message(message_ref); 
  }

   fn print_message<'a>(message: &'a str){   //引用变量作为参数传递在函数间传递时,需要加 'a 标签(生命周期注释)
      println !("{}", message)
   }
具体知识点1.借用和所有权的区别
  借用:你借用了朋友的车,但是车还是朋友的,你用完要还给他

    fn main(){
        let my_car = Car { model: "Tesla".to_string() }
        drive_car(&my_car)  //借用了car  // drive_car这个函数借用了my_car, 但是my_car还是属于main这个函数
        println !("Finish driving {}",my_car.model)   //main这个函数依旧可以使用car
     }
   
  所有权:你买了一个二手车 可以随用使用,也可以卖掉,完全由你控制。原来的人无法使用
     let my_car = Car { model: "Tesla".to_string() }
     buy_car(my_car)     //buy_car这个函数买了我的车 获得了车的所有权
     println !("{}",my_car)    //我无法继续使用这个车了  buy_car这个函数可以随意使用

 
2.函数中的生命周期
  你开车进了一个临时停车场(函数),车辆(数据)可以进来,但是当停车场关闭(函数结束),所有车辆必
须离开(数据清理)
  // 定义一个函数来处理消息
fn process_message<'a>(message: &'a str) {
    println!("Processing message: {}", message);
}

fn main() {
    let message = String::from("Hello, world!");
   
    // 调用 process_message 函数
    process_message(message.as_str());    //main函数时message.as_str()的所有者,因此main函数结束时,其数据被清理
}
3.结构体中的生命周期
  1.有一个房间放有多个保险箱(每个保险箱是一个结构体),这个房间里的保险箱钥匙的有效期(结构体字段)必须保留到房间
钥匙的有效期  
struct Book<'a> {
      content: &'a str  结构体字段的生命周期注释
}
impl<'a>Book<'a>{
  fn new(title: &'a str) ->self { //实例化该结构体
     Book {title}
  }

 fn title(&self) -> &str {  //返回结构体的title
        self.title
  }
}

fn main() {
    let title = String::from("Rust Programming");
    let book = Book::new(&title); // `book` 借用了 `title` 的一个切片

    println!("The book title is: {}", book.title());
    // 当 `title` 在这里仍然有效时,`book` 可以安全地使用 `title` 的引用。
}
文件与io文件读取 1.一次性读取整个文件
    1.如果文件不是很大,可以读取整个文件到内存中, 通过 std::fs::read_to_string 函数实现
          let  content = fs::
read_to_string("/path/to/file.txt").expect("Failed to read file");
          println!("File contents: {}",content)

2.逐行读取
    let file = File::
open("path/to/file.txt").expect("Failed to open file");
    let reader = 
BufReader::new(file);

    for line in reader.lines() {
        let line = line.expect("Failed to read line");
        println!("{}", line);
    }
文件写入 1.写入文本
       let data = "hello world"
       fs:
:write("path/file.txt",data).expect("failed to wirte file")    //fs:file system 的缩写

2.更复杂的写入:通过std::io.Write trait来写入
   use std::fs::File;
   use std::io::{self, Write};  // 引入必要的模块
   fn main() {
      let mut file = File::
create("path/to/output.txt").expect("Failed to create file"); //创建文件句柄
     
 writeln!(file, "Hello, Rustaceans!").expect("Failed to write to file");}
创建文件夹 通过 std::fs::create_dir_all 创建文件夹
    fs::
create_dir_all("path").expect("failed")
删除文件通过 std::fs::remove_file 删除目录
    fs::
remove_file("path").expect("failed")
删除空目录通过std::fs::remove_dir 删除空目录
    fs::
remove_dir("path").expect("failed")
删除非空目录需要递归删除子目录和文件,通过 fs_extra来支持
   extern 
crate fs_extra;
   use fs_extra::dir::
remove_dir_all;
   fn main() {
       remove_dir_all("path/to/non_empty_directory").expect("Failed to remove directory recursively");
   }
集合与字符串向量  1.向量是什么
  1.存放
多值的单数据结构,该结构将相同类型的值线性的存放在内存中
      
线性:在内存中以连续的方式排列 

2.向量如何定义 
  1.声明一个
空的向量
     let v :Vec<i32> = Vec::new();   //创建类型为i32的空向量
        
vec:vector 表示动态增长或缩小长度的数组
  2.通过
数组创建向量
     vec = vec![1,2,3,4]  //通过数组创建向量
        
vec:宏的名字
        
!:告诉rust,这是一个宏调用
       
 [1,2,3,4] 传给宏的参数,传给宏一组数字,宏会帮我们创建一个包含这些数字的向量

3.如何往向量中添加元素
   let mut vector = vec![1,2,4,8]
   vector.
push(16)

4.将一个向量拼接到另一个向量的尾部
   let mut v1: Vec<i32> = vec![1,2,3,8]
   let mut v2: Vec<i32> = vec![16,32,64]
   v1.
append(&mut v2);
5.获取向量中的某个值
   1.通过get方法获取 它返回一个
option
        let mut v = vec![1,2,3,4,5]
       println!("{}", 
match v.get(0)) {
           
Some(third) => println!("This third element is :{}",third),
           
None => println("There is no third element."),
       }
   2.通过
索引
       let v = vec![1,2,3,4,5]; 
       let third = v[2];
       注意:如果尝试访问一个不存在的索引,程序将会
panic并崩溃

字符串
1.字符串的类型
    1.str:不可变,
固定长度的字符串切片,通过以 &str形式出现
    2.String:一个
可增长的 堆分配的字符串类型。
   
 str:就像一本书的内容,你只读,不可修改,也不能带走(拥有数据)
    
String:就像自己的笔记,可以在上面写字,也可以随意携带

2.字符串字面量
   let s = "Hello, world";
 
3.创建字符串  
  1.创建一个空的字符串
      let mut s =
 String::new();
  2.用 to_string()  或 string::from() 将字符串切片转为String
     let s1 = 
"Hello".to_string()

4.字符串修改
  1.使用
push_str() 和 push() 方法向 String 添加文本
      let mut s = String::from("Hello")
      s.push_str(", world!);
      s.push("!")
  2.使用 + 或者 format! 宏来拼接字符串
     let s1 = String::from("Hello, ");
     let s2 = String::from("world!");
     let s3 = s1 
&s2
     s1被移动:s1的所有权被转移
     s2被借用:s2暂时被借用 但是所有权还在 
5.如何访问字符串中的字符
    1.不允许通过索引直接访问字符串中的字符  如 let h = s[0]
    2.使用 
chars()  方法迭代字符串中的字符
       for c in  "你好 世界".
chars() {
          println!("{}",c);
       }
    3.使用bytes()方法迭代字符串中的字符
       for b in "你好 世界".
bytes() {
          println!("{}",b)
       }

6.字符串切片
    1.允许使用字符串切片来引用字符串的一部分  但必须确保切片的边界在字符串边界
      let s = "你好 世界"
      let Slice = 
&s[0..3]

7.Rust字符串与UTF-8
     1.rust的String类型保证是有效的
UTF-8 
     2.rust的char类型代表一个Unicdoe标量值,意味着可以表示比ASCII更广泛的字符 
         ASCII:只包含128个字符,包括英文字母 数字和 一些特殊符号
         Unicode:可以表示ASCII中的字符,也可以表示ASCII之外的字符 如中文的 ”汉“  日语,或表情符号
映射表 1.创建hashMap
   use std::collections::HashMap;
   let mut map = HashMap::
new()

2.向hashMap中插入元素
    map
.insert(String::from("key1"),10)

3.访问元素
   使用get方法通过键来访问值
   let value = map.
get("key1")

4.更新已存在的元素
   1.map.
insert(String::from("key1"),20)
   2.map
.entry(String::from("key1")).and_modify(|e| * e +=1);
      |e| *e +=1是一个闭包的写法
        1.|e|:定义闭包, |e|表示闭包接收一个参数e 在上面例子中,e是一个可变引用,指向hashMap中与特定键关联的值
        2. *e: e是一个可变引用,需要使用 * 来访问它指向的实际值
        3. *e +=1: 将e指向的值增加1 
5.如何遍历hashMap
    1.使用for循环遍历 hashMap中所有键值对
       for (key,value) 
in &map{
          println!("{}:{}",key,value)
       }

6.哈希函数
   1.默认情况下,hashMap使用SipHash算法作为哈希函数,但可以通过指定不同的hasher来改变这一点
      use std::collections::HashMap;
      use std::collections::hash_map::DefaultHasher;
      let mut map = HashMap:
:with_hasher(DefaultHasher::new());

7.性能
   1.时间复杂度:平均情况下,插入 删除 查找的时间复杂度为
O(1)
   2.空间复杂度:HashMap 通常比存储同样数量元素的 Vec 需要更多的空间,因为它需要存储额外的哈希表信息
面向对象封装1.可以使用结构体struct来封装数据,使用关联函数和方法实现对数据的操作
  例如
      struct Rectangle {
          width: u32,
          height: u32,
      }

    impl Rectangle {
         fn new(width: u32, height: u32)-> Rectangle {
               Rectangle { width, heoght}
         }

      fn area(&self) -> u32 {
         self.width * self.height
      }
继承1.rust不支持继承 但是可以通过其他方式实现继承
2.rust实现继承的方式
  1.组合 
     1.比喻:一个学生有一个书包,则学生类可以继承书包类型的字段 
     2.代码示例
        //1.定义Book结构体
         struct Book {
            title: String,
            author: String,
         }

        //2.Book的方法
        impl Book {
           // Book自己的方法
           fn print_details(&self) {
              println!("Title: {}, Author: {}", self.title, self.author);
           }
         }

        //3.liabrary用book实例
        struct Labrary {
           book:Book
        }

      

       
       //4.实现library的构造函数
              impl Library {
                 fn new(book: Book) -> Library {
                     Library {book}
                 }
              }

      //5. main函数中使用library和book
     fn main() {
          // 创建一个Book实例
          let my_book = Book {
              title: String::from("Rust编程之道"),
              author: String::from("张三"),
          };

          // 使用Book实例来创建Library实例
          let my_library = Library::new(my_book);

          // 通过Library实例直接调用Book实例的print_details方法
          my_library.book.print_details();
}
3.使用使用trait实现继承
// 定义一个 Trait,描述用户的基本行为
trait UserBehavior {
    fn display_info(&self);
}

// 实现 UserBehavior Trait
impl UserBehavior for User {
    fn display_info(&self) {
        println!("Username: {}, Email: {}", self.username, self.email);
    }
}

impl UserBehavior for Admin {
    fn display_info(&self) {
        println!("Username: {}, Email: {}, Admin Level: {}", self.user.username, self.user.email, self.admin_level);
    }
}

// 用户信息结构体
struct User {
    username: String,
    email: String,
}


 
impl User {
    pub fn new(username: String, email: String) -> User {
        User { username, email }
    }
}

// 管理员结构体,包含用户信息
struct Admin {
    user: User,
    admin_level: u8,
}

impl Admin {
    pub fn new(username: String, email: String, admin_level: u8) -> Admin {
        let user = User::new(username, email);
        Admin { user, admin_level }
    }

    pub fn manage_users(&self) {
        println!("Managing users...");
    }
}
// 泛型函数,接受实现了 UserBehavior Trait 的引用
fn display_info_for_anyone<T: UserBehavior>(person: &T) {
    person.display_info();
}

fn main() {
    let user = User::new(String::from("user"), String::from("user@example.com"));
    let admin = Admin::new(String::from("admin"), String::from("admin@example.com"), 5);

    display_info_for_anyone(&user);
    display_info_for_anyone(&admin);
}
并发编程 rust并发编程的几个概念1.线程
     1.线程是操作系统调度的基本单位,可独立执行任务
     2. rust提供了 std::thread 模块,用于创建和管理线程

2.所有权和借用
     1.rust通过所有权和借用机制来确保内存安全
     2.在并发环境下,所有权和借用规则有助于防止数据竞争等问题 

3.原子操作
     1.原子操作可以在
多线程下安全更新共享数据
     2.使用 std::sync::atomic 实现原子操作

4.通道
      1.通道提供一种在
多线程间传递消息的机制,有助于实现无锁编程
      2.rust提供了 std::sync::mpsc模块,用于实现线程间的通信
          mpsc:Multiple Producer ,Single Consumter  
多个生产者线程发送消息到一个 通道,单个消费者线程接受该通道信息

5.Arc(Atomic Refrence Counting)
      1.Arc可以在多个线程间共享数据 同时提供
引用计数来管理生命周期
      2.rust提供了 std::sync::Arc 用于实现多线程环境下智能指针
      解释
        1.什么是自动引用计数
            每创建一个对象,系统会该对象分配一个计数器,每当有一个新的引用指向该对象时,计数器就会增加
            当引用不再存在时,计数器就会减少 
rust并发编程示例_创建线程use std::thread;
use std::time::Duration;

fn main() {
    println!("Hello from the main thread!");

    // 创建一个新的线程
    let handle = thread::
spawn(|| {      //spawn:生成,创建。它接受一个必包作为参数,定义了新线程将要执行的代码
                                           //     || : 作为必包的参数分隔符   ||表示该必包不接受任何参数
        for i in 1..10 {
            println!("Hello from the spawned thread! {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    // 等待新线程结束
    handle.join()
.unwrap(); //unwarp:解包,展开

    println!("Back to the main thread!");
}
rust并发编程示例_使用通道传递消息use std::sync::mpsc;
use std::thread;

fn main() {
    // 创建一个通道
    let (tx, rx) = 
mpsc::channel();

    // 创建一个线程来发送消息
    let tx1 = tx.
clone();
    thread::spawn(
move || { //move:当一个必包通过move关键字捕获变量时,它将取得这些变量的所有权
        let vals = vec![   //vec! 创建向量
            String::from("hello"),
            String::from("world"),
        ];
        for val in vals {
            tx1.
send(val).unwrap();
            thread::sleep(std::time::Duration::from_secs(1));
        }
    });

    thread::spawn(move || {
        let vals = vec![
            String::from("foo"),
            String::from("bar"),
        ];
        for val in vals {
            tx.send(val).unwrap();
            thread::sleep(std::time::Duration::from_secs(1));
        }
    });

    // 接收消息
    for received in
 rx {
        println!("Got: {}", received);
    }
}
rust并发编程示例_使用Arc实现共享数据use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let counter = 
Arc::new(Mutex::new(0)); //创建共享计数器   用 Arc 来允许这个互斥锁的所有权在多个线程之间共享
         //1.Mutext::new(0) :创建一个互斥锁,它内部包含了一个初始值为0的整数
               //比喻:你有一个数字(初始值是 0),并且你把它放在一个保险箱(互斥锁)里,这样只有拥有钥匙(锁)的人才能打开它并修改里面的数字。
         //2.Arc::new:增加引用计数,每个线程都有自己的数据引用
               //比喻:然后,你制作了这个保险箱的多个副本(使用 Arc),每个副本都可以被一个线程安全地使用,而原始的数字始终保持一致和安全。

    let mut handles = 
Vec::new();   //存储每个新创建的线程的句柄
          //1.Vec::new() 创建了一个空的动态数组。动态数组可以存储一系列的元素,并且可以根据需要
自动增长或缩小
          //比喻:创建了一个名为 handles 的新盒子(变量),并且这个盒子是可变的,意味着你可以往里面放东西或者把里面的东西拿出来。
                    //这个盒子(handles)目前是空的,但它是专门用来存放“句柄”的。

    for _ in 0..10 {
        let counter_clone = 
Arc::clone(&counter); // Arc::clone 方法来增加 Arc 的引用计数,这样每个线程都可以拥有 counter 的一个引用
        let handle = thread::spawn(move || {
            let mut num = counter_clone.lock().unwrap(); 使用 lock 方法来获取互斥锁,这会阻塞当前线程直到锁可用
            *num += 1; //一旦获取了互斥锁,就增加包装在互斥锁内部的计数器的值
        });
        handles.push(handle); //将新线程的句柄添加到 handles 向量中。
    }

    for handle in handles {
        handle.join().unwrap(); //使用 join 方法等待每个线程完成
    }

    println!("Final count: {}", *counter.lock().unwrap()); //再次获取互斥锁,打印出最终的计数器值
    //unwrap():这个方法用于处理 Result 类型。在这个上下文中,lock() 方法返回的是一个 Result 类型,它可以是 Ok,表示成功获取了锁,或者 Err,表示在尝试获取锁时发生了错误。unwrap() 方法会检查 Result,如果是 Ok,它会返回里面的值(在这个例子中是锁住的计数器),如果是 Err,程序将会 panic(即异常终止)
}
Arc上述代码解释(比喻) 想象一下,你有一个特殊的计数器(就像一个可以计数的魔法盒子),这个计数器放在一个保险箱里,这样只有拥有钥匙的人才能打开它并改变里面的数字。现在,你想让10个朋友在不同的房间里同时增加这个计数器的值,而且每个人只能增加一次。

以下是这段代码的步骤:

创建一个计数器和一把锁:
let counter = Arc::new(Mutex::new(0));
这行代码创建了一个初始值为0的计数器,并且把它放在一个互斥锁(Mutex)里,这样可以确保一次只有一个朋友能改变计数器的值。
然后,使用 Arc(原子引用计数)来创建这个计数器的一个安全副本,这样多个朋友(线程)都可以拥有这个计数器的引用,而不会担心数据的安全问题。
准备一个空列表来存放朋友的遥控器(线程句柄):
let mut handles = Vec::new();
这行代码创建了一个空的列表,用来存放每个朋友房间的遥控器。遥控器可以用来在最后确认朋友是否已经完成了增加计数器的任务。
创建10个朋友(线程)来增加计数器的值:
for _ in 0..10 { ... }
这个循环会执行10次,每次都创建一个新的朋友(线程)。
在循环内部,每个朋友都获得了一个计数器副本的遥控器(Arc::clone(&counter)),并且通过 thread::spawn 创建了一个新的线程。
在新线程中,朋友会先打开保险箱(counter_clone.lock().unwrap()),然后增加计数器的值(*num += 1;),最后保险箱会自动上锁。
等待所有朋友完成增加计数器的任务:
for handle in handles { handle.join().unwrap(); }
这行代码遍历列表中的所有遥控器,并使用 join 方法等待每个朋友完成他们的任务。这就像是在每个房间外等待,直到朋友告诉你他们已经完成了增加计数器的任务。
打印最终的计数器值:
println!("Final count: {}", *counter.lock().unwrap());
最后,打开保险箱,查看计数器的最终值,并打印出来。这个值应该是10,因为每个朋友都增加了1。
总的来说,这段代码展示了如何在 Rust 中使用多线程安全地增加一个共享计数器的值。
rust宏什么是宏 宏是编译时自动生成代码的技术。
   比喻:想象一下,你在做饭时有一个食谱,这个食谱告诉你一步一步做菜。宏就像一个食谱,它告诉计算机如何生成一段代码 
宏的类型1.声明式宏
   作用:让你用一种模板的方法生成代码,可以把它想象成填空题
   例子:
       macro_rules! max {
           ($x:expr, $y:expr) => {
               if $x > $y { $x } else { $y }
           };
       }
      let m = max!(3, 5); // 输出结果将是 5
      在这个例子中 max!就是一个填空题,你只需要填两个数字,它就会帮你生成一个if语句来比较两个数字,并返回较大的那个 
2.程序式宏
   作用:可以生成rust代码的函数,可以把它想象成一个“智能食谱”,这个食谱不仅告诉你怎么做菜,还能根据不同的食材自动调整做法
   例子:
     // 引入必要的 crate
     extern crate proc_macro;

     use proc_macro::TokenStream;
     use quote::quote;
     use syn::parse_macro_input;

     #[proc_macro_derive(Counter)]       //标记表明这是一个过程宏  用于派生(derive)宏,并且它的关联的宏的名称是Counter
     pub fn derive_counter(input: TokenStream) -> TokenStream {
         // 解析输入的结构体定义
         let input = parse_macro_input!(input as syn::DeriveInput); //将输入的 TokenStream 解析为 syn::DeriveInput 类型
 
         // 根据解析得到的信息生成代码
         let name = &input.ident;
         let gen = quote! {   //quote! 宏用于生成 Rust 代码
             impl #name {  //生成了一个 impl 块,这个块为结构体实现了一个名为 count_fields 的方法
                 fn count_fields() -> usize {
                     // 返回结构体字段的数量
                     5 // 假设结构体有五个字段
                 }
             }
         };

         // 返回生成的代码    将生成的代码转换回 TokenStream 并返回它
        gen.into()
}

#[derive(Counter)]  //告诉 Rust 编译器使用我们定义的 Counter 过程宏来为 MyStruct 结构体生成代码
struct MyStruct {
    field1: u32,
    field2: String,
    // ...更多字段
}
如何使用宏 1.直接调用: max!(3, 5)
2.作为属性:#[derive(Counter)] 作为结构体定义的一部分
使用宏的注意事项 1.错误信息不友好:宏是在编译中运行的,如果宏有错误,错误信息可能不如rust代码那么清晰
2.过度使用造成复杂性
智能指针 Box<T>概念:简单的智能指针 用于在堆上分配数据 
比喻:想象你有一件很重的东西(比如一大本书),你不想一直拿着它。所以,你把它放在一个盒子里(Box),然后只拿着盒子的标签(指针)。这样,你只需要记得标签的位置,就能找到那件东西。
用途:
  1.当你需要在堆上分配数据,而不是在栈上时 
     //堆和栈的区别:
        栈:操作系统
自动管理   存储函数的局部变量。 就像一摞盘子,用完就放回 
        堆:程序员
手动管理   存储程序运行动态分配的内存 如对象 数组。 就像一个仓库, 东西可以随意存放,但需要自己收拾
  2.当类型的实际大小只有在运行时才能确定
  3.想要将大对象的所有权转移给函数,但又不想复制数据 
Rc<T>概念:引用计数,允许多个所有者共享同一数据 
比喻:假设你和你的朋友都想读同一本书,但是你们不想每个人都购买,于是你们决定一起买一本。每个人都有个借阅卡Rc<T>,当你或者你朋友想读书时,
拿出借阅卡,当所有人都读完并且还借阅卡后,这书才会被处理掉
用途
  1.在单线程环境中共享数据
  2.希望
多个所有者可以访问同一个数据,但不需要同时修改它
例子
  use std::rc::Rc;
  let a = Rc::new(5);
  let b = Rc::clone(&a);
  println!("a = {}, b = {}", a, b);
Arc<T>概念:是Rc<T>的线程安全版本,全程是Atomic Reference Counting 它与Rc<T>类似,但可以安全的多线程间共享数据 
比喻:假设你和你朋友不仅想在同一本书上做笔记,还想同时做这件事情。为了安全起见,你们决定使用一种特殊的借阅卡Arc<T>  这种卡可以确保多人使用
也不会出错
用途
  1.需要在
多个线程中共享数据
  2.希望
多个线程可以安全地访问同一个数据
例子
  use std::sync::Arc;
  use std::thread;
 
  let a = Arc::new(5);
  let b = Arc::clone(&a)

  let handle = thread::spawn(move || {
     println!("b in thread = {}",b )
  })
RefCell<T>概念:提供运行检查时所有规则;通常情况下rust所有权规则是在编译时检查的,但是RefCell<T>允许你在运行时改变数据,即使在不可变引用存在的情况下
比喻:假设你有一本图书馆的书,图书馆规定你不能在书上做笔记。但是,你发现了一个秘密房间(
RefCell<T>),在这个房间里,你可以自由地在书上做笔记,只要没有人发现。如果有人发现了,程序会报错。
用途: 
  1.当你需要在不可变引用存在的情况下修改数据
  2.当你需要在运行时动态地检查借用规则
例子:
  use std::cell::RefCall;
  let c = RefCell::new(5);
  {
     let mut m = c.borrow_mut();
     *m +=1
  }
  println!("c = {}", c.borrow()); // 获取不可变引用
Cell<T>概念:允许你修改内部地值,即使在不可变引用地情况下,但它只能用于被复制地类型(即实现 Copy 或 Clone地类型)
比喻:想象你有一块橡皮擦,你可以在任何时候使用它擦掉写下的字,即使书已经在别人手上。
用途:需要在
不可变引用存在的情况下修改数据,但数据类型可以复制
例子:
   use std::cell::Cell;

   let d = Cell::new(5); // 创建一个 Cell
   d.set(6); // 修改数据
   println!("d = {}", d.get()); // 获取数据
UnsafeCell<T>概念:是 Cell<T> 的基础,它允许你修改任何类型的值,但使用它时必须非常小心,因为它绕过了 Rust 的所有权和生命周期系统,可能会导致不安全的行为
比喻:想象你有一个魔法盒子,你可以随时修改里面的东西,但如果你不小心,可能会引发灾难
用途:
  1.当你需要在不可变引用存在的情况下修改任何类型的数据,但需要非常小心以避免不安全的行为
异步编程rust异步编程的基础概念 1.异步函数:使用async关键字定义一个异步函数,异步函数返回一个实现了Future特性的类型,这个Feature类型代表一个可能还未完成的计算
   Feature特性的类型:rust的Feature就像一个将来会完成的任务 或者 一个将来会实现的承诺
2.Future: 代表异步操作最终的结果。 
3.轮询:由异步运行(tokio 或 async-std)来管理  
4.任务调度器:管理多个任务执行 
异步编程的基本结构 1.定义异步函数
  async fn my_async_function() -> Result<String, std::io::Error> {
     Ok("Hello World".to_string())
  }

2.调用异步函数
  let result = my_async_function().await;
  解释:
    1.调用异步函数 实际上是在创建 Future
    2.Future代表了一个异步操作,但它本身不会立即开始执行
 
3.运行异步代码
   #[tokio::main]
   async fn main(){
      let result = my_async_function().await
      println!("{}",result.unwrap());
   }
  解释:
      1.要使第二步的Future开始执行,必须放入一个异步运行(Tokio,Async-Std等)
      2.异步运行时负责调度和执行这些Future 
异步流1.什么是异步流
  1.异步流就像是一个流水线,会不断给你发过来数据。而你每收到一条数据,就可以马上处理这条数据
  2.不需要等所有数据都到齐了再处理

2.为什么使用异步流
  1.节省内存:不一次性把所有数据放到内存
  2.提高效率:尽早处理已经接收的数据 不用等待所有数据都准备好

3.示例
  #[tokio::main]
  async fn main() {
     let mut message = receive_message();  //receive_message返回一个异步流 
    
      while let Some(message) = messages.next().await { //每次接收到一条消息,就可以立即处理这条消息
        match message {
            Ok(msg) => println!("收到消息: {}", msg),
            Err(e) => eprintln!("出错: {}", e),
        }
    }
  }
rust实现并发编程的方式1.使用 tokio::spawn 启动新的异步任务
2.使用 tokio::join! 等待多个任务完成
并发编程的示例1.定义异步函数
   use reqwest;

   async fn download_data(url: &str) -> Result<String, reqwest::Error> {
      let response = reqwest::get(url).await?;  //发起网络请求 使用.await等待请求完成
      response.text().await  //获取响应的文本内容
   }

2.启动多个异步任务
use tokio;

#[tokio::main]
async fn main() {
    let urls = vec![   //包含需要下载的URL
        "https://example.com/data1",
        "https://example.com/data2",
        "https://example.com/data3",
    ];

    let tasks: Vec<_> = urls.into_iter()
        .map(|url| tokio::spawn(download_data(url)))  //map方法遍历urls,每个url调用tokio::spawn启动一个新的异步任务
        .collect();  //返回JoinHandle  讲joinHandle收集到一个向量tasks

3.等待所有任务执行
    for task in tasks {
        match task.await { //返回一个Result<_,_> 表示任务的执行结果
            Ok(result) => match result {
                Ok(data) => println!("下载完成: {}", data),
                Err(e) => eprintln!("下载出错: {}", e),
            },
            Err(e) => eprintln!("任务出错: {}", e),
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

simper_zxb

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值