English IPA

tags: Learning English 一些通用的规则: 音标后面的 ː 提示拖长音。 元音 大而圆 音标 中文 发音技巧 常见单词 拼读规则 /​æ​/ 爱 张大嘴发中文的「爱」,发音短促有力。 bag map dad sad a /​e​/ 爱 音同 /​æ​/ 但是嘴形要小一些。 get let pen yes e /​ɔː​/ 哦 嘴巴轮圆了发音,并拖长音 floor door store sport oor,ore,or /​ɔ​/ 哦 /​ɔː​/ 的短音 lot dog hot shop o 扁扁扁 音标 中文 发音技巧 常见单词 拼读规则 /iː​/ 一 相比一嘴要扁一些,稍稍更用力一些 see meet he she ee, e /​i​/ 一 /iː​/ 短音 happy daddy honey 词尾的 y 或 ey /​I​/ 一 用 /​e​/ 的嘴形发 /​i​/ this give it city i /əː​/ 呃 相比中文嘴要扁一些,稍稍更用力一些 work girl dirt sir or, ir /​ə​/ 呃 /əː​/ 的短音 again a father weather 单独的 a 及词尾的 er 需要额外注意的: ...

March 17, 2021 · 2 min · Gray King

二叉树的遍历

tags: Algorithm,Data Structures,Binary Search Tree 分为三种:前序、后序和中序,其中最容易用栈改写的是后序。 前序(Preorder):Root -> Left -> Right class Solution { public: void processPreorderTraversal(TreeNode* root, vector<int> & collector) { if (root == nullptr) { return; } processPreorderTraversal(root->left, collector); collector.push_back(root->val); processPreorderTraversal(root->right, collector); } vector<int> inorderTraversal(TreeNode* root) { vector<int> ret; if (root == nullptr) { return ret; } processPreorderTraversal(root, ret); return ret; } }; 中序(Inorder): Left -> Root -> Right class Solution { public: void processInorderTraversal(TreeNode* root, vector<int> & collector) { if (root == nullptr) { return; } processInorderTraversal(root->left, collector); collector.push_back(root->val); processInorderTraversal(root->right, collector); } vector<int> inorderTraversal(TreeNode* root) { vector<int> ret; if (root == nullptr) { return ret; } processInorderTraversal(root, ret); return ret; } }; 后序(Postorder):Left -> Right -> Root class Solution { public: void processPostorderTraversal(TreeNode* root, vector<int> & collector) { if (root == nullptr) { return; } processPostorderTraversal(root->left, collector); processPostorderTraversal(root->right, collector); collector.push_back(root->val); } vector<int> postorderTraversal(TreeNode* root) { vector<int> ret; if (root == nullptr) { return ret; } processPostorderTraversal(root, ret); return ret; } }; 非递归遍历 【刷题】二叉树非递归遍历 ...

February 20, 2021 · 1 min · Gray King

OX-HUGO 批量导出 Markdown

tags: Taking Notes, Org Mode 方案一:通过 Emacs 批处理模式 emacs file.org --batch -f org-hugo-export-wim-to-md --kill --batch 默认不启用配置文件,可以使用 -l emacs file.org --batch -l ~/.emacs.d/init.el -f org-hugo-export-wim-to-md --kill 方案二:通过 LISP 遍历 (mapc (lambda (x) (with-current-buffer (find-file-noselect x) (org-hugo-export-wim-to-md t))) (directory-files "/Users/wh/codes/notes/roam-research-notes-hugo/journal" nil "^[0-9]+$" t))

February 20, 2021 · 1 min · Gray King

中间件

tags: 技术,技术概念 中间件可以对系统进行解耦,比如上层系统对下层系统进行网络请求,考虑下面结构 - U - / | \ /--- | ---\ / | \ A B C 如果下层系统增加节点的话就需要重启 U。 引入 HAProxy 或者 Nginx 之类的中间件可以对两层系统进行解耦: U | +------+ | HA | +------+ / | \ /--- | ---\ / | \ A B C 这样上层和下层系统都依赖中间件,但是系统之间不再强耦合,下层系统可以依赖中间件随意的进行所扩容而不用被上层系统感知。 这时候中间件只要保证中间件稳定即可,可以在中间件上进行热重启。

February 20, 2021 · 1 min · Gray King

技术随想

项目代号 技术概念

February 20, 2021 · 1 min · Gray King

C/C++ thread-local storage

tags: C/C++ source: All about thread-local storage

February 19, 2021 · 1 min · Gray King

macOS max open files

tags: macOS fix “Too many open files in system” error

January 25, 2021 · 1 min · Gray King

GDB 打出所有线程的 Backtrace

tags: GDB thread apply all bt

January 18, 2021 · 1 min · Gray King

C++ LSP

tags: Emacs,LSP,C/C++,CMake 通过如下命令生成 clangd 识别的编译配置文件 mkdir build cd build cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 .. mv compile_commands.json ../ 然后重启 M-x lsp-restart-workspace RET 即可。

January 14, 2021 · 1 min · Gray King

Python behind the scenes #2: how the CPython compiler works

tags: Translate,Incomplete,Python Python 幕后 #2: CPython 编译器如何工作 今天的主题(Today’s subject) 在本系列的第一篇文章中我们研究了 Python 虚拟机。我们学了解到它通过执行一系列叫做字节码(bytecode)的指令。 我们也看到 Python 字节码没有完全描述代码片段的行为。这也是为什么存在一个代码对象(code object)的概念。 执行诸如函数或模块的代码块也就是执行对应的代码对象。代码对象包含了代码块的字节码,包含代码中使用的常量和变量名, 还有代码块的一些属性。 通常,一个 Python 程序员不用编写字节码,并且也不用创建代码对象,而是编写正常的 Python 代码。所有 CPython 必须 能够将源代码转换成代码对象。CPython 编译器就负责这部分工作。我们将通过这部分内容探索它是如何工作的。 Note: 本文参考 CPython 3.9。一些实现细节将必然会随着 CPython 的演进而改变。 我将会尝试关注一些重要的改变并添加更新备注。 什么是 CPython 编译器(What CPython compiler is) 我们已经了解了 CPython 编译器的职责,但是在我们进入到它是如何实现的之前,让我们首先来搞清楚为什么我们称之为编译器? 在通常情况加,编译器是一个将一个程序语言翻译到另一个与之等价的程序语言的程序。编译器有许多种类,但是通常情况下我们 讨论的都是静态编译:将一个高级语言的程序翻译成机器码。CPython 编译器也是这样吗?要回答这个问题,我们先看一下静态编 译器的传统三阶段设计(three-stage design)。 编译器前端(frontend)将源代码转换成一种中间语言(IR,intermediate representation)。然后优化器(optimzer)拿到中间语言 对其进行优化并把优化过的中间语言传递给编译器后端生成机器码。如果我们选择一种源语言和目标机器无关的中间语言,我们就 得到了三阶段设计的关键益处:对于一个编译器来说,支持一种新的源语言仅仅需要新增一个对应的编译器前端,支持一种新的目标机器 仅仅需要新增一个对应的编译器后端。 LLVM 工具集(toolchain)就是这个模型的一个很好的成功的例子。有很多编译器前端如 C、Rust、Swift 等其他很多编程语言基于 LLVM 提供给编译器更加复杂的部分。LLVM 的创建者 Chris Latter 提供了一个很好的 LLVM 架构概览。 CPython 尽管不需要支持多个源语言和目标机器,尔仅仅需要支持 Python 代码和 CPython 虚拟机。不过,CPython 同样实现了三阶段设计。 如果想知道为什么,我们需要更加详细的解释编译器的三阶段的每个阶段。 ...

October 15, 2020 · 5 min · Gray King

straight.el 命令

tags: Emacs M-x straight-thaw-versions RET 恢复到锁定的版本 M-x straight-pull-all RET 更新所有包 M-x straight-freeze-versions RET 锁定当前版本 Duplicated with straight.el 更新所有已安装的包

October 6, 2020 · 1 min · Gray King

straight.el 更新所有已安装的包

straight-thaw-versions straight-pull-all straight-freeze-versions

October 6, 2020 · 1 min · Gray King

Emacs Buffer 名字去重

tags: Emacs source: https://www.gnu.org/software/emacs/manual/html%5Fnode/emacs/Uniquify.html

September 27, 2020 · 1 min · Gray King

范数(norm)

具有“长度”概念的函数。在线性代数、泛函分析等相关数学领域,是一个函数,其为向量空间内所有向量赋予非零的正长度或大小。

September 12, 2020 · 1 min · Gray King

MAE(平均绝对误差)

\[MAE(X,h) = \frac{1}{m}\sum_{i=1}^{m}\left\lvert h(x^{(i)} - y ^{i})\right\rvert\]

September 12, 2020 · 1 min · Gray King

RMSE(均方根误差)

测量预测过程中的预测错误的标准差。 公式 \[RMSE(X,h)=\sqrt{\frac{1}{m}\sum_{i=1}^{m}(h(x^{(i)}) - y^{(i)})^2}\] m 是你在测量 RMSE 时,所使用的数据集中实例的数量 \(x^{(i)}\) 是数据集中第 \(i\) 个实例的所有特征值的向量(标签特征除外),\(y{(i)}\) 是标签(也就是我们期待该实例的输出值) X 是数据集中所有实例所有特征值的矩阵(标记特征除外)。每个实例一行,也就是说第 \(i\) 行等于 \(x^{(i)}\) 的转置矩阵1,记作 \((x^{(i)})^T\) h 是系统的预测函数,也称为一个假设。当给定系统一个实例的特征向量 \(x^{(i)}\) ,他会输出一个预测值 \(\hat{y}=h(x^{(i)})\) RMSE(X,h) 是使用假设 h 在示例上测量的成本函数。 转置运算符会将列向量转换成行向量。 ↩︎

September 12, 2020 · 1 min · Gray King

机器学习涉及数学概念

公式 RMSE(均方根误差) MAE(平均绝对误差) 概念 标准差(Std Dev) 范数 方差 皮尔逊相关系数(标准相关系数) 线性相关性

September 12, 2020 · 1 min · Gray King

Python behind the scenes #1: how the CPython VM works

tags: Translate,Python 原文链接:Python behind the scenes #1: how the CPython VM works。 Python 幕后 #1: CPython 虚拟机如何工作 介绍(Introduction) 你是否曾经好奇过当你运行 Python 代码时 python 做了些什么? $ python script.py 这篇文章将开启一个系列来尝试解答这个问题。我们将深入 Python 最流行的实现 CPython 的内部。 通过深入 CPython 的内部我们将更深一层的去理解这门编程语言本身。这也是我们这个系列的最主要的目标。 如果你熟悉 Python 并且可以阅读 C 代码,但是对 CPython 源码本身没有太多的经验, 那么你可能非常适合本系列,并且对本系列感兴趣。 什么是 CPython 并且为什么有人想学习它(What CPython is and why anyone would want to study it) 我们首先来说明一些众所周知的事情。CPython 是用 C 编写的 Python 解析器。他是 Python 语言的众多实现 的一种,其他还有诸如 PyPy、Jython、IronPython 等。CPython 的独特之处在于它是 Python 的起源、维护时间最长也是最受欢迎的。 CPython 实现了 Python,但是 Python 是什么?最简单的一个答案可能是:Python 是一门编程语言。 当正确问相同的问题,那么答案将会更加明确:什么定义了 Python?Python 不像 C 语言有正式的规范, 但是与之相近的是 Python 语言参考(Python Language Reference),它以如下内容开始: ...

September 8, 2020 · 6 min · Gray King

机器学习测试与验证

将数据分成两部分:训练集和测试集,通常使用 80% 的数据进行训练,20% 的数据用来测试。 验证集 单独分出来一个保留集合作为验证集,防止调整模型和超参数拟合测试集的最佳模型。 交叉验证 为避免验证集浪费太多数据,交叉验证将训练集分成若干个互补子集,然后每个模型都通过这些子集的不同组合来 训练,之后用剩余的子集进行验证。

September 7, 2020 · 1 min · Gray King

机器学习的主要挑战

训练数据的数量不足 训练数据不具代表性 质量差的数据 无关特征 特征工程 一个成功的机器学习项目,关键部分是提取一组好的用了训练的特征集,这个过程叫做特征工程。 特征选择 特征提取 通过手机数据创造新的特征 训练数据过度拟合 在模型的训练数据上表现良好,但是泛化时却不尽人如意。 解决方法 简化模型 收集更多的训练数据 减少训练数据中的噪声(修复数据错误和消除异常值) 正则化 通过约束模型使其更简单,并降低过度拟合风险。 超参数 通过调整超参数来调整应用正则化的程度。调整超参数是构建机器学习系统的非常重要的组成部分。 训练数据拟合不足 解决方法: 选择一个带有更多参数、更强大的模型 给学习算法提供更好的特征集 减少模型中的约束(如减少正则化超参数)

September 7, 2020 · 1 min · Gray King

机器学习系统的种类

监督式/无监督式学习 监督式学习 定义 训练数据经过标注包含素所需解决方案(标签或标记) 相关算法 K-邻近算法 线性回归 逻辑回归:广泛用于分类,输出“属于某个给定类别的概率”的值 支持向量机 决策树和随机森林 神经网络 适应场景 分类任务 预测变量 无监督式学习 定义 训练数据未经标注 相关算法 聚类算法 K-平均算法 分层聚类分析 最大期望算法 可视化和降维 主成分分析 核主成分分析 局部线性嵌入 t-分布随机临近嵌入 关联规则学习 Apriori Eclat 适应场景 通过聚类算法检测相似(层次聚类算法精度更高,可以再次细分) 可视化算法 降维:不丢失太多信息的前提下简化数据,方法之一是合并特征,过程叫做特征提取 异常检测:判断新的输入是正常还是异常,数据初筛、防作弊等 关联规则学习:发现属性之间有趣的联系 半监督式学习 大量未标记数据和少量标记数据进行学习。 强化学习 观察环境、作出选择、执行操作、并获得回报(负值则为惩罚)。 批量学习和在线学习 在数据流中进行增量学习。 批量学习 在线学习 在线学习也称为增量学习,同时支持恢复到上一状态,便于检测到性能下降及时中断和回滚。 核外学习 超大数据集超出一台计算机的主存储器,每次加载部分数据并不断重复直至完成训练。 学习率 学习率高系统迅速适应新数据,同时快速忘记老数据,学习率低则反之。 基于实例和基于模型的学习 基于实例的学习 系统完全记住学习示例,然后通过某种相似度度量方式将其泛化到新的实例。 基于模型的学习 模型选择 观察数据得出模型的过程。 衡量模型表现 定义效用函数(或适应度函数)来衡量模型有多好 定义成本函数来衡量模型有多差 线性回归通常选择成本函数来衡量线性模型的预测和训练实例之间的差距。 线性回归算法的意义所在:通过你提供的训练样本,找出最符合所提供数据的线性模型的参数,这就是训练过程。

September 7, 2020 · 1 min · Gray King

《机器学习实战》读书笔记

概览 机器学习系统的种类 机器学习的主要挑战 机器学习测试与验证 模型是观察的简化。 相关数学概念 机器学习涉及数学概念

September 5, 2020 · 1 min · Gray King

Machine Learning

scikit-learn 提供一些常见的机器学习算法 逻辑回归(Logistic Regression aka LR) 线性分类器 XGBoost 提供随机森林解决逻辑回归特征不明显的问题

September 3, 2020 · 1 min · Gray King

Rust Obscure Words for non-native English speakers

tags: Rust,Learning English unwinding

August 29, 2020 · 1 min · Gray King

Rust Asynchronous Programming

tags: Rust Future async fn 将一个代码块转换为一个 Future 对象, Future 对象维护一个状态机 Future 对象必须运行在一个 Executor 上 Executor futures::executor::block_on 阻塞当前线程直到 future 完成 // `block_on` blocks the current thread until the provided future has run to // completion. Other executors provide more complex behavior, like scheduling // multiple futures onto the same thread. use futures::executor::block_on; async fn hello_world() { println!("hello, world!"); } fn main() { let future = hello_world(); // Nothing is printed block_on(future); // `future` is run and "hello, world!" is printed } await await 异步的等待 future 完成,不阻塞当前线程,可以配合 futures::join! 可以同时 await 多个 future futures::try_join! 如果其中一个子 future 返回错误则立即返回(join! 需要等所有 future 全部返回) futures::select! 任意一个 future 完成则立即返回 async fn learn_and_sing() { // Wait until the song has been learned before singing it. // We use `.await` here rather than `block_on` to prevent blocking the // thread, which makes it possible to `dance` at the same time. let song = learn_song().await; sing_song(song).await; } async fn async_main() { let f1 = learn_and_sing(); let f2 = dance(); // `join!` is like `.await` but can wait for multiple futures concurrently. // If we're temporarily blocked in the `learn_and_sing` future, the `dance` // future will take over the current thread. If `dance` becomes blocked, // `learn_and_sing` can take back over. If both futures are blocked, then // `async_main` is blocked and will yield to the executor. futures::join!(f1, f2); } fn main() { block_on(async_main()); }

August 28, 2020 · 2 min · Gray King

Go Swagger 实现代码即文档

tags: Go 目标 当跟随这篇文章完成后将产出如下内容: 代码 http://gitlab.17zuoye.net/vgo/go-swagger-example 文档 http://swagger.17zuoye.net/?url=http%3A%2F%2F10.200.242.61%3A9090%2Fswagger.json 准备 Go1.14 及以上版本 安装 go-swagger :参见 官方文档。 接下来使用 gin 框架作为示例,如果之前没接触过可以先了解下该框架 创建一个项目 $ mkdir go-swagger-example $ cd go-swagger-example/ $ go mod init gitlab.17zuoye.net/vgo/go-swagger-example 开始使用 首先在你的 `main.go` 定义 go generate 像下面这样: //go:generate swagger generate spec -o ./swagger.yml package main func main() { println("Hello world!"); } 此时如果运行 go generate 在项目目录下就会生成一个 swagger.yml 文件: paths: {} swagger: "2.0" 使用单独的包托管 swagger 相关定义 在之前实践的过程中发现,如果在多个包中定义了相同名称的结构体会到只一个结构体覆盖另外一个结构体的定义。 所以为了解决这个问题,我把所有 swagger 相关的定义都放在同一个包下来避免相同名字的结构体。 创建 swagger/swagger.go 填充如下内容: // Package swagger defines API documentation. // // Swagger 演示后端接口 // // Schemes: http // Host: 10.200.242.35:8080 // BasePath: /api/ // Version: 0.1.0 // Contact: 王会<hui.wang.a@17zuoye.com> // // Consumes: // - application/json // // Produces: // - application/vnd.17zuoye.v1+json // // swagger:meta package swagger 上面文件通过注释来定义了一些接口相关的信息,包括: ...

August 28, 2020 · 5 min · Gray King

MySQL forget password

tags: MySQL 启动 mysqld 时加上 --skip-grant-tables 参数可以无密码进入 MySQL。

August 27, 2020 · 1 min · Gray King

MVCC

tags: MySQL https://liuzhengyang.github.io/2017/04/18/innodb-mvcc/ 来自《高性能 MySQL》: InnoDB 在每一行都隐式的多存储两个字段: 事务更新版本 事务删除版本 当事务开始时记录这两个版本,在读取的时候根据 Undo Log 和 Redo Log 来实现隔离级别的控制。序列化隔离级别下只能通过行锁来保证。 在可重复读隔离级别下: 首先判断事务更新版本是否大于事务开始前的版本 如果大于则根据 Undo Log 进行回退实现可重复读,这样在同一事务下不管读多少遍读取到的内容都是一样的。 可重复读隔离即便下无法避免幻读:即一开始没有读取到,随着其他插入事务的提交在同一事务里执行查询又能读取到的情况。

August 27, 2020 · 1 min · Gray King

MySQL grant subnet

tags: MySQL,Network https://stackoverflow.com/a/38389851/2873718 MySQL 授权用户子网段需要使用: 172.16.0.0/255.240.0.0 而不能使用 172.16.0.0/12

August 27, 2020 · 1 min · Gray King

Network

August 27, 2020 · 0 min · Gray King

逻辑右移

位移产生的空白填上 0,会导致有符号的负数变成正数。

August 2, 2020 · 1 min · Gray King

算数右移

位移的产生的空白填上符号位。

August 2, 2020 · 1 min · Gray King

汇编

tags: Computer Systems,《深入理解计算机系统》读书笔记 程序编码 $ gcc -Og -S mstore.c # outputs mstore.s $ gcc -Og -c mstore.c # outptus mstore.o $ objdump -d mstore.o 所有以 ‘.’ 开头额行都是指导汇编器和链接器工作额伪指令。 数据格式 C 声明 Intel 数据类型 汇编代码后缀 大小(字节) char 字节 b 1 short 字 w 2 int 双字 l 4 long 四字 q 8 char* 四字 q 8 float 单精度 l 4 double 双精度 q 8 访问信息 寄存器 一个 x86-64 的中央处理单元(CPU)包含一组 16 个存储 64 位值的 通用目的寄存器 。 四字 双字 字 字节 用途 %rax %eax %ax %al 返回值 %rbx %ebx %bx %bl 被调用者保存 %rcx %ecx %cx %cl 第四个参数 %rdx %edx %dx %dl 第三个参数 %rsi %esi %si %sil 第二个参数 %rdi %edi %di %dil 第一个参数 %rbp %ebp %bp %bpl 被调用者保存 %rsp %esp %sp %spl 栈指针 %r8 %r8d %r8w %r8b 第五个参数 %r9 %r9d %r9w %r9b 第六个参数 %r10 %r10d %r10w %r10b 调用者保存 %r11 %r11d %r11w %r11b 调用者保存 %r12 %r12d %r12w %r11b 被调用者保存 %r13 %r13d %r13w %r13b 被调用者保存 %r14 %r14d %r14w %r14 被调用者保存 %r15 %r15d %r15w %r15 被调用者保存 相关规则: ...

August 2, 2020 · 3 min · Gray King

IEEE 浮点数

tags: Computer Systems,《深入理解计算机系统》读书笔记 浮点数小数表示形式 .0111 = \(0x2^{-1}+2^{-2}+2^{-3}+2^{-4}\) IEEE 浮点数表示形式 \[ V=(-1)^s X M X 2^E \] s = 0 表示负数, s = 1 表示正数 M 是二进制表示的小数 E 是阶码 浮点数二进制组成 一个单独符号位 s 表吗符合 k 位阶码字段 exp 编码阶码 E n 位小数字段 frac 编码尾数 M 两种常见的格式 float s = 1 k = 8 n = 23 double s = 1 k = 11 n = 52 三种计算方式 前置的一些值 e 是 exp 位表示的无符号数 f 是 frac 位表示的小数 \(Bias = 2^{k-1} -1\) 规格化的值 规则:阶码字段 exp 的位模式即不全为 0,也不全为 1(单精度 255,双精度 2047) 计算方式 \(E = e - Bias\) $M = 1 + f $ 非规格化的值 规则:阶码字段 exp 全是 0(用于表示 0) 计算方式 \(E = 1 - Bias\) \(M = f\) 可以表示 +0 和 -0。 ...

August 2, 2020 · 1 min · Gray King

Choosing a Rust web framework, 2020 edition

tags: Rust source: Choosing a Rust web framework, 2020 edition

July 10, 2020 · 1 min · Gray King

SSH

SSH Port Forwarding -L 将远端机器的端口映射到本地 -R 将本地端口映射到远端

July 7, 2020 · 1 min · Gray King

Fearless Concurrency with Rust

tags: Rust 原文链接:https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html

July 3, 2020 · 1 min · Gray King

Rust Means Never Having to Close a Socket

tags: Translate,Rust,Rust Wrapper Types 原文链接:Rust Means Never Having to Close a Socket Rust 最酷的特性之一就是它可以自动地帮助你管理资源,同时在仍能保持安全(没有段错误)和高性能。 这是因为 Rust 是一门与众不同地编程语言,要理解我说的可能有点困难,让我来更近一步说明: Rust 就像带垃圾回收的编程语言,你无需手动释放内存 Rust 不同于其他带垃圾回收的编程语言,你无需1手动关闭或者释放像文件、套接字和锁这样的资源 Rust 达到以上这些特性不附带任何运行时开销(垃圾回收或者引用计数),并且不牺牲安全性。 如果你曾经造成过一个套接字或者文件泄漏,或者使用过一些抽象方法造成了这些资源的泄漏,那么你就会知道这有多重要。 你可能已经期望通过“使用后释放”来避免内存问题,而与此同时你并没有考虑到没有明确地关闭套接字可能出现类似的错误。我在这里告诉你,还有更好地办法。 如果你使用的是带垃圾回收的编程语言,则应密切关注本文提到的资源管理方面的内容。如果你使用的是像 C/C++ 这样底层编程语言,你可能会对安全方面更加感兴趣。 Rust 的许多特性都是从其他语言借鉴而来。Rust 之所以变得有趣是因为它把所有的这些特性放在了一起,并且在编程语言层面实现了更严格地保证。 实际上,这种编程语言层面的保证让这些特性更加实用。 所有权系统(The Ownership System) 让这种保证工作的方式是通过 Rust 的「所有权(ownership)」系统。不管任何时候你创建一个新的对象,都被创建它的「作用域(scope)」所拥有。 让我们通过一个例子来进一步说明:我们定义一个函数,函数拷贝输入文件到临时文件去处理它,然后拷贝输入文件到输出文件。 fn process(from: &Path, to: &Path) -> IoResult<()> { // creates a new tempdir with the specified suffix let tempdir = try!(TempDir::new("skylight")); // open the input file let mut from_file = try!(File::open(from)); // create a temporary file inside the tempdir let mut tempfile = try!(File::create(&tempdir.path().join("tmp1"))); // copy the input file into the tempfile try!(io::util::copy(&mut from_file, &mut tempfile)); // use an external program to process the tmpfile in place // after processing, copy the tempfile into the output file let mut out = try!(File::create(to)); io::util::copy(&mut tempfile, &mut out) } 在这个例子中,函数 process 的作用域再第一行创建了 TempDir 是其初始拥有者。在这个例子中, process 函数从未放弃所有权,所以当函数完成调用, 它就会自动被丢弃(dropped),也就是会删除 Tempfile 。 ...

June 29, 2020 · 4 min · Gray King

Rust 并发

并发 std::marker::Send 允许跨线程转移所有权 Send 标记特性用于标识实现该特性的类型的所有权可以在线程中间转移,几乎所有 Rust 类型都实现了 Send ,但是也有一些例外如: Rc<T> ,如果克隆后在多个线程中转移所有权可能会多个线程同时增加计数器,可以使用 Arc<T> 替代 如果一个新的类型组合的类型都实现了 Send 那么该类型也自动实现了 Send ,几乎所有原始类型都实现了 Send . std::marker::Sync 允许多个线程同时访问 Sync 标记特性用于标识实现该特性的类型可以在多个线程中引用,任何类型 T 是 Sync 并且 T 的引用 &T 是 Send 则意味着引用可以安全的发送给其他线程。 同 Send 类似,几乎所有原始类型都实现了 Sync ,组合了所有类型都是 Sync 的类型自动实现 Sync 。 以下只能指针没有实现 Sync Rc<T> 同样没有实现 Sync RcCell<T> 和 Cell<T> 自己实现 Sync 和 Send 是不安全的

June 29, 2020 · 1 min · Gray King

Rust 宏

三种宏 自定义 #[derive] 属性行为(attribute-like)宏,Rust 属性宏解析 函数行为(function-like)宏 宏 实现元编程,代码生成代码,必须带入当前命名空间内才能使用(不能使用 mod::sub::macro! 这种方式调用)。 #[macro_export] macro_rules! vec { ( $($x:expr), *) => { // $() 用于捕捉值,$x:expr 匹配 Rust 表达式并绑定到 $x,逗号(,)表明一个逗号可能出现在表达式后面,* 表明表达式匹配一次或多次 { let mut temp_vec = Vec::new(); ${ temp_vec.push($x); }* temp_vec } }; } 调用宏用于从属性生成代码 有点像编译期的装饰器: use proc_macro; #[some_attribute] pub fn some_name(input: TokenStream) -> TokenStream { // --snip-- }

June 29, 2020 · 1 min · Gray King

智能指针

智能指针 表现的像一个指针,拥有数据并允许在对数据进行维护。 通常通过 struct 实现并实现两个特性 Deref 和 Drop Deref 允许智能指针实例行为像一个引用,让代码可以同时处理引用和智能指针 Drop 允许自定义智能指针超出作用域的行为。 标准库常见的智能指针 Box<T> 用于在堆分配值 Rc<T> 引用计数类型,允许多个拥有者 Ref<T> 和 RefMut<T> 和通过 RefCell<T> 访问,运行时取代编译期强制检查借用规则 Box<T> 场景: 编译期未知大小的类型(递归类型(自己包含自己类型的类型,如链表)编译期无法确定大小) // 递归类型 enum List { Cons(i32, Box<List>), Nil, } fn main() { let b = Box::new(5); println!("b = {}", b); let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil)))))); } 避免大量数据转移所有权时发生拷贝 拥有一个实现特定特性的值(不关心具体类型)的所有权 Deref 用于自定义解引用操作符( * ) 的行为,智能指针通过实现该特性来模拟普通引用的行为。 对比 fn main() { let x = 5; let y = &x; assert_eq!(5, x); assert_eq!(5, *y); // must dereference } 和 ...

June 29, 2020 · 1 min · Gray King

迭代器

迭代器 所有的迭代器都实现了 Iterator 特性: pub trait Iterator { type Item; // 关联类型 fn next(&mut self) -> Option<Self::Item>; // 返回 None 结束循环 }

June 29, 2020 · 1 min · Gray King

生命周期

生命周期 Rust 中的每一个引用都有其生命周期:引用有效的作用域。 大部分情况下生命周期都是隐式和自举的,在无法完成的情况下就需要我们通过生命周期泛型参数帮助编译器进行注解。 生命周期的主要目标是避免悬空指针。 生命周期泛型参数定义各个引用之间(参数和参数、参数和返回值)的关系,并不改变(延长)变量原本的生命周期 &i32 // a reference &'a i32 // a reference with an explicit lifetime &'a mut i32 // a mutable reference with an explicit lifetime 参考以下代码 fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } 以上代码 标注生命周期 'a 函数有两个引用参数,都使用生命周期 'a 表示两个参数的生命周期必须一致(存活的周期一样长) 函数返回一个引用,并且存活的时间和生命周期 'a 一致 以上指定不改变任何传入的引用的生命周期,我们只是要求借用检查器(borrow checker)检查这些约束。 也就是说借用检查器要检查传入的两个引用的生命周期必须一致,返回的引用的存活周期不能超过传入的引用的存活周期 思考 当函数返回一个引用时,返回值的生命周期注解要和参数的其中之一相匹配,否则那么引用就是指向里函数内创建的值(不能返回)。 也就是说返回引用时,引用的声明周期必须和参数(其一)相关。如果想要返回函数内创建的值最好返回一个有所有权的值类型。 结构体生命周期 如果结构体需要持有引用,需要在定义结构体时给每一个引用都加上生命周期注解。 如果结构体声明了生命周期参数,那么 impl 同样也要声明。 struct ImportantExcerpt<'a> { part: &'a str, } impl<'a> ImportantExcerpt<'a> { fn announce_and_return_part(&self, announcement: &str) -> &str { println!("Attention please: {}", announcement); self.part } } 生命周期省略(elision)规则 函数参数的生命周期为输入生命周期 函数返回值的生命周期为输出生命周期 3 个规则用于 fn 和 impl : ...

June 29, 2020 · 1 min · Gray King

闭包

let add_one = | num | { num + 1 }; 由于闭包和当前上下文相关联,所以 Rust 可以进行类型推导,类型注解也就不是必要的,但是依然可以自己添加: let add_one = | num: i32 | { num + 1 }; fn add_one_v1 (x: u32) -> u32 { x + 1 } let add_one_v2 = |x: u32| -> u32 { x + 1 }; let add_one_v3 = |x| { x + 1 }; let add_one_v4 = |x| x + 1 ; 使用 Fn 存储闭包类型 struct Cacher<T> where T: Fn(u32) -> u32 { calculation: T, value: Option<u32>, } impl Cacher<T> where T: Fn(u32) -> u32 { fn new(calculation: T) -> Cacher<T> { Cacher { calculation, value: None, } } fn value(&mut self, arg: u32) -> u32 { if let Some(value) = self.value { value } else { let value = (self.calculation)(arg); self.value = Some(value); value } } } 闭包和函数的区别:闭包会捕捉当前环境,但是函数不会。 ...

June 29, 2020 · 1 min · Gray King

Traits

Traits 定义行为在多个类型中共享。 可以定义默认行为在实现者中间共享。 可以用于定义参数的行为,同样可以定义返回值行为,当用 trait 限定返回值类型时,不能同时(if/else)返回多种实现了该 trait 的类型。 pub trait Summary { fn summarize(&self) -> String; } pub struct Article{ pub title: String, } impl Summary for Article { fn summarize(&self) -> String { format!("{}", self.title) } } pub fn notify(item: impl Summary) { println!("{}", item.summarize()); } // trait bound 语法糖版本 pub fn notify<T: Summary>(item: T) { println!("{}", item.summarize()); } 定义参数行为 通过 impl : fn notify(item: impl TraitName) ,用于简单明了的场景,比如一个参数 通过 trait bound : fn notify<T: TraitName> (item: T) ,用于更复杂的场景,比如多个参数用于减少代码 可以通过 + 连接: fn notify(T: TraitName + Display) (item: T) ...

June 29, 2020 · 1 min · Gray King

错误处理

enum Result<T, E> { Ok(T), Err(E), } ? 操作符 对比 use std::io; use std::io::Read; use std::fs::File; fn read_username_from_file() -> Result<String, io::Error> { let f = File::open("hello.txt"); let mut f = match f { Ok(file) => file, Err(e) => return Err(e), }; let mut s = String::new(); match f.read_to_string(&mut s) { Ok(_) => Ok(s), Err(e) => Err(e), } } 和 use std::io; use std::io::Read; use std::fs::File; fn read_username_from_file() -> Result<String, io::Error> { let mut f = File::open("hello.txt")?; let mut s = String::new(); f.read_to_string(&mut s)?; Ok(s) } 和 ...

June 29, 2020 · 1 min · Gray King

if let

let some_u8_value = Some(0u8); // Without `if let` match some_u8_value { Some(3) => println!("three"); - => (), } // With `if let` if let Some(3) = some_u8_value { println!("three"); }

June 29, 2020 · 1 min · Gray King

模块化

包、crate 和模块 Cargo.toml 表示一个包 包含 0 个或 1 个库 crate( src/lib.rs ) 包含 0 个或多个可执行 crate ( src/main.rs src/bin/*.rs ) 可以同时包含以上两种 模块化系统 模块,一种组织代码和控制路径隐私的方法 所有的项(函数,方法,结构体,枚举,模块和常量)默认私有 不允许使用私有的子模块的代码 可以使用父模块和同级模块的代码 路径,一种命名项的方法 use , 一个将路径带到当前作用域的关键字 pub ,一个将项公开的关键字 as ,一个将带到当前作用域项重命名的关键字 super , 一个相当于文件系统里 .. 作用的关键字 * ,通配符用于使用制定路径下的所有项 pub use 用于重新暴露可以访问的模块 模块可以放在一个文件,也可以按照一定规则拆分到不同文件下 使用同一个 use 清理嵌套的 use use std::cmp::Ordering; use std::io; use std::{cmp::Ordering, io}; use std::io; use std::io::Write; use std::{self, Write}; mod sound { pub mod instrument { pub mod woodwind { pub fn clarinet() { super::breathe_in(); } } fn breathe_in() { } } mod voice { } } fn main() { // 绝对路径以 crate 开始,crate 表示根 crate::sound::instrument::woodwind::clarinet(); // 相对路径 sound::instrument::woodwind::clarinet(); } 上面例子 sound 模块并非公开,但是由于 main 和 sound 定义在同一模块下,所以允许在 main 中引用 sound 。 ...

June 29, 2020 · 1 min · Gray King

模式匹配

#[derive(Debug)] enum UsState { Alabama, Alaska, } enum Coin { Penny, Nickel, Dime, Quarter(UsState), } fn value_cents(coin: Coin) -> u8 { match coin { Coin::Peny => { println!("Lucy penny!"); 1 }, Coin::Nickel => 5, Coin::Dime => 10, Coin::Quarter(state) => { println!("State quarter from {:?}!", state); 25 }, } } // Option fn plus_one(x: Option<i32>) -> Option<i32> { match x { None => None, Some(i) => Some(i + 1), } } let five = Some(5); let six = plus_one(five); let none = plus_one(None); 编译器会检查 match 覆盖所有情况, _ 可以用于所有未覆盖的情况。 ...

June 29, 2020 · 1 min · Gray King

枚举

多种类型的集合体,一个类型的变量可以存储多种类型的值,枚举的每一项都是该枚举类型的变体: enum IpAddrKind { V4, V6, } fn main() { route(IpAddrKind::V4); route(IpAddrkind::V6); } fn route(kind: IpAddrKind) { // ... } 枚举的每一个变体都可以直接包含数据,并且每一个变体可以包含不同的数据类型和不同的数量,甚至可以直接放结构体(也可以是匿名的)。 struct Ipv4Addr { // --snip-- } enum IpAddr { V4(Ipv4Addr), V6(String), } let home = IpAddr::V4(127, 0, 0, 1); let loopback = IpAddr::V6(String::from("::1")); struct Message { Quit, Move{ x: i32, y: i32 }, // anonymous struct Write(String), ChangeColor(i32, i32, i32), // three i32 values } 枚举也可以通过 impl 实现方法 impl Message { fn call(&self) { match self { // ... } } } let m = Message::Write(String::from("hello")); m.call(); Option ...

June 29, 2020 · 1 min · Gray King