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

问答环节

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.mfbz.cn/a/555823.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Claude和chatgpt的区别

ChatGPT是OpenAI开发的人工智能的聊天机器人&#xff0c;它可以生成文章、代码并执行各种任务。是Open AI发布的第一款大语言模型&#xff0c;GPT4效果相比chatgpt大幅提升。尤其是最新版的模型&#xff0c;OpenAI几天前刚刚发布的GPT-4-Turbo-2024-04-09版本&#xff0c;大幅超…

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

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

云原生Kubernetes: K8S 1.29版本 部署Kuboard

目录 一、实验 1.环境 2.K8S 1.29版本 部署Kuboard (第一种方式) 3.K8S 1.29版本 部署Kuboard (第二种方式) 4.K8S 1.29版本 使用Kuboard 二、问题 1.docker如何在node节点间移动镜像 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 主机架构版本IP备注ma…

SPI接口的74HC595驱动数码管实现

摸鱼记录 Day_17 (((^-^))) review 前边已经学习了&#xff1a; 数码管显示原理&#xff1a;数码管动态扫描显示-CSDN博客 且挖了个SPI的坑坑 1. 今日份摸鱼任务 学习循环移位寄存器18 串行移位寄存器原理详解_哔哩哔哩_bilibili 学习SPI接口的74HC595驱动数码管19 SPI…

【机器学习300问】75、如何理解深度学习中Dropout正则化技术?

一、Dropout正则化的原理是什么&#xff1f; Dropout&#xff08;随机失活&#xff09;正则化是一种用于减少神经网络中过拟合现象的技术。Dropout正则化的做法是&#xff1a; 在训练过程中的每次迭代中&#xff0c;随机将网络中的一部分权重临时"丢弃"&#xff08;即…

apache是什么

​Apache(音译为阿帕奇)是世界使用排名第一的Web服务器软件。它可以运行在几乎所有广泛使用的计算机平台上&#xff0c;由于其跨平台和安全性被广泛使用&#xff0c;是最流行的Web服务器端软件之一。它快速、可靠并且可通过简单的API扩充&#xff0c;将Perl/Python等解释器编译…

Vue接收接口返回的mp3格式数据并支持在页面播放音频

一、背景简介 在实际工作中需要开发一个转音频工具&#xff0c;并且能够在平台页面点击播放按钮播放音频 二、相关知识介绍 2.1 JS内置对象Blob Blob对象通常用于处理大量的二进制数据&#xff0c;可以读取/写入/操作文件、音视频等二进制数据流。Blob表示了一段不可变的二…

SpringBoot(二)【整合第三方技术】

1、SpringBoot 整合第三方框架 1.1、整合 JUnit 我们先回顾一下在学习 SpringMVC 的时候&#xff0c;我们当时整合 Spring 和 JUnit 是这么整合的&#xff1a; 注意&#xff1a;如果测试类在 SpringBoot 启动类的包或者子包中&#xff0c;可以省略启动类的设置&#xff0c;也…

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的安装…

如何部署npm私有仓库以及在项目中如何使用

如何部署npm私有仓库以及在项目中如何使用 为什么要部署npm私有仓库&#xff1f; 安全性&#xff1a;私有仓库允许团队存放内部研发的、不宜公开发布的代码包&#xff0c;只对特定用户或者团队可见和可用&#xff0c;从而保护公司的知识产权和商业秘密。模块的复用性&#xf…

GUI02-在窗口上跟踪并输出鼠标位置(Win32版)

(1) 响应 WM_MOUSEMOVE 消息获得鼠标位置&#xff1b; (2) 响应 WM_PAINT 将鼠标位置输出到窗口中&#xff1b; (3) 学习二者之间的关键步骤&#xff1a;调用 InvalidateRect() 以通知窗口重绘。 零. 课堂视频 在窗口上跟踪输出鼠标位置-Win32版 一、关键知识点 1. BeginPaint…

安装Milvus的可视化工具Attu教程

提供两种方式来安装可视化工具Attu 一、docker安装 # 执行命令&#xff0c;加个 -d 在后台运行 docker run -d -p 8000:3000 -e MILVUS_URL127.0.0.1:19530 zilliz/attu:v2.2.8 至此安装完成&#xff01; 浏览器输入地址 http:127.0.0.1:8000即可访问 Attu主页 如果拉取最新…

windows下python opencv ffmpeg读取摄像头实现rtsp推流 拉流

windows下python opencv ffmpeg读取摄像头实现rtsp推流 拉流 整体流程1.下载所需文件1. 1下载rtsp推流服务器1.2 下载ffmpeg2. 开启RTSP服务器3. opencv 读取摄像头并调用ffmpeg进行推流4. opencv进行拉流整体流程 1.下载所需文件 1. 1下载rtsp推流服务器 下载 RTSP服务器 下…

Rust入门-所有权与借用

一、为什么、是什么、怎么用 1、为什么Rust要提出一个所有权和借用的概念 所有的程序都必须和计算机内存打交道&#xff0c;如何从内存中申请空间来存放程序的运行内容&#xff0c;如何在不需要的时候释放这些空间&#xff0c;成为所有编程语言设计的难点之一。 主要分为三种…

【opencv】dnn示例-vit_tracker.cpp 使用OpenCV库和ViTTrack模型实现的视频追踪程序

这段代码是一个使用OpenCV库和ViTTrack模型实现的视频追踪程序。程序通过摄像头或视频文件获取图像序列&#xff0c;并对选定的目标对象进行实时追踪。 代码主要分为以下几个部分&#xff1a; 导入必要的库&#xff1a;程序开始时先导入了iostream&#xff0c;cmath以及相关Ope…

Hive进阶(4)----MapReduce的计算过程(赋图助君理解)

MapReduce的计算过程 MapReduce是一种编程模型和处理大规模数据集的方法。它通常用于分布式计算环境中&#xff0c;能够将数据处理任务分解成独立的部分&#xff0c;分配给多台计算机进行并行处理。这个模型由Google提出&#xff0c;并在开源领域中得到了广泛的应用和实现。Map…

CSS显示模式

目录 CSS显示模式简介 CSS显示模式的分类 块元素 行元素 行内块元素 元素显示模式的转换 使块内文字垂直居中的方法 设计简单小米侧边栏&#xff08;实践&#xff09; CSS显示模式简介 元素显示模式就是元素&#xff08;标签&#xff09;以什么方式进行显示&#xff0…

前端三剑客 HTML+CSS+JavaScript ② HTML相关概念

他们这样形容我 是暴雨浇不灭的火 —— 24.4.18 学习目标 理解 HTML的概念 HTML的分类 HTML的关系 HTML的语义化 应用 HTML骨架格式 sublime基本使用 一、HTML初识 HTML指的是超文本标记语言&#xff0c;是用来描述网页的一种语言 超文本&#xff1a;暂且理解为“超级的文本”&…

BNB链融合

BNB Chain融合 BNB Chain目前有BNB智能链&#xff08;BSC&#xff09;&#xff0c;BNB信标链 BNB信标链&#xff1a;用作质押和投票的治理层&#xff0c;采用BEP-2代币标准BNB智能链(BSC)&#xff1a;用作EVM兼容层&#xff0c;提供DApp、DeFi服务、共识层、多链支持和其他Web3…

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

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