rust学习笔记
- 参考资料:https://doc.rust-lang.org/book/ch01-02-hello-world.html
一、 编译与运行
在 Rust 中,编译和运行代码的常用命令是使用 cargo
,这是 Rust 的包管理和构建工具。以下是使用 cargo
和 rustc
(Rust 编译器)的具体命令:
1. 使用 cargo
工具
cargo
是 Rust 的推荐工具,因为它可以处理依赖管理、构建、测试等多个功能。
-
编译和运行项目:
cargo run
该命令将编译项目并运行可执行文件。
cargo run
是一个方便的命令,适用于开发过程中快速测试代码。 -
仅编译项目:
cargo build
该命令只会编译项目,而不运行。编译后的可执行文件通常位于
target/debug/
目录下。 -
编译发布版本:
cargo build --release
使用
--release
选项来编译一个优化的发布版本。编译后的可执行文件通常位于target/release/
目录下。
2. 使用 rustc
命令
如果你想直接使用 Rust 编译器 rustc
,而不使用 cargo
,可以使用以下命令:
-
编译文件:
rustc main.rs
这将使用
rustc
编译器直接编译main.rs
文件,并生成一个名为main
(Windows 下为main.exe
)的可执行文件。 -
运行可执行文件:
编译完成后,运行生成的可执行文件:
./main # 在 Unix 或 Linux 系统上 main.exe # 在 Windows 系统上
二、如何显式的处理数字类型的溢出
相关教程章节:https://doc.rust-lang.org/book/ch03-02-data-types.html
Rust 提供了多种方法来处理整数溢出,这些方法可以帮助你根据需要选择适当的策略来应对可能的溢出。让我们逐个分析这些方法,并举例说明它们的用法。
1. wrapping_*
方法
- 描述:当发生溢出时,
wrapping_*
方法会绕回到类型的最小值(或最大值),即在所有模式下(包括debug
和release
模式)进行“环绕”(wrap around)处理。 - 使用场景:适用于希望溢出后从类型的边界“环绕”回来的情况,比如在循环计数时。
示例:
fn main() {let max = u8::MAX; // u8 的最大值是 255let wrapped = max.wrapping_add(1); // 255 + 1 应该是 256,但会“环绕”回 0println!("Wrapped value: {}", wrapped); // 输出: Wrapped value: 0
}
- 解释:
u8
类型的最大值是 255。wrapping_add(1)
将会“环绕”回 0,因为u8
只能存储 0 到 255 之间的值。
2. checked_*
方法
- 描述:
checked_*
方法在发生溢出时返回None
,否则返回Some(value)
。 - 使用场景:适用于需要检测溢出并希望在溢出时采取一些特定措施的情况(例如返回一个
None
值表示错误)。
示例
fn main() {let max = u8::MAX; // u8 的最大值是 255let checked = max.checked_add(1); // 255 + 1 会溢出match checked {Some(value) => println!("Checked value: {}", value),None => println!("Overflow occurred!"), // 输出: Overflow occurred!}
}
- 解释:
checked_add(1)
会检测到溢出,因为 255 + 1 超出了u8
类型的范围,因此返回None
。
3. overflowing_*
方法
- 描述:
overflowing_*
方法返回一个元组(value, bool)
,其中value
是操作结果,bool
表示是否发生了溢出(true
表示发生溢出)。 - 使用场景:适用于需要知道溢出发生与否,同时还需要获取操作结果的情况。
示例
fn main() {let max = u8::MAX; // u8 的最大值是 255let (overflowed_value, did_overflow) = max.overflowing_add(1); // 255 + 1 会溢出println!("Overflowing value: {}", overflowed_value); // 输出: Overflowing value: 0println!("Did overflow: {}", did_overflow); // 输出: Did overflow: true
}
- 解释:
overflowing_add(1)
返回一个元组,其中第一个值是溢出后的结果(即环绕后的值 0),第二个值是一个布尔值,表示是否发生了溢出(true
表示发生了溢出)。
4. saturating_*
方法
- 描述:
saturating_*
方法在溢出时返回类型的最小值或最大值,而不是产生环绕效果。 - 使用场景:适用于希望结果在类型的范围内“饱和”(即限制在最大值或最小值)而不发生环绕的情况,比如计算结果不能超过一个特定的上限或下限。
示例
fn main() {let max = u8::MAX; // u8 的最大值是 255let saturated = max.saturating_add(1); // 255 + 1 会溢出println!("Saturating value: {}", saturated); // 输出: Saturating value: 255
}
- 解释:
saturating_add(1)
在发生溢出时不会绕回到 0,而是返回u8
类型的最大值(255),这是“饱和”效果的表现。
总结
wrapping_*
方法:当发生溢出时,结果“环绕”回类型的边界值。checked_*
方法:当发生溢出时,返回None
,否则返回Some(value)
。overflowing_*
方法:返回一个元组(value, bool)
,表示结果和是否发生溢出。saturating_*
方法:当发生溢出时,返回类型的最小值或最大值(不发生环绕)。
三、控制台打印中{}
和{:?}
的区别
在 Rust 中,{:?}
和 {}
都是格式化占位符,但它们的用途和作用有所不同。
{:?}
和 {}
的区别
-
{:?}
:表示调试格式化(debug formatting)。它用于打印那些实现了Debug
trait 的类型的值。它的输出通常更详细,适用于开发和调试用途。使用{:?}
时,Rust 不仅会打印变量的值,还会尽可能地显示它们的内部结构。 -
{}
:表示显示格式化(display formatting)。它用于打印那些实现了Display
trait 的类型的值。Display
trait 更像是用户友好的输出格式,而不是用于调试的信息。并不是所有类型都实现了Display
trait,例如,常见的 Rust 标准类型(如Option
或Result
)默认没有实现Display
。
为什么用 {:?}
而不是 {}
在 Rust 中,大多数基本数据类型(如 bool
、i32
、f64
等)都同时实现了 Debug
和 Display
trait,因此你可以使用 {:?}
或 {}
来打印它们的值。但是,使用 {:?}
更通用,它适用于更广泛的类型。
例子:
println!("a = {:?}, b = {:?}", a, b);
这是用来打印布尔值 a
和 b
。虽然 bool
类型同时实现了 Debug
和 Display
,但一般情况下:
-
使用
{:?}
是一种好习惯,因为它可以打印更复杂的数据结构,例如元组、数组、向量、枚举等。在这些情况下,{:?}
可以打印更多细节而无需更改代码。 -
{:?}
更通用,它能确保你在需要打印复杂数据结构时不出错,而{}
只能用于实现了Display
的类型。
示例:{:?}
vs {}
让我们看一个例子来更好地理解这两个占位符的区别。
使用 {:?}
调试格式化
fn main() {let tuple = (42, "hello", vec![1, 2, 3]);// 使用调试格式化打印println!("{:?}", tuple);
}
输出:
(42, "hello", [1, 2, 3])
- 这里使用
{:?}
成功打印了元组的所有内容,包含整数、字符串和向量的详细信息。
使用 {}
显示格式化
fn main() {let tuple = (42, "hello", vec![1, 2, 3]);// 使用显示格式化打印println!("{}", tuple); // 编译错误
}
输出:
error[E0277]: `({integer}, &str, std::vec::Vec<{integer}>)` doesn't implement `std::fmt::Display`
- 上述代码会产生编译错误,因为元组
(42, "hello", vec![1, 2, 3])
没有实现Display
trait,因此不能使用{}
来打印它。
总结
{:?}
:用于调试格式化,适用于所有实现了Debug
trait 的类型。更通用,更适合打印复杂的数据结构和调试输出。{}
:用于显示格式化,适用于实现了Display
trait 的类型。输出更简洁,但不适用于所有类型。
四、BACKTRACE
在 Rust 中,当程序发生 panic(恐慌)时,会打印一个错误消息,描述导致 panic 的原因。
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
的含义
这条提示信息的意思是:如果你希望看到导致 panic 的完整调用栈(backtrace),可以运行程序时设置环境变量 RUST_BACKTRACE=1
。
什么是 Backtrace?
- Backtrace 是一种调试信息,它显示了程序在发生 panic 时的调用堆栈(函数调用的路径)。
- 通过查看 backtrace,可以了解程序是如何到达 panic 点的,这对调试程序非常有用。
- 在 Rust 中,默认情况下,当程序发生 panic 时,Rust 只会显示一个简短的错误信息(如你看到的那样),不会显示完整的 backtrace。
如何启用 Backtrace?
要启用 backtrace,可以在运行程序时设置 RUST_BACKTRACE
环境变量为 1
。这样 Rust 会在 panic 发生时打印出更详细的调用堆栈。
示例:在不同平台上启用 Backtrace
-
Linux/macOS:
在终端中运行以下命令:
RUST_BACKTRACE=1 cargo run
或者如果你直接使用编译后的可执行文件,可以这样运行:
RUST_BACKTRACE=1 ./your_program_name
-
Windows:
在命令提示符中运行以下命令:
set RUST_BACKTRACE=1 cargo run
或者,如果你直接运行可执行文件,可以这样运行:
set RUST_BACKTRACE=1 your_program_name.exe
启用 Backtrace 后的输出
启用 backtrace 后,运行程序时,你会看到类似以下的输出:
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
stack backtrace:0: std::panicking::begin_panic1: core::panicking::panic_bounds_check2: <your_function_name>...8: main9: __libc_start_main10: _start
解释
- 这些信息显示了程序中发生 panic 的函数调用栈。每一行对应一个函数调用。
- 通过查看这些信息,你可以追踪导致 panic 的路径,找到出错的代码段并进行修复。
五、解构式赋值
rust教程:https://course.rs/basic/variable.html
这段教程介绍了 Rust 1.59 版本后支持的一种新的赋值方式——解构式赋值。这种方式允许在赋值语句中使用元组、切片和结构体的模式,从而直接给多个变量赋值或更新变量的值。它类似于 let
语句的解构绑定,但不同的是,let
语句用于变量的初次绑定,而解构式赋值用于已经绑定的变量的再赋值。
什么是解构式赋值?
解构式赋值 是一种赋值方法,它允许你使用模式匹配的方式将值“解构”并赋给多个变量。在 Rust 1.59 之前,这种操作只能在 let
语句中使用,而在 Rust 1.59 之后,它被扩展到普通的赋值语句中,使得代码更加简洁和易于理解。
解释代码示例
我们来看一下你的代码示例,并逐步分析每一行:
struct Struct {e: i32
}fn main() {let (a, b, c, d, e);(a, b) = (1, 2);// _ 代表匹配一个值,但是我们不关心具体的值是什么,因此没有使用一个变量名而是使用了 _[c, .., d, _] = [1, 2, 3, 4, 5];Struct { e, .. } = Struct { e: 5 };assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
}
1. 结构体定义
struct Struct {e: i32
}
- 定义了一个简单的结构体
Struct
,它只有一个字段e
,类型是i32
。
2. 解构式赋值的例子
fn main() {let (a, b, c, d, e);
- 这里用
let
声明了五个变量a
,b
,c
,d
, 和e
,但是没有为它们赋值。
(a, b) = (1, 2);
- 解构式赋值:将元组
(1, 2)
中的值解构并分别赋给a
和b
。结果是a = 1
,b = 2
。
[c, .., d, _] = [1, 2, 3, 4, 5];
- 这里我们对数组
[1, 2, 3, 4, 5]
使用了解构赋值。c
取得第一个元素1
。..
表示忽略中间的元素(这里是[2, 3]
)。d
取得倒数第二个元素4
。_
匹配最后一个元素5
,但我们不关心它的值,因此使用_
忽略掉。- 结果是
c = 1
,d = 4
。
Struct { e, .. } = Struct { e: 5 };
- 对结构体进行解构赋值。
Struct { e, .. }
是一个模式,匹配Struct
结构体类型。..
表示忽略结构体中的其他字段(如果有的话)。e
取得结构体中e
字段的值(这里是5
)。- 结果是
e = 5
。
3. 断言
assert_eq!([1, 2, 1, 4, 5], [a, b, c, d, e]);
assert_eq!
宏用于断言两个值相等。- 它比较数组
[1, 2, 1, 4, 5]
和变量的值[a, b, c, d, e]
是否相等。 - 由于前面的解构式赋值,我们知道
a = 1
,b = 2
,c = 1
,d = 4
,e = 5
,因此断言成功,程序继续执行。
解构式赋值 vs. let
绑定
-
let
绑定:在初次声明变量时使用。let
语句会新建变量,并绑定一个值或解构多个值。let (x, y) = (10, 20); // 新建变量 x 和 y,并赋值
-
解构式赋值:在已经声明的变量上使用。解构式赋值仅仅是对已存在变量的再赋值。
let x; x = 5; // 给已声明的变量 x 赋值
注意事项
-
使用
+=
等运算符进行赋值时,还不支持解构式赋值。这意味着你不能对解构后的变量直接使用+=
等操作符,例如:(x, y) += (1, 2); // 这是不允许的,编译时会报错
总结
- 解构式赋值 是一种灵活的赋值方式,它允许你在赋值语句中使用元组、切片和结构体模式进行解构。
- 它保持了与
let
语句的使用一致性,但用于已经声明的变量,而不是创建新的变量。 - 使用这种方式可以简化对多值赋值的操作,使代码更加简洁和易读。
相关文章:
rust学习笔记
参考资料:https://doc.rust-lang.org/book/ch01-02-hello-world.html 一、 编译与运行 在 Rust 中,编译和运行代码的常用命令是使用 cargo,这是 Rust 的包管理和构建工具。以下是使用 cargo 和 rustc(Rust 编译器)的具…...

【有啥问啥】复习变分下界即证据下界(Evidence Lower Bound, ELBO):原理与应用
复习变分下界即证据下界(Evidence Lower Bound, ELBO):原理与应用 变分下界(Variational Lower Bound),也称为“证据下界”(Evidence Lower Bound, ELBO),是概率模型中的…...

Linux shell编程学习笔记78:cpio命令——文件和目录归档工具(上)
0 前言 在Linux系统中,除了tar命令,我们还可以使用cpio命令来进行文件和目录的归档。 1 cpio命令的功能,帮助信息,格式,选项和参数说明 1.1 cpio命令的功能 cpio 名字来自 "copy in, copy out"…...
为什么在 JSON 序列化中不使用 transient
有些小伙伴发现了,明明在返回的实体类中指定了属性为transient。为什么前端得到的返回json中还是有这个属性的值? 类: private String name; private transient String password;返回结果: { name:"刘大大", password:…...

K8S - Volume - NFS 卷的简介和使用
在之前的文章里已经介绍了 K8S 中两个简单卷类型 hostpath 和 emptydir k8s - Volume 简介和HostPath的使用 K8S - Emptydir - 取代ELK 使用fluentd 构建logging saidcar 但是这两种卷都有同1个限制, 就是依赖于 k8s nodes的空间 如果某个service pod中需要的vol…...

IO模型---BIO、NIO、IO多路复用、AIO详解
本篇将想给详细解释一下什么是BIO、NIO、IO多路复用以及AIO~ 同步的阻塞(BIO)和非阻塞(NIO)的区别 BIO:线程发来IO请求后,一直阻塞着IO线程,需要缓冲区这边数据准备好之后,才会进行下一步的操作。 举个🌰࿱…...

蓝桥杯真题——约翰的牛奶
输入样例: 8 9 10 输出样例: 1 2 8 9 10 本题是宽搜的模版题,不论怎么倒牛奶,A,B,C 桶里的牛奶可以看做一个三元点集 我们只要找到A桶是空的,B,C桶中的状态即可 #include <iostream> #include <cstring…...

单机docker-compose部署minio
单机多副本docker-compose部署minio 简单介绍 如果服务器有限可以单机挂载多硬盘实现多副本容错(生产不推荐) 部署好的文件状态 有两个重要文件 docker-compose.yaml和nginx.conf docker-compose.yaml是docker部署容器的配置信息包括4个minio和1个ng…...

Winform实现弹出定时框功能
1、程序 private void TimeDialogInitialize(){for(int i1; i<30;i){cbbTimeDialog.Items.Add(i);}}private void cbbTimeDialog_SelectedIndexChanged(object sender, EventArgs e){foreach(int i in cbbTimeDialog.Items){if(cbbTimeDialog.SelectedItem!null &&…...

【机器学习(四)】分类和回归任务-梯度提升决策树(GBDT)-Sentosa_DSML社区版
文章目录 一、算法概念一、算法原理(一) GBDT 及负梯度拟合原理(二) GBDT 回归和分类1、GBDT回归1、GBDT分类二元分类多元分类 (三)损失函数1、回归问题的损失函数2. 分类问题的损失函数: 三、G…...

Mini-Omni 语言模型在流式传输中边思考边听说应用
引入简介 Mini-Omni 是一个开源的多模态大语言模型,能够在思考的同时进行听觉和语言交流。它具有实时端到端语音输入和流媒体音频输出的对话能力。 语言模型的最新进展取得了显著突破。GPT-4o 作为一个新的里程碑,实现了与人类的实时对话,展示了接近人类的自然流畅度。为了…...
vue devtools的使用
vue devtools的使用 Vue Devtools 是一个强大的浏览器扩展,旨在帮助你调试和开发 Vue.js 应用。它支持 Chrome 和 Firefox 浏览器,并提供了一些工具和功能,可以让你更轻松地查看和调试 Vue 应用的状态和行为。以下是如何安装和使用 Vue Devtools 的详细指南。 安装 Vue De…...

无人机培训:无人机维护保养技术详解
随着无人机技术的飞速发展,其在航拍、农业、救援、环境监测等领域的应用日益广泛。然而,要确保无人机安全、高效地执行任务,定期的维护保养至关重要。本文将深入解析无人机维护保养的核心技术,涵盖基础构造理解、清洁与防尘、电机…...
Mac 创建 Python 虚拟环境
在 macOS 上,您可以使用以下步骤使用 virtualenv 创建虚拟环境: 首先,确保您已经安装了 Python 和 virtualenv。您可以在终端中运行以下命令来检查它们是否已安装: python --version virtualenv --version如果这些命令没有找到&am…...

安卓玩机工具-----无需root权限 卸载 禁用 删除当前机型app应用 ADB玩机工具
ADB玩机工具 ADB AppControl是很实用的安卓手机应用管理工具,无需root权限,通过usb连接电脑后,可以很方便的进行应用程序安装与卸载,还支持提取手机应用apk文件到电脑上,此外还有手机系统垃圾清理、上传文件等…...

中国科技统计年鉴1991-2020年
(数据收集)中国科技统计年鉴1991-2020年.Excel格式资源-CSDN文库https://download.csdn.net/download/2401_84585615/89475658 《中国科技统计年鉴》是由国家统计局社会科技和文化产业统计司与科学技术部战略规划司共同编辑的官方统计资料书,…...
OpenAI / GPT-4o:Python 返回结构化 / JSON 输出
在调用 OpenAI(比如:GPT-4o)接口时,希望返回的结果是能够在后续任务中自动化处理的结构化 / JSON 输出。GPT 版本:gpt-4o-2024-08-06,提供了这样的功能。 目标:从非结构化输入到结构化数据&…...

通信工程学习:什么是EDFA掺铒光纤放大器
EDFA:掺铒光纤放大器 EDFA,即掺铒光纤放大器(Erbium-Doped Fiber Amplifier),是一种在光纤通信中广泛使用的光放大器件。以下是对EDFA的详细解释: 一、定义与基本原理 EDFA是在石英光纤中掺入少量的稀土元…...
机器学习与深度学习的区别
随着人工智能技术的迅猛发展,机器学习(Machine Learning, ML)和深度学习(Deep Learning, DL)这两个术语越来越频繁地出现在人们的视野中。尽管它们之间有着紧密的联系,但实际上二者存在显著的区别。本文旨在…...

标准库标头 <barrier>(C++20)学习
此头文件是线程支持库的一部分。 类模板 std::barrier 提供一种线程协调机制,阻塞已知大小的线程组直至该组中的所有线程到达该屏障。不同于 std::latch,屏障是可重用的:一旦到达的线程组被解除阻塞,即可重用同一屏障。与 std::l…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验
Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...

21-Oracle 23 ai-Automatic SQL Plan Management(SPM)
小伙伴们,有没有迁移数据库完毕后或是突然某一天在同一个实例上同样的SQL, 性能不一样了、业务反馈卡顿、业务超时等各种匪夷所思的现状。 于是SPM定位开始,OCM考试中SPM必考。 其他的AWR、ASH、SQLHC、SQLT、SQL profile等换作下一个话题…...
MeanFlow:何凯明新作,单步去噪图像生成新SOTA
1.简介 这篇文章介绍了一种名为MeanFlow的新型生成模型框架,旨在通过单步生成过程高效地将先验分布转换为数据分布。文章的核心创新在于引入了平均速度的概念,这一概念的引入使得模型能够通过单次函数评估完成从先验分布到数据分布的转换,显…...