Rust随笔(八)
今天来谈谈Rust里面的Sync和Send,在随笔七里面我们提到这两个概念,今天我们来详细的说一下。这两个trait是rust异步编程的主要基石之一。我们首先看看定义:
- Send
Send表示一个类型的所有权可以安全地在线程之间转移。 - Sync
Sync表示一个类型的不可变引用(&T)可以安全地在线程之间共享。 只有同时实现这两者,才能在多线程环境下共享数据,看着定义我们会感到有点疑惑,接下来我们用例子说明。
今天来谈谈Rust里面的Sync和Send,在随笔七里面我们提到这两个概念,今天我们来详细的说一下。这两个trait是rust异步编程的主要基石之一。我们首先看看定义:
Send 表示一个类型的所有权可以安全地在线程之间转移。Sync 表示一个类型的不可变引用(&T)可以安全地在线程之间共享。
只有同时实现这两者,才能在多线程环境下共享数据,看着定义我们会感到有点疑惑,接下来我们用例子说明。在前面一章我们介绍了什么是OOP和Rust思想上的区别,以及Rust的一些基础语法和核心思想。
本章我们围绕OOP的Object和Interface,Rust的Struct和Trait,介绍他们的基础语法以及核心思想,并且对其进行对比以及优势比较。
对于OOP来说,Object和Interface几乎熟悉得不能再熟悉了,几乎所有的编程设计都是围绕Object,OOP的三大基本原则:
我们前面介绍了Arc<Mutex<T>>这个东西,本篇我们以此为引子,打开Rust编程中异步编程的门。
几乎每一个编程语言都能做到异步编程,那么什么是异步编程呢?这里举一个例子:
n * (m + w + t),公式看起来不大,但是如果时间单位是秒,n是一个很大的数(比如说1百万),那么处理起来就很吓人了,从上面我们得到一个条件,就是每一件事情没有关联,那么我们就可以在处理这些事情的时候,在等待期间就可以做其他事情,那么按照这样的思路做,时间就变成了(n * m) + w + (n * t)(调度的时间在这里为了简化所以忽略不计),这样看时间是不是少了一个数量级,如果我们加多一些人手来做,假设添加了p个人,那么时间就变成了(n * (m + t)) / p + w,这样是不是又少了一个数量级。所以这个就是异步编程的核心思想:
协作式调度与非阻塞等待。任务在遇到需要等待的操作(如网络请求)时,会主动让出执行权,这样同一个工作线程就可以立刻去处理其他准备就绪的任务。这极大地提高了在 I/O 密集型场景下的资源利用率。
这里只是大致的描述一下异步编程,异步编程本身又非常多的概念,比如说并行,并发,同步,异步,资源调度等等。这些概念可以自行了解。接下来,我们介绍一下操作异步的基本单位:协程,与之相关的:线程和进程。我们在随笔五中提到过Rc和RefCell的概念,这两个工具给复杂的变量引用场景给予了强大的支持,这里有个问题,我们首先看看下面这一段代码:
use std::rc::Rc;
use std::thread;
fn main() {
let data = Rc::new(5);
let data_clone = Rc::clone(&data);
thread::spawn(move || {
println!("Data from new thread: {}", data_clone);
});
}
rust编程语言,一个极其现代性的编程语言,由于其特殊的语法规则以及函数式概念,劝退了大量的来自OOP或者是第一门语言是OOP并且没有接触过函数式编程的新手。基于此,本文探讨如何编写rust程序。本文是一个系列文章,主要探讨的是rust这门语言如何从OOP基础上入门,而且本系列不做具体的项目分析和构建,而是采用从OOP的视角和添加一些函数式编程概念去解释rust语法和核心思想。所以,这个系列并不会很长,而且需要有一定的OOP编程经验才可以理解(如果只学过js或者其他脚本类型或可视化的编程语言,建议亲自编写OOP项目再行学习)。
本系列所有使用的例子,不一定都是符合rust编程理念的,但是一定符合新手学习的,而且,再看本系列文章之前,需要普及一个概念,就是:
本篇讨论两个东西:
Rc和RefCellReference (&) (在这里我们用引用代替Reference)
在前面的随笔中,提到了“引用”的概念,我们回顾一下这个基础函数:fn judge_size<'a>(x:&'a str,y:&'a str) -> &'a str {
if (x > y){
x
}else{
y
}
}
在 Rust 编程中,我们经常需要在不同的状态和结果表示之间进行转换。例如,一个简单的布尔条件判断可能需要升级为一个更具描述性的 Option 或包含错误信息的 Result。同样,我们也常常需要从 Result 或 Option 中提取出布尔状态。这些转换虽然基础,但掌握其实现方式能显著提升代码的可读性和函数的连贯性。这里是一些常见需求:
bool -> Resultbool -> OptionOption -> Result
基于这些需求,以下提供示例代码:这篇文章探讨一个常见的错误处理库——thiserror
首先我们回顾一下上一篇的Error第二种用法:
#[derive(Debug)]
pub enum AppError {
Config(ConfigError),
Database(DatabaseError),
Query(QueryError),
Io(io::Error), // 有时也可能直接暴露一些通用的 IO 错误
Initialization(String), // 其他初始化错误
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::Config(err) => write!(f, "{}", err), // 直接调用 ConfigError 的 Display
AppError::Database(err) => write!(f, "{}", err), // 直接调用 DatabaseError 的 Display
AppError::Query(err) => write!(f, "{}", err),
AppError::Io(err) => write!(f, "IO 错误: {}", err),
AppError::Initialization(msg) => write!(f, "初始化错误: {}", msg),
}
}
}
impl Error for AppError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
AppError::Config(err) => Some(err), // ConfigError 实现了 Error,可以作为 source
AppError::Database(err) => Some(err), // DatabaseError 实现了 Error
AppError::Query(err) => Some(err), // QueryError 实现了 Error
AppError::Io(err) => Some(err), // io::Error 本身就实现了 Error
AppError::Initialization(_) => None,
}
}
}
// 实现 From trait
impl From<ConfigError> for AppError {
fn from(err: ConfigError) -> Self {
AppError::Config(err)
}
}
impl From<DatabaseError> for AppError {
fn from(err: DatabaseError) -> Self {
AppError::Database(err)
}
}
impl From<QueryError> for AppError {
fn from(err: QueryError) -> Self {
AppError::Query(err)
}
}
impl From<io::Error> for AppError { // 有时也希望直接转换IO错误
fn from(err: io::Error) -> Self {
AppError::Io(err)
}
}
这篇文章探讨在Rust中自定义Error如何设计,我们先直接给出一个例子:
use std::fmt;
#[derive(Debug)]
enum CustomError {
CustomErrorKind1,
CustomErrorKind2(String),
CustomErrorKind3(String, String),
}
//实现Display,用于呈现错误结构
impl fmt::Display for CustomError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CustomError::CustomErrorKind1 => write!(f, "Custom Error Type 1 Occurred"),
CustomError::CustomErrorKind2(s) => write!(f, "Custom Error Type 2 Occurred: {}", s),
CustomError::CustomErrorKind3(s1, s2) => write!(f, "Custom Error Type 3 Occurred: {} - {}", s1, s2),
}
}
}
//通用化
impl std::error::Error for CustomError {}
// Example of how to use it:
fn main() {
let error1 = CustomError::CustomErrorKind1;
let error2 = CustomError::CustomErrorKind2("Something went wrong".to_string());
let error3 = CustomError::CustomErrorKind3("File not found".to_string(), "Ensure the path is correct".to_string());
println!("{}", error1);
println!("{}", error2);
println!("{}", error3);
// For debug formatting
println!("{:?}", error1);
println!("{:?}", error2);
println!("{:?}", error3);
}
最近将《the book》看到了闭包部分,感触良多,发现rust的设计其实和oop关系不大,并且“组合优于继承”这句话不仅仅只是和结构体说的,还是对函数说的。本篇探讨一下问题:
我这里不从具体的函数定义出发,我从闭包出发,在定义中,闭包有以下trait:
trait Fn;
trait FnOnce;
trait FnMut;