- 支持试读
内容简介
本章将带你了解本专题的整体内容。 - 支持试读
项目结构
本专题相对复杂,规划好结构非常必要。本专题至少包含四大部分:阶段一的单节点商城、阶段二的分布式商城、商城的UI和各种所需要的前置知识。 阶段一开篇:用户注册与激活
本章我们将开启本专题的旅程,既是阶段一的开篇,也是整个专题的开篇。本章我们将实现用户注册和激活功能。本章涉及两个数据模型:用户和激活码,同时还涉及邮件发送和人机验证。对于激活码,我们将使用 PostgreSQL 的某些特性来代替 Redis。前置知识:AXUM中间件
我们曾在《漫游AXUM》专题中讲解过中间件,随着版本迭代,AXUM中间件的机制发生了很大的改变。本章我们将基于当前最新版本的AXUM,讨论各种类型的中间件的实现以及如何在 handler 中,获取中间件里的数据。用户登录及 JWT 鉴权
本章我们将实现用户登录功能,与之对应的还有鉴权。我们将使用 JWT 配合 AXUM 中间件实现自动鉴权。同时,在 handler 中,还可以获取到中间件维护的鉴权数据。- 支持试读
前置知识:位运算
你或许有疑问,Web 应用也要位运算?正如你所疑惑的,和底层应用不同,作为高级应用的 Web 应用基本不需要考虑位运算,无论是本章还是后续涉及位运算的章节,都可以用其它数据结构代替。我们之所以会把位运算拿出来给大家介绍,是因为:一、让应用更高效;二、正因为大部分 Web 应用都没用上位运算,所以我们想阐明一件事:对于 Web 应用,位运算也同样能发挥重要作用。 前置知识:PostgreSQL树
我们来讨论 PostgreSQL 维护树的数据结构。由于 PostgreSQL 支持递归查询和递归视图,所以很多模式实现起来都非常方便。本章将讨论使用递归视图,实现《邻接表模式》和《路径枚举模式》结合的树模型。前置知识:PostgreSQL 的继承
你没看错,PostgreSQL 和 OOP 一样,支持继承。惊不惊喜?意不意外?本章我们将学习这一特性。商品分类和保证金设置
本章将实现包含保证金设置的商品分类。同时,你将学习到 PostgreSQL 强大的功能:表的继承的应用。
前置知识:位运算
你或许有疑问,Web 应用也要位运算?正如你所疑惑的,和底层应用不同,作为高级应用的 Web 应用基本不需要考虑位运算,无论是本章还是后续涉及位运算的章节,都可以用其它数据结构代替。我们之所以会把位运算拿出来给大家介绍,是因为:一、让应用更高效;二、正因为大部分 Web 应用都没用上位运算,所以我们想阐明一件事:对于 Web 应用,位运算也同样能发挥重要作用。
Linux 文件权限
首先要说,以文件(在 Linux 系统中,万物皆文件。这里指的是正常思维下的文件,也就是不包括目录、设备、句柄等在内的简单文件)为例,权限有三个部分,每个部分的取值的含义是一样的,区别只在于不同的用户组。所以,我们取其中一个,比如 755
里的 7
来说明。
所以,7
的意思是,可执行+可写+可读,即:1+2+4
。相应的,如果权限是3
,表示可执行+可写。
我们至少可以总结三点:
xwr
(习惯写成rwx
)它们的取值都是2的n次方,n大于等于0的整数- 任意多个值可以组合成新值
- 这个新值可以表达包含这些组合的值,比如
7
可以表达全部权限,即rwx
- 这个新值并不是2的n次方,比如
3
并不是2的n次方
- 这个新值可以表达包含这些组合的值,比如
那么,这些操作真的需要进行算术运算吗?答案显而易见,正如本章标题,可以使用位运算来更高效、直观地进行操作。
我们来看一下 Rust 位运算。和 C 不同,Rust 的位运算只支持简单的数字,如需使用枚举,只能使用C风格的枚举,并且需要进行强制类型转换。原因在于,Rust 除了 C 风格的枚举,还支持更多风格的枚举。而 C 枚举可以简单看作是整数的别名(同样地,此论点仅供参考)。
简单地说,Rust 既支持 C 风格的简单的枚举,一个枚举对应一个整数;也支持复杂的枚举,比如嵌入 tuple、结构体等,这也是 Rust 强大之处。我们先来看一下 Rust 的位运算吧。
简单数字的位运算
我们来模拟一下 Linux 文件权限:
fn main() {
// 定义权限
/// 读权限
const R: u8 = 4;
/// 写权限
const W: u8 = 2;
/// 执行权限
const X: u8 = 1;
// 组合权限:“或”运算
let p = R | W;
// 判断是否有权限:“与”运算,如果结果还是指定的权限,那么说明拥有该权限
if p & R == R {
println!("【1】有读权限");
}
if p & W == W {
println!("【1】有写权限");
}
if p & X == X {
println!("【1】有执行权限");
}
let p = R | W | X;
if p & R == R {
println!("【2】有读权限");
}
if p & W == W {
println!("【2】有写权限");
}
if p & X == X {
println!("【2】有执行权限");
}
// 去除权限:组合“与”、“非”运算
let p = p & !W;
if p & R == R {
println!("【3】有读权限");
} else {
println!("【3】无读权限")
}
if p & W == W {
println!("【3】有写权限");
} else {
println!("【3】无写权限");
}
if p & X == X {
println!("【3】有执行权限");
} else {
println!("【3】无执行权限");
}
println!("【4】当前权限:{p}");
}
对于权限来说,我们只需要了解三个位运算符,总结起来就是“非与或”。通过这三个运算符,我们能实现:
- 权限的组合,
|
运算符(位运算,或)。通过该运算符,可以将多个权限进行组合,例如例子中的let p = R | W;
,此时,p
拥有了R
和W
权限 - 判断是否具有某权限,
&
运算符(位运算,与)。通过权限变量和某具体的权限进行&
操作,如果结果还是该具体的权限,那么我们说,该权限变量具有该具体的权限,如例子中的if p & R == R
- 去除某权限,通过组合
&
运算符和!
运算符(位运算,非),可以去除指定的具体权限,如例子中的let p = p & !W;
本例,可以通过 Rust Play进行查看和运行。
C 风格的枚举值的位运算
- 必须是 C 风格的枚举,或者可以转换为整数的值
- 必要的时候,需要手动进行类型转换
enum Per {
X = 1,
W = 2,
R = 4,
}
fn main() {
let p = Per::R as u8 | Per::W as u8 | Per::X as u8;
if has_per(p, Per::R as u8) {
println!("【1】有读权限");
} else {
println!("【1】无读权限");
}
if has_per(p, Per::W as u8) {
println!("【1】有写权限");
} else {
println!("【1】无写权限");
}
if has_per(p, Per::X as u8) {
println!("【1】有执行权限");
} else {
println!("【1】无执行权限");
}
let p = p & !(Per::W as u8);
if has_per(p, Per::R as u8) {
println!("【2】有读权限");
} else {
println!("【2】无读权限");
}
if has_per(p, Per::W as u8) {
println!("【2】有写权限");
} else {
println!("【2】无写权限");
}
if has_per(p, Per::X as u8) {
println!("【2】有执行权限");
} else {
println!("【2】无执行权限");
}
println!("当前权限:{}", p);
}
/// 检查是否具有某权限
fn has_per(p: u8, target: u8) -> bool {
p & target == target
}
从以上例子可以看出,C 风格枚举的位运算本质就是简单数字的位运算,需要注意的是,我们需要手动进行类型转换。本示例代码可以在Rust Play上查看并运行。
BitFlags
Rust 的另一个强大之处在于,和许多现代语言一样,可以通过包装来实现自定义类型,并进行扩展。和其它语言不同的是,Rust 有宏,借助宏可以做到零成本抽象,把包装器做成和被包装对象一样的效率。
bigflags
,用于实现 C 风格枚举的位运算。
模拟 Linux 的文件权限
- 通过 bitflags ,我们可以很容易的实现 C 风格枚举的位运算
- 额外的,为了使用
==
运算符,我们需要实现PartialEq
- 为了不出现所有权问题,我们需要实现
Clone, Copy
本章代码位于K02.位运算
分支。