【Rust】Rust学习
文档:Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com)
墙裂推荐这个文档
第一章入门
入门指南 - Rust 程序设计语言 简体中文版
第二章猜猜看游戏
猜猜看游戏教程 - Rust 程序设计语言 简体中文版 (bootcss.com)
// 导入库
use std::io;
use std::cmp::Ordering;
use rand::Rng;fn main() {println!("Guess the number!");// 生成一个随机数,范围在[1, 100],,需要在配置文toml中加依赖let secret_number = rand::thread_rng().gen_range(1, 101);// loop循环loop {println!("Please input your guess.");// 定义一个可变变量,加mut是可变的,不加mut是不可变的let mut guess = String::new();// 读一行数据到guess中,返回值是Result(枚举,成员是Ok和Err)// Result 拥有expect方法// 如果 io::Result 实例的值是 Err,expect 会导致程序崩溃,并显示当做参数传递给 expect 的信息。// 如果 read_line 方法返回 Err,则可能是来源于底层操作系统错误的结果。如果 io::Result 实例的值是 Ok,expect 会获取 Ok 中的值并原样返回。io::stdin().read_line(&mut guess).expect("Failed to read line");// Rust 允许用一个新值来 隐藏 (shadow) guess 之前的值// String 实例的 trim 方法会去除字符串开头和结尾的空白字符。// 字符串的 parse 方法 将字符串解析成数字。// parse也是有返回值,成功的话就是返回num,失败则继续输入let guess: u32 = match guess.trim().parse() {Ok(num) => num,Err(_) => continue,};println!("You guessed: {}", guess);// 用match来匹配match guess.cmp(&secret_number) {Ordering::Less => println!("Too small!"), // guess < secret_numberOrdering::Greater => println!("Too big!"),Ordering::Equal => {println!("You win!"); // 匹配,跳出循环break;}}}
}
代码量不大,学了不少语法
测试

第三章常见编程概念
3.1 变量与可变性
变量不可改
fn main() {println!("Hello, world!");let i = 3;println!("i = {}", i);i = 23; // 变量不可以更改println!("i = {}", i);
}
结果

可变变量,可以修改
fn main() {println!("Hello, world!");let mut i = 3;println!("i = {}", i);i = 23;println!("i = {}", i);
}
变量与常量的区别
(1)不允许对常量使用mut;
(2)声明常量使用 const 关键字而不是 let,并且 必须 注明值的类型;
(3)常量可以在任何作用域中声明;
(4)常量只能被设置为常量表达式,而不能是函数调用的结果,或任何其他只能在运行时计算出的值。
const MAX_POINTS : u32 = 100_100;
隐藏(Shadowing)
fn main() {let x = 5;let x = x + 1;let x = x * 2;println!("The value of x is: {}", x);
}
隐藏与将变量标记为 mut 是有区别的。当不小心尝试对变量重新赋值时,如果没有使用 let 关键字,就会导致编译时错误。通过使用 let,我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。
mut 与隐藏的另一个区别是,当再次使用 let 时,实际上创建了一个新变量,我们可以改变值的类型,但复用这个名字。
3.2 数据类型
标量类型 标量(scalar)类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。
整数
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8-bit | i8 | u8 |
| 16-bit | i16 | u16 |
| 32-bit | i32 | u32 |
| 64-bit | i64 | u64 |
| 128-bit | i128 | u128 |
| arch | isize | usize |
整形字面值
| 数字字面值 | 例子 |
|---|---|
| Decimal | 98_222 |
| Hex | 0xff |
| Octal | 0o77 |
| Binary | 0b1111_0000 |
Byte (u8 only) | b'A' |
浮点型
Rust 也有两个原生的 浮点数(floating-point numbers)类型,它们是带小数点的数字。Rust 的浮点数类型是 f32 和 f64,分别占 32 位和 64 位。默认类型是 f64,因为在现代 CPU 中,它与 f32 速度几乎一样,不过精度更高。
Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取余。
fn main() {// 加法let _sum = 5 + 10;// 减法let _difference = 95.5 - 4.3;// 乘法let _product = 4 * 30;// 除法let _quotient = 56.7 / 32.2;// 取余let _remainder = 43 % 5;
}
布尔型
正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:true 和 false。Rust 中的布尔类型使用 bool 表示。
字符类型
在 Rust 中,拼音字母(Accented letters),中文、日文、韩文等字符,emoji(绘文字)以及零长度的空白字符都是有效的 char 值。Unicode 标量值包含从 U+0000 到 U+D7FF 和 U+E000 到 U+10FFFF 在内的值。不过,“字符” 并不是一个 Unicode 中的概念,所以人直觉上的 “字符” 可能与 Rust 中的 char 并不符合。
fn main() {let c = 'z';let z = 'ℤ';let heart_eyed_cat = '😻';
}
复合类型
复合类型(Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)
元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。
使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。
fn main() {let tup: (i32, f64, u8) = (500, 6.4, 1);let tup = (500, 6.4, 1);let (x, y, z) = tup;println!("The value of y is: {}", y);let x: (i32, f64, u8) = (500, 6.4, 1);let _five_hundred = x.0; // 解构let _six_point_four = x.1; // 解构let _one = x.2; // 解构}
另一个包含多个值的方式是 数组(array)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,因为 Rust 中的数组是固定长度的:一旦声明,它们的长度不能增长或缩小。
Rust 中,数组中的值位于中括号内的逗号分隔的列表中。
fn main() {let a = [1, 2, 3, 4, 5];let months = ["January", "February", "March", "April", "May", "June", "July","August", "September", "October", "November", "December"];let a: [i32; 5] = [1, 2, 3, 4, 5];let first = a[0];let second = a[1];let index = 3;let element = a[index];println!("The value of element is: {}", element);
}
3.3 函数如何工作
和C++大同小异,这里不同的是具有返回值的函数
fn five() -> i32 {// 没有return// 没有分号5
}fn main() {let x = five();println!("The value of x is: {}", x);
}
3.4 注释
不赘述
3.5 控制流
if-else
fn main() {let number = 6;// 注意没有括号if number % 4 == 0 {println!("number is divisible by 4");} else if number % 3 == 0 {println!("number is divisible by 3");} else if number % 2 == 0 {println!("number is divisible by 2");} else {println!("number is not divisible by 4, 3, or 2");}
}
在let语句中使用if
fn main() {let condition = true;let number = if condition {5} else {6};println!("The value of number is: {}", number);
}
if 的每个分支的可能的返回值都必须是相同类型
循环语句
Rust 有三种循环:loop、while 和 for。
loop
fn main() {let mut counter = 0;let result = loop {counter += 1;if counter == 10 {break counter * 2;}};println!("The result is {}", result);
}
while
fn main() {let mut number = 3;while number != 0 {println!("{}!", number);number = number - 1;}println!("LIFTOFF!!!");
}
for
fn main() {let a = [10, 20, 30, 40, 50];for element in a.iter() {println!("the value is: {}", element);}for number in (1..4).rev() {println!("{}!", number);}println!("LIFTOFF!!!");
}
第四章认识所有权
所有权(系统)是 Rust 最为与众不同的特性,它让 Rust 无需垃圾回收(garbage collector)即可保障内存安全。因此,理解 Rust 中所有权如何工作是十分重要的。
4.1 所有权
所有运行的程序都必须管理其使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存;在另一些语言中,程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。在运行时,所有权系统的任何功能都不会减慢程序。
所有权规则
- Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
- 值有且只有一个所有者。
- 当所有者(变量)离开作用域,这个值将被丢弃。
字符串字面值不可变,这里以String为例
fn main() {println!("Hello, world!");// String在堆上// from 函数基于字符串字面值来创建 Stringlet mut str = String::from("hello");str.push_str(", world"); // push_str() 在字符串后追加字面值println!("{}", str);
}
结果

对于 String 类型,为了支持一个可变,可增长的文本片段,需要在堆上分配一块在编译时未知大小的内存来存放内容。这意味着:
- 必须在运行时向操作系统请求内存。
- 需要一个当我们处理完
String时将内存返回给操作系统的方法。
第一部分由我们完成:当调用 String::from 时,它的实现 (implementation) 请求其所需的内存。这在编程语言中是非常通用的。
然而,第二部分实现起来就各有区别了。在有 垃圾回收(garbage collector,GC)的语言中, GC 记录并清除不再使用的内存,而我们并不需要关心它。没有 GC 的话,识别出不再使用的内存并调用代码显式释放就是我们的责任了,跟请求内存的时候一样。从历史的角度上说正确处理内存回收曾经是一个困难的编程问题。如果忘记回收了会浪费内存。如果过早回收了,将会出现无效变量。如果重复回收,这也是个 bug。我们需要精确的为一个
allocate配对一个free。
Rust 采取了一个不同的策略:内存在拥有它的变量离开作用域后就被自动释放。
fn main() {{let s = String::from("hello"); // 从此处起,s 是有效的// 使用 s} // 此作用域已结束,// s 不再有效
}
注意:在 C++ 中,这种 item 在生命周期结束时释放资源的模式有时被称作 资源获取即初始化(Resource Acquisition Is Initialization (RAII))。如果你使用过 RAII 模式的话应该对 Rust 的
drop函数并不陌生。
变量与数据交互的方式(一):移动
fn main() {let x = 5;let y = x;
}
基本变量赋值,x和y都在栈中,且值为5。
那么String类型呢?
fn main() {let s1 = String::from("hello");let s2 = s1;
}
String 由三部分组成,如图左侧所示:一个指向存放字符串内容内存的指针,一个长度,和一个容量。这一组数据存储在栈上。右侧则是堆上存放内容的内存部分。

长度表示 String 的内容当前使用了多少字节的内存。容量是 String 从操作系统总共获取了多少字节的内存。长度与容量的区别是很重要的,不过在当前上下文中并不重要,所以现在可以忽略容量。
当将 s1 赋值给 s2,String 的数据被复制了,这意味着我们从栈上拷贝了它的指针、长度和容量。我们并没有复制指针指向的堆上数据。

像C++中的浅拷贝
如果 Rust 也拷贝了堆上的数据,那么内存看起来就是这样的。如果 Rust 这么做了,那么操作 s2 = s1 在堆上数据比较大的时候会对运行时性能造成非常大的影响。

(浅拷贝)这就有了一个问题:当 s2 和 s1 离开作用域,他们都会尝试释放相同的内存。这是一个叫做 二次释放(double free)的错误,也是之前提到过的内存安全性 bug 之一。两次释放(相同)内存会导致内存污染,它可能会导致潜在的安全漏洞。
为了确保内存安全,这种场景下 Rust 的处理有另一个细节值得注意。与其尝试拷贝被分配的内存,Rust 则认为 s1 不再有效,因此 Rust 不需要在 s1 离开作用域后清理任何东西。看看在 s2 被创建之后尝试使用 s1 会发生什么;这段代码不能运行:
fn main() {let s1 = String::from("hello");let s2 = s1;println!("{}, world!", s1);}
结果:

如果你在其他语言中听说过术语 浅拷贝(shallow copy)和 深拷贝(deep copy),那么拷贝指针、长度和容量而不拷贝数据可能听起来像浅拷贝。不过因为 Rust 同时使第一个变量无效了,这个操作被称为 移动(move),而不是浅拷贝。上面的例子可以解读为 s1 被 移动 到了 s2 中。那么具体发生了什么,如图

这样就解决了之前的问题!因为只有 s2 是有效的,当其离开作用域,它就释放自己的内存。
另外,这里还隐含了一个设计选择:Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何 自动 的复制可以被认为对运行时性能影响较小。
变量与数据交互的方式(二):克隆
如果 确实 需要深度复制 String 中堆上的数据,而不仅仅是栈上的数据,可以使用一个叫做 clone 的通用函数。
fn main() {let s1 = String::from("hello");let s2 = s1.clone();println!("{}, world!", s1);println!("{}, world!", s2);
}
这段代码的实际结果就是如下图

只在栈上的数据:拷贝
但这段代码似乎与我们刚刚学到的内容相矛盾:没有调用 clone,不过 x 依然有效且没有被移动到 y 中。
fn main() {let x = 5;let y = x;println!("x = {}, y = {}", x, y);
}
原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 y 后使 x 无效。换句话说,这里没有深浅拷贝的区别,所以这里调用 clone 并不会与通常的浅拷贝有什么不同,可以不用管它。
所有权与函数
将值传递给函数在语义上与给变量赋值相似。向函数传递值可能会移动或者复制,就像赋值语句一样。
简单的案例,见注释
fn main() {let s = String::from("hello"); // s 进入作用域takes_ownership(s); // s 的值移动到函数里 ...// ... 所以到这里不再有效// println!("{}", s); // 报错let x = 5; // x 进入作用域makes_copy(x); // x 应该移动函数里,// 但 i32 是 Copy 的,所以在后面可继续使用 x} // 这里, x 先移出了作用域,然后是 s。但因为 s 的值已被移走,// 所以不会有特殊操作fn takes_ownership(some_string: String) { // some_string 进入作用域println!("{}", some_string);
} // 这里,some_string 移出作用域并调用 `drop` 方法。占用的内存被释放fn makes_copy(some_integer: i32) { // some_integer 进入作用域println!("{}", some_integer);
} // 这里,some_integer 移出作用域。不会有特殊操作
复杂一点的
fn main() {let s = String::from("hello"); // s 进入作用域takes_ownership(s); // s 的值移动到函数里 ...// ... 所以到这里不再有效// println!("{}", s); // 报错
}
fn takes_ownership(some_string: String) { // some_string 进入作用域let str = String::from(some_string);println!("{}", some_string); // 报错
}
返回值与作用域
返回值也可以转移所有权
见注释
fn main() {let s1 = gives_ownership(); // gives_ownership 将返回值// 移给 s1let s2 = String::from("hello"); // s2 进入作用域let s3 = takes_and_gives_back(s2); // s2 被移动到// takes_and_gives_back 中, // 它也将返回值移给 s3
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,// 所以什么也不会发生。s1 移出作用域并被丢弃fn gives_ownership() -> String { // gives_ownership 将返回值移动给// 调用它的函数let some_string = String::from("hello"); // some_string 进入作用域.some_string // 返回 some_string 并移出给调用的函数
}// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域a_string // 返回 a_string 并移出给调用的函数
}
变量的所有权总是遵循相同的模式:将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时,其值将通过 drop 被清理掉,除非数据被移动为另一个变量所有。
在每一个函数中都获取所有权并接着返回所有权有些啰嗦。如果想要函数使用一个值但不获取所有权该怎么办呢?如果还要接着使用它的话,每次都传进去再返回来就有点烦人了,除此之外,也可能想返回函数体中产生的一些数据。可以使用元组来返回多个值
fn main() {let s1 = String::from("hello");let (s2, len) = calculate_length(s1);println!("The length of '{}' is {}.", s2, len);
}fn calculate_length(s: String) -> (String, usize) {let length = s.len(); // len() 返回字符串的长度(s, length)
}
好
返回参数的所有权
见引用部分
4.4 引用与借用
下面是如何定义并使用一个calculate_length 函数,它以一个对象的引用作为参数而不是获取值的所有权:
fn main() {let s1 = String::from("hello");let len = calculate_length(&s1);println!("The length of '{}' is {}.", s1, len);
}fn calculate_length(s: &String) -> usize {s.len()
}
& 符号就是 引用,它允许你使用值但不获取其所有权。

注意:与使用
&引用相反的操作是 解引用(dereferencing),它使用解引用运算符,*。
变量 s 有效的作用域与函数参数的作用域一样,不过当引用离开作用域后并不丢弃它指向的数据,因为我们没有所有权。
将获取引用作为函数参数称为 借用(borrowing)。
正如变量默认是不可变的,引用也一样(默认)不允许修改引用的值。
fn main() {let s = String::from("hello");change(&s);
}fn change(some_string: &String) {some_string.push_str(", world");
}
结果

可变引用
fn main() {// s也必须是mut的let mut s = String::from("hello");change(&mut s);
}// 注意形参的形式
fn change(some_string: &mut String) {some_string.push_str(", world");
}
不过可变引用有一个很大的限制:在特定作用域中的特定数据有且只有一个可变引用。
fn main() {let mut s = String::from("hello");let r1 = &mut s;let r2 = &mut s; // 错误println!("{}, {}", r1, r2);}
相关文章:
【Rust】Rust学习
文档:Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com) 墙裂推荐这个文档 第一章入门 入门指南 - Rust 程序设计语言 简体中文版 第二章猜猜看游戏 猜猜看游戏教程 - Rust 程序设计语言 简体中文版 (bootcss.com) // 导入库 use std::io; use s…...
Linux 常用命令之配置环境变量 PATH
PATH是系统环境变量中的一种,同时将一些二进制文件的绝对路径追加进去,则在系统终端中可以发现这些路径下的文件。 一. 环境变量设置 export PATH<二进制文件的绝对路径>:$PATH 以下为结合实际例子的操作 1、临时设置 打开一个终端执行如下命令 e…...
flask-----蓝图
1.引入蓝图 flask都写在一个文件中,项目这样肯定不行,会导致循环导入的问题,分目录,分包,使用蓝图划分目录。 2.使用蓝图 步骤如下: -1 实例化得到一个蓝图对象-order_blueBlueprint(order,__name__,tem…...
学习左耳听风栏目90天——第一天 1-90(学习左耳朵耗子的工匠精神,对技术的热爱)【洞悉技术的本质,享受科技的乐趣】
洞悉技术的本质,享受科技的乐趣 第一篇,我的感受就是 耗叔是一个热爱技术,可以通过代码找到快乐的技术人。 作为it从业者,我们如何可以通过代码找到快乐呢?这是一个问题? 至少目前,我还没有这种…...
后端登录安全的一种思路
PS:作者是小白能接触到的就只会这样写。勿喷。 前提 思路: 结合io流将登录token存储到配置文件中,不将token存储到浏览器端,从而避免盗取。 下面jwt的学习可以参考下这个: JWT --- 入门学习_本郡主是喵的博客-CSDN博客 JWT工具类 Component public class JWTtU…...
【深度学习_TensorFlow】激活函数
写在前面 上篇文章我们了解到感知机使用的阶跃函数和符号函数,它们都是非连续,导数为0的函数: 建议回顾上篇文章,本篇文章将介绍神经网络中的常见激活函数,这些函数都是平滑可导的,适合于梯度下降算法。 写…...
机器学习笔记之优化算法(七)线搜索方法(步长角度;非精确搜索;Wolfe Condition)
机器学习笔记之优化算法——线搜索方法[步长角度,非精确搜索,Wolfe Condition] 引言回顾: Armijo \text{Armijo} Armijo准则及其弊端 Glodstein \text{Glodstein} Glodstein准则及其弊端 Wolfe Condition \text{Wolfe Condition} Wolfe Condi…...
十四.redis哨兵模式
redis哨兵模式 1.概述2.测试3.哨兵模式优缺点 redis哨兵模式基础是主从复制 1.概述 主从切换的技术方法:当主节点服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费时费力,还会造成一段时间内服…...
采用UWB技术开发的智慧工厂人员定位系统源码【UWB定位基站、卡牌】
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。UWB定位系统依托在移动通信,雷达,微波电路,云计算与大数据…...
当你软件测试遇上加密接口,是不是就不能测了?
相信大家在工作中做接口测试的时候,肯定会遇到一个场景,那就是你们的软件,密码是加密存储的。 那么这样的话,我们在执行接口的时候,对于密码的处理就开始头疼了。 所以,本文将使用jmeter这款java开源的接…...
Flink
Flink(Apache Flink)是一个开源的分布式流处理引擎和批处理框架。它是由 Apache 软件基金会维护的项目,旨在处理大规模数据的实时流式处理和批处理任务。Flink 提供了强大的流处理和批处理功能,具有低延迟、高吞吐量和高容错性&am…...
python入门常用操作
python常用操作 1、ndarry数组的切片2、print用法2.1格式化输出format2.2字符串格式化输出 3、均值滤波函数 1、ndarry数组的切片 例如一个5列的ndarry数组,想要获取第2列和第3列数据,可以用 #(1)用法1 data[:,1:3],…...
SpringBoot复习:(21)自定义ImportBeanDefinitionRegistrar
要达到的目的:将某个包下使用了某个自定义注解(比如MyClassMapper)的类注册到Spring 容器。 一、自定义注解: package com.example.demo.service;import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy;Rete…...
小黑子—JavaWeb:第五章 - JSP与会话跟踪技术
JavaWeb入门5.0 1. JSP1.1 JSP快速入门1.2 JSP原理1.3 JSP脚本1.3.1 JSP缺点 1.4 EL 表达式1.5 JSTL 标签1.5.1 JSTL 快速入门1.5.1 - I JSTL标签if1.5.1 - II JSTL标签forEach 1.6 MVC模式1.7 三层架构1.8 实现案例1.8.1 环境准备1.8.2 查询所有1.8.3 添加数据1.8.4 修改1.8.4…...
Python - 【socket】 客户端client重连处理简单示例Demo(一)
一. 前言 在Python中,使用socket进行网络通信时,如果连接断开,可以通过以下步骤实现重连处理 二. 示例代码 1. 定义一个函数,用于建立socket连接 import socketdef connect_socket(host, port):while True:try:# 建立socket连…...
Redis 基础
1.定义 Redis 是一个高性能的key-value数据库,key是字符串类型。 2.核心特点: 单进程: Redis的服务器程序采用的是单进程模型来处理客户端的请求。对读写时间的响 应是通过对epoll函数的包装来做到的。 3.数据类型: 键的类型…...
【0805作业】Linux中 AB终端通过两根有名管道进行通信聊天(半双工)
作业一:打开两个终端,要求实现AB进程对话【两根管道】 打开两个终端,要求实现AB进程对话 A进程先发送一句话给B进程,B进程接收后打印B进程再回复一句话给A进程,A进程接收后打印重复1.2步骤,当收到quit后&am…...
ruby - ckeditor 设置编辑器高度
参考:Blogs <% f.cktext_area :zh_content, ckeditor: { height: 1000} %>...
WMS仓库管理系统研发规划说明
01 产品背景 1.1 背景概述 aboss WMS东南亚仓库管理系统是一个基于BigSeller系统的使用基础上,加上多仓库的解决思路,解决入库业务、出库业务、仓库调拨、库存调拨和虚仓管理等功能,对批次管理、物料对应、库存盘点、质检管理、虚仓管理和即…...
JavaScript |(六)DOM事件 | 尚硅谷JavaScript基础实战
学习来源:尚硅谷JavaScript基础&实战丨JS入门到精通全套完整版 文章目录 📚事件对象📚事件的冒泡📚事件的委派📚事件的绑定🐇赋值绑定🐇addEventListener()🐇attachEvent()&…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
