当前位置: 首页 > news >正文

Rust 学习笔记:错误处理

Rust 学习笔记:错误处理

  • Rust 学习笔记:错误处理
    • 不可恢复的错误
    • 带有结果的可恢复错误
      • 匹配不同的错误
      • 出现错误时 panic 的快捷方式:unwrap 和 expect
      • 传播错误
      • 传播错误的快捷方式:? 操作符
      • 哪里可以使用 ? 操作符
    • panic or not panic,这是一个问题
      • 示例、原型代码和测试
      • 拥有比编译器更多信息的情况
      • 为验证创建自定义类型
      • 通过 from trait 返回自定义错误
      • 错误处理指南
    • 总结

Rust 学习笔记:错误处理

错误在软件中是不可避免的,所以 Rust 有很多特性可以处理出错的情况。

Rust 将错误分为两大类:可恢复的和不可恢复的错误。对于可恢复的错误,我们可能报告该错误并重试操作;对于不可恢复的错误,我们希望立即停止程序。

大多数语言不区分这两种错误,使用异常等机制进行处理。Rust 没有异常,类型为 Result<T, E> 表示可恢复的错误,而 panic! 宏在程序遇到不可恢复的错误时停止执行。

不可恢复的错误

在实践中,有两种方法可以引起 panic:

  • 采取导致代码 panic 的操作(例如访问超过结束的数组)
  • 显式调用 panic! 宏

默认情况下,panic 将打印一条失败消息,沿着堆栈返回并清理遇到的每个函数的数据,最后退出。该情况下,Rust 在 panic 发生时显示调用堆栈,以便更容易地跟踪 panic 的来源。

还有一种情况,在 Cargo.toml 中的 [profile] 部分中添加 panic = 'abort',设置在 panic 时立即中止,这将在不进行清理的情况下结束程序。

[profile.release]
panic = 'abort'

示例 1:

fn main() {panic!("crash and burn");
}

调用 panic 会导致最后两行中包含的错误消息,显示了我们的 panic 消息和源代码中发生 panic 的位置。

在这里插入图片描述
示例 2:

fn main() {let v = vec![1, 2, 3];v[99];
}

如果试图在一个不存在的索引上读取一个元素,Rust 将停止执行并 panic。

在这里插入图片描述

在 C 语言中,试图读取超出数据结构末端的数据是未定义的行为。你可能会得到内存中与数据结构中那个元素对应的位置,即使内存不属于那个结构。这被称为缓冲区过读,如果攻击者能够以这样一种方式操纵索引,从而读取存储在数据结构之后的不允许他们读取的数据,则可能导致安全漏洞。

在这里插入图片描述

Rust 还提醒我们:可以设置 RUST_BACKTRACE 环境变量(为任意不为 0 的值),以获得导致错误的确切回溯信息。

回溯是所有被调用的函数的列表。Rust 中的回溯和其他语言中的一样:从顶部开始读,直到看到自己写的文件,这就是问题产生的地方。该点上方的行是代码调用过的代码,下面几行代码调用了您的代码。

注意,这里使用 Windows 的 PowerShell 是无法运行的,可以切换 git bash 运行即可。

在这里插入图片描述

backtrace 的第 5 行指向项目中导致问题的行:src/main.rs 的第 4 行。如果我们不想让程序陷入恐慌,就应该从提到我们编写的文件的第一行所指向的位置开始调查。

根据操作系统和 Rust 版本的不同,backtrace 的确切输出可能会有所不同。

带有结果的可恢复错误

大多数错误并不严重到需要程序完全停止。有时,当一个函数失败时,它的原因很容易解释和响应。例如,如果您尝试打开一个文件,但由于该文件不存在而导致该操作失败,那么您可能希望创建该文件,而不是终止该进程。

Result 枚举被定义为有两个变体,Ok 和 Err,如下所示:

enum Result<T, E> {Ok(T),Err(E),
}

T 和 E 是泛型类型参数,T 表示成功情况下在 Ok 变量中返回的值的类型,而 E 表示失败情况下在 Err 变量中返回的错误的类型。我们可以在许多不同的情况下使用 Result 类型及其上定义的函数,其中我们希望返回的成功值和错误值可能不同。

让我们调用一个返回 Result 值的函数,因为该函数可能会失败:

use std::fs::File;fn main() {let greeting_file_result = File::open("hello.txt");
}

File::open 的返回类型是 Result<T, E>。泛型参数 T 是由 File::open 的实现用成功值 std::fs::File 的类型填充的,它是一个文件句柄。错误值中使用的 E 类型为 std::io:: error。File::open 函数需要有一种方法来告诉我们它是成功还是失败,同时给我们文件句柄或错误信息。这个信息正是 Result 枚举所传达的。

在 File::open 成功的情况下,变量 greeting_file_result 中的值将是一个包含文件句柄的 Ok 实例。在失败的情况下,greeting_file_result 中的值将是Err的一个实例,其中包含有关发生的错误类型的更多信息。

使用 match 能很好地处理 Result<T, E> 类型:

use std::fs::File;fn main() {let greeting_file_result = File::open("hello.txt");let greeting_file = match greeting_file_result {Ok(file) => file,Err(error) => panic!("Problem opening the file: {error:?}"),};
}

当结果为 Ok 时,这段代码将返回 Ok 变量的内部文件值,然后我们将该文件句柄值赋给变量 greeting_file。匹配之后,我们可以使用文件句柄进行读写。

当结果为 Err 时,我们选择调用 panic! 宏输出错误信息。

匹配不同的错误

我们希望针对不同的失败原因采取不同的行动。如果 File::open 因为文件不存在而失败,我们希望创建文件并返回新文件的句柄。如果 File::open 由于任何其他原因而失败,我们仍然希望代码 panic。

use std::fs::File;
use std::io::ErrorKind;fn main() {let greeting_file_result = File::open("hello.txt");let greeting_file = match greeting_file_result {Ok(file) => file,Err(error) => match error.kind() {ErrorKind::NotFound => match File::create("hello.txt") {Ok(fc) => fc,Err(e) => panic!("Problem creating the file: {e:?}"),},_ => {panic!("Problem opening the file: {error:?}");}},};
}

File::open 在 Err 变量中返回的值的类型是 io::Error,它是标准库提供的结构体。这个结构体有一个方法 kind,我们可以调用它来获取 io::ErrorKind 的值。

枚举 io::ErrorKind 由标准库提供,具有代表 io 操作可能导致的不同类型错误的变体。我们想使用的变体是 ErrorKind::NotFound,它表示我们试图打开的文件还不存在。这种情况下,我们尝试用 file::create 创建文件。但是,由于 File::create 也可能失败,我们还需要在内部匹配表达式中添加另一个 match:创建成功时,同样返回文件句柄;无法创建时,打印一条不同的错误消息。

出现错误时 panic 的快捷方式:unwrap 和 expect

使用 match 可能有点冗长,并且不能很好地传达意图。Result<T, E> 类型在其上定义了许多帮助器方法来执行各种更具体的任务。

unwrap 方法是一个快捷方法,如果 Result 值是 Ok 变体,则 unwrap 将返回 Ok 内部的值;如果 Result 是 Err 变体,unwrap 将调用 panic! 宏。

use std::fs::File;fn main() {let greeting_file = File::open("hello.txt").unwrap();
}

如果我们在没有 hello.txt 文件的情况下运行这段代码,我们将看到 panic 发出的错误消息:

在这里插入图片描述

使用 expect 可以自定义 panic 错误信息,并使跟踪恐慌的来源更容易。

expect 的语法是这样的:

use std::fs::File;fn main() {let greeting_file = File::open("hello.txt").expect("hello.txt should be included in this project");
}

我们使用 expect 的方式与 unwrap 相同:返回文件句柄或调用 panic! 宏。expect 在调用 panic! 宏时打印我们传递的参数。

在这里插入图片描述

unwrap 更常用,因为可以在调试中使用更多的信息。

传播错误

当函数的实现调用可能失败的东西时,可以将错误返回给调用代码。这被称为传播错误,并为调用代码提供了更多的控制。

示例:

use std::fs::File;
use std::io::{self, Read};fn read_username_from_file() -> Result<String, io::Error> {let username_file_result = File::open("hello.txt");let mut username_file = match username_file_result {Ok(file) => file,Err(e) => return Err(e),};let mut username = String::new();match username_file.read_to_string(&mut username) {Ok(_) => Ok(username),Err(e) => Err(e),}
}

让我们先看看函数的返回类型:Result<String, io::Error>。这意味着函数返回一个 Result<T, E> 类型的值,其中泛型参数 T 已经用具体类型 String 填充,泛型类型E已经用具体类型 io::Error 填充。

如果这个函数成功,调用这个函数的代码将收到一个 Ok 值,该值包含一个 String——这个函数从文件中读取的用户名。如果此函数遇到任何问题,调用代码将接收一个 Err 值,该值包含 io::Error 的实例,该实例包含有关问题所在的更多信息。

之所以选择 io::Error 作为函数的返回类型,是因为在函数体中调用的 File::open 函数和 read_to_string 方法这两个可能失败的操作返回的错误值的类型恰好是 io::Error。

这种传播错误的模式在 Rust 中非常常见,因此 Rust 为了方便起见提供了问号操作符 ? 。

传播错误的快捷方式:? 操作符

use std::fs::File;
use std::io::{self, Read};fn read_username_from_file() -> Result<String, io::Error> {let mut username_file = File::open("hello.txt")?;let mut username = String::new();username_file.read_to_string(&mut username)?;Ok(username)
}

? 放置在 Result 值之后,其工作方式与之前为处理 Result 值而定义的匹配表达式几乎相同。如果 Result 的值为Ok,则该表达式将返回 Ok 中的值,程序将继续执行。如果该值为 Err,则整个函数将返回 Err,就像我们使用了 return 关键字一样,因此错误值将传播到调用代码。

? 操作符消除了大量的样板文件,使这个函数的实现更简单。我们甚至可以通过在 ? 之后立即链接方法调用来进一步缩短代码:

use std::fs::File;
use std::io::{self, Read};fn read_username_from_file() -> Result<String, io::Error> {let mut username = String::new();File::open("hello.txt")?.read_to_string(&mut username)?;Ok(username)
}

代码还能更短吗?当然可以!

将文件读入字符串是一种相当常见的操作,因此标准库提供了方便的 fs::read_to_string 函数,该函数打开文件,创建一个新的 String,读取文件的内容,将内容放入该 String并返回。

最终,函数被简化成了 one line code:

use std::fs;
use std::io;fn read_username_from_file() -> Result<String, io::Error> {fs::read_to_string("hello.txt")
}

哪里可以使用 ? 操作符

? 操作符只能在返回类型与使用 ? 的值兼容的函数中使用,即 Result 类型。

让我们看看一个错误示例:

use std::fs::File;fn main() {let greeting_file = File::open("hello.txt")?;
}

在这里插入图片描述

这段代码打开一个文件,可能会失败。? 操作符在 File::open 返回的 Result 值之后,但是这个主函数的返回类型是 (),而不是 Result。

这个错误指出我们只允许使用 ? 返回 Result、Option 或其他实现 FromResidual 的类型的函数中的操作符。

要修复这个错误,有两种选择。一种选择是更改函数的返回类型,使其与使用的值兼容。只要没有限制,就继续操作。另一种选择是使用 match 或 Result<T, E> 方法之一,以任何合适的方式处理 Result<T, E>。

? 操作符在Option<T>上调用时的行为与在 Result<T, E> 上调用时的行为相似:如果值为 None,则在该点将提前从函数返回 None。如果值是 Some,则 Some 内的值是表达式的结果值,函数继续执行。

下面给出一个示例:

fn last_char_of_first_line(text: &str) -> Option<char> {text.lines().next()?.chars().last()
}

这个函数返回 Option<char>,因为这里可能有字符,但也可能没有。

如果 text 不是空字符串,next 将返回一个 Some 值,其中包含 text 中第一行的字符串切片。? 提取字符串切片,然后调用该字符串切片上的 chars 来获取其字符的迭代器,调用 last 来返回迭代器中的最后一项。

注意,可以使用 ? 操作符对返回 Result 的函数中的 Result 进行操作,也可以使用 ? 操作符在返回 Option 的函数中对 Option 进行操作,但不能混合匹配。? 操作符不会自动将结果转换为选项,反之亦然。在这些情况下,可以使用 Result 上的 ok 方法或 Option 上的 ok_or 方法等方法显式地进行转换。

到目前为止,我们使用的所有 main函数都是 return ()。main 函数的特殊之处在于它是可执行程序的入口点和出口点,它的返回类型是有限制的,这样程序才能按照预期的方式运行。

幸运的是,main 函数也可以返回 Result<(), E>。我们将 main 函数的返回类型更改为 Result<(), Box<dyn Error>>,并在末尾添加返回值 Ok(())。这段代码现在可以编译了。

use std::error::Error;
use std::fs::File;fn main() -> Result<(), Box<dyn Error>> {let greeting_file = File::open("hello.txt")?;Ok(())
}

Box<dyn Error> 类型是一个 trait 对象,表示“任何类型的错误”。使用 ? 允许在错误类型为 Box<dyn error> 的主函数中返回 Result 值,因为它允许提前返回任何 Err 值,即使在 main 函数体中添加更多返回其他错误的代码,这个签名仍然是正确的。

当 main 函数返回 Result<(), E> 时,如果 main 函数返回 Ok(()),可执行程序将以 0 的值退出;如果 main 函数返回 Err 值,可执行程序将以非 0 的值退出。这与用 C 编写的可执行程序的约定兼容。

main 函数可以返回任何实现 std::process::Termination trait 的类型,它包含一个返回 ExitCode 的函数报告。

panic or not panic,这是一个问题

本节将总结一些关于如何决定是否在库代码中 panic 的一般指导原则。

示例、原型代码和测试

当编写示例来说明某些概念时,使用 panic 可以使得示例变得清晰。

类似地,在决定如何处理错误之前,unwrap 和 expect 方法在原型制作时非常方便,它们会在代码中留下清晰的标记。

如果一个方法调用在测试中失败,希望整个测试都失败,用 panic 比较好。

拥有比编译器更多信息的情况

当代码确保 Result 将具有 Ok 值时,调用 unwrap 或 expect 也是合适的,但仍然有一个需要处理的 Err 变体。

示例:

    use std::net::IpAddr;let home: IpAddr = "127.0.0.1".parse().expect("Hardcoded IP address should be valid");

我们肯定希望以更健壮的方式处理结果。except 中处理了Result 为 Err 的情况。

为验证创建自定义类型

让我们进一步考虑使用 Rust 的类型系统来确保我们有一个有效的值,并看看如何创建一个用于验证的自定义类型。

完善之前的猜数游戏,引导用户进行有效的猜测,并在用户猜测超出范围的数字时与用户键入(例如字母)时具有不同的行为。

    loop {// --snip--let guess: i32 = match guess.trim().parse() {Ok(num) => num,Err(_) => continue,};if guess < 1 || guess > 100 {println!("The secret number will be between 1 and 100.");continue;}match guess.cmp(&secret_number) {// --snip--}

if 表达式检查我们的值是否超出了范围,之后继续进行 guess 和秘密数之间的比较。然而,这不是一个理想的解决方案:如果程序只对 1 到 100 之间的值进行操作是绝对关键的,并且它有许多具有此需求的函数,那么在每个函数中进行这样的检查将是乏味的(并且可能影响性能)。

相反,我们可以在专用模块中创建新类型,并将验证放入函数中以创建该类型的实例,而不是到处重复验证。这样,函数就可以安全地在其签名中使用新类型,并放心地使用它们接收到的值。我们定义 Guess 类型的一种方法,该方法只在新函数接收到 1 到 100 之间的值时创建实例。

pub struct Guess {value: i32,
}impl Guess {pub fn new(value: i32) -> Guess {if value < 1 || value > 100 {panic!("Guess value must be between 1 and 100, got {value}.");}Guess { value }}pub fn value(&self) -> i32 {self.value}
}

首先,我们创建一个名为 guessing_game 的新模块。接下来,我们在该模块中定义了一个名为 Guess 的结构体,该结构体有一个 i32 类型、名为 value 的字段,这是存储数字的地方。

然后,我们在 Guess 上实现一个名为 new 的关联函数,该函数创建 Guess 值的实例。这个新函数被定义为有一个名为 value 的参数,类型为 i32,并返回 Guess。代码测试 value 以确保它在 1 到 100 之间。如果 value 没有通过这个测试,就会 panic。

接下来,我们实现一个名为 value 的方法,它借用 self,没有任何其他参数,并返回一个 i32。这种方法有时被称为 getter,因为它的目的是从字段中获取一些数据并返回这些数据。这个公共方法是必要的,因为 Guess 结构体的 value 字段是私有的。

如果一个函数有一个参数或者只返回 1 到 100 之间的数字,那么它就可以在它的签名中声明它接受或返回一个 Guess 而不是一个 i32,并且不需要在函数体中做任何额外的检查。

通过 from trait 返回自定义错误

这里将 io::Error 和 ParseIntError 通过 from trait 转换成枚举 MyError。

use std::{fs, io};
use std::fs::File;
use std::io::{read_to_string, Read};
use std::num::ParseIntError;#[derive(Debug)]
pub enum MyError {Io(io::Error),ParseInt(ParseIntError),Other(String),
}impl From<io::Error> for MyError {fn from(err: io::Error) -> Self {MyError::Io(err)}
}impl From<ParseIntError> for MyError {fn from(err: ParseIntError) -> Self {MyError::ParseInt(err)}
}fn read_username_from_file() -> Result<String, MyError> {let mut name = String::new();let file = File::open("username.txt")?.read_to_string(&mut name)?;let num: i32 = "55".parse()?;Ok(name)
}fn main() {}

错误处理指南

当您的代码可能以糟糕的状态结束时,建议让代码陷入 panic。坏状态包含:

  • 无效值、矛盾值或丢失值被传递给代码
  • 用户以错误的格式输入数据

然而,当失败在所难免时,返回 Result 比制造 panic 更合适。

总结

Rust 的错误处理特性旨在帮助编写更健壮的代码。

panic! 宏表示程序处于无法处理的状态,并将进程停止,而不是尝试使用无效或不正确的值继续。

枚举 Result 使用 Rust 的类型系统来指示操作可能失败的方式,代码可以从中恢复。

何时使用 panic:不可恢复的错误场景

  • 程序进入不可预期的 bad state
  • 安全问题
  • 代码不能继续执行
  • 违反函数契约或关键假设

何时使用 Result:可能恢复的错误场景

  • 提供恢复选项
  • 预期可能发生的错误
  • 希望调用者决定如何处理错误

推荐使用 panic 的情况:

  • 原型代码、示例、测试
  • 安全性关键的输入验证
  • 调用外部不可控代码时的异常状态

推荐使用 Result 的情况:

  • 处理可预期的错误
  • HTTP 请求失败
  • 解析错误
  • 用户输入验证

更多资料参见 Rust Book: Error Handling - To panic! or not to panic!

相关文章:

Rust 学习笔记:错误处理

Rust 学习笔记&#xff1a;错误处理 Rust 学习笔记&#xff1a;错误处理不可恢复的错误带有结果的可恢复错误匹配不同的错误出现错误时 panic 的快捷方式&#xff1a;unwrap 和 expect传播错误传播错误的快捷方式&#xff1a;? 操作符哪里可以使用 ? 操作符 panic or not pan…...

【Linux】系统指令与开发全栈(vim、ssh、gcc)

【Linux】系统指令与开发全栈&#xff08;vim、ssh、gcc&#xff09; 一、Linux 系统指令大全 1、文件与目录管理 基础操作 指令参数说明典型用例注意事项cd~ 家目录&#xff0c;- 返回上级&#xff0c;.. 上级目录cd ~/Documents 进入文档目录无目录权限时会报错ls-l 详情&am…...

用 CodeBuddy 搭建「MiniGoal 小目标打卡器」:一次流畅的 UniApp 开发体验

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 在日常生活中&#xff0c;我们总是希望能够坚持一些小习惯&#xff0c;比如每天锻炼十分钟、读一页书、早睡十分…...

前端(vue)学习笔记(CLASS 6):路由进阶

1、路由的封装抽离 将之前写在main.js文件中的路由配置与规则抽离出来&#xff0c;放置在router/index.js文件中&#xff0c;再将其导入回main.js文件中&#xff0c;即可实现路由的封装抽离 例如 //index.js import { createMemoryHistory, createRouter } from vue-routerim…...

ubuntu 安装 Redis新版Redis 7.x

以下是在Ubuntu系统中安装Redis的详细指南&#xff0c; 一、官方APT源安装 sudo apt install redis-server -y 默认安装最新APT源版本&#xff08;Ubuntu 22.04通常为Redis 6.x&#xff09; 服务自动启动&#xff0c;配置文件路径&#xff1a;/etc/redis/redis.conf验证安装 …...

Httphelper: Http请求webapi小记

文章目录 1、HttpHelper.cs Framework4.812、HttpHelper.cs NET83、JsonHelper.cs Framework4.814、JsonHelper.cs NET85、uniapp request.js 访问WEBAPI 每次查找、测试都比较费事&#xff0c;记录一下把 1、HttpHelper.cs Framework4.81 using System; using System.IO; usi…...

【Linux】进程控制(进程创建、进程终止、进程等待、进程替换)

目录 一、进程创建 1、fork函数 2、页表权限 二、进程终止 1、main函数返回值&#xff08;退出码&#xff09; 2、常见错误码及其对应的错误描述&#xff1a; 将错误退出码转化为错误描述的方法&#xff1a; 3、进程退出的三种场景 4、由上我们可以知道&#xff1a; 5…...

java+selenium专题->启动浏览器下篇

1.简介 上一篇文章&#xff0c;我们已经在搭建的java项目环境中实践了&#xff0c;今天就在基于maven项目的环境中演示一下。 2.eclipse中新建maven项目 1.依次点击eclipse的file - new - other &#xff0c;如下图所示&#xff1a; 2.在搜索框输入关键字“maven”&#xff…...

sqlserver 循环删除1000行

在SQL Server中&#xff0c;如果你想循环删除1000行数据&#xff0c;有几种方法可以实现&#xff0c;但值得注意的是&#xff0c;频繁使用循环删除操作可能会对数据库性能造成影响&#xff0c;尤其是在处理大量数据时。下面介绍几种方法&#xff0c;并讨论它们的优缺点。 方法…...

亚信电子与联发科技携手打造AIoT新未来

[台湾新竹讯, 2025年5月19日] 智能物联网&#xff08;AIoT&#xff09;融合人工智能与物联网技术&#xff0c;通过边缘AI的实时数据分析及设备智能联网能力&#xff0c;加速智能物联网创新应用的蓬勃发展。为满足AIoT产业对多网络端口的应用需求&#xff0c;全球半导体公司【联…...

【成品设计】基于STM32的人体健康监测系统

《基于STM32的人体健康监测系统》 Ps:有4个版本。 V1硬件设计&#xff1a; 主控&#xff1a;STM32F103C8T6&#xff1a;作为系统主控芯片。 血氧心率传感器&#xff1a;用于采集当前心率、血氧值。 温湿度传感器&#xff1a;用于采集当前环境温湿度。 有源低电平触发蜂鸣器&…...

【MySQL进阶】了解linux操作系统下mysql的配置文件和常用选项

前言 &#x1f31f;&#x1f31f;本期讲解关于linux下mysql配置选项的详细介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么…...

LeetCode 219.存在重复元素 II

目录 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 思路&#xff1a; 核心思路&#xff1a; 思路详解&#xff1a; 代码&#xff1a; C代码&#xff1a; Java代码&#xff1a; 题目&#xff1a; 题目描述&#xff1a; 题目链接&#xff1a; 219. 存…...

解释:神经网络

在过去的10年里&#xff0c;表现最好的artificial-intelligence系统——比如智能手机上的语音识别器或谷歌最新的自动翻译——都是由一种叫做“深度学习”的技术产生的 深度学习实际上是一种被称为神经网络的人工智能方法的新名称&#xff0c;这种方法已经流行了70多年。1944年…...

Java 泛型详解

在 Java 的类型系统中&#xff0c;泛型&#xff08;Generics&#xff09; 是一个非常重要的特性。它让我们能够编写更通用、更安全的代码&#xff0c;尤其是在处理集合类&#xff08;如 List、Map 等&#xff09;时&#xff0c;泛型的使用可以大大减少类型转换的麻烦&#xff0…...

React集成百度【JSAPI Three】教程(001):快速入门

文章目录 1、快速入门1.1 创建react项目1.2 安装与配置1.3 静态资源配置1.4 配置百度地图AK1.5 第一个DEMO1、快速入门 JSAPI Three版本是一套基于Three.js的三维数字孪生版本地图服务引擎,一套引擎即可支持2D、2.5D、3D全能力的地理投影与数据源加载,帮助开发者轻松搞定平面…...

WPF中资源(Resource)与嵌入的资源(Embedded Resource)的区别及使用场景详解

🌟 开发WPF项目时图片、SVG、配置文件等到底该设置为哪种资源?如何正确读取、跨程序集访问?一篇文章全解答。 在使用 WPF 进行项目开发时,很多开发者在设置文件“生成操作(Build Action)”时,常常会在“资源(Resource)”和“嵌入的资源(Embedded Resource)”之间感…...

如何在 Windows 11 或 10 上安装 Fliqlo 时钟屏保

了解如何在 Windows 11 或 10 上安装 Fliqlo,为您的 PC 或笔记本电脑屏幕添加一个翻转时钟屏保以显示时间。 Fliqlo 是一款适用于 Windows 和 macOS 平台的免费时钟屏保。它也适用于移动设备,但仅限于 iPhone 和 iPad。Fliqlo 的主要功能是在用户不活动时在 PC 或笔记本电脑…...

【STM32】ST-Link V2.1制作

一、下载烧写工具及程序 下载器制作&#xff08;ST-Link V2.1&#xff09; 链接: 提取码&#xff1a;6666https://pan.baidu.com/s/1n0RYNDEw5mBT_CsTFoqrIg?pwd6666 二、安装STM32 CubeProgrammer 双击安装包&#xff0c;点击Next 继续点击Next 选择安装路径&#xff0c;再…...

day30python打卡

知识点回顾&#xff1a; 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑&#xff1a;找到根目录&#xff08;python解释器的目录和终端的目录不一致&#xff09; 作业&#xff1a;自己新建几个不同路径文件尝试下如何导入 一、导入官方库 我们复盘下学习py…...

AI大语言模型评测体系演进与未来展望

随着人工智能技术的飞速发展,大语言模型(LLMs)已成为自然语言处理领域的核心研究方向。2025年最新行业报告显示,当前主流模型的评测体系已从单一任务评估转向多维度、全链路的能力剖析。例如,《全球首个大语言模型意识水平”识商”白盒DIKWP测评报告》通过数据、信息、知识…...

用Python将 PDF 中的表格提取为 Excel/CSV

*用Python将 PDF 中的表格提取为 Excel/CSV&#xff0c;*支持文本型 PDF 和 扫描件/图片型 PDF&#xff08;需 OCR 识别&#xff09;。程序包含以下功能&#xff1a; 1.自动检测 PDF 类型&#xff08;文本 or 扫描件&#xff09; 2.提取表格数据并保存为 Excel/CSV 3.处理多页…...

【工具】ncdu工具安装与使用指南:高效管理Linux磁盘空间

磁盘空间管理是Linux系统维护中的关键任务。当系统提示"磁盘空间不足"时&#xff0c;快速找出占用大量空间的文件和目录变得尤为重要。虽然传统的du命令可以完成这项工作&#xff0c;但其输出往往难以阅读和分析。本文介绍的ncdu&#xff08;NCurses Disk Usage&…...

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Progress Steps (步骤条)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— Progress Steps 组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ ✨ 组件目标 展示一个多步骤的进度条&#xff0c;指示当前所…...

数据分析—Excel数据清洗函数

在做数据分析的过程中&#xff0c;我们从数据库或者网页中获取的外部数据&#xff0c;通常是无法直接使用进行数据分析的。数据经常会有尾随的空格、奇奇怪怪的前缀和非打印字符等等问题&#xff0c;那么我们就需要先对数据进行清洗。下面介绍一些在数据清洗过程中常用的Excel函…...

CEF源码历史版本编译避坑指南

cef编译&#xff0c;网上查到的相关资料大多是官网上自动化编译的翻版&#xff0c;可能较新的版本按照那个步骤编译是没问题的。但是&#xff0c;对于历史版本的编译就会遇到各种坑。步骤大同小异&#xff0c;所以不再赘述&#xff0c;重点记录下针对历史版本编译要注意的点&am…...

看之前熟悉双亲委派加载机制,看之后了解双亲委派加载机制

今天面试被拷打双亲委派加载机制了&#xff0c;麻了。 首先要介绍双亲委派加载机制&#xff0c;就需要先搞明白啥是Java的类加载机制。 一.介绍 Java虚拟机&#xff08;JVM&#xff09;作为Java语言的核心运行环境&#xff0c;承担着将Java字节码转换为机器码并执行的重任。…...

std::ranges::views::stride 和 std::ranges::stride_view

std::ranges::views::stride 是 C23 中引入的一个范围适配器&#xff0c;用于创建一个视图&#xff0c;该视图只包含原始范围中每隔 N 个元素的元素&#xff08;即步长为 N 的元素&#xff09;。 基本概念 std::ranges::stride_view 是一个范围适配器&#xff0c;接受一个输…...

IBM Spectrum Scale (GPFS) 日常运维命令大全

目录 1. 集群管理命令 1.1 集群启动与停止 1.2 节点管理 1.3 集群配置查看与修改 2. 文件系统管理 2.1 文件系统创建与删除 2.2 文件系统挂载与卸载 2.3 文件系统属性修改 3. 存储池与磁盘管理 3.1 存储池管理 3.2 物理磁盘管理 3.3 磁盘故障处理 4. 性能监控与调优…...

IDE 使用技巧与插件推荐

在现代软件开发中&#xff0c;集成开发环境&#xff08;IDE&#xff09;不仅是代码编辑器&#xff0c;更是提升开发效率和代码质量的强大平台。本文将从基础使用技巧、高级功能、插件生态、定制化配置及实战案例五大方面&#xff0c;帮助你全面掌握 IDE&#xff0c;提高编程体验…...

【MySQL】使用文件进行交互

目录 准备工作 1.从文本文件中读取数据&#xff08;导入&#xff09; 1.1.CSV 文件 1.2.设置导入导出的路径 1.3.导入文件 1.4.将数据写入文本文件&#xff08;导出&#xff09; 2.从文件中读取并执行SQL命令 2.1.通过mysql监视器执行编写在文件里面的SQL语句 2.2.通过…...

Redis 学习笔记 5:分布式锁

Redis 学习笔记 5&#xff1a;分布式锁 在前文中学习了如何基于 Redis 创建一个简单的分布式锁。虽然在大多数情况下这个锁已经可以满足需要&#xff0c;但其依然存在以下缺陷&#xff1a; 事实上一般而言&#xff0c;我们可以直接使用 Redisson 提供的分布式锁而非自己创建。…...

【硬核数学】2. AI如何“学习”?微积分揭秘模型优化的奥秘《从零构建机器学习、深度学习到LLM的数学认知》

在上一篇中&#xff0c;我们探索了线性代数如何帮助AI表示数据&#xff08;向量、矩阵&#xff09;和变换数据&#xff08;矩阵乘法&#xff09;。但AI的魅力远不止于此&#xff0c;它最核心的能力是“学习”——从数据中自动调整自身&#xff0c;以做出越来越准确的预测或决策…...

[Java][Leetcode middle] 151. 反转字符串中的单词

思路挺简单的 自己想的&#xff0c;步骤挺复杂的 先统计处开头和结尾的空格数跳过开头这些空格&#xff0c;将单词放到数组中统计最后一个可能漏过的单词&#xff08;例如&#xff1a;“hello word”&#xff0c;没有空格退出&#xff09;倒序输出 public String reverseWor…...

力扣每日一题5-18

class Solution { public int colorTheGrid(int m, int n) { // 每一列可能的状态总数 每个单元有3可能 int totalState 1; for (int i 0; i < m; i) totalState * 3; // pre[k] 代表前一轮dp 状态为k 的方案总数 int [] pre new int [totalState]; // 初始化合法填色 的…...

leetcode 74. Search a 2D Matrix

题目描述 要求时间复杂度必须是log(m*n)。那么对每一行分别执行二分查找就不符合要求&#xff0c;这种做法的时间复杂度是m*log(n)。 方法一&#xff0c;对每一行分别执行二分查找&#xff1a; class Solution { public:bool searchMatrix(vector<vector<int>>&a…...

养生指南:重塑健康生活的实用方案

一、饮食&#xff1a;均衡膳食&#xff0c;滋养身心 三餐以 “轻盐、轻油、轻糖” 为准则。早餐搭配全麦三明治、无糖酸奶和一小把蓝莓&#xff0c;补充优质碳水与抗氧化物质&#xff1b;午餐选用糙米饭、白灼虾及蒜蓉西蓝花&#xff0c;保证蛋白质与膳食纤维摄入&#xff1b;…...

IPTABLES四表五链祥解

在Linux中&#xff0c;iptables 是一个强大的防火墙工具&#xff0c;用于管理和过滤网络流量。iptables 使用四个不同的表&#xff0c;每个表都包含多个链&#xff0c;来控制流量的处理。 一、iptables四个表 表名功能说明filter默认表&#xff0c;负责对进出数据包的过滤操作…...

嵌入式学习--江协51单片机day8

这个本来应该周末写的&#xff0c;可是一直想偷懒&#xff0c;只能是拖到周一了&#xff0c;今天把51结个尾&#xff0c;明天开始学32了。 学习内容LCD1602&#xff0c;直流电机&#xff0c;AD/DA&#xff0c;红外遥控 LCD1602 内部的框架结构 屏幕小于数据显示区&#xff…...

内网穿透与内网映射是什么?

在互联网技术快速迭代的当下&#xff0c;网络通信架构日益复杂&#xff0c;内网穿透与内网映射作为实现公网访问内网资源的核心技术&#xff0c;在企业办公、个人开发、智能家居等领域发挥着关键作用。尽管两者都致力于打通公网与内网的连接通道&#xff0c;但它们在底层原理、…...

51单片机点亮一个LED介绍

LED介绍 LED就是发光二极管&#xff0c;一般来说如果是直插式的&#xff0c;那就是长正短负&#xff0c;如果是贴片式的&#xff0c;那就带彩色标记是阴极&#xff0c;如果是三角形的&#xff0c;水平箭头指的就是阴极&#xff0c;通常一般的工作电压在3mA~20mA&#xff0c;当…...

WebRTC技术EasyRTC嵌入式音视频通信SDK助力智能电视搭建沉浸式实时音视频交互

一、方案概述​ EasyRTC是一款基于WebRTC技术的开源实时音视频通信解决方案&#xff0c;具备低延迟、高画质、跨平台等优势。将EasyRTC功能应用于智能电视&#xff0c;能够为用户带来全新的交互体验&#xff0c;满足智能电视在家庭娱乐、远程教育、远程办公、远程医疗等多种场…...

uniapp 小程序 CSS 实现多行文本展开收起 组件

效果 组件 <template><!-- 最外层弹性盒子 --><div class"box" :style"boxStyle"><!-- 文本区域&#xff0c;动态类名控制展开/收起状态 --><div ref"textRef" :class"[text-cont, btnFlag ? text-unfold : t…...

嵌入式51单片机:C51

sbit TISCON^1的意思是定义TI为SCON的次低位&#xff08;最低位标记为0&#xff0c;其次为1&#xff0c;再次为2&#xff09;...

【回眸】香橙派zero2 嵌入式数据库SQLite

前言 SQLite介绍 安装SQLite3 SQLite 使用 创建数据库 创建一张表格 插入数据 查看数据库的记录 删除一条记录 更改一条记录 删除一张表 增加一列&#xff08;性别&#xff09; SQLite编程操作 前言 还有2个项目没更新完...披星戴月更新中... SQLite介绍 基于嵌入…...

vue3个生命周期解析,及setup

合理使用各生命周期&#xff0c;切勿乱用&#xff0c;不是所有东西都需要&#xff0c;合理使用可以提高效率和性能。 Vue 3 生命周期钩子详解 Vue 3的生命周期钩子分为以下几个阶段&#xff1a; onBeforeMount 调用时机&#xff1a;在组件挂载到DOM之前调用。使用场景&#xf…...

MySQL死锁:面试通关“三部曲”心法

想象一下&#xff0c;你的MySQL数据库里有两张桌子&#xff08;数据表&#xff09;&#xff0c;比如一张“产品库存表”&#xff0c;一张“订单表”。现在来了两个顾客&#xff08;并发事务&#xff09;&#xff0c;都想同时操作这两张桌子上的东西&#xff1a; 顾客A 先锁住了…...

Spring Boot 与 RabbitMQ 的深度集成实践(四)

实战案例 业务场景描述 在电商系统中&#xff0c;用户下单是一个核心业务操作。当用户成功下单后&#xff0c;系统需要执行一系列后续任务&#xff0c;如发送邮件通知用户订单已成功提交&#xff0c;更新库存信息以确保商品库存的准确性&#xff0c;以及记录订单相关的日志信…...

ES6详解

一、变量声明 let 与 const 块级作用域&#xff1a;替代 var 的函数作用域 const 声明常量&#xff08;不可重新赋值&#xff0c;但对象属性可修改&#xff09; if (true) {let x 10const PI 3.14 } console.log(x) // 报错 二、箭头函数 简写语法与 this 绑定 // 传统函数…...

C语言—字符函数和字符串函数

1.字符分类函数 字符控制函数&#xff1a;int iscntrl ( int c ); 控制字符通常不是可打印字符&#xff0c;该函数是用来判断参数是否为控制字符&#xff0c;需要的头文件为<ctype.h>标准ASCII码中&#xff0c;不可打印字符主要包括以下两类&#xff1a; 控制字符&…...