心学从0开始学习rust-十万个为什么篇章(持续更新篇章)

news2024/9/24 1:14:52

问答环节

1.const x = 1;和let x = 1有何区别呢,const申请的是全局变量所以会一直有效对吗?

const 声明的常量具有全局作用域,但它们不能直接在函数内部声明。常量通常用于定义整个程序中使用的值,如配置常量或数学常量。
let 声明的变量具有局部作用域,它们只能在声明它们的代码块或函数内部使用。
const 声明的常量没有生命周期的概念,因为它们在编译时就已经确定,并且存储在程序的二进制代码中。
let 声明的变量具有生命周期,它们的生命周期由 Rust 的所有权系统管理。变量的生命周期从声明点开始,到它们离开作用域结束。

2.#[derive(Serialize)]到底是怎么工作的?

在 Rust 中,#[derive(Serialize)] 是一个属性宏(attribute macro),用于自动结构体或枚举类型实现 serde::Serialize trait。这个 trait 是来自于 serde 库的,它是一个流行的 Rust 序列化框架,允许你将 Rust 数据结构转换为各种格式,如 JSON、YAML、XML 等。
当你在结构体或枚举上使用 #[derive(Serialize)] 时,Rust 编译器会自动生成实现 Serialize trait 所需的代码。这意味着你可以在不手动编写序列化逻辑的情况下,将你的数据结构转换为字符串或其他序列化格式。
如果结构体不在这个宏下声明就需要手动实现序列化方法
反序列化和序列化一般会同时声明:

use serde_derive::{Deserialize, Serialize}; 
#[derive(Serialize)]
struct SubscriptionMsg {
    time: u64,
    channel: &'static str,
    event: &'static str,
    payload: Vec<&'static str>,
}

// WebSocket服务器返回的数据结构
#[derive(Deserialize, Debug)]
struct WebSocketResponse {
    // 根据实际情况定义
    result: bool,
}
use serde::{Deserialize};

#[derive(Deserialize)]
struct User {
    name: String,
    age: u8,
    email: Option<String>,
}

fn main() {
    let data = r#"
    {
        "name": "Alice",
        "age": 30,
        "email": "alice@example.com"
    }"#;

    let user: User = serde_json::from_str(data).unwrap();
    println!("Name: {}", user.name);
    println!("Age: {}", user.age);
    println!("Email: {}", user.email.unwrap_or_default());
}

3.为什么使用connect_async建立websocket连接的时候是let (ws_stream, _) =connect_async(WS_URL).await?;而不是let ws_stream = connect_async(WS_URL).await?;

解答:
在这种情况下,使用 let (ws_stream, _) = connect_async(WS_URL).await?; 的形式是为了忽略 connect_async 返回的连接结果的一部分。具体来说,connect_async 函数返回一个 Result,其中包含了建立的 WebSocket 连接(WebSocketStream),以及一个用于处理连接结果的对象(通常是 ClientHandshake)。在 let (ws_stream, ) = connect_async(WS_URL).await?; 中, 是一个通配符,用于匹配 ClientHandshake 对象。由于我们通常不需要使用 ClientHandshake 对象,因此可以使用 _ 进行忽略。
这里有关的内容是模式匹配

4.let (mut write, mut read) = ws_stream.split();这个是在干什么呢?

这行代码在 Rust 中的 WebSocket 编程中常用于分离 WebSocket 连接的读写部分,以便于分别进行写入和读取操作。
具体来说,split() 方法是 WebSocketStream 类型的一个方法,用于将一个完整的 WebSocket 连接分为两个部分:一个负责写入数据的部分,一个负责读取数据的部分。
在这行代码中,let (mut write, mut read) = ws_stream.split(); 将 ws_stream 分为 write 和 read 两个部分,分别代表写入和读取数据的操作。mut 关键字表示这两个变量是可变的,因为在通常情况下,你会对这两个部分进行读写操作。
分离 WebSocket 连接的读写部分可以方便地进行并发操作,比如在一个线程中进行数据的读取,而在另一个线程中进行数据的写入,这样可以提高程序的并发性能。

5.Utc::now().timestamp() as u64什么意思?

这段代码是一个例子,它使用了特定编程语言或框架提供的函数或方法。看起来像是Rust语言中的一行代码。
Utc::now() 是Rust语言中 chrono 库的一部分,用于获取当前的UTC时间。
.timestamp() 是 chrono 库提供的 DateTime 结构体的方法之一,它将日期时间转换为UNIX时间戳,即从1970年1月1日UTC开始的秒数。
as u64 是类型转换,它将UNIX时间戳从默认的 i64 类型(有符号64位整数)转换为 u64 类型(无符号64位整数),因为UNIX时间戳可以是负数,但通常我们更关心非负数。
综合起来,这行代码的作用是获取当前UTC时间,并将其转换为一个64位无符号整数(u64),表示自1970年1月1日以来的秒数

6.什么是 tokio_tungstenite::tungstenite::protocol::Message;

tokio_tungstenite::tungstenite::protocol::Message 是 Rust 编程语言中的一个类型,它来自于 tokio_tungstenite crate,是一个基于 Tokio 的 WebSocket 库。这个类型代表了 WebSocket 连接中可以发送或接收的不同类型的消息。通常,它包括以下变体:
Text(String): 表示文本消息。
Binary(Vec): 表示二进制消息。
Ping(Vec): 表示 ping 控制帧。
Pong(Vec): 表示 pong 控制帧。
Close(Option): 表示关闭控制帧,可选地包含关闭原因。
这些变体封装了 WebSocket 连接中可以传输的不同类型的数据。

7.r# #中间包裹json数据是为了什么?

json数据中会有""这样的符号,会造成歧义,如果包裹在r##里面就直接当作字符串去进行处理这个json数据,否则的话则需要使用转义字符来防止歧义的发生。

let data = r#"
    {
        "name": "Alice",
        "age": 30,
        "email": "alice@example.com"
    }
"#;

let data = "{\"name\": \"Alice\", \"age\": 30, \"email\": \"alice@example.com\"}";

上下两段的效果是等价的

8.&'static str

不可变性:由于字符串字面量是不可变的,所以 &'static str 引用也是不可变的。你不能通过这个引用来修改字符串的内容。
生命周期:'static 生命周期是 Rust 中所有其他生命周期的超集。这意味着任何 'static 生命周期的引用都可以在任何其他生命周期上下文中安全使用,无需担心生命周期问题。
内存管理:因为字符串字面量存储在程序的二进制文件中,所以它们占用的空间是固定的,并且在程序的整个生命周期内都不会改变。这使得 &'static str 非常适合用于那些需要长期存在且不会改变的数据。
性能:由于不需要在运行时分配和释放内存,使用 &'static str 可以提高性能,尤其是在创建大量字符串引用时。

9.什么叫做工作窃取?

每一个工作单元都有一个任务队列,如果自己的任务做完了难道让cpu空闲吗?这显然是不合理的,因此去访问其他工作单元的任务队列进行并发执行,当然在这个过程中需要注意mutex对任务进行加锁防止产生冲突。

10.pin的某些用法

pin可以把数据固定在stack中,观察如下代码

use tokio::pin;

async fn my_async_fn() {
    // async logic here
}

#[tokio::main]
async fn main() {
    let future = my_async_fn();
    pin!(future);

    (&mut future).await;
}

这个future被固定在了stack中,按理说一个future只能够被.await调用一次,但是一旦固定了这个future,就可以被await多次调用。
future是惰性的,如果你不使用await进行调用的话,他的一系列操作是不会执行的

11. select!的简单理解

首先观察如下代码

use tokio;

// 假设这是我们想要并发执行的两个异步任务
async fn task1() -> Result<(), Box<dyn std::error::Error>> {
    tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
    Ok(())
}

async fn task2() -> Result<(), Box<dyn std::error::Error>> {
    tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 启动两个异步任务
    let handle1 = tokio::spawn(task1());
    let handle2 = tokio::spawn(task2());

    // 使用 select! 宏等待这两个任务中的任何一个完成
    select! {
        _ = handle1 => {
            println!("Task 1 completed");
        },
        _ = handle2 => {
            println!("Task 2 completed");
        },
    }

    // 这里我们不需要再次等待 handle1 或 handle2,因为 select! 宏已经处理了其中一个任务的完成
    // 如果我们想要等待另一个任务完成,我们可以再次使用 select! 宏或者 simply await the remaining handle

    // 例如,等待另一个任务完成
    if handle1.is_alive() {
        handle1.await.unwrap();
    } else {
        handle2.await.unwrap();
    }

    Ok(())
}

select会等待某个任务的先执行完成并进行相应处理。
1. 竞态条件处理
在某些情况下,你可能需要在多个异步操作之间进行选择,例如,当两个异步任务同时尝试访问共享资源时,你可能需要决定哪个任务应该先执行。select! 宏可以帮助你实现这种逻辑,确保资源在任何给定时间只被一个任务访问。
2. 并发流处理
当你使用 Stream 处理数据流时,你可能想要同时处理多个数据流。select! 宏允许你等待来自多个数据流的事件,并根据哪个流先产生数据来做出决策。
3. 服务多个客户端
在编写网络服务时,你可能需要同时处理多个客户端的连接。使用 select! 宏,你可以等待来自多个客户端的操作,并为每个操作提供服务,而不会阻塞其他客户端。
4. 超时处理
select! 宏可以用于实现超时逻辑。例如,你可以同时等待一个异步操作完成和一个超时发生,然后根据哪个先发生来执行相应的操作。
5. 并行任务执行
当你需要并行执行多个任务并根据它们的结果做出决策时,select! 宏可以非常有用。你可以启动多个异步任务,并使用 select! 宏等待它们中的任何一个完成。
==select!==是异步的,在等待的过程中并不会影响其他进程的进行。

12.与select几乎同父异母的join

当你有多个 Future 需要完成,并且需要它们的输出来继续执行后续操作时,join! 宏允许你等待所有的 Future 完成并收集它们的结果。
看如下代码

async fn future1() -> i32 {
    // 异步操作返回一个 i32
    42
}

async fn future2() -> String {
    // 异步操作返回一个 String
    "Hello".to_string()
}

#[tokio::main]
async fn main() {
    let result1 = future1();
    let result2 = future2();
    let (res1, res2) = tokio::join!(result1, result2);

    println!("Result 1: {}, Result 2: {}", res1, res2);
}

join和select的区别在于,select在遇到一个任务结束时就会结束,但是join会等待语句中的任务全部完成。
join同样是异步的,也就是等待任务执行的过程中并不会阻塞其他任务执行

13、关于lazy_static

lazy_static 是 Rust 语言中的一个 crate,它允许你在结构体中创建静态变量,而这些变量将会在第一次被使用时进行初始化这个特性在需要延迟加载或者减少启动时间的场景中非常有用,尤其是当你有一个重量级的初始化过程或者初始化过程依赖于运行时环境的时候。

use lazy_static::lazy_static;

lazy_static! {
    static ref MY_STATIC: Vec<i32> = {
        let mut v = Vec::new();
        for i in 0..1000 {
            v.push(i);
        }
        v
    };
}

fn main() {
    // 访问静态变量,这将触发初始化
    println!("First element is: {}", MY_STATIC[0]);
}

14.unwrap_or_else

unwrap_or_else 是 Rust 标准库中 Result 和 Option 类型提供的一个方法,它为错误处理提供了一种灵活的方式。当调用 unwrap_or_else 方法时,如果 Result 或 Option 是 Ok 或 Some,则返回其值;如果是 Err 或 None,则执行提供的函数,并返回该函数的返回值。
在这里插入图片描述

15 mpsc的rx为什么是mut?

在 Rust 中,mpsc(多生产者单消费者)通道的接收端 Receiver 被设计为可变的(mut),这是因为接收操作可能会消耗(consume)通道中的消息,从而改变通道的状态。在 Rust 的所有权和借用规则下,这样的操作需要可变性。
当一个消息被发送到 mpsc 通道时,它被放入一个内部缓冲区。接收端 Receiver 负责从这个缓冲区中取出消息。每次取出消息后,该消息就不再存在于通道中了,因此接收端必须具有可变性来改变这个状态。因此,Receiver 必须是可变的,以便能够从通道中移除消息并更新通道的内部状态。这是 Rust 语言设计的一部分,旨在通过所有权和借用规则来保证内存安全和线程安全。

16.如果接收端的rx被多线程共享,多线程访问队列时会同时获取同一份数据吗?

当一个 mpsc 通道的接收端 rx 被多个线程共享时,每个线程都会拥有 rx 的一个独立克隆(clone),这些克隆都是对同一个底层队列的引用。然而,尽管多个线程可以同时接收来自通道的消息,但每次从通道接收消息的操作是原子的,这意味着在任何给定时刻,只有一个线程可以从通道中接收消息。
这是因为 mpsc 接收端 Receiver 的实现通常会使用内部锁或其他同步机制来确保并发安全。当一个线程尝试从 Receiver 中接收消息时,它会尝试获取锁;如果锁已经被其他线程持有,则当前线程会被阻塞,直到锁可用。这样可以保证消息不会被多个线程同时读取,从而避免了数据竞争和不一致的状态。

17.println(“{}”,x)和println(“{}”,x)的区别?

在 Rust 中,println! 宏是用来打印输出的,它可以使用不同的格式化占位符来指定如何显示输出的值。当你使用 {:?} 作为格式化占位符时,它会调用值的 Debug 格式方法,而使用 {} 作为格式化占位符时,它会调用值的 Display 格式方法。
对于字符串切片(&str 类型),Debug 格式方法会将字符串包围在双引号中,并在前面加上一个反斜杠,这是因为 Debug 格式旨在提供一种稳定且易于识别的输出,用于调试和开发目的。所以当你使用 {:?} 打印字符串时,它会以 “BTC_USDT” 的形式显示,这里的反斜杠是转义字符,表示后面的双引号是字符串的一部分,而不是字符串的结束。
另一方面,Display 格式方法旨在提供一种适合终端用户查看的输出。它通常会省略调试信息中的额外信息,如双引号和转义字符。因此,当你使用 {} 打印字符串时,它会直接显示字符串的内容,即 “BTC_USDT”。

18.我的collect方法构建一个由(f64,f64)这个元组构成的vector数组,collect方法可以收集None吗

实际上,collect方法并不会收集None值。在Rust中,collect方法是用来将迭代器中的元素收集到某种集合类型的。当迭代器中的元素是Option类型时,collect方法会将Some(T)值收集到集合中,而None值会被忽略,不会包含在最终的集合中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1606495.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并发执行一定比串行执行快吗?

一、多线程的两个主要方向 并发:多线程之间各自执行各自的互不影响 并行:多线程之间互相竞争资源&#xff0c;进行读写的时候可能会产生相互覆盖 二、上下文切换 1.什么是上下文切换 在多线程编程中一般线程的个数都大于cpu的核心数,而一个cpu核心在任意时刻都只能被一个线…

ElasticSearch实战之项目搜索高亮

文章目录 1. 前情配置2、数据操作2.1 操作API2.2 数据入库 3. 高亮搜索3.1 方法封装3.2 高亮搜索 1. 前情配置 为满足ElasticSearch可在项目中实现搜索高亮&#xff0c;我们需要先做一些前情配置 导入ElasticSearch依赖 <dependency><groupId>org.springframewor…

怎么设置启用远程桌面? 如何让外网电脑远程本地内网?

如何远程控制电脑&#xff1f;最简单实用的方案是开启电脑系统自带的远程桌面功能&#xff0c;如果涉及跨网、内外网互通&#xff0c;可以同时用快解析内网映射外网。下面是方案的具体实施步骤&#xff0c;供大家参考。 怎么打开设置启用远程桌面&#xff1f; 1.在目标需要远…

【详细讲解CentOS常用的命令】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

android 创建module

文章目的&#xff1a; 快速创建module并使用 创建步骤&#xff1a; 1 创建module 2 修改module下的build.gradle文件 3 修改清单文件中MainActivity属性&#xff0c;否则APP会因为有多个启动界面而崩溃 4 在主项目build.gradle引用该object Module 至此&#xff0c;可在APP中…

排序(五)——非比较排序+排序总结

1.非比较排序 我们前面讲的排序算法都是通过比较大小来进行排序的&#xff0c;他们都是比较排序。 像基数排序、计数排序和桶排序等都不是通过比较大小来排序的&#xff0c;是非比较排序&#xff0c;在这里我们讲一下其中的计数排序和基数排序&#xff0c;而桶排序实现起来太…

在k8s 中部署有状态服务MongoDB高可用集群详解(附带镜像)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、k8s简介 2、MongoDB介绍 3、为什么要…

网络爬虫软件学习

1 什么是爬虫软件 爬虫软件&#xff0c;也称为网络爬虫或网络蜘蛛&#xff0c;是一种自动抓取万维网信息的程序或脚本。它基于一定的规则&#xff0c;自动地访问网页并抓取需要的信息。爬虫软件可以应用于大规模数据采集和分析&#xff0c;广泛应用于舆情监测、品牌竞争分析、…

【 书生·浦语大模型实战营】作业(五):LMDeploy 量化部署

【 书生浦语大模型实战营】作业&#xff08;五&#xff09;&#xff1a;LMDeploy 量化部署 &#x1f389;AI学习星球推荐&#xff1a; GoAI的学习社区 知识星球是一个致力于提供《机器学习 | 深度学习 | CV | NLP | 大模型 | 多模态 | AIGC 》各个最新AI方向综述、论文等成体系…

Vue2之组件通信(爆肝)

大家有什么想看的可以在评论区留言&#xff0c;我尽量满足&#xff0c;感谢大家&#xff01; 组件通信是vue中一个非常重要的内容&#xff0c;我们需要掌握好组件通信&#xff0c;那么让我为大家介绍几种组件通信的方式吧&#xff01; 一、props 这是父传子的方式&#xff0…

FFmpeg合并音视频文件操作备忘(mac版)

利用NDM嗅探插件从B站下载下来的文件是音视频分开的&#xff0c;用剪辑软件合并时发现导出时文件都特别大&#xff0c;于是使用FFmpeg处理 环境&#xff1a; MBP M1芯片版 系统 macOS Sonama 14.4.1 操作步骤&#xff1a; 一、官方下载链接&#xff1a;https://evermeet.cx/…

MySQL 锁机制全面解析

目录 1. MySQL的锁类型1.1 全局锁1.2 表锁1.3 行锁1.4 共享锁&#xff08;读锁&#xff09;1.5 排它锁&#xff08;写锁&#xff09;1.6 死锁 2 乐观锁和悲观锁2.1 乐观锁2.2 悲观锁 3 意向锁4 间隙锁5 临键锁6. 事务隔离级别对锁的影响6.1 读未提交&#xff08;Read Uncommitt…

npm内部机制与核心原理

npm 的核心目标&#xff1a; Bring the best of open source to you, your team and your company. npm 最重要的任务是安装和维护开源库。 npm 安装机制与背后思想 npm 的安装机制非常值得探究。Ruby 的 Gem&#xff0c;Python的pip都是全局安装机制&#xff0c;但是npm的安装…

️️️Vue3+Element-Plus二次封装一个可定制化的table组件

前言 为什么需要二次封装 开发后台管理系统,会接触到很多表格和表单,一但表格表单多起来,仅仅只需要一小部分改变&#xff0c;都需要在中重写一大堆代码,许多重复逻辑,我们可以把重复逻辑抽离出来二次封装一个组件 使用,减少在开发中需要编写的代码。 为什么需要定制化 每个…

【AI工具之Prezo如何自动生成PPT操作步骤】

先说优缺点&#xff1a; 最大的优点就是免费&#xff08;但说实话功能和体验方面很弱&#xff09;支持中文提问&#xff08;最好用英文&#xff09;&#xff0c;智能生成图文&#xff08;但是只能生成英文内容&#xff09;可以AI生成图片&#xff0c;图片很精美酷炫&#xff0…

数据可视化(四):Pandas技术的高级操作案例,豆瓣电影数据也能轻松分析!

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

(八)Pandas窗口数据与数据读写 学习简要笔记 #Python #CDA学习打卡

一. 窗口数据(Window Functions) Pandas提供了窗口函数(Window Functions)用于在数据上执行滑动窗口操作&#xff0c;可以对数据进行滚动计算、滑动统计等操作。需要注意的是&#xff0c;在使用窗口函数时&#xff0c;需要根据实际需求选择合适的窗口大小和窗口函数&#xff0…

硬件设备杂记——12G SDI及 AES67/EBU

常见的 SDI线缆规格&#xff0c;HD-SDI又被称为1.5G-SDI&#xff0c;具体参数以秋叶原的参数为例 AES67/EBU 目前音频网络标准主要集中在OSI网络体系的第二层和第三层。 第二层音频标准的弊端在于构建音频网络时需要专用的交换机&#xff0c;无法利用现有的以太网络&#xff0c…

布局香港之零售中小企篇 | 传承之味,迈向数字化经营的时代

随着内地与香港两地经贸合作日渐紧密&#xff0c;越来越多内地消费品牌将目光投向香港这片充满机遇的热土&#xff0c;纷纷入驻香港市场。「北店南下」蔚然成风&#xff0c;其中不乏已在内地市场深耕多年的传统老字号。数字化经营时代&#xff0c;老字号焕新刻不容缓&#xff0…

QoS流量整形

流量整形是一种带宽技术形式&#xff0c;它延迟某些类型的网络数据包的流动&#xff0c;以确保更高优先级应用程序的网络性能&#xff0c;它主要涉及调整数据传输速率&#xff0c;以确保网络资源以最佳容量得到利用。流量整形的目的是防止网络拥塞并提高网络的整体性能&#xf…