Rust精简核心笔记:第三波,基础语法完结篇

今天是Rust精简核心笔记第三波,也是完结篇。之前已经介绍了二波,Rust精简核心笔记:第一波,深入浅出语法精华-CSDN博客Rust精简核心笔记:第二波,语法精髓部分解锁-CSDN博客,通过三波分享把Rust的核心语法快速进行掌握,也方便进行知识点回顾和速查的左右。

这些精简语法虽可以快速的掌握核心知识点,但如果深入学习和应用还需要结合项目进行实战和练习(更多最新文章也可关注微信公号: 良技漫谈)

Rust精简笔记(三)

  • 第三波主要包括函数&闭包&迭代器、指针&智能指针、引用模块的相关介绍

  • 参考The Rust Programming Language & Rust in Action

十,函数、闭包、迭代器

函数:
  • 函数的定义方式及在结构体实现里关联函数,关联函数与方法的使用区别

    use std::primitive;

    struct Point {
    x: i32,
    y: i32,
    }
    impl Point {
    // 关联函数(没有self相关参数)
    fn new(x: i32, y: i32) -> Point {
    Point { x: x, y: y }
    }
    // 方法(参数为&self,是个隐示的,调用时无需传递表明是该类型而已)
    fn get_x(&self) -> i32 {
    self.x
    }
    }
    fn main() {
    //关联函数使用:: 方法使用类型.方法,如Point::new, point.get_x
    let point = Point::new(5, 6);
    println!("get x={}", point.get_x());
    }

闭包:
  • **闭包(closures)**是可以保存在一个变量中或作为参数传递给其他函数的匿名函数。闭包的定义以一对竖线(|)开始,在竖线中指定闭包的参数

    fn add_one_v1 (x: u32) -> u32 { x + 1 } //函数的定义
    let add_one_v2 = |x: u32| -> u32 { x + 1 }; // 完整标注的闭包定义
    let add_one_v3 = |x| { x + 1 }; // 闭包定义中省略了类型注解
    let add_one_v4 = |x| x + 1 ; // 闭包体只有一行,去掉了大括号

  • 闭包会捕获其环境:

  • 可以捕获其环境并访问其被定义的作用域的变量。如下边 x 并不是 equal_to_x 的一个参数,equal_to_x 闭包也被允许使用变量 x,因为它与 equal_to_x 定义于相同的作用域

    fn main() {
    let x = 4;
    let equal_to_x = |z| z == x;
    let y = 4;

      assert!(equal_to_x(y));
    

    }

  • 当闭包从环境中捕获一个值,闭包会在闭包体中储存这个值以供使用,这会使用内存并产生额外的开销。

  • 闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。

    FnOnce 消费从周围作用域捕获的变量,闭包周围的作用域被称为其 环境,environment。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的 Once 部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次
    FnMut 获取可变的借用值所以可以改变其环境
    Fn 从其环境获取不可变的借用值

由于所有闭包都可以被调用至少一次,所以所有闭包都实现了 FnOnce .大部分需要指定一个 Fn 系列 trait bound 的时候,可以从 Fn 开始,而编译器会根据闭包体中的情况告诉你是否需要 FnMut 或 FnOnce。

  • 带有泛型和 Fn trait 的闭包: 可以创建一个存放闭包和调用闭包结果的结构体, 目的:结构体只会在需要结果时执行闭包,并会缓存结果值,再次调用闭包可以复用该值.

    struct Cacher<T>
    where
    T: Fn(u32) -> u32,
    {
    calculation: T,
    value: Option<u32>,
    }

创建Cache的结构体,泛型T类型使用where 声明类型为闭包,结构体包含一个闭包,和一个用于存放闭包返回的值的u32类型,因为有可能第一次没有缓存,所有使用Option 的类型。即可能是some(u32) 或者None

  • 官方完整例子:

    use std::thread;
    use std::time::Duration;

    struct Cacher<T>
    where
    T: Fn(u32) -> u32,
    {
    calculation: T,
    value: Option<u32>,
    }

    impl<T> Cacher<T>
    where
    T: Fn(u32) -> u32,
    {
    fn new(calculation: T) -> Cacher<T> {
    Cacher {
    calculation,
    value: None,
    }
    }

      fn value(&mut self, arg: u32) -> u32 {
          match self.value {
              Some(v) => v,
              None => {
                  let v = (self.calculation)(arg);
                  self.value = Some(v);
                  v
              }
          }
      }
    

    }

    fn generate_workout(intensity: u32, random_number: u32) {
    let mut expensive_result = Cacher::new(|num| {
    println!("calculating slowly...");
    thread::sleep(Duration::from_secs(2));
    num
    });

      if intensity < 25 {
          println!("Today, do {} pushups!", expensive_result.value(intensity));
          println!("Next, do {} situps!", expensive_result.value(intensity));
      } else {
          if random_number == 3 {
              println!("Take a break today! Remember to stay hydrated!");
          } else {
              println!(
                  "Today, run for {} minutes!",
                  expensive_result.value(intensity)
              );
          }
      }
    

    }

    fn main() {
    let simulated_user_specified_value = 10;
    let simulated_random_number = 7;

      generate_workout(simulated_user_specified_value, simulated_random_number);
    

    }

a.这样可以起到了使用结构体缓存了闭包执行的结果,会先从结构体里查找缓存的值,没有再计算。b.同理也可以改造value的类型为HashMap, 可以通过key来找值,避免返回之前计算的始终同一个值。

iterator:

  • 迭代器(iterator): 负责遍历序列中的每一项和决定序列何时结束的逻辑。

      let v1 = vec![1, 2, 3];
      let v1_iter = v1.iter();
      let total: i32 = v1_iter.sum();
      println!("value = {}", { total })
    
  • next 是 Iterator 实现者被要求定义的唯一方法

    let v1 = vec![1, 2, 3];
    let mut v1_iter = v1.iter();
    assert_eq!(v1_iter.next(), Some(&1));
    
  • 调用 map 方法创建一个新迭代器,接着调用 collect 方法消费新迭代器并创建一个 vector

    //next 一次返回迭代器中的一个项,封装在 Some 中,当迭代器结束时,它返回 None
    fn main() {
    let v1: Vec<i32> = vec![1, 2, 3];
    let mut newiter = v1.iter().map(|x| x + 1);
    let newVector: Vec<_> = newiter.collect();
    assert_eq!(newVector, vec![2, 3, 4]);
    }

  • 迭代器 iter()、iter_mut()、into_iter()区别:

  1. iter()返回的是值的不可变引用. 即&T

  2. iter_mut() 返回的是值的可变引用. 即&mut T

  3. into_iter() 返回的是T类型的值

    use core::num;
    fn main() {
    // iter() 返回的是值的不可变引用,即&T.(此处map里闭包x本身无法改变)
    let vec = vec![1, 2, 3, 4];
    let new_vec: Vec<_> = vec.iter().map(|x| x + 1).collect();
    println!("{:?}", vec);
    println!("{:?}", new_vec);

     //iter_mut() 返回的是值的可变引用,即&mut T.(此处map里闭包x本身+1)
     let mut vec = vec![1, 2, 3, 4];
     vec.iter_mut().for_each(|x| *x += 1);
     println!("{:?}", vec);
    
     //into_iter() 返回的是T类型的值  (因为所有权 vec是不能再使用)
     let vec = vec![1, 2, 3, 4];
     let new_vec: Vec<_> = vec.into_iter().filter(|x| *x == 2).collect();
     // println!("{:?}", vec); // 无法编译
     println!("{:?}", new_vec);
    

    }

  • 实现Iterator trait 来创建自定义迭代器:

    struct Counter {
    count: u32,
    }

    impl Counter {
    fn new() -> Counter {
    Counter { count: 0 }
    }
    }

    //Counter 类型实现 Iterator trait,通过定义 next 方法来指定使用迭代器时的行为
    impl Iterator for Counter {
    type Item = u32;
    //将迭代器的关联类型 Item 设置为 u32,意味着迭代器会返回 u32 值集合
    fn next(&mut self) -> OptionSelf::Item {
    if self.count < 5 {
    self.count += 1;
    Some(self.count)
    } else {
    None
    }
    }
    }

  • Rust里iterator的定义:

    pub trait Iterator {
    type Item;

      fn next(&mut self) -> Option<Self::Item>;
    

    }

十一,指针&智能指针

  • 指针是一个包含内存地址的变量的通用概念, 智能指针(smart pointers)是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和功能

  • 智能指针通常使用结构体实现,智能指针其实现了 Deref 和 Drop trait(离开作用域时运行的代码)

1. Box 用于在堆上分配值:
let b = Box::new(1);
2. Rc 引用计数智能指针:
  • Rc

    只能用于单线程场景

     //Rc::clone 只会增加引用计数, 这样a,b都是指向1
      let a = Rc::new(1);
      let b = Rc::clone(&a);
    
3. RefCell<T> 和内部可变性模式:
  • RefCell<T> 代表其数据的唯一的所有权, 他具有如下特点:

    // 在任意给定时刻,只能拥有一个可变引用或任意数量的不可变引用 之一(而不是两者)。
    //引用必须总是有效的。

      let num = 1;
      let r1 = RefCell::new(1);
      // Ref - 只有一个不可变借用
      let r2 = r1.borrow();
      // RefMut - mutable  可变借用
      let r3 = r1.borrow_mut();
      // RefMut - 可变借用
      let r4 = r1.borrow_mut();
    
  • 内部可变性(Interior mutability): 是Rust 中的一个设计模式,它允许你即使在有不可变引用时也可以改变数据。

  • 实现是通过不可变的Rc<T>, 此时的T的类型为RefCell<T>, 即结合成Rc<RefCell<T>> 来实现内部可变性,而外部是无法修改的。

  • let value = Rc::new(RefCell::new(5)) 完整例子如下:

    #[derive(Debug)]
    enum List {
    Cons(Rc<RefCell<i32>>, Rc<List>),
    Nil,
    }

    use crate::List::{Cons, Nil};
    use std::cell::RefCell;
    use std::rc::Rc;

    fn main() {
    let value = Rc::new(RefCell::new(5));

      let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));
    
      let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
      let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));
    
      *value.borrow_mut() += 10;
    
      println!("a after = {:?}", a);
      println!("b after = {:?}", b);
      println!("c after = {:?}", c);
    

    }

十二,使用和引用模块代码:

  • 模块的创建和引用

    fn some_function() {}
    mod outer_module {
    // private module
    pub mod inner_module {
    // public module
    pub fn inner_public_function() {
    super::super::some_function();
    }
    fn inner_private_function() {}
    }
    }
    fn main() {
    // 绝对路径 从 crate 根开始,以 crate 名或者字面值 crate 开头。
    crate::outer_module::inner_module::inner_public_function();
    // 相对路径(relative path)从当前模块开始,以 self、super 或当前模块的标识符开头。
    outer_module::inner_module::inner_public_function();
    // 使用 use 关键字将路径引入作用域
    use outer_module::inner_module;
    inner_module::inner_public_function();
    }

连续三波的精简笔记整理完了,这些是比较好的快速学习rust和日常回顾的资料,但这些还只是rust基础。

想要把rust理解和运用熟练,和其他编程语言一样,先写上几万行代码,做几个项目。

PS: 也欢迎大家评论和交流~ 更多文章也可关注微信公号: 良技漫谈

相关推荐
GDAL23 分钟前
npm入门教程13:npm workspace功能
前端·npm·node.js
Pioneer0000134 分钟前
Spring Boot应用开发:从入门到精通
spring boot·后端·firefox
呼叫694534 分钟前
为什么说vue是双向数据流
前端·javascript·vue.js
我命由我1234544 分钟前
CesiumJS 案例 P20:监听鼠标滚轮、监听鼠标左键按下与松开、监听鼠标右键按下与松开、监听鼠标左击落点
开发语言·前端·javascript·前端框架·html·css3·html5
wumu_Love1 小时前
npm 和 node 总结
前端·npm·node.js
顾辰呀1 小时前
css 文字一行没有放满不进行换行
前端·javascript·css·vue.js·css3
Source.Liu1 小时前
【用Rust写CAD】前言
开发语言·rust
AI_小站1 小时前
多模态大模型微调实践!PAI+LLaMA Factory搭建AI导游
人工智能·程序人生·语言模型·大模型·llm·产品经理·多模态大模型
hummhumm1 小时前
Oracle 第20章:数据库调优
java·数据库·后端·python·mysql·oracle·database
零七点072 小时前
Django遍历文件夹及文件
后端·python·django