20天学会rust(四)常见系统库的使用
前面已经学习了rust的基础知识,今天我们来学习rust强大的系统库,从此coding事半功倍。
集合
数组&可变长数组
在 Rust 中,有两种主要的数组类型:固定长度数组(Fixed-size Arrays)和可变长度数组(Dynamic-size Arrays)。
- 固定长度数组(Fixed-size Arrays):
固定长度数组的长度在编译时就确定,并且长度不可改变。你可以使用以下语法定义一个固定长度数组:
fn main() {let arr: [i32; 5] = [1, 2, 3, 4, 5];
}
在上面的例子中,我们定义了一个类型为 i32 的固定长度数组 arr
,长度为 5,并初始化了数组的元素。
- 可变长度数组(Dynamic-size Arrays):
Rust 中没有直接支持可变长度数组的语法。但是,你可以使用Vec<T>
类型来创建一个动态增长的数组。Vec<T>
是一个可变长度的动态数组,可以根据需要动态添加和删除元素。以下是一个使用Vec<T>
的示例:
fn main() {let mut vec: Vec<i32> = Vec::new();vec.push(1);vec.push(2);vec.push(3);
}
在上面的例子中,我们首先创建了一个空的 Vec<i32>
,然后使用 push
方法向数组中添加元素。 Vec<T>
会根据需要自动调整大小。
需要注意的是,可变长度数组( Vec<T>
)和固定长度数组( [T; N]
)是不同的类型,它们具有不同的性质和用途。下面我们再看下Vec的一些基础用法:
创建Vec
在 Rust 中,有几种创建 Vec 的方式:
- 使用 Vec::new() 创建一个空的 Vec:
let mut vec = Vec::new();
这会创建一个长度为 0 的空向量。
- 使用 vec![] 宏创建一个非空的 Vec:
let mut vec = vec![1, 2, 3];
这会创建一个长度为 3、值为 [1, 2, 3] 的可变数组。
- 使用 Vec::with_capacity() 创建一个指定容量的 Vec:
let mut vec = Vec::with_capacity(10);
这会创建一个长度为 0 但预分配了 10 个元素空间的向量。这意味着,在不重新分配内存的情况下,vec 可以增长到 10 个元素。
- 使用 iterator 方法创建一个 Vec:
let mut vec = (1..10).collect::<Vec<i32>>();
这会创建一个长度为 9、值为 [1, 2, 3, 4, 5, 6, 7, 8, 9] 的向量。
这些方法的主要区别在于:
- Vec::new() 和 vec![] 创建的向量初始长度为 0 或指定长度。
- Vec::with_capacity() 创建的向量初始长度为 0,但预分配了指定的容量。这可以提高向量在后续增长时的性能,因为不需要频繁重新分配内存。
- 使用 iterator 方法创建的向量会根据 iterator 的长度创建指定大小的向量。
总的来说,如果你知道向量的大致大小,使用 Vec::with_capacity() 可以获得最好的性能。否则,Vec::new() 和 vec![] 也是不错的选择。这里的区别类似Java。
操作Vec
在 Rust 中, Vec<T>
是一个可变长度的动态数组,用于存储相同类型的多个值。它有以下主要的方法:
- 添加元素 - 使用
push()
方法:
let mut vec = Vec::new();
vec.push(1);
- 删除元素 - 使用
pop()
方法删除最后一个元素:
let mut vec = vec![1, 2, 3];
vec.pop(); // vec = [1, 2]
- 查找元素 - 使用索引(
[]
)访问元素:
let vec = vec![1, 2, 3];
let elem = vec[0]; // elem = 1
- 修改元素 - 也使用索引(
[]
)访问元素,然后对其进行修改:
let mut vec = vec![1, 2, 3];
vec[0] = 5;
// vec = [5, 2, 3]
除此之外, Vec<T>
还有其他方法,比如:
len()
- 获取向量长度iter()
- 创建一个迭代器以便于遍历元素iter_mut()
- 创建一个可变迭代器以便于修改元素first()
- 获取第一个元素last()
- 获取最后一个元素get()
- 安全地访问一个元素,返回 Option<&T>get_mut()
- 安全地访问一个可变元素,返回 Option<&mut T>
这些方法可以满足你对 Vec<T>
的基本操作需求。
map
在 Rust 中,有几种主要的 map 实现:
- HashMap - 散列表映射,基于哈希表实现。这是 Rust 中最常用的映射类型。
- BTreeMap - 二叉树映射,基于二叉树实现。键值是有序的。
- LinkedHashMap - 链表散列表映射,基于哈希表和双向链表实现。键值保持插入顺序。
- IndexMap - 基于数组的映射。键必须实现
Index
trait。 - FxHashMap - 散列表映射,使用第三方
fxhash
算法实现。性能可能优于标准库的HashMap
这里主要介绍HashMap的用法,其他的大同小异,后续会出一个专门的用法介绍。
在 Rust 中,HashMap 的主要用法如下:
- 创建一个空的 HashMap:
let mut map = HashMap::new();
- 插入键值对:
map.insert(1, "a");
- 获取值:
let a = map.get(&1); // a = Some("a")
- 删除键值对:
map.remove(&1);
- 迭代 HashMap:
for (key, value) in &map {println!("{}: {}", key, value);
}
- 检查键是否存在:
map.contains_key(&1); // true 或 false
- 获取 HashMap 的长度:
let len = map.len();
- 清空 HashMap:
map.clear();
- 只迭代键或值:
for key in map.keys() {println!("{}", key);
}for value in map.values() {println!("{}", value);
}
这些是 HashMap 在 Rust 中最基本和最常用的方法。HashMap 是一个无序的 map,键类型必须实现 Eq
和 Hash
trait。
Set
- HashSet - 散列表集合,基于哈希表实现。用于存储唯一的键。
- BTreeSet - 二叉树集合,基于二叉树实现。键值是有序的。
这里我们也重点介绍HashSet的用法:
- HashSet基础用法:
let mut set = HashSet::new();
set.insert(1); // 插入元素:
set.remove(&1);// 删除元素:
set.contains(&1); // 检查元素是否存在: true 或 false
let len = set.len();// 获取 HashSet 的长度:for elem in &set { // 迭代 HashSet:println!("{}", elem);
}set.clear(); // 清空 HashSet:
- 取两个 HashSet 的交集、并集、差集:
let set1 = HashSet::from([1, 2, 3]);
let set2 = HashSet::from([2, 3, 4]);let intersection = set1.intersection(&set2).collect(); // [2, 3]
let union = set1.union(&set2).collect(); // [1, 2, 3, 4]
let difference = set1.difference(&set2).collect(); // [1]
HashSet 是一个无序的集合,元素类型必须实现 Eq
和 Hash
trait。
迭代器&流式编程(Iterator)
在 Rust 中,迭代器(Iterator)是一种用于遍历集合元素的抽象。它提供了一个统一的接口,使你可以对各种不同类型的集合进行迭代。
要使用迭代器,你可以按照以下步骤进行操作:
- 创建一个迭代器:
你可以通过调用集合上的.iter()
或.iter_mut()
方法来创建一个不可变或可变的迭代器。例如:
let vec = vec![1, 2, 3];
let iter = vec.iter(); // 不可变迭代器
let mut_iter = vec.iter_mut(); // 可变迭代器
- 使用迭代器方法:
一旦你创建了迭代器,你可以使用迭代器的方法来处理集合的元素。一些常见的方法包括.next()
、.map()
、.filter()
、.fold()
等。例如:
let vec = vec![1, 2, 3];
let mut iter = vec.iter();// 使用 .next() 方法逐个获取元素
while let Some(item) = iter.next() {println!("{}", item);
}// 使用 .map() 方法对元素进行转换
let vec2: Vec<i32> = vec.iter().map(|x| x * 2).collect();
println!("{:?}", vec2); // 输出 [2, 4, 6]// 使用 .filter() 方法过滤元素
let vec3: Vec<i32> = vec.iter().filter(|x| *x > 1).cloned().collect();
println!("{:?}", vec3); // 输出 [2, 3]
- 链式调用迭代器方法:
你可以使用链式调用来组合多个迭代器方法,以实现复杂的操作。例如:
let vec = vec![1, 2, 3];
let result: i32 = vec.iter().filter(|x| *x > 1).map(|x| x * 2).sum();
println!("{}", result); // 输出 10
通过这些方法,你可以对集合进行各种操作,如过滤、映射、折叠等。
- 自定义迭代器
在 Rust 中,你可以通过实现 Iterator
trait 来自定义迭代器。 Iterator
trait 有以下定义:
trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;// 默认方法fn size_hint(&self) -> (usize, Option<usize>) { ... }fn count(self) -> usize { ... }fn last(self) -> Option<Self::Item> { ... }fn nth(&mut self, n: usize) -> Option<Self::Item> { ... }fn step_by(self, step: usize) -> StepBy<Self> { ... }// 等等
}
要实现这个 trait,你需要:
-
定义
Item
类型,表示迭代器返回的元素类型。 -
实现
next
方法,返回迭代器的下一个元素,如果迭代器结束则返回None
。 -
可选:实现其他默认方法来改善迭代器的行为。
以下是一个自定义迭代器的示例:
struct Counter {count: u32,
}impl Iterator for Counter {type Item = u32;fn next(&mut self) -> Option<Self::Item> {if self.count < 5 {let c = self.count;self.count += 1;Some(c)} else {None}}
}fn main() {let mut iter = Counter { count: 0 };while let Some(i) = iter.next() {println!("{}", i);}
}
这个迭代器会返回 0 到 4 的数字,然后结束。我们实现了 next
方法来定义这个行为。
通过实现 Iterator
trait,你可以创建各种自定义的迭代器,以满足不同的需求。希望这能帮助你理解 Rust 中的迭代器和如何自定义迭代器!如果还有其他问题,请随时提问。
并发
多线程处理
下面我们通过一个例子学习: 创建一个线程,并每隔2s输出“hello world”
上代码:
use std::thread;
use std::time::Duration;fn main() {thread::spawn(|| {loop {println!("hello world");thread::sleep(Duration::from_secs(2));}});
}
这个代码会:
- 使用
thread::spawn
创建一个新线程 - 在线程中有一个无限循环
- 每次循环会打印 “hello world”
- 使用
thread::sleep
使线程睡眠 2 秒 - 所以这个线程会每隔 2 秒打印一次 “hello world”
Duration::from_secs(2)
是创建一个表示 2 秒的 Duration。我们将其传递给 thread::sleep
来使线程睡眠 2 秒。
Ok,我们掌握了线程的基础用法, 再来看一个复杂的例子: 用多线程实现观察者模式
要实现观察者模式,可以使用通道(channel)在线程间通信。例如:
use std::sync::mpsc;
use std::thread;struct Subject {observers: Vec<mpsc::Sender<i32>>,
}impl Subject {fn new() -> Subject {Subject { observers: vec![] }}fn attach(&mut self, observer: mpsc::Sender<i32>) {self.observers.push(observer);}fn notify(&self) {for observer in self.observers.iter() {observer.send(1).unwrap();}}
}fn main() {let (tx1, rx1) = mpsc::channel();let (tx2, rx2) = mpsc::channel();let mut subject = Subject::new();subject.attach(tx1);subject.attach(tx2);thread::spawn(move || {let msg = rx1.recv().unwrap();println!("Got: {}", msg);});thread::spawn(move || {let msg = rx2.recv().unwrap();println!("Got: {}", msg);});subject.notify();
}
在这个例子中:
- Subject 结构体充当主题(subject),维护多个观察者(observers)的列表。
- attach 方法用于添加观察者(通道的发送端)。
- notify 方法用于通知所有观察者(通过通道发送消息)。
- 我们创建两个线程作为观察者,通过通道接收主题的通知。
- 当调用 subject.notify() 时,两个观察者线程会接收到通知并打印消息。
线程间通信
在上面的例子中,有一个新的知识点:使用通道(channel)在线程间通信。 那么什么是channel呢?
在 Rust 中,channel 用于在线程之间发送消息和通信。它可以在不同的线程之间安全地传递数据。
channel 的主要作用有:
-
线程间通信:channel 可以在不同的线程之间发送消息,用于线程间的通信和协作。
-
安全地共享数据:channel 可以在线程之间安全地传递数据,避免数据竞争。
-
限制并发:channel 的发送端和接收端各有一个缓存,这可以限制线程之间并发发送和接收消息的数量。
举个例子:
use std::sync::mpsc::channel;
use std::thread;fn main() {let (tx, rx) = channel();thread::spawn(move || {tx.send(10).unwrap();});let received = rx.recv().unwrap();println!("Got: {}", received);
}
在这个例子中:
- 我们使用
channel()
创建一个 channel,它返回一个发送端tx
和一个接收端rx
。 - 然后我们创建一个线程,在线程中通过
tx
发送消息 10。 - 在主线程中,我们通过
rx
接收该消息,并打印结果。 - 这样,通过 channel 我们就在两个线程之间安全地传递了数据。
channel 在 Rust 的并发编程中非常有用,它可以用于线程池、工作窃取等并发模式中。
再看一个用法: 工作窃取
这里是一个使用 channel 实现工作窃取的例子:
use std::sync::mpsc::channel;
use std::thread;fn main() {let (tx, rx) = channel();let mut threads = vec![];for i in 0..10 {let tx = tx.clone();threads.push(thread::spawn(move || {let mut work = rx.recv().unwrap();while let Some(job) = work.recv() {println!("Worker {} got job {}", i, job);}}));}for job in 0..10 {let thread_id = job % threads.len();threads[thread_id].send(job).unwrap();}
}
这个例子做了以下工作:
-
创建一个
channel
,得到发送端 tx 和接收端 rx。 -
创建 10 个工作线程,每个线程从 rx 接收工作。
-
将 10 个工作(
job
)发送到不同的工作线程,实现工作窃取。每个工作会被发送到线程 ID 与工作 ID 取余后的线程。 -
每个工作线程接收工作,并打印接收到的工作 ID。
-
主线程将所有工作分发完成后结束。
这个例子展示了如何使用 channel 在线程之间传递工作,实现工作窃取的模式。每个工作线程从 channel 接收工作,而不是固定的工作队列。这使得工作可以在线程之间动态分配,实现工作窃取。
线程池
熟悉java的同学可能会问了,有线程? 那有线程池吗?必须有!!
在 Rust 中,你可以使用 threadpool
crate 来创建一个线程池。以下是一个基本的示例:
use threadpool::ThreadPool;fn main() {let pool = ThreadPool::new(4);for i in 0..10 {let j = i;pool.execute(move || {println!("Hello from thread {}", j);});}
}
这会创建一个包含 4 个线程的线程池。然后我们使用 pool.execute()
方法在线程池中执行 10 个闭包。这些闭包会被线程池的线程执行,并打印出执行线程的索引。
threadpool
crate 提供了以下主要功能:
ThreadPool::new(size)
:创建一个包含size
个线程的线程池。pool.execute(closure)
:在线程池的某个线程中执行提供的闭包。pool.join()
:等待线程池中的所有线程结束。ThreadPoolBuilder
:可以用来自定义线程池的各种设置,如线程名称、堆栈大小等。
一个更复杂的例子:
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use threadpool::ThreadPool;fn main() {let pool = ThreadPool::new(4);let (tx, rx) = mpsc::channel();let tx = Arc::new(Mutex::new(tx));for i in 0..10 {let tx = tx.clone();pool.execute(move || {let mut tx = tx.lock().unwrap();tx.send(i).unwrap();});}for _ in 0..10 {let j = rx.recv().unwrap();println!("Got: {}", j);}pool.join();
}
这里我们使用了 mpsc
库创建一个通道,并使用 Arc<Mutex<T>>
在线程之间共享该通道的发送端。然后我们在 10 个任务中发送数字到通道,并在主线程中接收这些数字。
序列化
在后端开发中,序列化是一个绕不过的话题,前后端交互,后端之间交互都需要对数据做序列化与反序列化。 在 Rust 中,有几种常用的序列化方式:
-
Serde:这是 Rust 中最流行的序列化库。它提供了多种序列化格式的支持,如 JSON、YAML、TOML 等。使用 Serde 可以方便地将 Rust 结构体序列化为这些格式,以及反序列化回 Rust 结构体。
-
Bincode:这是一个用于在 Rust 中进行二进制序列化的库。它可以高效地将 Rust 的基本数据结构编码为二进制,并解码回原数据结构。
-
Ron:这是一个用于 Rust 对象表示法 (RON) 的序列化格式和解析器。RON 是一个人类友好的二进制序列化格式,设计用于在 Rust 中存储和传输数据。
-
CBOR:这是一个实现了约束二进制对象表示法 (CBOR) 的 Rust 库。CBOR 是一种二进制序列化格式,它比 JSON 更紧凑,也更适用于嵌入式系统。
-
MessagePack:这是一个实现 MessagePack 二进制序列化格式的 Rust 库。MessagePack 是一个高效的二进制序列化格式,可以用于在不同语言之间交换数据。
-
Protobuf:这是 Google 开发的一种数据序列化格式,在 Rust 中可以使用
prost
或protobuf
库来实现。Protobuf 是一种语言无关、平台无关的可扩展机制,用于序列化结构化数据。
以上就是 Rust 中常用的几种序列化方式。总的来说,如果你需要一个通用的序列化方式,可以选择 Serde。如果你需要一个高效紧凑的二进制格式,可以选择 Bincode、CBOR 或 MessagePack。如果你需要跨语言支持,可以选择 Protobuf。
这里主要演示下Serde的用法,它提供了一组宏和 trait,用于将 Rust 数据结构转换为各种格式的序列化表示,并将序列化表示转换回 Rust 数据结构。
要使用 Serde,首先需要在 Cargo.toml
文件中添加以下依赖项:
[dependencies]
serde = "1.0"
serde_json = "1.0"
接下来,我们将给出一个使用 Serde 进行 JSON 序列化和反序列化的例子:
use serde::{Serialize, Deserialize};
use serde_json::{Result, Value};#[derive(Serialize, Deserialize, Debug)]
struct Person {name: String,age: u8,address: String,
}fn main() -> Result<()> {// 序列化为 JSONlet person = Person {name: "Alice".to_string(),age: 25,address: "123 ABC Street".to_string(),};let serialized = serde_json::to_string(&person)?;println!("Serialized: {}", serialized);// 反序列化为 Rust 结构体let deserialized: Person = serde_json::from_str(&serialized)?;println!("Deserialized: {:?}", deserialized);Ok(())
}
在上面的例子中,我们定义了一个 Person
结构体,并使用 #[derive(Serialize, Deserialize)]
宏为其实现了 Serde 的 Serialize
和 Deserialize
trait。这使得我们可以将 Person
结构体序列化为 JSON 字符串,并将 JSON 字符串反序列化为 Person
结构体。
在 main
函数中,我们首先创建一个 Person
实例,然后使用 serde_json::to_string
方法将其序列化为 JSON 字符串,并打印出来。接着,我们使用 serde_json::from_str
方法将 JSON 字符串反序列化为 Person
结构体,并打印出来。
网络请求
请求Http接口
在 Rust 中,你可以使用第三方库来进行 HTTP 请求。最常用的库之一是 reqwest
,它提供了简单且易于使用的 API 来发送 HTTP 请求。
首先,你需要在 Cargo.toml
文件中添加 reqwest
依赖项:
[dependencies]
reqwest = "0.11"
接下来,我们将给出一个使用 reqwest
发送 GET 请求的示例:
use reqwest::Error;#[tokio::main]
async fn main() -> Result<(), Error> {let response = reqwest::get("https://api.example.com/users").await?;let body = response.text().await?;println!("Response Body: {}", body);Ok(())
}
在上面的示例中,我们使用 reqwest::get
方法发送一个 GET 请求到 https://api.example.com/users
。然后,我们使用 response.text().await?
来获取响应的文本内容,并打印出来。
需要注意的是,我们使用了 tokio::main
宏来异步运行请求。因此,你需要在 main
函数之前添加 tokio
作为依赖项,并在 main
函数前面使用 #[tokio::main]
注解。
到目前为止,我们学习了集合、并发、序列化和网络请求,最后再留一个作业: 请求百度首页,并解析出所有的http链接。 结合本文所学哦
相关文章:
20天学会rust(四)常见系统库的使用
前面已经学习了rust的基础知识,今天我们来学习rust强大的系统库,从此coding事半功倍。 集合 数组&可变长数组 在 Rust 中,有两种主要的数组类型:固定长度数组(Fixed-size Arrays)和可变长度数组&…...

drawio----输出pdf为图片大小无空白(图片插入论文)
自己在写论文插入图片时为了让论文图片放大不模糊,啥方法都试了,最后摸索出来这个。 自己手动画图的时候导出pdf总会出现自己的图片很小,pdf的白边很大如下如所示,插入论文的时候后虽然放大不会模糊,但是白边很大会显…...

2021年09月 C/C++(二级)真题解析#中国电子学会#全国青少年软件编程等级考试
第1题:字符统计 给定一个由a-z这26个字符组成的字符串,统计其中哪个字符出现的次数最多。 输入 输入包含一行,一个字符串,长度不超过1000。 输出 输出一行,包括出现次数最多的字符和该字符出现的次数,中间以…...

HCIP VRRP技术
一、VRRP概述 VRRP(Virtual Router Pedundancy Protocol)虚拟路由器冗余协议,既能够实现网关的备份,又能够解决多个网关之间互相冲突的问题,从而提高网络可靠性。 局域网中的用户的终端通常采用配置一个默认网关的形…...
JAVA AES ECB/CBC 加解密
JAVA AES ECB/CBC 加解密 1. AES ECB2. AES CBC 1. AES ECB package org.apache.jmeter.functions;/*** author yuyang*/import org.apache.commons.lang3.StringUtils; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec;/*** a…...

Android FrameWork 层 Handler源码解析
Handler生产者-消费者模型 在android开发中,经常会在子线程中进行一些耗时操作,当操作完毕后会通过handler发送一些数据给主线程,通知主线程做相应的操作。 其中:子线程、handler、主线程,其实构成了线程模型中经典的…...

list
目录 迭代器 介绍 种类 本质 介绍 模拟实现 注意点 代码 迭代器 介绍 在C中,迭代器(Iterators)是一种用于遍历容器(如数组、vector、list等)中元素的工具 无论容器的具体实现细节如何,访问容器中的元素的方…...

ABeam×Startup丨德硕管理咨询(深圳)创新研究团队前往灵境至维·既明科技进行拜访交流
近日,德硕管理咨询(深圳)(以下简称“ABeam-SZ”)创新研究团队一行前往灵境至维既明科技有限公司(以下简称“灵境至维”)进行拜访交流,探讨线上虚拟空间的商业模式。 现场合影 &…...
TCP的相关性质
文章目录 流量控制拥塞控制拥塞窗口 延迟应答捎带应答面向字节流粘包问题TCP的异常 流量控制 由于接收端处理数据的速度是有限的,如果发送端发的太快,那么接收端的缓冲区就可能会满。此时如果发送端还发数据,就会出现丢包现象,并…...
pointpillars在2D CNN引入自适应注意力机制
在给定的代码中,您想要引入自适应注意力机制。自适应注意力机制通常用于增强模型的感受野,从而帮助模型更好地捕捉特征之间的关系。在这里,我将展示如何在您的代码中引入自适应注意力机制,并提供详细的解释。 首先,让…...

【每日一题】1572. 矩阵对角线元素的和
【每日一题】1572. 矩阵对角线元素的和 1572. 矩阵对角线元素的和题目描述解题思路 1572. 矩阵对角线元素的和 题目描述 给你一个正方形矩阵 mat,请你返回矩阵对角线元素的和。 请你返回在矩阵主对角线上的元素和副对角线上且不在主对角线上元素的和。 示例 1&a…...
leetcode原题:检查子树
题目: 检查子树。你有两棵非常大的二叉树:T1,有几万个节点;T2,有几万个节点。设计一个算法,判断 T2 是否为 T1 的子树。 如果 T1 有这么一个节点 n,其子树与 T2 一模一样,则 T2 为…...

2023年国赛数学建模思路 - 案例:ID3-决策树分类算法
文章目录 0 赛题思路1 算法介绍2 FP树表示法3 构建FP树4 实现代码 建模资料 0 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 1 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法,就是频繁模…...
可视化绘图技巧100篇进阶篇(七)-三维堆积柱形图(3D Stacked Bar Chart)
目录 前言 适用场景 图例 绘图工具及代码实现 HighCharts echarts MATLAB...

React源码解析18(7)------ 实现事件机制(onClick事件)
摘要 在上一篇中,我们实现了useState的hook,但由于没有实现事件机制,所以我们只能将setState挂载在window上。 而这一篇主要就是来实现事件系统,从而实现通过点击事件进行setState。 而在React中,虽然我们是将事件绑…...

Android app专项测试之耗电量测试
前言 耗电量指标 待机时间成关注目标 提升用户体验 通过不同的测试场景,找出app高耗电的场景并解决 01、需要的环境准备 1、python2.7(必须是2.7,3.X版本是不支持的) 2、golang语言的开发环境 3、Android SDK 此三个的环境搭建这里就不详细说了&am…...
设计模式-面试常问
1.单例模式 保证系统中,一个类,只有一个实例,并且提供对外访问。 优点:只有一个对象,可以节省资源。适合频繁创建销毁对象的场景。 实现:要用到static,静态私有对象。暴露单例的静态方法。 &…...

聊聊在集群环境中本地缓存如何进行同步
前言 之前有发过一篇文章聊聊如何利用redis实现多级缓存同步。有个读者就给我留言说,因为他项目的redis版本不是6.0版本,因此他使用我文章介绍通过MQ来实现本地缓存同步,他的同步流程大概如下图 他原来的业务流程是每天凌晨开启定时器去爬取…...

【C++深入浅出】初识C++上篇(关键字,命名空间,输入输出,缺省参数,函数重载)
目录 一. 前言 二. 什么是C 三. C关键字初探 四. 命名空间 4.1 为什么要引入命名空间 4.2 命名空间的定义 4.3 命名空间使用 五. C的输入输出 六. 缺省参数 6.1 缺省参数的概念 6.2 缺省参数的分类 七. 函数重载 7.1 函数重载的概念 7.2 函数重载的条件 7.3 C支…...
租房合同范本
房屋租赁合同 甲方(出租方): 身份证: 联系电话: 乙方(承租方): 身份证: 联系电话: …...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...

React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...

Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...