You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
568tools/tools/rusty-book/print.html

4932 lines
281 KiB

2 years ago
<!DOCTYPE HTML>
<html lang="zh-CN" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Rusty Book(锈书)</title>
<meta name="robots" content="noindex" />
<!-- Custom HTML head -->
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="highlight.css">
<link rel="stylesheet" href="tomorrow-night.css">
<link rel="stylesheet" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="theme/style1.css">
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('js');
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="about.html">Rusty Book</a></li><li class="chapter-item expanded affix "><li class="part-title">Awesome</li><li class="spacer"></li><li class="chapter-item expanded "><a href="daily-dev.html">日常开发常用库</a></li><li class="chapter-item expanded "><a href="superstar.html">Rust 明星项目</a></li><li class="chapter-item expanded "><a href="empowering-js.html">使用 Rust 增强 JS</a></li><li class="chapter-item expanded "><a href="games.html">Rust开发的游戏</a></li><li class="chapter-item expanded "><a href="gamedev.html">游戏引擎</a></li><li class="chapter-item expanded affix "><li class="part-title">Awesome + Cookbook</li><li class="spacer"></li><li class="chapter-item expanded "><a href="algos/awesome.html">实用算法</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="algos/randomness.html">生成随机值</a></li><li class="chapter-item expanded "><a href="algos/sorting.html">Vec 排序</a></li><li class="chapter-item expanded "><div>压缩算法</div><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="algos/compression/tar.html">使用.tar包</a></li></ol></li><li class="chapter-item expanded "><div>密码学</div><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="algos/cryptography/hashing.html">哈希</a></li><li class="chapter-item "><a href="algos/cryptography/encryption.html">加密</a></li></ol></li><li class="chapter-item expanded "><div>数学计算</div><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item "><a href="algos/math/linear-algebra.html">线性代数</a></li><li class="chapter-item "><a href="algos/math/trigonometry.html">三角函数</a></li><li class="chapter-item "><a href="algos/math/complex.html">复数</a></li><li class="chapter-item "><a href="algos/math/statistics.html">统计学</a></li><li class="chapter-item "><a href="algos/math/misc.html">杂项</a></li></ol></li></ol></li><li class="chapter-item expanded "><a href="datastructures/awesome.html">数据结构</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="datastructures/bitfield.html">位字段</a></li></ol></li><li class="chapter-item expanded "><a href="cmd/awesome.html">命令行</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="cmd/parsing.html">参数解析</a></li><li class="chapter-item expanded "><a href="cmd/ansi.html">终端输出格式化</a></li></ol></li><li class="chapter-item expanded "><a href="os/awesome.html">操作系统</a><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="os/processor.html">处理器</a></li><li class="chapter-item expanded "><a href="os/command.html">调用系统命令</a></li></ol></li><li class="chapter-item expanded "><div>并发</div><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="cocurrency/threads.html">线程</a></li><li class="chapter-item expanded "><a href="cocurrency/parallel.html">使用rayon并行处理数据</a></li></ol></li><li class="chapter-item expanded "><div>数据库</div><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="database/sqlite.html">SQLite</a></li><li class="chapter-item expanded "><a href="database/postgres.html">Postgres</a></li></ol></li><li class="chapter-item expanded "><div>日期和时间</div><a class="toggle"><div></div></a></li><li><ol class="section"><li class="chapter-item expanded "><a href="datetime/duration.html">时间计算和转换</a></li><li class="chapter-item expanded "><a href="datetime/parsing.html">解析和显示</a></li></ol></li><li class="chapter-item expanded "><div>开发者工具</div><a class="toggle"><div></div></a></li><li><ol clas
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rusty Book(锈书)</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/studyrs/rusty-book" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<!-- Page table of contents -->
<div class="sidetoc"><nav class="pagetoc"></nav></div>
<main>
<img width="100%" src="https://github.com/studyrs/rusty-book/blob/main/assets/banner.gif?raw=true" />
<p>每个同学可能都遇到过以下疑惑:</p>
<ul>
<li>学完 Rust 后,还做了些题,接下可以做些什么?</li>
<li>需要找一个依赖,但是去哪里找?哪些比较好用?哪些有坑?愁啊</li>
<li>要访问一个文件,哎,但记不住代码,要不百度或谷歌一下吧,最后发现结果往往不尽如人意</li>
</ul>
<p>而 Rusty Book 就是帮助大家解决这些问题的。</p>
<p>在 Rust 元宇宙,夸奖别人的最高境界就是 <code>rusty</code>: 今天你&quot;&quot;了吗? 你的 Rust 代码好锈啊!</p>
<p>而本书,就是精选了各种开源库和代码片段,帮助大家打造优&quot;&quot;的 Rust 项目。</p>
<h2 id="以往的锈"><a class="header" href="#以往的锈">以往的锈</a></h2>
<p>以往,想要锈起来,你需要做到以下两步:</p>
<ol>
<li>
<p>为项目挑选 Awesome 依赖库
但是目前已有的 awesome-rust项目有非常大的问题里面鱼龙混杂因为它的目的是列出所有项目但对用户而言更想看到的是可以在生产中使用的、稳定更新的优秀项目。</p>
</li>
<li>
<p>在 Cookbook 中查询实用的代码片段,直接复制到项目中
对于开发者而言Cookbook 非常实用,几乎每一门编程语言都是如此。原因无他:聪明的开发者大部分时间不是在复制粘贴就是在复制粘贴的路上。而 CookBook 恰恰为各种实用场景提供了可供直接复制粘贴的代码,例如网络协议、数据库和文件操作、随机数生成、命令行解析等。</p>
</li>
</ol>
<p>但目前的 Rust Cookbook 更新非常不活跃,里面缺少了大量实用库,还有一些过时的老库。</p>
<h2 id="现在的锈"><a class="header" href="#现在的锈">现在的锈</a></h2>
<p>鉴于以上痛点,我们决定打造一本真正的锈书:一本足够&quot;&quot;但是又不会锈的书。</p>
<p>这本书其实就是 Awesome Rust + Rust Cookbook 的结合体,但是我们不是简单粗暴的对内容进行了合并,而是从深层次将两者进行了融合,希望大家能喜欢。</p>
<h2 id="这本书的读者"><a class="header" href="#这本书的读者">这本书的读者</a></h2>
<p>本书适合所有程度的 Rust 开发者使用:</p>
<ul>
<li>新手用来了解 Rust 的常用库和常用代码片段</li>
<li>老手在写代码时,可以直接用来复制粘贴,大幅提升工作效率</li>
</ul>
<p>毕竟咱不是在面试造飞机,谁脑袋中能记住文件操作的各种细节,对不?</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="日常开发常用库"><a class="header" href="#日常开发常用库">日常开发常用库</a></h1>
<h3 id="目录索引"><a class="header" href="#目录索引">目录索引</a></h3>
<ul>
<li><a href="daily-dev.html#%E5%A4%9A%E7%BA%BF%E7%A8%8B">多线程</a> </li>
<li><a href="daily-dev.html#webhttp">Web/HTTP</a>, <a href="daily-dev.html#SQL%E5%AE%A2%E6%88%B7%E7%AB%AF">SQL客户端</a>, <a href="daily-dev.html#NoSql%E5%AE%A2%E6%88%B7%E7%AB%AF">NoSql客户端</a> <a href="daily-dev.html#%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1%E5%8D%8F%E8%AE%AE">网络通信协议</a>, <a href="daily-dev.html#%E5%BC%82%E6%AD%A5%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B">异步网络编程</a></li>
<li><a href="daily-dev.html#%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0">服务发现</a>, <a href="daily-dev.html#%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97">消息队列</a>, <a href="daily-dev.html#%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E">搜索引擎</a></li>
<li><a href="daily-dev.html#%E7%BC%96%E8%A7%A3%E7%A0%81">编解码</a>, <a href="daily-dev.html#Email">Email</a>, <a href="daily-dev.html#%E5%B8%B8%E7%94%A8%E6%AD%A3%E5%88%99%E6%A8%A1%E7%89%88">常用正则模版</a></li>
<li><a href="daily-dev.html#%E6%97%A5%E5%BF%97%E7%9B%91%E6%8E%A7">日志监控</a>, <a href="daily-dev.html#%E4%BB%A3%E7%A0%81Debug">代码Debug</a>, <a href="daily-dev.html#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96">性能优化</a></li>
</ul>
<h3 id="webhttp"><a class="header" href="#webhttp">Web/HTTP</a></h3>
<ul>
<li>
<p>HTTP客户端</p>
<ul>
<li><a href="https://github.com/seanmonstar/reqwest">reqwest</a> 一个简单又强大的HTTP客户端<code>reqwest</code>是目前使用最多的HTTP库 </li>
</ul>
</li>
<li>
<p>Web框架</p>
<ul>
<li><a href="https://github.com/tokio-rs/axum">axum</a> 基于Tokio和Hyper打造模块化设计较好目前口碑很好值得使用Ergonomic and modular web framework built with Tokio, Tower, and Hyper</li>
<li><a href="https://github.com/SergioBenitez/Rocket">Rocket</a> 功能强大API简单的Web框架但是主要开发者目前因为个人原因无法进行后续开发未来存在不确定性</li>
<li><a href="https://github.com/actix/actix-web">actix-web</a> 性能极高的Web框架就是团队内部有些问题未来存在一定的不确定性</li>
<li>总体来说上述三个web框架都有很深的用户基础其实都可以选用如果让我推荐顺序如下: <code>axum</code> &gt; <code>Rocket</code> &gt; <code>actix-web</code>。 不过如果你不需要多么完善的web功能只需要一个性能极高的http库那么<code>actix-web</code>是非常好的选择,它的性能非常非常非常高!</li>
</ul>
</li>
</ul>
<h3 id="日志监控"><a class="header" href="#日志监控">日志监控</a></h3>
<ul>
<li>日志
[<a href="https://crates.io/keywords/log">crates.io</a>] [<a href="https://github.com/search?q=rust+log">github</a>]
<ul>
<li><a href="https://github.com/tokio-rs/tracing">tokio-rs/tracing</a> 强大的日志框架同时还支持OpenTelemetry格式无缝打通未来的监控</li>
<li><a href="https://github.com/rust-lang/log">rust-lang/log</a> 官方日志库事实上的API标准, 但是三方库未必遵循</li>
<li><a href="https://github.com/estk/log4rs">estk/log4rs</a> 模仿JAVA <code>logback</code><code>log4j</code>实现的日志库, 可配置性较强</li>
<li>在其它文章中也许会推荐slog但是我们不推荐一个是因为近半年未更新一个是<code>slog</code>自己也推荐使用<code>tracing</code></li>
</ul>
</li>
<li>监控
<ul>
<li><a href="https://github.com/open-telemetry/opentelemetry-rust">OpenTelemetry</a> <code>OpenTelemetry</code>是现在非常火的可观测性解决方案提供了协议、API、SDK等核心工具用于收集监控数据最后将这些metrics/logs/traces数据写入到<code>prometheus</code>, <code>jaeger</code>等监控平台中。最主要是,它后台很硬,后面有各大公司作为背书,未来非常看好!</li>
<li><a href="https://github.com/vectordotdev/vector">vectordotdev/vector</a> 一个性能很高的数据采集agent采集本地的日志、监控等数据发送到远程的kafka、jaeger等数据下沉端它最大的优点就是能从多种数据源(包括Opentelemetry)收集数据,然后推送到多个数据处理或者存储等下沉端。</li>
</ul>
</li>
</ul>
<h3 id="sql客户端"><a class="header" href="#sql客户端">SQL客户端</a></h3>
<ul>
<li>
<p>性能对比</p>
<ul>
<li><a href="https://github.com/diesel-rs/metrics">metrics</a> 该库对Rust现存的数据库连接服务进行性能测试若大家有性能上的需求值得一看</li>
</ul>
</li>
<li>
<p>通用</p>
<ul>
<li><a href="https://github.com/launchbadge/sqlx">launchbadge/sqlx</a> 异步实现、高性能、纯Rust代码的SQL库支持<code>PostgreSQL</code>, <code>MySQL</code>, <code>SQLite</code>,和 <code>MSSQL</code>.</li>
</ul>
</li>
<li>
<p>ORM</p>
<ul>
<li><a href="https://github.com/rbatis/rbatis">rbatis/rbatis</a> 国内团队开发的ORM异步、性能高、简单易上手</li>
<li><a href="https://github.com/diesel-rs/diesel">diesel-rs/diesel</a> 安全、扩展性强的Rust ORM库支持<code>Mysql</code><code>Postgre</code><code>SqlLite</code></li>
</ul>
</li>
<li>
<p>Mysql</p>
<ul>
<li><a href="https://github.com/blackbeam/rust-mysql-simple">blackbeam/rust-mysql-simple</a> 纯Rust实现的Mysql驱动,提供连接池</li>
<li><a href="https://github.com/blackbeam/mysql_async">blackbeam/mysql_async</a> 基于Tokio实现的异步Mysql驱动</li>
<li>上面两个都是一个团队出品前者文档更全、star更多建议使用前者</li>
</ul>
</li>
<li>
<p>Postgre</p>
<ul>
<li><a href="https://github.com/sfackler/rust-postgres">sfackler/rust-postgres</a> 纯Rust实现的Postgre客户端</li>
</ul>
</li>
<li>
<p>Sqlite</p>
<ul>
<li><a href="https://github.com/rusqlite/rusqlite">rusqlite</a> 用于<a href="https://www.sqlite.org/index.html">Sqlite3</a>的Rust客户端</li>
</ul>
</li>
</ul>
<h3 id="nosql客户端"><a class="header" href="#nosql客户端">NoSql客户端</a></h3>
<ul>
<li>
<p>Redis</p>
<ul>
<li><a href="https://github.com/mitsuhiko/redis-rs">mitsuhiko/redis-rs</a> 虽然最近更新不太活跃但是它依然是最好的redis客户端说实话我期待更好的可能这也是Rust生态的未来可期之处吧</li>
</ul>
</li>
<li>
<p>Canssandra</p>
<ul>
<li><a href="https://github.com/krojew/cdrs-tokio">krojew/cdrs-tokio</a> [<a href="https://crates.io/crates/cdrs-tokio">cdrs-tokio</a>] 生产可用的Cassandra客户端异步、纯Rust实现就是个人项目 + star较少未来不确定会不会不维护</li>
<li><a href="https://github.com/scylladb/scylla-rust-driver">scylla-rust-driver</a> ScyllaDB提供的官方库支持cql协议由于背靠大山未来非常可期</li>
</ul>
</li>
<li>
<p>MongoDB</p>
<ul>
<li><a href="https://github.com/mongodb/mongo-rust-driver">mongodb/mongo-rust-driver</a> 官方MongoDB客户端闭着眼睛选就对了</li>
</ul>
</li>
</ul>
<h3 id="分布式"><a class="header" href="#分布式">分布式</a></h3>
<h4 id="服务发现"><a class="header" href="#服务发现">服务发现</a></h4>
<ul>
<li><a href="https://github.com/luncj/etcd-rs">luncj/etcd-rs</a> 异步实现的Rust etcd客户端优点是有一定的文档、作者较为活跃,意味着你提问题他可能会回答不过如果你不放心还是考虑使用HTTP的方式访问ETCD</li>
</ul>
<h4 id="消息队列"><a class="header" href="#消息队列">消息队列</a></h4>
<ul>
<li>Kafka
<ul>
<li><a href="https://github.com/fede1024/rust-rdkafka">fede1024/rust-rdkafka</a> Rust Kafka客户端基于C版本的Kafka库[librdkafka]实现,文档较全、功能较为全面</li>
<li><a href="https://github.com/kafka-rust/kafka-rust">kafka-rust/kafka-rust</a> 相比上一个库它算是纯Rust实现文档还行支持Kafka0.8.2及以后的版本但是对于部分0.9版本的特性还不支持。同时有一个问题:最初的作者不维护了,转给了现在的作者,但是感觉好像也不是很活跃</li>
</ul>
</li>
<li>Nats
<ul>
<li><a href="https://github.com/nats-io/nats.rs">nats-io/nats.rs</a> Nats官方提供的客户端</li>
</ul>
</li>
</ul>
<h3 id="网络通信协议"><a class="header" href="#网络通信协议">网络、通信协议</a></h3>
<ul>
<li>Websocket
<ul>
<li><a href="https://github.com/snapview/tokio-tungstenite">snapview/tokio-tungstenite</a> 更适合Web应用使用的生产级Websocket库它是异步非阻塞的基于基于下下面的<code>tungstenite-rs</code>库和tokio实现</li>
<li><a href="https://github.com/websockets-rs/rust-websocket">rust-websocket</a> 老牌Websocket库提供了客户端和服务器端实现但是。。。很久没更新了</li>
<li><a href="https://github.com/snapview/tungstenite-rs">snapview/tungstenite-rs</a> 轻量级的Websocket流实现该库更偏底层例如你可以用来构建其它网络库</li>
</ul>
</li>
<li>gRPC
<ul>
<li><a href="https://github.com/hyperium/tonic">hyperium/tonic</a> 纯Rust实现的gRPC客户端和服务器端支持async/await异步调用文档和示例较为清晰</li>
<li><a href="https://github.com/tikv/grpc-rs">tikv/grpc-rs</a> 国产开源之光Tidb团队出品的gRPC框架, 基于C的代码实现, 就是最近好像不是很活跃</li>
<li>其实这两个实现都很优秀,把<code>tonic</code>放在第一位主要是因为它是纯Rust实现同时社区也更为活跃但是并不代表它比<code>tikv</code>的更好!</li>
<li><a href="https://github.com/tokio-rs/prost">tokio-rs/prost</a> 纯Rust实现的<a href="https://developers.google.com/protocol-buffers/">Protocol Buffers</a>类库Prost 支持从 proto2 和 proto3 文件生成简单、实用的代码。</li>
</ul>
</li>
<li>QUIC
<ul>
<li><a href="https://github.com/cloudflare/quiche">cloudflare/quiche</a> 大名鼎鼎<code>cloudflare</code>提供的QUIC实现据说在公司内部重度使用有了大规模生产级别的验证非常值得信任同时该库还实现了HTTP/3</li>
<li><a href="https://github.com/quinn-rs/quinn">quinn-rs/quinn</a> 提供异步API调用纯Rust实现同时提供了几个有用的网络库</li>
</ul>
</li>
<li>MQTT
<ul>
<li><a href="https://github.com/bytebeamio/rumqtt">bytebeamio/rumqtt</a> MQTT3.1.1/5协议库同时实现了客户端与服务器端broker</li>
<li><a href="https://github.com/ntex-rs/ntex-mqtt">ntex-rs/ntex-mqtt</a> 客户端与服务端框架支持MQTT3.1.1与5协议</li>
<li><a href="https://github.com/eclipse/paho.mqtt.rust">eclipse/paho.mqtt.rust</a> 老牌MQTT框架对MQTT支持较全, 其它各语言的实现也有</li>
</ul>
</li>
</ul>
<h3 id="异步网络编程"><a class="header" href="#异步网络编程">异步网络编程</a></h3>
<ul>
<li><a href="https://github.com/tokio-rs/tokio">tokio-rs/tokio</a> 最火的异步网络库除了复杂上手难度高一些外没有其它大的问题。同时tokio团队提供了多个非常优秀的Rust库整个生态欣欣向荣用户认可度很高</li>
<li><a href="https://async.rs/">async-std</a> 跟标准库API很像的异步网络库相对简单易用但是貌似开发有些停滞还有就是功能上不够完善。但是对于普通用户来说这个库非常值得一试它在功能和简单易用上取得了很好的平衡</li>
<li><a href="https://github.com/actix/actix">actix</a> 基于Actor模型的异步网络库但这个库的开发貌似已经停滞他们团队一直在专注于<code>actix-web</code>的开发</li>
<li><a href="https://github.com/tokio-rs/mio">mio</a> 严格来说MIO与之前三个不是同一个用途的MIO = Meta IO是一个底层IO库往往用于构建其它网络库当然如果你对应用网络性能有非常极限的要求 可以考虑它,因为它的层次比较低,所带来的抽象负担小,所以性能损耗小</li>
<li>如果你要开发生产级别的项目,我推荐使用<code>tokio</code>,稳定可靠,功能丰富,控制粒度细;自己的学习项目或者没有那么严肃的开源项目,我推荐<code>async-std</code>简单好用值得学习当你确切知道需要Actor网络模型时就用<code>actix</code></li>
</ul>
<h3 id="搜索引擎"><a class="header" href="#搜索引擎">搜索引擎</a></h3>
<ul>
<li>
<p>ElasticSearch客户端</p>
<ul>
<li><a href="https://github.com/elastic/elasticsearch-rs">elastic/elasticsearch</a> 官方es客户端目前第三方的基本都处于停滞状态所以不管好坏用呗</li>
</ul>
</li>
<li>
<p>Rust搜索引擎</p>
<ul>
<li><a href="https://github.com/quickwit-inc/tantivy">Tantivy</a> Tantivy是Rust实现的本地搜索库功能对标<code>lucene</code>如果你不需要分布式那么引入tantivy作为自己本地Rust服务的一个搜索是相当不错的选择该库作者一直很活跃而且最近还创立了搜索引擎公司感觉大有作为. 该库的优点在于纯Rust实现性能高(lucene的2-3倍),资源占用低(对比java自然不是一个数量级),社区活跃。</li>
</ul>
</li>
<li>
<p>Rust搜索平台</p>
<ul>
<li><a href="https://github.com/quickwit-inc/quickwit">quickwit</a> 对标ElasticSearch一个通用目的的分布式搜索平台目前还在起步阶段(0.2版本),未来非常可期,目前还不建议使用</li>
<li><a href="https://github.com/meilisearch/MeiliSearch">MeiliSearch</a> 虽然也是一个搜索平台,但是并不是通用目的的,<code>MeiliSearch</code>目标是为终端用户提供边输入边提示的即刻搜索功能因此是一个轻量级搜索平台不适用于数据量大时的搜索目的。总之如果你需要在网页端或者APP为用户提供一个搜索条然后支持输入容错、前缀搜索时就可以使用它。</li>
<li></li>
</ul>
</li>
</ul>
<h3 id="代码debug"><a class="header" href="#代码debug">代码Debug</a></h3>
<ul>
<li>GDB
<ul>
<li><a href="https://github.com/cs01/gdbgui">gdbgui</a> 提供浏览器支持的gdb debug工具支持CC++Rust和Go.</li>
</ul>
</li>
<li>LLDB
<ul>
<li><a href="https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb">CodeLLDB</a> — 专门为VSCode设计的LLDB Debug扩展</li>
</ul>
</li>
</ul>
<h3 id="性能优化"><a class="header" href="#性能优化">性能优化</a></h3>
<ul>
<li><a href="https://github.com/bheisler/criterion.rs">bheisler/criterion.rs</a> 比官方提供的benchmark库更好目前已经成为事实上标准的性能测试工具</li>
<li><a href="https://github.com/koute/bytehound">Bytehound</a> Linux下的内存分析工具可以用来分析内存泄漏、内存分配、调用栈追踪甚至它还有一个浏览器UI! 懂的人都懂性能测试工具的UI服务是多么稀缺和珍贵</li>
<li><a href="https://github.com/llogiq/flame">llogiq/flame</a> 专为Rust打造的火焰图分析工具可以告诉你程序在哪些代码上花费的时间过多非常适合用于代码性能瓶颈的分析。与<code>perf</code>不同,<code>flame</code>库允许你自己定义想要测试的代码片段,只需要在代码前后加上相应的指令即可,非常好用</li>
<li><a href="https://github.com/sharkdp/hyperfine">sharkdp/hyperfine</a> 一个命令行benchmark工具支持任意shell命令支持缓存清除、预热、多次运行统计分析等尽量保证结果的准确性</li>
</ul>
<h3 id="多线程"><a class="header" href="#多线程">多线程</a></h3>
<ul>
<li>消息通道channel
<ul>
<li><a href="https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel"><strong>crossbeam-channel</strong></a>, 老牌强库,功能较全,性能较强,之前是独立的库,但是后面合并到了<code>crossbeam</code>主仓库中</li>
<li><a href="https://github.com/zesterer/flume"><strong>flume</strong></a>, 官方给出的性能数据要比crossbeam更好些但是貌似最近没怎么更新</li>
</ul>
</li>
<li>并发原语(锁)
<ul>
<li><a href="https://crates.io/crates/parking_lot">parking_lot</a>, 社区较为活跃star较多更新较为活跃</li>
<li><a href="https://crates.io/crates/spin">spin</a>, 在多数场景中性能比<code>parking_lot</code>高一点,最近没怎么更新</li>
<li>如果不是追求特别极致的性能,建议选择前者</li>
</ul>
</li>
</ul>
<h3 id="编解码"><a class="header" href="#编解码">编解码</a></h3>
<ul>
<li><a href="https://github.com/serde-rs/serde">Serde</a> 一个超高性能的通用序列化/反序列化框架,可以跟多种协议的库联合使用,实现统一编解码格式</li>
<li>CSV
<ul>
<li><a href="https://github.com/BurntSushi/rust-csv">BurntSushi/rust-csv</a> 高性能CSV读写库支持<a href="https://github.com/serde-rs/serde">Serde</a></li>
</ul>
</li>
<li>JSON
<ul>
<li><a href="https://github.com/serde-rs/json">serde-rs/json</a> 快到上天的JSON库也是Rust事实上的标准JSON库你也可以使用它的大哥<a href="https://github.com/serde-rs/serde">serde</a>,一个更通用的序列化/反序列化库</li>
</ul>
</li>
<li>MsgPack
<ul>
<li><a href="https://github.com/3Hren/msgpack-rust">3Hren/msgpack-rust</a> 纯Rust实现的MessagePack编解码协议</li>
</ul>
</li>
<li>ProtocolBuffers
<ul>
<li><a href="https://github.com/tokio-rs/prost">tokio-rs/prost</a> tokio出品基本都属精品此库也不例外简单易用文档详细</li>
<li><a href="https://github.com/stepancheg/rust-protobuf">stepancheg/rust-protobuf</a> 纯Rust实现</li>
</ul>
</li>
<li>TOML
<ul>
<li><a href="https://github.com/alexcrichton/toml-rs">alexcrichton/toml-rs</a> TOML编码/解码,可以配合<code>serde</code>使用</li>
</ul>
</li>
<li>XML
<ul>
<li><a href="https://github.com/tafia/quick-xml">tafia/quick-xml</a> 高性能XML库可以配合<code>serde</code>使用,文档较为详细</li>
</ul>
</li>
<li>YAML
<ul>
<li><a href="https://github.com/dtolnay/serde-yaml">dtolnay/serde-yaml</a> 使用<code>serde</code>编解码<code>YAML</code>格式的数据</li>
</ul>
</li>
</ul>
<h3 id="ui-开发框架"><a class="header" href="#ui-开发框架">UI 开发框架</a></h3>
<ul>
<li>跨平台
<ul>
<li><a href="https://github.com/DioxusLabs/dioxus">DioxusLabs/Dioxus</a> 跨平台 UI 开发框架,支持 <code>WASM</code><code>Desktop</code><code>TUI</code> 等应用开发,文档较为详细</li>
</ul>
</li>
</ul>
<h3 id="email"><a class="header" href="#email">Email</a></h3>
<ul>
<li><a href="https://github.com/lettre/lettre">lettre/lettre</a> — Rust SMTP库</li>
</ul>
<h3 id="常用正则模版"><a class="header" href="#常用正则模版">常用正则模版</a></h3>
<div style="break-before: page; page-break-before: always;"></div><h1 id="明星项目"><a class="header" href="#明星项目">明星项目</a></h1>
<blockquote>
<p>滚滚长江东逝水,浪花淘尽英雄,是非成败转头空 - 临江仙·滚滚长江东逝水</p>
</blockquote>
<p>经过大浪淘沙留下来的才是真金白银,对于开源项目也是如此。对于明星项目,本文不仅仅以<code>star</code>数的多少作为评判维度,还会结合项目规模、影响力、活跃度、社区活跃度等多个方面进行评定,希望大家能喜欢。</p>
<p>需要注意本文列出的几乎都是平台级项目因此并不是star多就能名列其中例如很多<code>star</code>很多的工具、Rust库、书籍都没有列入如果大家想要看更多的子类项目请访问对应的文件进行查看。</p>
<h2 id="deno"><a class="header" href="#deno">deno</a></h2>
<p>首先出场的自然是咖位最重的之一,可以说正是因为<code>deno</code><code>swc</code>的横空出世才让一堆观望的大神对于Rust实现<code>Javascript</code>基建有了更强的信心。</p>
<p><a href="https://github.com/topics/rust?l=rust&amp;o=desc&amp;s=stars"><code>deno</code></a><code>node</code>半逆转后的字序,从此可以看出<code>deno</code><code>Node.js</code>的替代,它的目标是为<code>Typescript/Javascript</code>提供一个更现代化、更安全、更强大 的运行时同时内置了很多强大的工具可以用于打包、编译成可执行文件、文档、测试、lint等。</p>
<h2 id="alacritty"><a class="header" href="#alacritty">alacritty</a></h2>
<p><a href="https://github.com/alacritty/alacritty">alacritty</a>是一个跨平台、基于OpenGL的终端性能极高的同时还支持丰富的自定义和可扩展性可以说是非常优秀的现代化终端。</p>
<p>目前已经是<code>beta</code>阶段,可以作为日常工具来使用。</p>
<img alt="alacritty screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/alacritty.png?raw=true" class="center" />
<h2 id="starship"><a class="header" href="#starship">starship</a></h2>
<p><a href="https://github.com/starship/starship">starship</a>是一个命令行提示,支持任何<code>shell</code>,包括<code>zsh</code>,简单易用、非常快且拥有极高的可配置性, 同时支持智能提示。</p>
<img alt="starship screenshot" width="100%" src="https://raw.githubusercontent.com/starship/starship/master/media/demo.gif" class="center" />
<h2 id="meilisearch"><a class="header" href="#meilisearch">MeiliSearch</a></h2>
<p><a href="https://github.com/meilisearch/MeiliSearch">MeiliSearch</a>是一个搜索平台,但是跟<code>ElasticSearch</code>不同,<code>MeiliSearch</code>并不是通用目的的,它的目标是为终端用户提供边输入边提示的即刻搜索功能,因此是一个轻量级搜索平台,不适用于数据量大时的搜索目的。</p>
<p>总之如果你需要在网页端或者APP为用户提供一个搜索条然后支持输入容错、前缀搜索时就可以使用它。</p>
<img alt="meilisearch screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/meilisearch.gif?raw=true" class="center" />
<h2 id="swc-195k"><a class="header" href="#swc-195k">swc 🌟19.5k</a></h2>
<p><a href="https://github.com/swc-project/swc"><code>swc</code></a><code>Typescript/Javascript</code>编译器它可以用来编译、压缩和打包JS同时支持使用插件进行扩展例如做代码变换等。</p>
<p><code>swc</code>目前正在被一些知名项目所使用,包括<code>Next.js</code>,<code>Parcel</code><code>Deno</code>,还有些著名的公司也在使用它,例如<code>Vercel</code>、字节跳动、腾讯等。</p>
<p>它的性能非常非常高,官方号称,在单线程下比<code>Babel</code>快20倍在4核心下比<code>Babel</code>快70倍</p>
<p>几个使用案例:</p>
<ul>
<li><a href="http://nextjs.org/12">nextjs 12</a>, 通过使用<code>swc</code>获得了更好的扩展性、性能以及wasm的支持其中性能方面提升了3倍刷新速度、5倍打包速度</li>
<li><a href="https://parceljs.org/">Parcel</a>,通过使用<code>swc</code>改善了10倍的性能</li>
</ul>
<img alt="parcel screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/parcel.png?raw=true" class="center" />
<h2 id="tauri"><a class="header" href="#tauri">tauri</a></h2>
<p><a href="https://tauri.studio">tauri</a>可以用来更小、更快、更安全的桌面应用,它想要替代的是<code>electro.js</code></p>
<p>下面是援引自<a href="https://tauri.studio/benchmarks">官网</a>的性能对比图:</p>
<img alt="tauri1 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/tauri1.png?raw=true" class="center" />
<img alt="tauri2 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/tauri2.png?raw=true" class="center" />
<h2 id="yew"><a class="header" href="#yew">yew</a></h2>
<p><a href="https://github.com/yewstack/yew"><code>yew</code></a>是一个正在活跃开发的<code>Rust/Wasm</code>框架,用于构建<code>Web</code>应用。</p>
<img alt="yew screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/yew.jpg?raw=true" class="center" />
<h2 id="firecracker"><a class="header" href="#firecracker">firecracker</a></h2>
<p><a href="https://github.com/firecracker-microvm/firecracker"><code>firecracker</code></a>是一个安全、高性能的无服务计算虚拟机(FaaS)支持多租户、资源隔离等高级特性由Amazon公司开发为AWS部分云计算服务提供了强力有的支持。BTW亚马逊Amazon公司对于Rust语言的喜爱是众所周知的几乎已经成了Rust的形象大使之一了:</p>
<h2 id="nushell"><a class="header" href="#nushell">nushell</a></h2>
<p><a href="https://github.com/nushell/nushell"><code>nushell</code></a>是一个全新的<code>shell</code>,使用<code>Rust</code>实现。它的目标是创建一个现代化的<code>shell</code>:虽然依然基于<code>Unix</code>的哲学,但是更适合现在的时代。例如,你可以使用<code>SQL</code>语法来选择你想要的内容!</p>
<img alt="delta screenshot" width="100%" src="https://github.com/nushell/nushell/raw/main/images/nushell-autocomplete5.gif" class="center" />
<h2 id="tokio"><a class="header" href="#tokio">tokio</a></h2>
<p><a href="https://github.com/tokio-rs/tokio"><code>tokio</code></a>的名声可以说是如雷贯耳如果学过Rust但是没有听说过它那我觉得可能要回炉重造下:)</p>
<p><code>tokio</code>是一个异步IO的运行时提供了<code>I/O</code>、网络、调度、定时器等等异步编程所必须的功能和工具,性能和功能都异常强大。</p>
<h2 id="appflowy"><a class="header" href="#appflowy">AppFlowy</a></h2>
<p><a href="https://github.com/AppFlowy-IO/appflowy">AppFlowy</a><a href="https://www.notion.so/zh-cn"><code>Notion</code></a>的开源实现,使用<code>Rust</code><code>Flutter</code>进行开发,用于用户文档和数据的管理,支持丰富的自定义特性。</p>
<img alt="appflowy screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/appflowy.png?raw=true" class="center" />
<h2 id="bevy"><a class="header" href="#bevy">Bevy</a></h2>
<p><a href="https://github.com/bevyengine/bevy">bevy</a>是一个数据驱动的游戏引擎支持2D和3D图形开发优点是社区活跃、更新快、模块化设计优秀、性能高缺点是还处于快速开发中并不适合生产使用。</p>
<p>同时<code>bevy</code>的文档齐全,官方示例很多,非常适合学习和使用。</p>
<img alt="bevy screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/bevy.jpg?raw=true" class="center" />
<h2 id="actix-web"><a class="header" href="#actix-web">actix-web</a></h2>
<p><a href="">actix-web</a>是全世界最快的web框架之一甚至可以把之一去掉因为排在它前面的看上去像是一个专为跑分而生的轻量级框架<code>actix-web</code>可是功能相当多的!</p>
<p>下面给出<code>actix</code>和Go语言<code>Gin</code>框架的性能对比:</p>
<img alt="actix screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/actix.png?raw=true" class="center" />
<h2 id="iced"><a class="header" href="#iced">iced</a></h2>
<p><a href="https://github.com/iced-rs/iced"><code>iced</code></a>是一个跨平台GUI库具有简单易用、模块化设计、响应式布局等优点。</p>
<img alt="iced screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/iced.gif?raw=true" class="center" />
<h2 id="cubejs"><a class="header" href="#cubejs">cube.js</a></h2>
<p><a href="https://github.com/cube-js/cube.js"><code>cube.js</code></a>是一个数据分析API平台可以用于构建内部的BI或为现有的应用增加客户数据统计等功能使用<code>Rust</code><code>Typescript</code>构建。</p>
<img alt="cubejs screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/cubejs.png?raw=true" class="center" />
<h2 id="wasmer"><a class="header" href="#wasmer">wasmer</a></h2>
<p><a href="https://github.com/wasmerio/wasmer">wasmer</a>是业界领先的<code>WASM</code>运行时,支持<code>WASI</code><code>Emscripten</code></p>
<pre><code class="language-shell">$ wasmer qjs.wasm
QuickJS - Type &quot;\h&quot; for help
qjs &gt; const i = 1 + 2;
qjs &gt; console.log(&quot;hello &quot; + i);
hello 3
</code></pre>
<h2 id="tikv"><a class="header" href="#tikv">tikv</a></h2>
<p><a href="https://github.com/tikv/tikv"><code>tikv</code></a>相信大家都已知道,<code>tidb</code>的底层存储服务,国人之光项目,在数据之外,还做了大量的技术知识普及工作,值得敬佩!</p>
<p><code>tikv</code>是分布式<code>KV</code>数据库,支持分布式事务。</p>
<img alt="tikv screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/tikv.png?raw=true" class="center" />
<h2 id="ruffle"><a class="header" href="#ruffle">ruffle</a></h2>
<p><a href="https://github.com/ruffle-rs/ruffle"><code>ruffle</code></a>是用Rust写的<code>Flash Player</code>模拟器同时支持桌面端和Web端其中后者通过WASM提供支持。</p>
<h2 id="rustdesk"><a class="header" href="#rustdesk">rustdesk</a></h2>
<p><a href="https://github.com/rustdesk/rustdesk"><code>rustdesk</code></a>是国内团队开发的一款远程桌面软件。</p>
<img alt="rustdesk screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/rustdesk.jpg?raw=true" class="center" />
<h2 id="rustpython"><a class="header" href="#rustpython">RustPython</a></h2>
<p>[<code>RustPython</code>]是使用<code>Rust</code>实现的<code>Python</code>解释器, 支持<code>Python3</code>CPython &gt;= 3.9.0)。</p>
<p>大家可以通过官方提供的<a href="https://rustpython.github.io/demo/">在线网址</a>进行尝试。</p>
<img alt="rustpython screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/rustpython.jpg?raw=true" class="center" />
<h2 id="vector"><a class="header" href="#vector">vector</a></h2>
<p><a href="https://github.com/vectordotdev/vector"><code>vector</code></a>是一个性能很高的数据采集agent采集本地的日志、监控等数据发送到远程的kafka、jaeger等数据下沉端它最大的优点就是能从多种数据源(包括Opentelemetry)收集数据,然后推送到多个数据处理或者存储等下沉端。</p>
<img alt="vector screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/vector.jpg?raw=true" class="center" />
<h2 id="mdbook"><a class="header" href="#mdbook">mdbook</a></h2>
<p><a href="https://github.com/rust-lang/mdBook"><code>mdbbok</code></a>可以基于<code>markdown</code>文件自动创建在线电子书,非常简单好用,目前的问题就是缺乏章节内部的目录跳转和中文搜索。</p>
<img alt="mdbook screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/mdbook.jpg?raw=true" class="center" />
<h2 id="zola"><a class="header" href="#zola">zola</a></h2>
<p><a href="https://github.com/getzola/zola"><code>zola</code></a>是一个静态网站生成器,类似<code>hugo</code></p>
<img alt="zola screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/zola.jpg?raw=true" class="center" />
<h2 id="gitui"><a class="header" href="#gitui">gitui</a></h2>
<p><a href="https://github.com/extrawurst/gitui"><code>gitui</code></a>是一个奇快无比的Git终端UI无需浏览器即可使用。</p>
<img alt="gitui screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/gitui.gif?raw=true" class="center" />
<h2 id="solana"><a class="header" href="#solana">solana</a></h2>
<p><a href="https://github.com/solana-labs/solana"><code>solana</code></a>是知名的区块链平台,快速、安全、去中心化,还自带应用市场。</p>
<h2 id="ripgrep"><a class="header" href="#ripgrep">ripgrep</a></h2>
<p><a href="https://github.com/BurntSushi/ripgrep"><code>ripgrep</code></a>是一个性能极高的现代化<code>grep</code>实现,后者是<code>Unix/Linux</code>下的内置文件搜索工具。该项目是Rust的明星项目一个是因为性能极其的高另一个就是源代码质量很高值得学习, 同时<code>Vscode</code>使用它作为内置的搜索引擎。</p>
<p>从功能来说,除了全面支持<code>grep</code>的功能外,<code>repgre</code>支持使用正则递归搜索指定的文件目录,默认使用<code>.gitignore</code>对指定的文件进行忽略。</p>
<img alt="ripgrep screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/ripgrep.png?raw=true" class="center" />
<h2 id="citybound"><a class="header" href="#citybound">citybound</a></h2>
<p><a href="https://github.com/citybound/citybound"><code>citybound</code></a>是一个多人在线模拟游戏使用Rust + WASM + JS开发。</p>
<img alt="citybound screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/citybound.png?raw=true" class="center" />
<h2 id="bottlerocket"><a class="header" href="#bottlerocket">bottlerocket</a></h2>
<p><a href="https://github.com/bottlerocket-os/bottlerocket"><code>bottlerocket</code></a>是一个基于<code>Linux</code>的操作系统,它的目标是为容器提供宿主环境。</p>
<h2 id="lemmy"><a class="header" href="#lemmy">lemmy</a></h2>
<p><a href="https://github.com/LemmyNet/lemmy"><code>lemmy</code></a>是一个<code>reddit</code>克隆,可以通过连接聚合的方式来构建社区,支持桌面和移动端。</p>
<img alt="lemmy screenshot" width="100%" src="/superstar/lemmy.jpg?raw=true" class="center" />
<h2 id="tantivy"><a class="header" href="#tantivy">tantivy</a></h2>
<p><a href="https://github.com/quickwit-inc/tantivy"><code>tantivy</code></a>是Rust实现的本地搜索库功能对标<code>lucene</code>如果你不需要分布式那么引入tantivy作为自己本地Rust服务的一个搜索是相当不错的选择该库作者一直很活跃而且最近还创立了搜索引擎公司感觉大有作为. 该库的优点在于纯Rust实现性能高(lucene的2-3倍),资源占用低(对比java自然不是一个数量级),社区活跃。</p>
<h2 id="sled"><a class="header" href="#sled">sled</a></h2>
<p><a href="https://github.com/spacejam/sled"><code>sled</code></a>是本地嵌入式的数据库。</p>
<pre><pre class="playground"><code class="language-rust edition2021">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>let tree = sled::open(&quot;/tmp/welcome-to-sled&quot;)?;
// insert and get, similar to std's BTreeMap
let old_value = tree.insert(&quot;key&quot;, &quot;value&quot;)?;
assert_eq!(
tree.get(&amp;&quot;key&quot;)?,
Some(sled::IVec::from(&quot;value&quot;)),
);
// range queries
for kv_result in tree.range(&quot;key_1&quot;..&quot;key_9&quot;) {}
// deletion
let old_value = tree.remove(&amp;&quot;key&quot;)?;
// atomic compare and swap
tree.compare_and_swap(
&quot;key&quot;,
Some(&quot;current_value&quot;),
Some(&quot;new_value&quot;),
)?;
// block until all operations are stable on disk
// (flush_async also available to get a Future)
tree.flush()?;
<span class="boring">}
</span></code></pre></pre>
<h3 id="redox"><a class="header" href="#redox">redox</a></h3>
<p><a href="https://github.com/redox-os/redox"><code>Redox</code></a>是一个<code>Unix</code>风格的微内核操作系统,使用<code>Rust</code>实现。<code>redox</code>的目标是安全、快速、免费、可用,它在内核设计上借鉴了很多优秀的内核,例如:<code>SeL4</code>, <code>MINIX</code>, <code>Plan 9</code><code>BSD</code></p>
<p><code>redox</code>不仅仅是一个内核,它还是一个功能齐全的操作系统,提供了操作系统该有的功能,例如:内存分配器、文件系统、显示管理、核心工具等等。你可以大概认为它是一个<code>GNU</code><code>BSD</code>生态,但是是通过一门现代化、内存安全的语言实现的。</p>
<blockquote>
<p>不过据我仔细观察redox目前的开发进度不是很活跃不知道发生了什么未来若有新的发现会在这里进行更新 - Sunface</p>
</blockquote>
<img alt="redox1 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/redox1.jpg?raw=true" class="center" />
<img alt="redox2 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/redox2.jpeg?raw=true" class="center" />
<h3 id="youki"><a class="header" href="#youki">youki</a></h3>
<p><a href="https://github.com/containers/youki"><code>youki</code></a>是一个容器运行时,实现了<code>OCI</code>标准,性能非常好的同时具备非常高的安全性, 目前来说,它的性能跟<code>crun</code>差不多,比<code>runc</code>快50%以上。</p>
<h3 id="sixtyfps"><a class="header" href="#sixtyfps">sixtyfps</a></h3>
<p><a href="https://github.com/sixtyfpsui/sixtyfps"><code>sixtyfps</code></a>是一个GUI工具集同时适用于嵌入式系统、桌面系统、移动端、浏览器(WASM),支持使用多种语言进行开发,背后有商业公司的支持,未来前景看好。</p>
<img alt="sixtyfps screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/sixtyfps.png?raw=true" class="center" />
<h3 id="wasmtime"><a class="header" href="#wasmtime">wasmtime</a></h3>
<p><a href="https://github.com/bytecodealliance/wasmtime">wasmtime</a>是一个为<code>WASM</code>设计的<code>JIT</code>风格的独立运行时,支持<code>WASI</code></p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
println!(&quot;Hello, world!&quot;);
}
</code></pre></pre>
<pre><code class="language-shell">$ rustup target add wasm32-wasi
$ rustc hello.rs --target wasm32-wasi
$ wasmtime hello.wasm
Hello, w
</code></pre>
<h3 id="polkadot"><a class="header" href="#polkadot">polkadot</a></h3>
<p><a href="https://github.com/paritytech/polkadot"><code>polkadot</code></a>是知名的区块链平台,它是从<a href="https://github.com/paritytech/substrate"><code>Substrate</code></a>抽离出来,后者是下一代区块链开发框架。</p>
<h3 id="lapce"><a class="header" href="#lapce">lapce</a></h3>
<p><a href="https://github.com/lapce/lapce"><code>lapce</code></a>是一款性能极高、功能强大、基于<code>wgpu</code>渲染的代码编辑器,基于<code>Xi-Editor</code>开发,后者<code>Xi-Editor</code>曾经也红极一时,可惜不再维护了,但是依然非常适合做一个编辑器内核。</p>
<img alt="lapce screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/lapce.jpg?raw=true" class="center" />
<h3 id="rust-gpu"><a class="header" href="#rust-gpu">rust-gpu</a></h3>
<p><a href="https://github.com/EmbarkStudios/rust-gpu">rust-gpu</a>的目标是让Rust成为GPU编程的第一梯队语言由大名鼎鼎的<code>Embark</code>公司开发,后台较硬。</p>
<p>如果需要通用的<code>GPU</code>编程,选它就对了。</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="使用rust增强javascript"><a class="header" href="#使用rust增强javascript">使用Rust增强Javascript</a></h1>
<p><code>Javascript</code>是目前全世界使用最广的语言(TIOBE排行榜比较迷JS并没有排在第一位我个人并不认同它的排名)。在过去这么多年中,围绕着<code>Javascript</code>已经建立了庞大的基础设施生态:例如使用<code>webpack</code>来将多个<code>js</code>文件打包成一个;使用<code>Babel</code>允许你用现代化的<code>js</code>语法编写兼容旧浏览器的代码;使用<code>Eslint</code>帮助开发找出代码中潜在的问题,类似<code>cargo clippy</code></p>
<p>以上的种种都在帮助<code>js</code>成为更好的语言和工具,它们是<code>Web</code>应用程序得以顺利、高效的开发和运行的基石。这些工具往往使用<code>Javascript</code>语言编写,一般来说,是没有问题的,但是在某些时候,可能会存在性能上的瓶颈或者安全隐患,因此阴差阳错、机缘巧合下,<code>Rust</code>成为了一个搅局者。</p>
<h2 id="javascript基建库"><a class="header" href="#javascript基建库">Javascript基建库</a></h2>
<h3 id="deno-1"><a class="header" href="#deno-1">deno</a></h3>
<p>首先出场的自然是咖位最重的之一,可以说正是因为<code>deno</code><code>swc</code>的横空出世才让一堆观望的大神对于Rust实现<code>Javascript</code>基建有了更强的信心。</p>
<p><code>deno</code><code>node</code>半逆转后的字序,从此可以看出<code>deno</code><code>Node.js</code>的替代,它的目标是为<code>Typescript/Javascript</code>提供一个更现代化、更安全、更强大 的运行时同时内置了很多强大的工具可以用于打包、编译成可执行文件、文档、测试、lint等。</p>
<p>值得一提的是,<code>deno</code>的不少工具都使用了<code>swc</code>进行建造,包括代码审查、格式化、文档生成等。</p>
<p>通过包引入的方式来对比下<code>deno</code><code>node</code>,大家可以自己品味下。</p>
<pre><code class="language-js">// node
const koa = require(&quot;koa&quot; );
cost logger = require(&quot;@adesso/logger&quot;)
// deno
import { Application } from &quot;https://deno.land/x/oak/mod.ts&quot;;
import { Logger } from &quot;https://adesso.de/lib/logger.ts&quot;
</code></pre>
<h3 id="swc"><a class="header" href="#swc">swc</a></h3>
<p><a href="https://github.com/swc-project/swc"><code>swc</code></a><code>Typescript/Javascript</code>编译器它可以用来编译、压缩和打包JS同时支持使用插件进行扩展例如做代码变换等。</p>
<p><code>swc</code>目前正在被一些知名项目所使用,包括<code>Next.js</code>,<code>Parcel</code><code>Deno</code>,还有些著名的公司也在使用它,例如<code>Vercel</code>、字节跳动、腾讯等。</p>
<p>它的性能非常非常高,官方号称,在单线程下比<code>Babel</code>快20倍在4核心下比<code>Babel</code>快70倍</p>
<p>几个使用案例:</p>
<ul>
<li><a href="http://nextjs.org/12">nextjs 12</a>, 通过使用<code>swc</code>获得了更好的扩展性、性能以及wasm的支持其中性能方面提升了3倍刷新速度、5倍打包速度</li>
<li><a href="https://parceljs.org/">Parcel</a>,通过使用<code>swc</code>改善了10倍的性能</li>
</ul>
<img alt="parcel screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/parcel.png?raw=true" class="center" />
<p>官方还提供了一个在线运行的<a href="https://swc.rs/playground">demo</a>,功能齐全,可以试试。</p>
<img alt="swc screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/swc.jpg?raw=true" class="center" />
<h3 id="rome"><a class="header" href="#rome">Rome</a></h3>
<p><a href="https://github.com/rome/tools"><code>Rome</code></a>可以用来对<code>JavaScript</code><code>TypeScript</code><code>HTML</code><code>JSON</code><code>Markdown</code><code>CSS</code> 进行lint、编译、打包等功能它的目标是替代<code>Babel</code><code>ESLint</code><code>webpack</code><code>Prettier</code><code>Jest</code>等。</p>
<p>一开始<code>Rome</code>是使用<code>Typescript</code>开发,目前正在用<code>Rust</code>进行重写。有趣的是: <code>Rome</code>的作者也是<code>Babel</code>的作者, 后者还是他在学习编译原理时做的。</p>
<h3 id="fnm"><a class="header" href="#fnm">fnm</a></h3>
<p><a href="https://github.com/Schniz/fnm"><code>fnm</code></a>是一个简单易用、高性能的<code>Node</code>版本管理工具,还支持<code>.nvmrc</code>文件(<code>nvm</code><code>node</code>版本描述文件)</p>
<img alt="fnm screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/fnm.svg?raw=true" class="center" />
<h3 id="boa"><a class="header" href="#boa">boa</a></h3>
<p><a href="https://github.com/boa-dev/boa"><code>boa</code></a>是一个高性能的<code>javascript</code>词法分析器,解析器和解释器,目前还是实验性质的。</p>
<img alt="boa screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/boa.gif?raw=true" class="center" />
<h3 id="napi"><a class="header" href="#napi">napi</a></h3>
<p><a href="https://github.com/napi-rs/napi-rs"><code>napi</code></a>可以用于构建基于<code>Node API</code><code>Nodejs</code>插件,目前由<code>nextjs</code>主导开发。</p>
<h3 id="volt"><a class="header" href="#volt">volt</a></h3>
<p><a href="https://github.com/voltpkg/volt"><code>volt</code></a>是一个现代化的、高性能、安全可靠的<code>Javascript</code>包管理工具。目前该库正处于活跃开发阶段,只供学习使用。</p>
<img alt="volt screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/volt.png?raw=true" class="center" />
<h3 id="neon"><a class="header" href="#neon">neon</a></h3>
<p><a href="https://github.com/neon-bindings/neon"><code>neon</code></a>可以用于写安全、高性能的原生<code>Nodejs</code>模块。</p>
<pre><pre class="playground"><code class="language-rust edition2021">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn make_an_array(mut cx: FunctionContext) -&gt; JsResult&lt;JsArray&gt; {
// 创建一些值:
let n = cx.number(9000);
let s = cx.string(&quot;hello&quot;);
let b = cx.boolean(true);
// 创建一个新数组:
let array: Handle&lt;JsArray&gt; = cx.empty_array();
// 将值推入数组中
array.set(&amp;mut cx, 0, n)?;
array.set(&amp;mut cx, 1, s)?;
array.set(&amp;mut cx, 2, b)?;
// 返回数组
Ok(array)
}
register_module!(mut cx, {
cx.export_function(&quot;makeAnArray&quot;, make_an_array)
})
<span class="boring">}
</span></code></pre></pre>
<h3 id="resvg-js"><a class="header" href="#resvg-js">resvg-js</a></h3>
<p><a href="https://github.com/yisibl/resvg-js">resvg-js</a>是一个高性能<code>svg</code>渲染库使用Rust + Typescript实现。下面的图片通过<code>svg</code>实现(羞~~~): </p>
<img alt="resvg screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/resvg.svg?raw=true" class="center" />
<h3 id="deno_lint"><a class="header" href="#deno_lint">deno_lint</a></h3>
<p><a href="https://github.com/denoland/deno_lint">deno_lint</a>, 由<code>deno</code>团队出品的<code>lint</code>工具,支持<code>Javascript/Typescript</code>,支持<code>Deno</code>也支持<code>Node</code></p>
<p>优点之一就是极致的快:</p>
<pre><code class="language-shell">[
{
&quot;name&quot;: &quot;deno_lint&quot;,
&quot;totalMs&quot;: 105.3750100000002,
&quot;runsCount&quot;: 5,
&quot;measuredRunsAvgMs&quot;: 21.07500200000004,
&quot;measuredRunsMs&quot;: [
24.79783199999997,
19.563640000000078,
20.759051999999883,
]
},
{
&quot;name&quot;: &quot;eslint&quot;,
&quot;totalMs&quot;: 11845.073306000002,
&quot;runsCount&quot;: 5,
&quot;measuredRunsAvgMs&quot;: 2369.0146612000003,
&quot;measuredRunsMs&quot;: [
2686.1039550000005,
2281.501061,
2298.6185210000003,
]
}
]
</code></pre>
<h3 id="rslint"><a class="header" href="#rslint">rslint</a></h3>
<p><a href="https://github.com/rslint/rslint">rslint</a>是一个高性能、可定制性强、简单易用的<code>Javascript/Typescript</code> lint分析工具。</p>
<pre><code class="language-shell">$ echo &quot;let a = foo.hasOwnProperty('bar');&quot; &gt; foo.js
$ rslint ./foo.js
error[no-prototype-builtins]: do not access the object property `hasOwnProperty` directly from `foo`
┌─ ./foo.js:1:9
1 │ let a = foo.hasOwnProperty('bar');
│ ^^^^^^^^^^^^^^^^^^^^^^^^^
help: get the function from the prototype of `Object` and call it
1 │ let a = Object.prototype.hasOwnProperty.call(foo, 'bar');
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
╧ note: the method may be shadowed and cause random bugs and denial of service vulnerabilities
Outcome: 1 fail, 0 warn, 0 success
help: for more information about the errors try the explain command: `rslint explain &lt;rules&gt;`
</code></pre>
<h3 id="rusty_v8"><a class="header" href="#rusty_v8">rusty_v8</a></h3>
<p><a href="https://github.com/denoland/rusty_v8">rusty_v8</a><code>v8</code>的Rust语言绑定底层封装了<code>c++ API</code></p>
<h2 id="用wasm增强js"><a class="header" href="#用wasm增强js">用WASM增强JS</a></h2>
<h3 id="wasm"><a class="header" href="#wasm">wasm</a></h3>
<p><a href="https://webassembly.org/docs/use-cases/">wasm(web assembly)</a>是一种低级语言,它运行在浏览器中,可以和<code>javascript</code>相互调用,几乎所有浏览器都支持, 而且目前有多种高级语言都可以直接编译成<code>wasm</code>,更是大大增强了它的地位。</p>
<p>目前来说Rust可以编译成<code>wasm</code>,虽然还不够完美,但是它正在以肉眼可见的速度快速发展中。因此同时使用<code>Rust</code><code>Javascript</code>成为了一种可能:将<code>Rust</code>编译成<code>wasm</code>,再跟<code>js</code>进行交互,两者共生共存,各自解决擅长的场景(<code>wasm</code>性能高,<code>js</code>开发速度快)。</p>
<h3 id="yew-1"><a class="header" href="#yew-1">yew</a></h3>
<p><a href="https://github.com/yewstack/yew"><code>yew</code></a>是一个正在活跃开发的<code>Rust/Wasm</code>框架,用于构建<code>Web</code>客户端应用。</p>
<img alt="yew screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/yew.jpg?raw=true" class="center" />
<h3 id="gloo"><a class="header" href="#gloo">gloo</a></h3>
<p>[gloo]是一个模块化的工具,使用<code>Rust/WASM</code>构建快速、可靠的<code>Web</code>应用。</p>
<pre><pre class="playground"><code class="language-rust edition2021">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use gloo::{events::EventListener, timers::callback::Timeout};
use wasm_bindgen::prelude::*;
pub struct DelayedHelloButton {
button: web_sys::Element,
on_click: events::EventListener,
}
impl DelayedHelloButton {
pub fn new(document: &amp;web_sys::Document) -&gt; Result&lt;DelayedHelloButton, JsValue&gt; {
// 创建 `&lt;button&gt;` 元素.
let button = document.create_element(&quot;button&quot;)?;
// 监听button上的`click`事件
let button2 = button.clone();
let on_click = EventListener::new(&amp;button, &quot;click&quot;, move |_event| {
// 一秒后更新button中的文本
let button3 = button2.clone();
Timeout::new(1_000, move || {
button3.set_text_content(Some(&quot;Hello from one second ago!&quot;));
})
.forget();
});
Ok(DelayedHelloButton { button, on_click })
}
}
<span class="boring">}
</span></code></pre></pre>
<h3 id="wasm-bindgen"><a class="header" href="#wasm-bindgen">wasm-bindgen</a></h3>
<p><a href="https://github.com/rustwasm/wasm-bindgen">wasm-bindgen</a>可以让<code>WASM</code>模块和<code>Javascript</code>模块进行更好的交互。</p>
<pre><pre class="playground"><code class="language-rust edition2021">
<span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use wasm_bindgen::prelude::*;
// 从Web导入 `window.alert` 函数
#[wasm_bindgen]
extern &quot;C&quot; {
fn alert(s: &amp;str);
}
// 从Rust导出一个`greet`函数到Javascript该函数会`alert`一条欢迎信息
#[wasm_bindgen]
pub fn greet(name: &amp;str) {
alert(&amp;format!(&quot;Hello, {}!&quot;, name));
}
<span class="boring">}
</span></code></pre></pre>
<h3 id="wasm-pack"><a class="header" href="#wasm-pack">wasm-pack</a></h3>
<p><a href="https://github.com/rustwasm/wasm-pack">wasm-pack</a>是一站式的解决方案用于构建和使用Rust生成的WASM支持在浏览器中或后台的<code>Node.js</code>中与<code>Javascript</code>进行交互。</p>
<img alt="wasm-pack screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/wasm-pack.gif?raw=true" class="center" />
<h3 id="wasmer-1"><a class="header" href="#wasmer-1">wasmer</a></h3>
<p><a href="https://github.com/wasmerio/wasmer">wasmer</a>是业界领先的<code>WASM</code>运行时,支持<code>WASI</code><code>Emscripten</code></p>
<pre><code class="language-shell">$ wasmer qjs.wasm
QuickJS - Type &quot;\h&quot; for help
qjs &gt; const i = 1 + 2;
qjs &gt; console.log(&quot;hello &quot; + i);
hello 3
</code></pre>
<h3 id="wasmtime-1"><a class="header" href="#wasmtime-1">wasmtime</a></h3>
<p><a href="https://github.com/bytecodealliance/wasmtime">wasmtime</a>是一个为<code>WASM</code>设计的<code>JIT</code>风格的独立运行时。</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
println!(&quot;Hello, world!&quot;);
}
</code></pre></pre>
<pre><code class="language-shell">$ rustup target add wasm32-wasi
$ rustc hello.rs --target wasm32-wasi
$ wasmtime hello.wasm
Hello, world!
</code></pre>
<h3 id="trunk"><a class="header" href="#trunk">trunk</a></h3>
<p><a href="https://github.com/thedodd/trunk">trunk</a>是一个<code>WASM</code>构建、打包、Web发布工具。</p>
<h3 id="photon"><a class="header" href="#photon">photon</a></h3>
<p><a href="">photon</a>是高性能的、跨平台的图片处理库,使用<code>Rust</code>开发,编译成<code>WASM</code>运行为你的Web应用和<code>Node.js</code>应用提供无与伦比的图片处理速度,当然,它既然使用<code>Rust</code>开发,也可以作为一个库被你的后台程序所使用。</p>
<img alt="photon screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/photon.jpg?raw=true" class="center" />
<h3 id="tinysearch"><a class="header" href="#tinysearch">tinysearch</a></h3>
<p><a href="https://github.com/tinysearch/tinysearch">tinysearch</a>是一个搜索工具,用于静态网站中的内容搜索,使用<code>Rust</code><code>WASM</code>构建。优点是体积小(适用于浏览器)、性能高、全文索引。</p>
<img alt="tinysearch screenshot" width="80%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/tinysearch.gif?raw=true" class="center" />
<h3 id="wasm-pdf"><a class="header" href="#wasm-pdf">wasm-pdf</a></h3>
<p><a href="https://github.com/jussiniinikoski/wasm-pdf">wasm-pdf</a>通过<code>Javascript</code><code>WASM</code>来生成<code>PDF</code>,可以直接在浏览器中使用。</p>
<h3 id="makepad"><a class="header" href="#makepad">makepad</a></h3>
<p><a href="https://github.com/makepad/makepad">makepad</a>是一个充满创意的Rust开发平台支持编译成<code>wasm</code>,并使用<code>webGL</code>进行渲染。</p>
<img alt="makepad screenshot" width="80%" src="https://github.com/studyrs/cookbook-images/blob/main/javascript/makepad.jpg?raw=true" class="center" />
<h2 id="rust--javascript学习教程"><a class="header" href="#rust--javascript学习教程">Rust + Javascript学习教程</a></h2>
<h3 id="wasm-book"><a class="header" href="#wasm-book">wasm-book</a></h3>
<p><a href="https://github.com/rustwasm/book">wasm-book</a>是一本讲述<code>Rust</code><code>wasm</code>的书,篇幅不算长,但是值得学习,还包含了几个很酷的例子。</p>
<h3 id="wasm-learning"><a class="header" href="#wasm-learning">wasm-learning</a></h3>
<p><a href="https://github.com/second-state/wasm-learning"><code>wasm-learning</code></a>是一个英文教程,用于学习<code>Rust</code>, <code>wasm</code><code>Node.js</code>,你可以学会如何使用<code>Rust</code>来为<code>Nodejs</code>构建函数,可以同时利用<code>Rust</code>的性能、<code>wasm</code>的安全性和可移植性、<code>js</code>的易用性。</p>
<h3 id="rust-js-snake-game"><a class="header" href="#rust-js-snake-game">rust-js-snake-game</a></h3>
<p><a href="https://github.com/RodionChachura/rust-js-snake-game"><code>rust-js-snake-game</code></a>是一个用<code>rust + js + wasm</code>构建的贪食蛇游戏。</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="游戏"><a class="header" href="#游戏">游戏</a></h1>
<p>我们精心挑选了一些用Rust写得优秀游戏希望大家喜欢)</p>
<h3 id="目录索引-1"><a class="header" href="#目录索引-1">目录索引</a></h3>
<table><thead><tr><th>游戏名</th><th>描述</th></tr></thead><tbody>
<tr><td><a href="games.html#veloren">veloren</a></td><td>多人在线3D PRG游戏</td></tr>
<tr><td><a href="games.html#citybound">citybound</a></td><td>多人在线城市模拟游戏</td></tr>
<tr><td><a href="games.html#sandspiel">sandspiel</a></td><td>创意游戏-落沙世界</td></tr>
<tr><td><a href="games.html#fish-fight">fish fight</a></td><td>多人2D射击策略游戏</td></tr>
<tr><td><a href="games.html#doukutsu">doukutsu</a></td><td><code>Cave Story</code>重制版</td></tr>
<tr><td><a href="games.html#rusted-ruins">rusted ruins</a></td><td>开发世界、像素游戏</td></tr>
<tr><td><a href="games.html#sulis">sulis</a></td><td>回合制策略游戏</td></tr>
<tr><td><a href="games.html#zemeroth">zemeroth</a></td><td>2D棋盘策略游戏</td></tr>
<tr><td><a href="games.html#mk48">mk48</a></td><td>2D多人在线海战游戏</td></tr>
<tr><td><a href="games.html#theta-wave">theta wave</a></td><td>2D太空射击游戏</td></tr>
<tr><td><a href="games.html#rust-doom">rust doom</a></td><td>模仿<code>Doom</code>的射击游戏</td></tr>
</tbody></table>
<h3 id="veloren"><a class="header" href="#veloren">veloren</a></h3>
<p><a href="https://gitlab.com/veloren/veloren">Veloren</a>是一款多人在线3D RPG游戏该游戏借鉴了<code>Cube World</code><code>Minecraft(我的世界)</code><code>Dwarf fortress</code></p>
<p>目前游戏开发非常活跃也是Rust目前游戏中最有前景的之一值得看好当前已经可以玩你可以通过<a href="https://veloren.net/account/">官方地址</a>在线试玩。</p>
<img alt="veloren screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/veloren.jpg?raw=true" class="center" />
<h3 id="citybound-1"><a class="header" href="#citybound-1">citybound</a></h3>
<p><a href="https://github.com/citybound/citybound">citybound</a>是一个多人在线模拟游戏使用Rust + WASM + JS开发。</p>
<img alt="citybound screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/citybound.png?raw=true" class="center" />
<h3 id="sandspiel"><a class="header" href="#sandspiel">sandspiel</a></h3>
<p><a href="https://github.com/MaxBittker/sandspiel">sandspiel</a>是一款很有创意、很艺术的游戏通过天上落下的沙子来构建美丽的沙世界。该游戏使用Rust + wasm + webgl + js(胶水,用来粘合前几个)构建。</p>
<p>你可以<a href="https://sandspiel.club">在线试玩</a>,尝试构建自己的沙世界。</p>
<img alt="sandspiel screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/sandspiel.png?raw=true" class="center" />
<h3 id="fish-fight"><a class="header" href="#fish-fight">fish-fight</a></h3>
<p><a href="https://github.com/fishfight/FishFight">Fish Fight</a>是一款2D射击策略游戏支持最多4人一起玩可以通过在线的方式或共享屏幕的方式玩总之这是一款相当不错的游戏<a href="https://fishfight.org">官网</a>做得也很酷。</p>
<img alt="fish-fight screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/fish-fight.jpg?raw=true" class="center" />
<h3 id="doukutsu"><a class="header" href="#doukutsu">doukutsu</a></h3>
<p><a href="https://github.com/doukutsu-rs/doukutsu-rs">doukutsu</a>是2004年发行的视频游戏<code>Cave Story</code>的重制版使用Rust开发。</p>
<p><img alt="doukutsu1 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/doukutsu1.
png?raw=true" class="center" />
<img alt="doukutsu2 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/doukutsu2.
png?raw=true" class="center" /></p>
<h3 id="rusted-ruins"><a class="header" href="#rusted-ruins">rusted-ruins</a></h3>
<p><a href="https://github.com/garkimasera/rusted-ruins">rusted-ruins</a>是一个开放世界2D像素游戏用户可以在里面探索各种野外和废墟。</p>
<p>目前游戏还处于较为早期阶段,但是开发活跃。</p>
<p><img alt="rusted-ruins1 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/rusted-ruins1.
png?raw=true" class="center" />
<img alt="rusted-ruins2 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/rusted-ruins2.
png?raw=true" class="center" /></p>
<h3 id="sulis"><a class="header" href="#sulis">sulis</a></h3>
<p><a href="https://github.com/Grokmoo/sulis">sulis</a>是一款回合制策略游戏,包含了一个从零开发的引擎,目前游戏已经具备相当高的可玩性,你还可以选择不同的势力,感兴趣的同学可以一试。</p>
<img alt="sulis screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/sulis.jpg?raw=true" class="center" />
<h3 id="zemeroth"><a class="header" href="#zemeroth">zemeroth</a></h3>
<p><a href="https://github.com/ozkriff/zemeroth">zemeroth</a>是一个2D棋盘策略游戏通过Rust + WASM实现。</p>
<p>你可以通过<a href="https://ozkriff.itch.io/zemeroth">在线网址</a>使用WASM来体验这个游戏。</p>
<p><img alt="zemeroth1 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/zemeroth1.
png?raw=true" class="center" />
<img alt="zemeroth2 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/zemeroth2.
jpeg?raw=true" class="center" /></p>
<h3 id="mk48"><a class="header" href="#mk48">mk48</a></h3>
<p><a href="https://github.com/SoftbearStudios/mk48">mk48</a>是一个2D海战游戏支持多人在线和和多语言(包括中文),你可以通过官方网址在线试玩: <a href="https://mk48.io">https://mk48.io</a></p>
<p><img alt="mk48 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/mk48.
jpg?raw=true" class="center" /></p>
<h3 id="theta-wave"><a class="header" href="#theta-wave">theta wave</a></h3>
<p><a href="https://github.com/thetawavegame/thetawave-legacy">Theta Wave</a>是一款2D太空射击游戏基于<code>Amethyst</code>引擎开发。</p>
<p>在游戏中你扮演的是保卫目标的飞船游戏的目标是通过摧毁敌人来存活还可以收集货币用于购买对你的生存有帮助的物品并击败最终BOSS取得胜利。</p>
<p><img alt="theta-wave screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/theta.
jpg?raw=true" class="center" /></p>
<h3 id="rust-doom"><a class="header" href="#rust-doom">rust doom</a></h3>
<p><a href="https://github.com/cristicbz/rust-doom">Rust doom</a>是一款模仿<code>Doom 1&amp;2</code>的简单射击游戏,需要注意,它并不是一个<code>Doom</code>移植版。</p>
<p><img alt="rust-doom screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/game/rust-doom.
jpg?raw=true" class="center" /></p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="游戏开发"><a class="header" href="#游戏开发">游戏开发</a></h1>
<p>我在这里大胆预言Rust未来会成为和<code>C++</code>同级别的游戏开发语言,特别是在游戏引擎方面,会大放异彩。</p>
<h2 id="目录索引-2"><a class="header" href="#目录索引-2">目录索引</a></h2>
<ul>
<li><a href="gamedev.html#%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E"><strong>游戏引擎</strong></a> <a href="gamedev.html#bevy">bevy</a> <a href="gamedev.html#fyrox%E5%89%8Drg3d">fyrox</a> <a href="gamedev.html#ggez">ggez</a> <a href="gamedev.html#oxygengine">oxygengine</a> <a href="gamedev.html#macroquad">macroquad</a> <a href="gamedev.html#godot-rust">godot-rust</a> <a href="gamedev.html#piston">piston</a> <a href="gamedev.html#amethyst">amethyst</a></li>
<li><a href="gamedev.html#gpu%E5%92%8C%E5%9B%BE%E5%BD%A2%E6%B8%B2%E6%9F%93"><strong>GPU和图形渲染</strong></a> <a href="gamedev.html#wgpu">wgpu</a> <a href="gamedev.html#rust-gpu">rust-gpu</a><a href="gamedev.html#kajiya">kajiya</a> <a href="gamedev.html#lyon">lyon</a> <a href="gamedev.html#ash">ash</a> <a href="gamedev.html#vulkano">vulkano</a> <a href="gamedev.html#rend3">rend3</a> <a href="gamedev.html#rafx">rafx</a> <a href="gamedev.html#gfx">gfx</a> <a href="gamedev.html#luminance">luminance</a> <a href="gamedev.html#miniquad">miniquad</a> <a href="gamedev.html#glow">glow</a></li>
<li><a href="gamedev.html#%E5%AD%A6%E4%B9%A0%E8%B5%84%E6%96%99"><strong>学习资料和新闻</strong></a></li>
</ul>
<h2 id="游戏引擎"><a class="header" href="#游戏引擎">游戏引擎</a></h2>
<h3 id="bevy-1"><a class="header" href="#bevy-1">Bevy</a></h3>
<p><a href="https://github.com/bevyengine/bevy">bevy</a>是一个数据驱动的游戏引擎支持2D和3D图形开发优点是社区活跃、更新快、模块化设计优秀、性能高缺点是还处于快速开发中并不适合生产使用。</p>
<p>同时<code>bevy</code>的文档齐全,官方示例很多,非常适合学习和使用。</p>
<img alt="fyrox screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/bevy.jpg?raw=true" class="center" />
<h3 id="fyrox前rg3d"><a class="header" href="#fyrox前rg3d">Fyrox(前rg3d)</a></h3>
<p><a href="https://github.com/FyroxEngine/Fyrox">fyrox</a>是一个<code>2D</code><code>3D</code>游戏图形化引擎,功能丰富,生产可用(官方宣称)。</p>
<p>该项目前身是<code>rg3d</code>,但是被收购后,更名为<code>fyrox</code>,潜力应该是相当好的,下面截图来源于基于该引擎开发的游戏<a href="https://github.com/mrDIMAS/StationIapetus"><code>StationIapetus</code></a></p>
<img alt="fyrox screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/fyrox.jpg?raw=true" class="center" />
<h3 id="ggez"><a class="header" href="#ggez">ggez</a></h3>
<p><a href="https://github.com/ggez/ggez">ggez</a>是一个轻量级的<code>2D</code>游戏图形引擎,它的目标是让游戏开发尽量的简单,因此它的功能并不是很强大,例如如果你想要强大且真实的物理引擎,它可能无能为力,但你可以选择在它的基础上构建自己的更高级的引擎。</p>
<img alt="ggez screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/ggez.png?raw=true" class="center" />
<h3 id="oxygengine"><a class="header" href="#oxygengine">oxygengine</a></h3>
<p><a href="https://github.com/PsichiX/oxygengine">oxygengine</a>是一个<code>2D</code> HTML5游戏引擎支持编译成WASM在浏览器中运行。</p>
<img alt="oxygengine screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/oxygengine.gif?raw=true" class="center" />
<h3 id="macroquad"><a class="header" href="#macroquad">macroquad</a></h3>
<p><a href="https://github.com/not-fl3/macroquad">macroquad</a>是一个<code>2D</code>游戏引擎特点是简单易用例如它试图让使用者不会遇到Rust生命周期的难题。</p>
<h3 id="godot-rust"><a class="header" href="#godot-rust">godot-rust</a></h3>
<p><a href="https://github.com/godot-rust/godot-rust">godot-rust</a>是大名鼎鼎的<code>godot</code>引擎的<code>Rust</code>绑定,<a href="https://github.com/godotengine/godot"><code>godot</code></a><code>c++</code>开发的游戏<code>2D/3D</code>引擎但是对Rust语言提供了很好的支持。</p>
<img alt="godot screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/godot.jpg?raw=true" class="center" />
<h3 id="piston"><a class="header" href="#piston">piston</a></h3>
<p><a href="https://github.com/PistonDevelopers/piston">piston</a>是前两年较火的模块化的游戏引擎,但是最近半年开发速度缓慢,我调查了一番,但不清楚发生了什么。</p>
<h3 id="amethyst"><a class="header" href="#amethyst">Amethyst</a></h3>
<p><a href="https://github.com/amethyst/amethyst">Amethyst</a>, 前几年较火的Rust游戏引擎但是最近开发已经停滞经过我调查是因为作者团队转型Rust游戏开发知识分享因此项目被<a href="https://amethyst.rs/posts/amethyst--starting-fresh">放弃</a></p>
<h2 id="gpu和图形渲染"><a class="header" href="#gpu和图形渲染">GPU和图形渲染</a></h2>
<h3 id="wgpu"><a class="header" href="#wgpu">wgpu</a></h3>
<p><a href="https://github.com/gfx-rs/wgpu">wgpu</a>是一个纯Rust实现的图形化API库具有安全、可移植等优点如果你使用基于<code>wgpu</code>构建的库那该库可以很多平台上运行Linux, windows, MacOS, Android和IOS。</p>
<p>它可以原生的运行在<code>Vulkan</code>, <code>Metal</code>等主流平台上,且可以使用<code>wasm</code>的方式运行在<code>WebGPU</code>同时API兼容<code>WebGPU</code>标准。</p>
<p>总之,如果你要使用<code>WebGPU</code>, 选它就对了。</p>
<h3 id="rust-gpu-1"><a class="header" href="#rust-gpu-1">rust-gpu</a></h3>
<p><a href="https://github.com/EmbarkStudios/rust-gpu">rust-gpu</a>的目标是让Rust成为GPU编程的第一梯队语言由大名鼎鼎的<code>Embark</code>公司开发,后台较硬。</p>
<p>如果需要通用的<code>GPU</code>编程,选它就对了。</p>
<h3 id="kajiya"><a class="header" href="#kajiya">kajiya</a></h3>
<p><a href="https://github.com/EmbarkStudios/kajiya">kajiya</a>是一个实时的、全局光照渲染系统,由<code>Embark</code>公司开发该公司在秘密研究急于Rust的游戏引擎据说准备应用在新游戏上有朝一日它可能会是推动Rust游戏引擎爆发式发展的功臣。</p>
<p><code>kajiya</code>应用了非常先进的论文和设计理念,因此非常值得有志于游戏引擎开发的同学学习。但目前还不适用于生产级使用,具体见<a href="https://medium.com/embarkstudios/homegrown-rendering-with-rust-1e39068e56a7">这里</a></p>
<img alt="kajiya screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/kajiya.jpg?raw=true" class="center" />
<h3 id="lyon"><a class="header" href="#lyon">lyon</a></h3>
<p><a href="https://github.com/nical/lyon">lyon</a>可以使用GPU进行向量路径渲染例如高效渲染复杂的<code>svg</code>等。</p>
<h3 id="ash"><a class="header" href="#ash">ash</a></h3>
<p><a href="https://github.com/MaikKlein/ash">ash</a>是一个轻量级的<code>Vulkan</code>绑定。</p>
<img alt="ash screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/ash.png?raw=true" class="center" />
<h3 id="vulkano"><a class="header" href="#vulkano">vulkano</a></h3>
<p><a href="https://github.com/vulkano-rs/vulkano">vulkano</a>是一个安全、特性丰富的<code>Vulkan</code>绑定。</p>
<h3 id="rend3"><a class="header" href="#rend3">rend3</a></h3>
<p><a href="https://github.com/BVE-Reborn/rend3">rend3</a>是一个简单易用、可定制性强、高效的3D渲染库基于<code>wgpu</code>开发。</p>
<img alt="rend3 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/rend3.jpg?raw=true" class="center" />
<h3 id="rafx"><a class="header" href="#rafx">rafx</a></h3>
<p><a href="https://github.com/aclysma/rafx">rafx</a>是一个多后端渲染器,目标是性能、扩展性和生产力。</p>
<img alt="rafx screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/rafx.jpg?raw=true" class="center" />
<h3 id="gfx"><a class="header" href="#gfx">gfx</a></h3>
<p><a href="https://github.com/gfx-rs/gfx">gfx</a>是一个底层的图形库,目前已经不怎么活跃,主要原因是:它的核心组件<code>gfx-hal</code>最开始的目标是为<code>wgpu</code>提供功能,但是后面<code>wgpu</code>实现了自己的<code>wgpu-hal</code>,因此<code>gfx-hal</code>目前仅处于维护状态。</p>
<h3 id="luminance"><a class="header" href="#luminance">luminance</a></h3>
<p><a href="https://github.com/phaazon/luminance-rs">luminance</a>是一个类型安全、无状态的图形框架,目标是让图形渲染变得简单和优雅,最开始是通过<code>Haskell</code>语言实现,然后在<code>2016</code>年移植到<code>Rust</code>上。</p>
<p>它很简单,功能也不够强大,如果你没有<code>OpenGL</code><code>Vulkan</code>的经验,可以使用它做一些简单的图形渲染项目试试。</p>
<h3 id="miniquad"><a class="header" href="#miniquad">miniquad</a></h3>
<p><a href="https://github.com/not-fl3/miniquad">miniquad</a>是一个安全和跨平台的图形渲染库它提供了较为底层的API如果需要抽象层次更高的API可以使用之前提到的<a href="https://github.com/not-fl3/macroquad">macroquad</a>,后者是基于<code>miniquad</code>封装实现。</p>
<img alt="miniquad screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/gamedev/miniquad.gif?raw=true" class="center" />
<h3 id="glow"><a class="header" href="#glow">glow</a></h3>
<p><a href="https://github.com/grovesNL/glow">glow</a>提供了各种<code>GL</code>绑定(OpenGL, WebGL), 提供了一定的抽象,避免你写平台相关的特定代码实现。</p>
<h2 id="学习资料"><a class="header" href="#学习资料">学习资料</a></h2>
<h3 id="游戏开发最新新闻"><a class="header" href="#游戏开发最新新闻">游戏开发最新新闻</a></h3>
<ul>
<li><a href="https://gamedev.rs">gamedev</a></li>
</ul>
<h3 id="一些学习资料英文"><a class="header" href="#一些学习资料英文">一些学习资料(英文)</a></h3>
<ul>
<li><a href="https://pragprog.com/titles/hwrust/hands-on-rust/">Hands-on Rust</a></li>
<li>使用<a href="https://github.com/amethyst/bracket-lib">bracket-lib</a>和其<a href="https://bfnightly.bracketproductions.com/rustbook/">配套书籍</a>进行学习</li>
<li>想要没有困难的开发一个跨平台的2D游戏使用<a href="https://macroquad.rs"><code>macroquad</code></a>,并且可以参考用它开发的两个游戏: <a href="https://github.com/fishfight/FishFight">fish fight</a><a href="https://github.com/ozkriff/zemeroth">zemeroth</a></li>
<li>想要开发一个简单的3D游戏并且需要一个编辑器可以试试<a href="https://github.com/rg3dengine/rg3d"><code>fyrox(rg3d)</code></a></li>
<li>想要开发一个复杂的游戏或者想要做一个demo未来可以基于该demo继续开发最终完成一个复杂游戏可以试试<code>godot</code>引擎提供的<code>Rust</code>绑定:<a href="https://godot-rust.github.io">godot-rust</a></li>
<li>喜欢钻研前沿技术?试试<a href="https://bevyengine.org"><code>bevy</code></a>,它拥有最好的<code>ECS</code>实现和最先进的设计理念(可能)</li>
</ul>
<h3 id="ecsentity-component-system和dod面向数据设计资料"><a class="header" href="#ecsentity-component-system和dod面向数据设计资料">ECS(Entity Component System)和DOD(面向数据设计)资料</a></h3>
<p>我们在上面提到的很多系统都使用了<code>ECS</code><code>DOD</code>,因此这两者对于游戏开发是极其重要的,下面是一些相关的英文资料(部分需要翻墙),可以帮助大家理解相关概念。</p>
<ul>
<li><a href="https://github.com/Ralith/hecs">hecs</a>, 一个用Rust实现的ECS世界</li>
<li><a href="https://www.youtube.com/watch?v=0_Byw9UMn9g">Understanding data-oriented design for entity component systems - Unity at GDC 2019</a></li>
<li><a href="https://www.youtube.com/watch?v=yy8jQgmhbAU">CppCon 2018: Stoyan Nikolov “OOP Is Dead, Long Live Data-oriented Design”</a></li>
<li><a href="https://www.youtube.com/watch?v=aKLntZcp27M">RustConf 2018 - Closing Keynote - Using Rust For Game Development by Catherine West</a></li>
<li><a href="https://dataorienteddesign.com/dodbook/">&quot;Data-Oriented Design&quot; web book by Richard Fabian</a></li>
</ul>
<h3 id="一些游戏开发的生产力工具"><a class="header" href="#一些游戏开发的生产力工具">一些游戏开发的生产力工具</a></h3>
<ul>
<li><a href="https://www.blender.org">Blender</a>用于3D建模</li>
<li><a href="https://krita.org/en">Krita</a>用于创建2D图片</li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="awesome-算法"><a class="header" href="#awesome-算法">Awesome 算法</a></h1>
<h2 id="通用算法"><a class="header" href="#通用算法">通用算法</a></h2>
<hr />
<h3 id="rust-algorithms"><a class="header" href="#rust-algorithms">rust-algorithms</a></h3>
<p><a href="https://github.com/EbTech/rust-algorithms">rust-algorithms</a>收集了一些经典的算法和数据结构,更强调算法实现的美观性,因此该库更适用于教学目的,请不要把它当成一个实用算法库在生产环境使用。</p>
<h3 id="thealgorithmsrust"><a class="header" href="#thealgorithmsrust">TheAlgorithms/Rust</a></h3>
<p><a href="https://github.com/TheAlgorithms/Rust">TheAlgorithms/Rust</a>项目所属的<a href="https://github.com/TheAlgorithms">组织</a>使用各种语言实现了多种算法,但是仅适用于演示的目的。</p>
<h2 id="leetcode"><a class="header" href="#leetcode">Leetcode</a></h2>
<hr />
<h3 id="rustgym"><a class="header" href="#rustgym">rustgym</a></h3>
<p><a href="https://github.com/warycat/rustgym">rustgym</a> 实现了相当多的 leetcode 和 Avent of Code 题解。</p>
<h2 id="分布式算法"><a class="header" href="#分布式算法">分布式算法</a></h2>
<hr />
<h3 id="raft-rs"><a class="header" href="#raft-rs">raft-rs</a></h3>
<p><a href="https://github.com/tikv/raft-rs">raft-rs</a> 是由 Tikv 提供的 Raft 分布式算法实现。<a href="https://raft.github.io">Raft</a>是一个强一致性的分布式算法,比 Paxos 协议更简单、更好理解</p>
<h2 id="密码学"><a class="header" href="#密码学">密码学</a></h2>
<hr />
<h3 id="rust-crypto"><a class="header" href="#rust-crypto">Rust Crypto</a></h3>
<p><a href="https://github.com/RustCrypto">Rust Crypto</a>提供了一些常用的密码学算法实现,更新较为活跃。</p>
<h2 id="专用算法"><a class="header" href="#专用算法">专用算法</a></h2>
<hr />
<h3 id="rust-bio"><a class="header" href="#rust-bio">rust-bio</a></h3>
<p><a href="https://github.com/rust-bio/rust-bio">rust-bio</a> 有常用的生物信息学所需的算法和数据结构。</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="生成随机值"><a class="header" href="#生成随机值">生成随机值</a></h1>
<h3 id="生成随机数"><a class="header" href="#生成随机数">生成随机数</a></h3>
<p>使用 <a href="https://docs.rs/rand/*/rand/fn.thread_rng.html">rand::thread_rng</a> 可以获取一个随机数生成器 <a href="https://docs.rs/rand/0.8.5/rand/trait.Rng.html">rand::Rng</a> ,该生成器需要在每个线程都初始化一个。</p>
<p>整数的随机分布范围等于类型的取值范围,但是浮点数只分布在 <code>[0, 1)</code> 区间内。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let n1: u8 = rng.gen();
let n2: u16 = rng.gen();
println!(&quot;Random u8: {}&quot;, n1);
println!(&quot;Random u16: {}&quot;, n2);
println!(&quot;Random u32: {}&quot;, rng.gen::&lt;u32&gt;());
println!(&quot;Random i32: {}&quot;, rng.gen::&lt;i32&gt;());
println!(&quot;Random float: {}&quot;, rng.gen::&lt;f64&gt;());
}
</code></pre></pre>
<h3 id="指定范围生成随机数"><a class="header" href="#指定范围生成随机数">指定范围生成随机数</a></h3>
<p>使用 <a href="https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html">Rng::gen_range</a> 生成 [0, 10) 区间内的随机数( 右开区间,不包括 <code>10</code> )。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
println!(&quot;Integer: {}&quot;, rng.gen_range(0..10));
println!(&quot;Float: {}&quot;, rng.gen_range(0.0..10.0));
}
</code></pre></pre>
<p><a href="https://docs.rs/rand/*/rand/distributions/uniform/struct.Uniform.html">Uniform</a> 可以用于生成<ruby>均匀分布<rt>uniform distribution</rt></ruby>的随机数。当需要在同一个范围内重复生成随机数时,该方法虽然和之前的方法效果一样,但会更快一些。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rand::distributions::{Distribution, Uniform};
fn main() {
let mut rng = rand::thread_rng();
let die = Uniform::from(1..7);
loop {
let throw = die.sample(&amp;mut rng);
println!(&quot;Roll the die: {}&quot;, throw);
if throw == 6 {
break;
}
}
}
</code></pre></pre>
<h3 id="使用指定分布来生成随机数"><a class="header" href="#使用指定分布来生成随机数">使用指定分布来生成随机数</a></h3>
<p>默认情况下,<code>rand</code> 包使用均匀分布来生成随机数,而 <a href="https://docs.rs/rand_distr/*/rand_distr/index.html">rand_distr</a> 包提供了其它类型的分布方式。</p>
<p>首先,你需要获取想要使用的分布的实例,然后在 <a href="https://docs.rs/rand/*/rand/trait.Rng.html">rand::Rng</a> 的帮助下使用 <a href="https://docs.rs/rand/*/rand/distributions/trait.Distribution.html#tymethod.sample">Distribution::sample</a> 对该实例进行取样。</p>
<p>如果想要查询可用的分布列表,可以访问<a href="https://docs.rs/rand_distr/*/rand_distr/index.html">这里</a>,下面的示例中我们将使用 <a href="https://docs.rs/rand_distr/0.4.3/rand_distr/struct.Normal.html">Normal</a> 分布:</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rand_distr::{Distribution, Normal, NormalError};
use rand::thread_rng;
fn main() -&gt; Result&lt;(), NormalError&gt; {
let mut rng = thread_rng();
let normal = Normal::new(2.0, 3.0)?;
let v = normal.sample(&amp;mut rng);
println!(&quot;{} is from a N(2, 9) distribution&quot;, v);
Ok(())
}
</code></pre></pre>
<h3 id="在自定义类型中生成随机值"><a class="header" href="#在自定义类型中生成随机值">在自定义类型中生成随机值</a></h3>
<p>使用 <a href="https://docs.rs/rand/*/rand/distributions/trait.Distribution.html">Distribution</a> 特征包裹我们的自定义类型,并为 <a href="https://docs.rs/rand/*/rand/distributions/struct.Standard.html">Standard</a> 实现该特征,可以为自定义类型的指定字段生成随机数。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rand::Rng;
use rand::distributions::{Distribution, Standard};
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
impl Distribution&lt;Point&gt; for Standard {
fn sample&lt;R: Rng + ?Sized&gt;(&amp;self, rng: &amp;mut R) -&gt; Point {
let (rand_x, rand_y) = rng.gen();
Point {
x: rand_x,
y: rand_y,
}
}
}
fn main() {
let mut rng = rand::thread_rng();
// 生成一个随机的 Point
let rand_point: Point = rng.gen();
println!(&quot;Random Point: {:?}&quot;, rand_point);
// 通过类型暗示( hint )生成一个随机的元组
let rand_tuple = rng.gen::&lt;(i32, bool, f64)&gt;();
println!(&quot;Random tuple: {:?}&quot;, rand_tuple);
}
</code></pre></pre>
<h3 id="生成随机的字符串a-z-a-z-0-9"><a class="header" href="#生成随机的字符串a-z-a-z-0-9">生成随机的字符串(A-Z, a-z, 0-9)</a></h3>
<p>通过 <a href="https://docs.rs/rand/0.8.5/rand/distributions/struct.Alphanumeric.html">Alphanumeric</a> 采样来生成随机的 ASCII 字符串,包含从 <code>A-Z, a-z, 0-9</code> 的字符。</p>
<pre><pre class="playground"><code class="language-rust editble edition2021">use rand::{thread_rng, Rng};
use rand::distributions::Alphanumeric;
fn main() {
let rand_string: String = thread_rng()
.sample_iter(&amp;Alphanumeric)
.take(30)
.map(char::from)
.collect();
println!(&quot;{}&quot;, rand_string);
}
</code></pre></pre>
<h3 id="生成随机的字符串-用户指定-ascii-字符-"><a class="header" href="#生成随机的字符串-用户指定-ascii-字符-">生成随机的字符串( 用户指定 ASCII 字符 )</a></h3>
<p>通过 <a href="https://docs.rs/rand/0.8.5/rand/trait.Rng.html#method.gen_range">gen_string</a> 生成随机的 ASCII 字符串,包含用户指定的字符。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
use rand::Rng;
const CHARSET: &amp;[u8] = b&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789)(*&amp;^%$#@!~&quot;;
const PASSWORD_LEN: usize = 30;
let mut rng = rand::thread_rng();
let password: String = (0..PASSWORD_LEN)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect();
println!(&quot;{:?}&quot;, password);
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h2 id="vector-排序"><a class="header" href="#vector-排序">Vector 排序</a></h2>
<h3 id="对整数-vector-排序"><a class="header" href="#对整数-vector-排序">对整数 Vector 排序</a></h3>
<p>以下示例使用 <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort">Vec::sort</a> 来排序,如果大家希望获得更高的性能,可以使用 <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort_unstable">Vec::sort_unstable</a>,但是该方法无法保留相等元素的顺序。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let mut vec = vec![1, 5, 10, 2, 15];
vec.sort();
assert_eq!(vec, vec![1, 2, 5, 10, 15]);
}
</code></pre></pre>
<h3 id="对浮点数-vector-排序"><a class="header" href="#对浮点数-vector-排序">对浮点数 Vector 排序</a></h3>
<p>浮点数数组可以使用 <a href="https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by">Vec::sort_by</a><a href="https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#tymethod.partial_cmp">PartialOrd::partial_cmp</a> 进行排序。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0];
vec.sort_by(|a, b| a.partial_cmp(b).unwrap());
assert_eq!(vec, vec![1.1, 1.123, 1.15, 2.0, 5.5]);
}
</code></pre></pre>
<h3 id="对结构体-vector-排序"><a class="header" href="#对结构体-vector-排序">对结构体 Vector 排序</a></h3>
<p>以下示例中的结构体 <code>Person</code> 将实现基于字段 <code>name</code><code>age</code> 的自然排序。为了让 <code>Person</code> 变为可排序的,我们需要为其派生 <code>Eq、PartialEq、Ord、PartialOrd</code> 特征,关于这几个特征的详情,请见<a href="https://course.rs/advance/confonding/eq.html">这里</a></p>
<p>当然,还可以使用 <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort_by">vec:sort_by</a> 方法配合一个自定义比较函数,只按照 <code>age</code> 的维度对 <code>Person</code> 数组排序。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
struct Person {
name: String,
age: u32
}
impl Person {
pub fn new(name: String, age: u32) -&gt; Self {
Person {
name,
age
}
}
}
fn main() {
let mut people = vec![
Person::new(&quot;Zoe&quot;.to_string(), 25),
Person::new(&quot;Al&quot;.to_string(), 60),
Person::new(&quot;John&quot;.to_string(), 1),
];
// 通过派生后的自然顺序(Name and age)排序
people.sort();
assert_eq!(
people,
vec![
Person::new(&quot;Al&quot;.to_string(), 60),
Person::new(&quot;John&quot;.to_string(), 1),
Person::new(&quot;Zoe&quot;.to_string(), 25),
]);
// 只通过 age 排序
people.sort_by(|a, b| b.age.cmp(&amp;a.age));
assert_eq!(
people,
vec![
Person::new(&quot;Al&quot;.to_string(), 60),
Person::new(&quot;Zoe&quot;.to_string(), 25),
Person::new(&quot;John&quot;.to_string(), 1),
]);
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="使用tar包"><a class="header" href="#使用tar包">使用tar包</a></h1>
<h2 id="解压-tar-包"><a class="header" href="#解压-tar-包">解压 tar 包</a></h2>
<p>以下代码将解压缩( <a href="https://docs.rs/flate2/*/flate2/read/struct.GzDecoder.html">GzDecoder</a> )当前目录中的 <code>archive.tar.gz</code> ,并将所有文件抽取出( <a href="https://docs.rs/tar/*/tar/struct.Archive.html#method.unpack">Archive::unpack</a> )来后当入到当前目录中。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::fs::File;
use flate2::read::GzDecoder;
use tar::Archive;
fn main() -&gt; Result&lt;(), std::io::Error&gt; {
let path = &quot;archive.tar.gz&quot;;
let tar_gz = File::open(path)?;
let tar = GzDecoder::new(tar_gz);
let mut archive = Archive::new(tar);
archive.unpack(&quot;.&quot;)?;
Ok(())
}
</code></pre></pre>
<h2 id="将目录压缩成-tar-包"><a class="header" href="#将目录压缩成-tar-包">将目录压缩成 tar 包</a></h2>
<p>以下代码将 <code>/var/log</code> 目录压缩成 <code>archive.tar.gz</code>:</p>
<ul>
<li>创建一个 <a href="https://doc.rust-lang.org/std/fs/struct.File.html">File</a> 文件,并使用 <a href="https://docs.rs/flate2/*/flate2/write/struct.GzEncoder.html">GzEncoder</a><a href="https://docs.rs/tar/*/tar/struct.Builder.html">tar::Builder</a> 对其进行包裹</li>
<li>通过 <a href="https://docs.rs/tar/*/tar/struct.Builder.html#method.append_dir_all">Builder::append_dir_all</a><code>/var/log</code> 目录下的所有内容添加到压缩文件中,该文件在 <code>backup/logs</code> 目录下。</li>
<li><a href="https://docs.rs/flate2/*/flate2/write/struct.GzEncoder.html">GzEncoder</a> 负责在写入压缩文件 <code>archive.tar.gz</code> 之前对数据进行压缩。</li>
</ul>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::fs::File;
use flate2::Compression;
use flate2::write::GzEncoder;
fn main() -&gt; Result&lt;(), std::io::Error&gt; {
let tar_gz = File::create(&quot;archive.tar.gz&quot;)?;
let enc = GzEncoder::new(tar_gz, Compression::default());
let mut tar = tar::Builder::new(enc);
tar.append_dir_all(&quot;backup/logs&quot;, &quot;/var/log&quot;)?;
Ok(())
}
</code></pre></pre>
<h2 id="解压的同时删除指定的文件前缀"><a class="header" href="#解压的同时删除指定的文件前缀">解压的同时删除指定的文件前缀</a></h2>
<p>遍历目录中的文件 <a href="https://docs.rs/tar/*/tar/struct.Archive.html#method.entries">Archive::entries</a>,若解压前的文件名包含 <code>bundle/logs</code> 前缀,需要将前缀从文件名移除( <a href="https://doc.rust-lang.org/std/path/struct.Path.html#method.strip_prefix">Path::strip_prefix</a> )后,再解压。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::fs::File;
use std::path::PathBuf;
use flate2::read::GzDecoder;
use tar::Archive;
fn main() -&gt; Result&lt;()&gt; {
let file = File::open(&quot;archive.tar.gz&quot;)?;
let mut archive = Archive::new(GzDecoder::new(file));
let prefix = &quot;bundle/logs&quot;;
println!(&quot;Extracted the following files:&quot;);
archive
.entries()? // 获取压缩档案中的文件条目列表
.filter_map(|e| e.ok())
// 对每个文件条目进行 map 处理
.map(|mut entry| -&gt; Result&lt;PathBuf&gt; {
// 将文件路径名中的前缀移除,获取一个新的路径名
let path = entry.path()?.strip_prefix(prefix)?.to_owned();
// 将内容解压到新的路径名中
entry.unpack(&amp;path)?;
Ok(path)
})
.filter_map(|e| e.ok())
.for_each(|x| println!(&quot;&gt; {}&quot;, x.display()));
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="哈希"><a class="header" href="#哈希">哈希</a></h1>
<h3 id="计算文件的-sha-256-摘要"><a class="header" href="#计算文件的-sha-256-摘要">计算文件的 SHA-256 摘要</a></h3>
<p>写入一些数据到文件中,然后使用 <a href="https://briansmith.org/rustdoc/ring/digest/struct.Context.html">digest::Context</a> 来计算文件内容的 SHA-256 摘要 <a href="https://briansmith.org/rustdoc/ring/digest/struct.Digest.html">digest::Digest</a></p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>use data_encoding::HEXUPPER;
use ring::digest::{Context, Digest, SHA256};
use std::fs::File;
use std::io::{BufReader, Read, Write};
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> Decode(data_encoding::DecodeError);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn sha256_digest&lt;R: Read&gt;(mut reader: R) -&gt; Result&lt;Digest&gt; {
let mut context = Context::new(&amp;SHA256);
let mut buffer = [0; 1024];
loop {
let count = reader.read(&amp;mut buffer)?;
if count == 0 {
break;
}
context.update(&amp;buffer[..count]);
}
Ok(context.finish())
}
fn main() -&gt; Result&lt;()&gt; {
let path = &quot;file.txt&quot;;
let mut output = File::create(path)?;
write!(output, &quot;We will generate a digest of this text&quot;)?;
let input = File::open(path)?;
let reader = BufReader::new(input);
let digest = sha256_digest(reader)?;
println!(&quot;SHA-256 digest is {}&quot;, HEXUPPER.encode(digest.as_ref()));
Ok(())
}
</code></pre></pre>
<h3 id="使用-hmac-摘要来签名和验证消息"><a class="header" href="#使用-hmac-摘要来签名和验证消息">使用 HMAC 摘要来签名和验证消息</a></h3>
<p>使用 <a href="https://briansmith.org/rustdoc/ring/hmac/">ring::hmac</a> 创建一个字符串签名并检查该签名的正确性。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use ring::{hmac, rand};
use ring::rand::SecureRandom;
use ring::error::Unspecified;
fn main() -&gt; Result&lt;(), Unspecified&gt; {
let mut key_value = [0u8; 48];
let rng = rand::SystemRandom::new();
rng.fill(&amp;mut key_value)?;
let key = hmac::Key::new(hmac::HMAC_SHA256, &amp;key_value);
let message = &quot;Legitimate and important message.&quot;;
let signature = hmac::sign(&amp;key, message.as_bytes());
hmac::verify(&amp;key, message.as_bytes(), signature.as_ref())?;
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="加密"><a class="header" href="#加密">加密</a></h1>
<h3 id="使用--pbkdf2-对密码进行哈希和加盐-salt-"><a class="header" href="#使用--pbkdf2-对密码进行哈希和加盐-salt-">使用 PBKDF2 对密码进行哈希和加盐( salt )</a></h3>
<p><a href="https://briansmith.org/rustdoc/ring/pbkdf2/index.html">ring::pbkdf2</a> 可以对一个加盐密码进行哈希。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">
use data_encoding::HEXUPPER;
use ring::error::Unspecified;
use ring::rand::SecureRandom;
use ring::{digest, pbkdf2, rand};
use std::num::NonZeroU32;
fn main() -&gt; Result&lt;(), Unspecified&gt; {
const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
let n_iter = NonZeroU32::new(100_000).unwrap();
let rng = rand::SystemRandom::new();
let mut salt = [0u8; CREDENTIAL_LEN];
// 生成 salt: 将安全生成的随机数填入到字节数组中
rng.fill(&amp;mut salt)?;
let password = &quot;Guess Me If You Can!&quot;;
let mut pbkdf2_hash = [0u8; CREDENTIAL_LEN];
pbkdf2::derive(
pbkdf2::PBKDF2_HMAC_SHA512,
n_iter,
&amp;salt,
password.as_bytes(),
&amp;mut pbkdf2_hash,
);
println!(&quot;Salt: {}&quot;, HEXUPPER.encode(&amp;salt));
println!(&quot;PBKDF2 hash: {}&quot;, HEXUPPER.encode(&amp;pbkdf2_hash));
// `verify` 检查哈希是否正确
let should_`succeed = pbkdf2::verify(
pbkdf2::PBKDF2_HMAC_SHA512,
n_iter,
&amp;salt,
password.as_bytes(),
&amp;pbkdf2_hash,
);
let wrong_password = &quot;Definitely not the correct password&quot;;
let should_fail = pbkdf2::verify(
pbkdf2::PBKDF2_HMAC_SHA512,
n_iter,
&amp;salt,
wrong_password.as_bytes(),
&amp;pbkdf2_hash,
);
assert!(should_succeed.is_ok());
assert!(!should_fail.is_ok());
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="线性代数"><a class="header" href="#线性代数">线性代数</a></h1>
<h3 id="矩阵相加"><a class="header" href="#矩阵相加">矩阵相加</a></h3>
<p>使用 <a href="https://docs.rs/ndarray/*/ndarray/fn.arr2.html">ndarray::arr2</a> 可以创建二阶矩阵,并计算它们的和。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use ndarray::arr2;
fn main() {
let a = arr2(&amp;[[1, 2, 3],
[4, 5, 6]]);
let b = arr2(&amp;[[6, 5, 4],
[3, 2, 1]]);
// 借用 a 和 b求和后生成新的矩阵 sum
let sum = &amp;a + &amp;b;
println!(&quot;{}&quot;, a);
println!(&quot;+&quot;);
println!(&quot;{}&quot;, b);
println!(&quot;=&quot;);
println!(&quot;{}&quot;, sum);
}
</code></pre></pre>
<h3 id="矩阵相乘"><a class="header" href="#矩阵相乘">矩阵相乘</a></h3>
<p><a href="https://docs.rs/ndarray/0.15.4/ndarray/struct.ArrayBase.html#method.dot-1">ndarray::ArrayBase::dot</a> 可以用于计算矩阵乘法。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use ndarray::arr2;
fn main() {
let a = arr2(&amp;[[1, 2, 3],
[4, 5, 6]]);
let b = arr2(&amp;[[6, 3],
[5, 2],
[4, 1]]);
println!(&quot;{}&quot;, a.dot(&amp;b));
}
</code></pre></pre>
<h3 id="标量向量矩阵相乘"><a class="header" href="#标量向量矩阵相乘">标量、向量、矩阵相乘</a></h3>
<p><code>ndarry</code>1 阶数组根据上下文既可以作为行向量也可以作为列向量。如果对你来说,这个行或列的方向很重要,可以考虑使用一行或一列的 2 阶数组来表示。</p>
<p>在下面例子中,由于 1 阶数组处于乘号的右边位置,因此 <code>dot</code> 会把它当成列向量来处理。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use ndarray::{arr1, arr2, Array1};
fn main() {
let scalar = 4;
let vector = arr1(&amp;[1, 2, 3]);
let matrix = arr2(&amp;[[4, 5, 6],
[7, 8, 9]]);
let new_vector: Array1&lt;_&gt; = scalar * vector;
println!(&quot;{}&quot;, new_vector);
let new_matrix = matrix.dot(&amp;new_vector);
println!(&quot;{}&quot;, new_matrix);
}
</code></pre></pre>
<h3 id="向量比较"><a class="header" href="#向量比较">向量比较</a></h3>
<p>浮点数通常是不精确的,因此比较浮点数不是一件简单的事。<a href="https://docs.rs/approx/*/approx/index.html">approx</a> 提供的 <a href="https://docs.rs/approx/0.5.1/approx/macro.assert_abs_diff_eq.html">assert_abs_diff_eq!</a> 宏提供了方便的按元素比较的方式。为了使用 <code>approx</code> ,你需要在 <code>ndarray</code> 的依赖中开启相应的 feature例如<code>Cargo.toml</code> 中修改 <code>ndarray</code> 的依赖引入为 <code>ndarray = { version = &quot;0.13&quot;, features = [&quot;approx&quot;] }</code></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use approx::assert_abs_diff_eq;
use ndarray::Array;
fn main() {
let a = Array::from(vec![1., 2., 3., 4., 5.]);
let b = Array::from(vec![5., 4., 3., 2., 1.]);
let mut c = Array::from(vec![1., 2., 3., 4., 5.]);
let mut d = Array::from(vec![5., 4., 3., 2., 1.]);
// 消耗 a 和 b 的所有权
let z = a + b;
// 借用 c 和 d
let w = &amp;c + &amp;d;
assert_abs_diff_eq!(z, Array::from(vec![6., 6., 6., 6., 6.]));
println!(&quot;c = {}&quot;, c);
c[0] = 10.;
d[1] = 10.;
assert_abs_diff_eq!(w, Array::from(vec![6., 6., 6., 6., 6.]));
}
</code></pre></pre>
<h3 id="向量范数-norm-"><a class="header" href="#向量范数-norm-">向量范数( norm )</a></h3>
<p>需要注意的是 <code>Array</code><code>ArrayView</code> 都是 <code>ArrayBase</code> 的别名。因此一个更通用的参数应该是 <code>&amp;ArrayBase&lt;S, Ix1&gt; where S: Data</code>,特别是在你提供一个公共 API 给其它用户时,但由于咱们是内部使用,因此更精准的 <code>ArrayView1&lt;f64&gt;</code> 会更适合。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use ndarray::{array, Array1, ArrayView1};
fn l1_norm(x: ArrayView1&lt;f64&gt;) -&gt; f64 {
x.fold(0., |acc, elem| acc + elem.abs())
}
fn l2_norm(x: ArrayView1&lt;f64&gt;) -&gt; f64 {
x.dot(&amp;x).sqrt()
}
fn normalize(mut x: Array1&lt;f64&gt;) -&gt; Array1&lt;f64&gt; {
let norm = l2_norm(x.view());
x.mapv_inplace(|e| e/norm);
x
}
fn main() {
let x = array![1., 2., 3., 4., 5.];
println!(&quot;||x||_2 = {}&quot;, l2_norm(x.view()));
println!(&quot;||x||_1 = {}&quot;, l1_norm(x.view()));
println!(&quot;Normalizing x yields {:?}&quot;, normalize(x));
}
</code></pre></pre>
<h3 id="矩阵的逆变换"><a class="header" href="#矩阵的逆变换">矩阵的逆变换</a></h3>
<p>例子中使用 <a href="https://docs.rs/nalgebra/*/nalgebra/base/type.Matrix3.html">nalgebra::Matrix3</a> 创建一个 3x3 的矩阵,然后尝试对其进行逆变换,获取一个逆矩阵。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use nalgebra::Matrix3;
fn main() {
let m1 = Matrix3::new(2.0, 1.0, 1.0, 3.0, 2.0, 1.0, 2.0, 1.0, 2.0);
println!(&quot;m1 = {}&quot;, m1);
match m1.try_inverse() {
Some(inv) =&gt; {
println!(&quot;The inverse of m1 is: {}&quot;, inv);
}
None =&gt; {
println!(&quot;m1 is not invertible!&quot;);
}
}
}
</code></pre></pre>
<h3 id="序列反序列化一个矩阵"><a class="header" href="#序列反序列化一个矩阵">序列/反序列化一个矩阵</a></h3>
<p>下面将展示如何将矩阵序列化为 JSON ,然后再反序列化为原矩阵。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">extern crate nalgebra;
extern crate serde_json;
use nalgebra::DMatrix;
fn main() -&gt; Result&lt;(), std::io::Error&gt; {
let row_slice: Vec&lt;i32&gt; = (1..5001).collect();
let matrix = DMatrix::from_row_slice(50, 100, &amp;row_slice);
// 序列化矩阵
let serialized_matrix = serde_json::to_string(&amp;matrix)?;
// 反序列化
let deserialized_matrix: DMatrix&lt;i32&gt; = serde_json::from_str(&amp;serialized_matrix)?;
// 验证反序列化后的矩阵跟原始矩阵相等
assert!(deserialized_matrix == matrix);
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="三角函数"><a class="header" href="#三角函数">三角函数</a></h1>
<h3 id="三角形边长计算"><a class="header" href="#三角形边长计算">三角形边长计算</a></h3>
<p>计算角为 2 弧度、对边长度为 80 的直角三角形的斜边长度。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let angle: f64 = 2.0;
let side_length = 80.0;
let hypotenuse = side_length / angle.sin();
println!(&quot;Hypotenuse: {}&quot;, hypotenuse);
}
</code></pre></pre>
<h3 id="验证-tan--sin--cos"><a class="header" href="#验证-tan--sin--cos">验证 tan = sin / cos</a></h3>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let x: f64 = 6.0;
let a = x.tan();
let b = x.sin() / x.cos();
assert_eq!(a, b);
}
</code></pre></pre>
<h3 id="地球上两点间的距离"><a class="header" href="#地球上两点间的距离">地球上两点间的距离</a></h3>
<p>下面的代码使用 <a href="https://blog.csdn.net/Hardict/article/details/105267473">Haversine 公式</a> 计算地球上两点之间的公里数。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let earth_radius_kilometer = 6371.0_f64;
let (paris_latitude_degrees, paris_longitude_degrees) = (48.85341_f64, -2.34880_f64);
let (london_latitude_degrees, london_longitude_degrees) = (51.50853_f64, -0.12574_f64);
let paris_latitude = paris_latitude_degrees.to_radians();
let london_latitude = london_latitude_degrees.to_radians();
let delta_latitude = (paris_latitude_degrees - london_latitude_degrees).to_radians();
let delta_longitude = (paris_longitude_degrees - london_longitude_degrees).to_radians();
let central_angle_inner = (delta_latitude / 2.0).sin().powi(2)
+ paris_latitude.cos() * london_latitude.cos() * (delta_longitude / 2.0).sin().powi(2);
let central_angle = 2.0 * central_angle_inner.sqrt().asin();
let distance = earth_radius_kilometer * central_angle;
println!(
&quot;Distance between Paris and London on the surface of Earth is {:.1} kilometers&quot;,
distance
);
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="复数"><a class="header" href="#复数">复数</a></h1>
<h3 id="创建复数"><a class="header" href="#创建复数">创建复数</a></h3>
<p><a href="https://autumnai.github.io/cuticula/num/complex/struct.Complex.html">num::complex::Complex</a> 可以帮助我们创建复数,其中实部和虚部必须是一样的类型。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let complex_integer = num::complex::Complex::new(10, 20);
let complex_float = num::complex::Complex::new(10.1, 20.1);
println!(&quot;Complex integer: {}&quot;, complex_integer);
println!(&quot;Complex float: {}&quot;, complex_float);
}
</code></pre></pre>
<h3 id="复数相加"><a class="header" href="#复数相加">复数相加</a></h3>
<p>复数计算和 Rust 基本类型的计算并无区别。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let complex_num1 = num::complex::Complex::new(10.0, 20.0); // Must use floats
let complex_num2 = num::complex::Complex::new(3.1, -4.2);
let sum = complex_num1 + complex_num2;
println!(&quot;Sum: {}&quot;, sum);
}
</code></pre></pre>
<h3 id="数学函数"><a class="header" href="#数学函数">数学函数</a></h3>
<p><a href="https://autumnai.github.io/cuticula/num/complex/struct.Complex.html">num::complex::Complex</a> 中定义了一些内置的数学函数,可用于对复数进行数学运算。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::f64::consts::PI;
use num::complex::Complex;
fn main() {
let x = Complex::new(0.0, 2.0*PI);
println!(&quot;e^(2i * pi) = {}&quot;, x.exp()); // =~1
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="统计"><a class="header" href="#统计">统计</a></h1>
<h3 id="测量中心趋势"><a class="header" href="#测量中心趋势">测量中心趋势</a></h3>
<p>下面的一些例子为 Rust 数组中的数据计算它们的中心趋势。</p>
<h4 id="平均值"><a class="header" href="#平均值">平均值</a></h4>
<p>首先计算的是平均值。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
let sum = data.iter().sum::&lt;i32&gt;() as f32;
let count = data.len();
let mean = match count {
positive if positive &gt; 0 =&gt; Some(sum / count as f32),
_ =&gt; None
};
println!(&quot;Mean of the data is {:?}&quot;, mean);
}
</code></pre></pre>
<h4 id="中位数"><a class="header" href="#中位数">中位数</a></h4>
<p>下面使用快速选择算法来计算中位数。该算法只会对可能包含中位数的数据分区进行排序,从而避免了对所有数据进行全排序。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::cmp::Ordering;
fn partition(data: &amp;[i32]) -&gt; Option&lt;(Vec&lt;i32&gt;, i32, Vec&lt;i32&gt;)&gt; {
match data.len() {
0 =&gt; None,
_ =&gt; {
let (pivot_slice, tail) = data.split_at(1);
let pivot = pivot_slice[0];
let (left, right) = tail.iter()
.fold((vec![], vec![]), |mut splits, next| {
{
let (ref mut left, ref mut right) = &amp;mut splits;
if next &lt; &amp;pivot {
left.push(*next);
} else {
right.push(*next);
}
}
splits
});
Some((left, pivot, right))
}
}
}
fn select(data: &amp;[i32], k: usize) -&gt; Option&lt;i32&gt; {
let part = partition(data);
match part {
None =&gt; None,
Some((left, pivot, right)) =&gt; {
let pivot_idx = left.len();
match pivot_idx.cmp(&amp;k) {
Ordering::Equal =&gt; Some(pivot),
Ordering::Greater =&gt; select(&amp;left, k),
Ordering::Less =&gt; select(&amp;right, k - (pivot_idx + 1)),
}
},
}
}
fn median(data: &amp;[i32]) -&gt; Option&lt;f32&gt; {
let size = data.len();
match size {
even if even % 2 == 0 =&gt; {
let fst_med = select(data, (even / 2) - 1);
let snd_med = select(data, even / 2);
match (fst_med, snd_med) {
(Some(fst), Some(snd)) =&gt; Some((fst + snd) as f32 / 2.0),
_ =&gt; None
}
},
odd =&gt; select(data, odd / 2).map(|x| x as f32)
}
}
fn main() {
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
let part = partition(&amp;data);
println!(&quot;Partition is {:?}&quot;, part);
let sel = select(&amp;data, 5);
println!(&quot;Selection at ordered index {} is {:?}&quot;, 5, sel);
let med = median(&amp;data);
println!(&quot;Median is {:?}&quot;, med);
}
</code></pre></pre>
<h4 id="众数-mode-"><a class="header" href="#众数-mode-">众数( mode )</a></h4>
<p>下面使用了 <code>HashMap</code> 对不同数字出现的次数进行了分别统计。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::collections::HashMap;
fn main() {
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
let frequencies = data.iter().fold(HashMap::new(), |mut freqs, value| {
*freqs.entry(value).or_insert(0) += 1;
freqs
});
let mode = frequencies
.into_iter()
.max_by_key(|&amp;(_, count)| count)
.map(|(value, _)| *value);
println!(&quot;Mode of the data is {:?}&quot;, mode);
}
</code></pre></pre>
<h3 id="标准偏差"><a class="header" href="#标准偏差">标准偏差</a></h3>
<p>下面一起来看看该如何计算一组测量值的标准偏差和 z-score。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn mean(data: &amp;[i32]) -&gt; Option&lt;f32&gt; {
let sum = data.iter().sum::&lt;i32&gt;() as f32;
let count = data.len();
match count {
positive if positive &gt; 0 =&gt; Some(sum / count as f32),
_ =&gt; None,
}
}
fn std_deviation(data: &amp;[i32]) -&gt; Option&lt;f32&gt; {
match (mean(data), data.len()) {
(Some(data_mean), count) if count &gt; 0 =&gt; {
let variance = data.iter().map(|value| {
let diff = data_mean - (*value as f32);
diff * diff
}).sum::&lt;f32&gt;() / count as f32;
Some(variance.sqrt())
},
_ =&gt; None
}
}
fn main() {
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
let data_mean = mean(&amp;data);
println!(&quot;Mean is {:?}&quot;, data_mean);
let data_std_deviation = std_deviation(&amp;data);
println!(&quot;Standard deviation is {:?}&quot;, data_std_deviation);
let zscore = match (data_mean, data_std_deviation) {
(Some(mean), Some(std_deviation)) =&gt; {
let diff = data[4] as f32 - mean;
Some(diff / std_deviation)
},
_ =&gt; None
};
println!(&quot;Z-score of data at index 4 (with value {}) is {:?}&quot;, data[4], zscore);
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="杂项"><a class="header" href="#杂项">杂项</a></h1>
<h3 id="大整数-big-int"><a class="header" href="#大整数-big-int">大整数 Big int</a></h3>
<p>使用 <a href="https://docs.rs/num/0.2.0/num/struct.BigInt.html">BitInt</a> 可以对超过 128bit 的整数进行计算。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use num::bigint::{BigInt, ToBigInt};
fn factorial(x: i32) -&gt; BigInt {
if let Some(mut factorial) = 1.to_bigint() {
for i in 1..=x {
factorial = factorial * i;
}
factorial
}
else {
panic!(&quot;Failed to calculate factorial!&quot;);
}
}
fn main() {
println!(&quot;{}! equals {}&quot;, 100, factorial(100));
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="数据结构"><a class="header" href="#数据结构">数据结构</a></h1>
<div style="break-before: page; page-break-before: always;"></div><h1 id="位字段"><a class="header" href="#位字段">位字段</a></h1>
<h3 id="定义和操作位字段"><a class="header" href="#定义和操作位字段">定义和操作位字段</a></h3>
<p>使用 <a href="https://docs.rs/bitflags/1.3.2/bitflags/macro.bitflags.html"><code>bitflags!</code></a> 宏可以帮助我们创建安全的位字段类型 <code>MyFlags</code>,然后为其实现基本的 <code>clear</code> 操作。以下代码展示了基本的位操作和格式化:</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use bitflags::bitflags;
use std::fmt;
bitflags! {
struct MyFlags: u32 {
const FLAG_A = 0b00000001;
const FLAG_B = 0b00000010;
const FLAG_C = 0b00000100;
const FLAG_ABC = Self::FLAG_A.bits
| Self::FLAG_B.bits
| Self::FLAG_C.bits;
}
}
impl MyFlags {
pub fn clear(&amp;mut self) -&gt; &amp;mut MyFlags {
self.bits = 0;
self
}
}
impl fmt::Display for MyFlags {
fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
write!(f, &quot;{:032b}&quot;, self.bits)
}
}
fn main() {
let e1 = MyFlags::FLAG_A | MyFlags::FLAG_C;
let e2 = MyFlags::FLAG_B | MyFlags::FLAG_C;
assert_eq!((e1 | e2), MyFlags::FLAG_ABC);
assert_eq!((e1 &amp; e2), MyFlags::FLAG_C);
assert_eq!((e1 - e2), MyFlags::FLAG_A);
assert_eq!(!e2, MyFlags::FLAG_A);
let mut flags = MyFlags::FLAG_ABC;
assert_eq!(format!(&quot;{}&quot;, flags), &quot;00000000000000000000000000000111&quot;);
assert_eq!(format!(&quot;{}&quot;, flags.clear()), &quot;00000000000000000000000000000000&quot;);
assert_eq!(format!(&quot;{:?}&quot;, MyFlags::FLAG_B), &quot;FLAG_B&quot;);
assert_eq!(format!(&quot;{:?}&quot;, MyFlags::FLAG_A | MyFlags::FLAG_B), &quot;FLAG_A | FLAG_B&quot;);
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h2 id="命令行工具"><a class="header" href="#命令行工具">命令行工具</a></h2>
<p>对于每一个程序员而言,命令行工具都非常关键。你对他越熟悉,在使用计算机、处理工作流程等越是高效。</p>
<p>下面我们收集了一些优秀的Rust所写的命令行工具它们相比目前已有的其它语言的实现可以提供更加现代化的代码实现、更加高效的性能以及更好的可用性。</p>
<h3 id="索引目录"><a class="header" href="#索引目录">索引目录</a></h3>
<table><thead><tr><th>新工具</th><th>替代的目标或功能描述</th></tr></thead><tbody>
<tr><td><a href="cmd/awesome.html#bat">bat</a></td><td>cat</td></tr>
<tr><td><a href="cmd/awesome.html#exa">exa</a></td><td>ls</td></tr>
<tr><td><a href="cmd/awesome.html#lsd">lsd</a></td><td>ls</td></tr>
<tr><td><a href="cmd/awesome.html#fd">fd</a></td><td>find</td></tr>
<tr><td><a href="cmd/awesome.html#procs">procs</a></td><td>ps</td></tr>
<tr><td><a href="cmd/awesome.html#sd">sd</a></td><td>sed</td></tr>
<tr><td><a href="cmd/awesome.html#dust">dust</a></td><td>du</td></tr>
<tr><td><a href="cmd/awesome.html#starship">starship</a></td><td>现代化的命令行提示</td></tr>
<tr><td><a href="cmd/awesome.html#ripgrep">ripgrep</a></td><td>grep</td></tr>
<tr><td><a href="cmd/awesome.html#tokei">tokei</a></td><td>代码统计工具</td></tr>
<tr><td><a href="cmd/awesome.html#hyperfine">hyperfine</a></td><td>命令行benchmark工具</td></tr>
<tr><td><a href="cmd/awesome.html#bottom">bottom</a></td><td>top</td></tr>
<tr><td><a href="cmd/awesome.html#tealdear">teeldear</a></td><td>tldr</td></tr>
<tr><td><a href="cmd/awesome.html#grex">grex</a></td><td>根据文本示例生成正则</td></tr>
<tr><td><a href="cmd/awesome.html#bandwhich">bandwitch</a></td><td>显示进程、连接网络使用情况</td></tr>
<tr><td><a href="cmd/awesome.html#zoxide">zoxide</a></td><td>cd</td></tr>
<tr><td><a href="cmd/awesome.html#delta">delta</a></td><td>git可视化</td></tr>
<tr><td><a href="cmd/awesome.html#nushell">nushell</a></td><td>全新的现代化shell</td></tr>
<tr><td><a href="cmd/awesome.html#mcfly">mcfly</a></td><td>替代<code>ctrl + R</code>命令搜索</td></tr>
<tr><td><a href="cmd/awesome.html#fselect">fselect</a></td><td>使用SQL语法查找文件</td></tr>
<tr><td><a href="cmd/awesome.html#pueue">pueue</a></td><td>命令行任务管理工具</td></tr>
<tr><td><a href="cmd/awesome.html#watchexec">watchexec</a></td><td>监视目录文件变动并执行命令</td></tr>
<tr><td><a href="cmd/awesome.html#dura">dura</a></td><td>更加安全的使用git</td></tr>
<tr><td><a href="cmd/awesome.html#alacritty">alacritty</a></td><td>强大的基于OpenGL的终端</td></tr>
<tr><td><a href="cmd/awesome.html#broot">broot</a></td><td>可视化访问目录树</td></tr>
</tbody></table>
<h3 id="bat"><a class="header" href="#bat">bat</a></h3>
<p><a href="https://github.com/sharkdp/bat">bat</a><code>克隆了**cat**的功能并提供了语法高亮和Git集成它支持</code>Windows<code></code>MacOS<code></code>Linux`。同时,它默认提供了多种文件后缀的语法高亮。</p>
<img alt="bat screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/bat.png?raw=true" class="center" />
<h3 id="exa"><a class="header" href="#exa">exa</a></h3>
<p><a href="https://github.com/ogham/exa">exa</a><code>ls</code>命令的现代化实现,后者是目前<code>Unix/Linux</code>系统的默认命令,用于列出当前目录中的内容。</p>
<img alt="exa screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/exa.jpg?raw=true" class="center" />
<h3 id="lsd"><a class="header" href="#lsd">lsd</a></h3>
<p><a href="https://github.com/Peltoche/lsd">lsd</a> 也是 <code>ls</code> 的新实现同时增加了很多特性例如颜色标注、icons、树形查看、更多的格式化选项等。</p>
<img alt="lsd screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/lsd.png?raw=true" class="center" />
<h3 id="fd"><a class="header" href="#fd">fd</a></h3>
<p><a href="https://github.com/sharkdp/fd">fd</a> 是一个更快、对用户更友好的<strong>find</strong>实现,后者是 <code>Unix/Linux</code> 内置的文件目录搜索工具。之所以说它用户友好,一方面是 <code>API</code> 非常清晰明了,其次是它对最常用的场景提供了有意义的默认值:例如,想要通过名称搜索文件:</p>
<ul>
<li><code>fd</code>: <code>fd PATTERN</code></li>
<li><code>find</code>: <code>find -iname 'PATTERN'</code></li>
</ul>
<p>同时 <code>fd</code> 性能非常非常高,还提供了非常多的搜索选项,例如允许用户通过 <code>.gitignore</code> 文件忽略隐藏的目录、文件等。</p>
<img alt="fd screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/fd.svg?raw=true" class="center" />
<h3 id="procs"><a class="header" href="#procs">procs</a></h3>
<p><a href="https://github.com/dalance/procs">procs</a><strong>ps</strong> 的默认实现,后者是 <code>Unix/Linux</code> 的内置命令,用于获取进程( <code>process</code> )的信息。<code>proc</code> 提供了更便利、可读性更好的格式化输出。</p>
<img alt="procs screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/procs.jpg?raw=true" class="center" />
<h3 id="sd"><a class="header" href="#sd">sd</a></h3>
<p><a href="https://github.com/chmln/sd">sd</a><strong>sed</strong> 命令的现代化实现,后者是 <code>Unix/Linux</code> 中内置的工具,用于分析和转换文本。</p>
<p><code>sd</code> 拥有更简单的使用方式,而且支持方便的正则表达式语法,<code>sd</code> 拥有闪电般的性能,比 <code>sed</code><strong>2x-11x</strong> 倍。</p>
<p>以下是其中一个性能测试结果:</p>
<p><em>对1.5G大小的 JSON 文本进行简单替换</em></p>
<p><code>hyperfine -w 3 'sed -E &quot;s/\&quot;/\'/g&quot; *.json &gt;/dev/null' 'sd &quot;\&quot;&quot; &quot;\'&quot; *.json &gt;/dev/null' --export-markdown out.md</code></p>
<table><thead><tr><th style="text-align: left">Command</th><th style="text-align: right">Mean [s]</th><th style="text-align: right">Min…Max [s]</th></tr></thead><tbody>
<tr><td style="text-align: left"><code>sed -E &quot;s/\&quot;/'/g&quot; *.json &gt;/dev/null</code></td><td style="text-align: right">2.338 ± 0.008</td><td style="text-align: right">2.332…2.358</td></tr>
<tr><td style="text-align: left"><code>sed &quot;s/\&quot;/'/g&quot; *.json &gt;/dev/null</code></td><td style="text-align: right">2.365 ± 0.009</td><td style="text-align: right">2.351…2.378</td></tr>
<tr><td style="text-align: left"><code>sd &quot;\&quot;&quot; &quot;'&quot; *.json &gt;/dev/null</code></td><td style="text-align: right"><strong>0.997 ± 0.006</strong></td><td style="text-align: right">0.987…1.007</td></tr>
</tbody></table>
<p>结果: ~2.35 times faster</p>
<h3 id="dust"><a class="header" href="#dust">dust</a></h3>
<p><a href="https://github.com/bootandy/dust">dust</a> 是一个更符合使用习惯的<strong>du</strong>,后者是 <code>Unix/Linux</code> 内置的命令行工具,用于显示硬盘使用情况的统计。</p>
<img alt="dust screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/dust.png?raw=true" class="center" />
<h3 id="starship-1"><a class="header" href="#starship-1">starship</a></h3>
<p><a href="https://github.com/starship/starship">starship</a> 是一个命令行提示,支持任何 <code>shell</code> ,包括 <code>zsh</code> ,简单易用、非常快且拥有极高的可配置性, 同时支持智能提示。</p>
<img alt="starship screenshot" width="100%" src="https://raw.githubusercontent.com/starship/starship/master/media/demo.gif" class="center" />
<h3 id="ripgrep-1"><a class="header" href="#ripgrep-1"><a href="https://github.com/BurntSushi/ripgrep">ripgrep</a></a></h3>
<p><a href="https://github.com/BurntSushi/ripgrep">ripgrep</a> 是一个性能极高的现代化 <code>grep</code> 实现,后者是 <code>Unix/Linux</code> 下的内置文件搜索工具。该项目是 Rust 的明星项目,一个是因为性能极其的高,另一个就是源代码质量很高,值得学习, 同时 <code>Vscode</code> 使用它作为内置的搜索引擎。</p>
<p>从功能来说,除了全面支持 <code>grep</code> 的功能外,<code>repgre</code> 支持使用正则递归搜索指定的文件目录,默认使用 <code>.gitignore</code> 对指定的文件进行忽略。</p>
<img alt="ripgrep screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/ripgrep.png?raw=true" class="center" />
<h3 id="tokei"><a class="header" href="#tokei">tokei</a></h3>
<p><a href="https://github.com/XAMPPRocky/tokei">tokei</a> 可以分门别类的统计目录内的代码行数,速度非常快!</p>
<img alt="tokei screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/tokei.png?raw=true" class="center" />
<h3 id="hyperfine"><a class="header" href="#hyperfine">hyperfine</a></h3>
<p><a href="https://github.com/sharkdp/hyperfine">hyperfine</a> 是命令行benchmark工具它支持在多次运行中提供静态的分析同时支持任何的 <code>shell</code> 命令,准确的 <code>benchmark</code> 进度和当前预估等等高级特性。</p>
<img alt="hyperfine screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/hyperfine.gif?raw=true" class="center" />
<h3 id="bottom"><a class="header" href="#bottom">bottom</a></h3>
<p><a href="https://github.com/ClementTsang/bottom">bottom</a> 是一个现代化实现的 <code>top</code>,可以跨平台、图形化的显示进程/系统的当前信息。</p>
<img alt="bottom screenshot" width="100%" src="https://github.com/ClementTsang/bottom/raw/master/assets/demo.gif" class="center" />
<h3 id="tealdear"><a class="header" href="#tealdear">tealdear</a></h3>
<p><a href="https://github.com/dbrgn/tealdeer">tealdear</a> 是一个更快实现的<strong>tldr</strong>, 一个用于显示 <code>man pages</code> 的命令行程序,简单易用、基于例子和社区驱动是主要特性。</p>
<img alt="teeldear screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/teeldear.gif?raw=true" class="center" />
<h3 id="bandwhich"><a class="header" href="#bandwhich">bandwhich</a></h3>
<p><a href="https://github.com/imsnif/bandwhich">bandwhich</a> 是一个客户端实用工具,用于显示当前进程、连接、远程 IP( hostname ) 的网络信息。</p>
<img alt="bandwhich screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/bandwhich.gif?raw=true" class="center" />
<h3 id="grex"><a class="header" href="#grex">grex</a></h3>
<p><a href="https://github.com/pemistahl/grex">grex</a> 既是一个命令行工具又是一个库,可以根据用户提供的文本示例生成对应的正则表达式,非常强大。</p>
<img alt="grex screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/grex.gif?raw=true" class="center" />
<h3 id="zoxide"><a class="header" href="#zoxide">zoxide</a></h3>
<p><a href="https://github.com/ajeetdsouza/zoxide">zoxide</a> 是一个智能化的 <code>cd</code> 命令,它甚至会记忆你常用的目录。</p>
<img alt="zoxide screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/zoxide.webp?raw=true" class="center" />
<h3 id="delta"><a class="header" href="#delta">delta</a></h3>
<p><a href="https://github.com/dandavison/delta">delta</a> 是一个 <code>git</code> 分页展示工具,支持语法高亮、代码比对、输出 <code>grep</code> 等。</p>
<img alt="delta screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/delta.png?raw=true" class="center" />
<h3 id="nushell-1"><a class="header" href="#nushell-1">nushell</a></h3>
<p><a href="https://github.com/nushell/nushell">nushell</a> 是一个全新的 <code>shell</code> ,使用 <code>Rust</code> 实现。它的目标是创建一个现代化的 <code>shell</code> :虽然依然基于 <code>Unix</code> 的哲学,但是更适合现在的时代。例如,你可以使用 <code>SQL</code> 语法来选择你想要的内容!</p>
<img alt="delta screenshot" width="100%" src="https://github.com/nushell/nushell/raw/main/images/nushell-autocomplete5.gif" class="center" />
<h3 id="mcfly"><a class="header" href="#mcfly">mcfly</a></h3>
<p><a href="https://github.com/cantino/mcfly">mcfly</a> 会替换默认的 <code>ctrl-R</code>,用于在终端中搜索历史命令, 它提供了智能提示功能,并且会根据当前目录中最近执行过的上下文命令进行提示。<code>mcfly</code> 甚至使用了一个小型的神经网络用于智能提示!</p>
<img alt="mcfly screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/mcfly.png?raw=true" class="center" />
<h3 id="fselect"><a class="header" href="#fselect">fselect</a></h3>
<p><a href="https://github.com/jhspetersson/fselect">fselect</a> 允许使用 SQL 语法来查找系统中的文件。它支持复杂查询、聚合查询、.gitignore 忽略文件、通过宽度高度搜索图片、通过 hash 搜索文件、文件属性查询等等,相当强大!</p>
<pre><code class="language-shell"># 复杂查询
fselect &quot;name from /tmp where (name = *.tmp and size = 0) or (name = *.cfg and size &gt; 1000000)&quot;
# 聚合函数
fselect &quot;MIN(size), MAX(size), AVG(size), SUM(size), COUNT(*) from /home/user/Downloads&quot;
# 格式化函数
fselect &quot;LOWER(name), UPPER(name), LENGTH(name), YEAR(modified) from /home/user/Downloads&quot;
</code></pre>
<h3 id="pueue"><a class="header" href="#pueue">pueue</a></h3>
<p><a href="https://github.com/nukesor/pueue">pueue</a> 是一个命令行任务管理工具,它可以管理你的长时间运行的命令,支持顺序或并行执行。简单来说,它可以管理一个命令队列。</p>
<img alt="pueue screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/pueue.gif?raw=true" class="center" />
<h3 id="watchexec"><a class="header" href="#watchexec">watchexec</a></h3>
<p><a href="https://github.com/watchexec/watchexec">watchexec</a> 可以监视指定的目录、文件的改动,并执行你预设的命令,支持多种配置项和操作系统。</p>
<pre><code class="language-shell"># 监视当前目录/子目录中的所有js、css、html文件一旦发生改变运行`npm run build`命令
$ watchexec -e js,css,html npm run build
# 当前目录/子目录下任何python文件发生改变时重启`python server.py`
$ watchexec -r -e py -- python server.py
</code></pre>
<h3 id="dura"><a class="header" href="#dura">dura</a></h3>
<p><a href="https://github.com/tkellogg/dura">dura</a> 运行在后台,监视你的 <code>git</code> 目录,提交你未提交的更改但是并不会影响 <code>HEAD</code>、当前的分支和 <code>git</code> 索引(staged文件)。</p>
<p>如果你曾经遇到过**&quot;完蛋, 我这几天的工作内容丢了&quot;**的情况,那么就可以尝试下 <code>dura</code><code>checkout dura brach</code>,然后代码就可以顺利恢复了:)</p>
<p><strong>恢复代码</strong></p>
<ol>
<li>你可以使用 <code>dura</code> 分支来恢复</li>
</ol>
<pre><code class="language-console">$ echo &quot;dura/$(git rev-parse HEAD)&quot;
</code></pre>
<ol>
<li>也可以手动恢复</li>
</ol>
<pre><code class="language-console"># Or, if you don't trust dura yet, `git stash`
$ git reset HEAD --hard
# get the changes into your working directory
$ git checkout $THE_HASH
# last few commands reset HEAD back to master but with changes uncommitted
$ git checkout -b temp-branch
$ git reset master
$ git checkout master
$ git branch -D temp-branch
</code></pre>
<h2 id="alacritty-1"><a class="header" href="#alacritty-1">alacritty</a></h2>
<p><a href="https://github.com/alacritty/alacritty">alacritty</a> 是一个跨平台、基于OpenGL的终端性能极高的同时还支持丰富的自定义和可扩展性可以说是非常优秀的现代化终端。</p>
<p>目前已经是 <code>beta</code> 阶段,可以作为日常工具来使用。</p>
<img alt="alacritty screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/superstar/alacritty.png?raw=true" class="center" />
<h2 id="broot"><a class="header" href="#broot">broot</a></h2>
<p><a href="https://github.com/Canop/broot"><code>broot</code></a> 允许你可视化的去访问一个目录结构。</p>
<img alt="broot screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/command-line/broot.png?raw=true" class="center" /><div style="break-before: page; page-break-before: always;"></div><h1 id="参数解析"><a class="header" href="#参数解析">参数解析</a></h1>
<h2 id="clap"><a class="header" href="#clap">Clap</a></h2>
<p>下面的程序给出了使用 <code>clap</code> 来解析命令行参数的样式结构,如果大家想了解更多,在 <code>clap</code> <a href="https://docs.rs/clap/">文档</a>中还给出了另外两种初始化一个应用的方式。</p>
<p>在下面的构建中,<code>value_of</code> 将获取通过 <code>with_name</code> 解析出的值。<code>short</code><code>long</code> 用于设置用户输入的长短命令格式,例如短命令 <code>-f</code> 和长命令 <code>--file</code></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use clap::{Arg, App};
fn main() {
let matches = App::new(&quot;My Test Program&quot;)
.version(&quot;0.1.0&quot;)
.author(&quot;Hackerman Jones &lt;hckrmnjones@hack.gov&gt;&quot;)
.about(&quot;Teaches argument parsing&quot;)
.arg(Arg::with_name(&quot;file&quot;)
.short(&quot;f&quot;)
.long(&quot;file&quot;)
.takes_value(true)
.help(&quot;A cool file&quot;))
.arg(Arg::with_name(&quot;num&quot;)
.short(&quot;n&quot;)
.long(&quot;number&quot;)
.takes_value(true)
.help(&quot;Five less than your favorite number&quot;))
.get_matches();
let myfile = matches.value_of(&quot;file&quot;).unwrap_or(&quot;input.txt&quot;);
println!(&quot;The file passed is: {}&quot;, myfile);
let num_str = matches.value_of(&quot;num&quot;);
match num_str {
None =&gt; println!(&quot;No idea what your favorite number is.&quot;),
Some(s) =&gt; {
match s.parse::&lt;i32&gt;() {
Ok(n) =&gt; println!(&quot;Your favorite number must be {}.&quot;, n + 5),
Err(_) =&gt; println!(&quot;That's not a number! {}&quot;, s),
}
}
}
}
</code></pre></pre>
<p><code>clap</code> 针对上面提供的构建样式,会自动帮我们生成相应的使用方式说明。例如,上面代码生成的使用说明如下:</p>
<pre><code class="language-shell">My Test Program 0.1.0
Hackerman Jones &lt;hckrmnjones@hack.gov&gt;
Teaches argument parsing
USAGE:
testing [OPTIONS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-f, --file &lt;file&gt; A cool file
-n, --number &lt;num&gt; Five less than your favorite number
</code></pre>
<p>最后,再使用一些参数来运行下我们的代码:</p>
<pre><code class="language-shell">$ cargo run -- -f myfile.txt -n 251
The file passed is: myfile.txt
Your favorite number must be 256.
</code></pre>
<h2 id="structopt"><a class="header" href="#structopt">Structopt</a></h2>
<p>@todo</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="ansi-终端"><a class="header" href="#ansi-终端">ANSI 终端</a></h1>
<p><a href="https://crates.io/crates/ansi_term">ansi_term</a> 包可以帮我们控制终端上的输出样式,例如使用颜色文字、控制输出格式等,当然,前提是在 ANSI 终端上。</p>
<p><code>ansi_term</code> 中有两个主要数据结构:<a href="https://docs.rs/ansi_term/0.12.1/ansi_term/type.ANSIString.html">ANSIString</a><a href="https://docs.rs/ansi_term/0.12.1/ansi_term/struct.Style.html">Style</a></p>
<p><code>Style</code> 用于控制样式:颜色、加粗、闪烁等,而前者是一个带有样式的字符串。</p>
<h2 id="颜色字体"><a class="header" href="#颜色字体">颜色字体</a></h2>
<pre><pre class="playground"><code class="language-rust editable edition2021">use ansi_term::Colour;
fn main() {
println!(&quot;This is {} in color, {} in color and {} in color&quot;,
Colour::Red.paint(&quot;red&quot;),
Colour::Blue.paint(&quot;blue&quot;),
Colour::Green.paint(&quot;green&quot;));
}
</code></pre></pre>
<h2 id="加粗字体"><a class="header" href="#加粗字体">加粗字体</a></h2>
<p>比颜色复杂的样式构建需要使用 <code>Style</code> 结构体:</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use ansi_term::Style;
fn main() {
println!(&quot;{} and this is not&quot;,
Style::new().bold().paint(&quot;This is Bold&quot;));
}
</code></pre></pre>
<h2 id="加粗和颜色"><a class="header" href="#加粗和颜色">加粗和颜色</a></h2>
<p><code>Colour</code> 实现了很多跟 <code>Style</code> 类似的函数,因此可以实现链式调用。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use ansi_term::Colour;
use ansi_term::Style;
fn main(){
println!(&quot;{}, {} and {}&quot;,
Colour::Yellow.paint(&quot;This is colored&quot;),
Style::new().bold().paint(&quot;this is bold&quot;),
// Colour 也可以使用 bold 方法进行加粗
Colour::Yellow.bold().paint(&quot;this is bold and colored&quot;));
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="操作系统"><a class="header" href="#操作系统">操作系统</a></h1>
<p>操作系统范畴很大本章节中精选的内容聚焦在用Rust实现的操作系统以及用Rust写操作系统的教程。</p>
<h2 id="目录"><a class="header" href="#目录">目录</a></h2>
<table><thead><tr><th>系统</th><th>描述</th></tr></thead><tbody>
<tr><td><a href="os/awesome.html#redox">redox</a></td><td><code>Unix</code>风格的微内核OS</td></tr>
<tr><td><a href="os/awesome.html#tock">tock</a></td><td>嵌入式操作系统</td></tr>
<tr><td><a href="os/awesome.html#theseus">theseus</a></td><td>独特设计的OS</td></tr>
<tr><td><a href="os/awesome.html#writing-an-os-in-rust">writing os in rust</a></td><td>使用Rust开发简单的操作系统</td></tr>
<tr><td><a href="os/awesome.html#rust-raspberrypi-os-tutorials">rust-raspberrypi-OS-tutorials</a></td><td>Rust嵌入式系统开发教程</td></tr>
<tr><td><a href="os/awesome.html#rcore-os">rcore-os</a></td><td>清华大学提供的<code>rcore</code>操作系统教程</td></tr>
<tr><td><a href="os/awesome.html#edu-os">edu-os</a></td><td>亚琛工业大学操作系统课程的配套项目</td></tr>
</tbody></table>
<h3 id="redox-1"><a class="header" href="#redox-1">redox</a></h3>
<p><a href="https://github.com/redox-os/redox">redox</a> 是一个 <code>Unix</code> 风格的微内核操作系统,使用 <code>Rust</code> 实现。<code>redox</code> 的目标是安全、快速、免费、可用,它在内核设计上借鉴了很多优秀的内核,例如:<code>SeL4</code>, <code>MINIX</code>, <code>Plan 9</code><code>BSD</code></p>
<p><code>redox</code> 不仅仅是一个内核,它还是一个功能齐全的操作系统,提供了操作系统该有的功能,例如:内存分配器、文件系统、显示管理、核心工具等等。你可以大概认为它是一个 <code>GNU</code><code>BSD</code> 生态,但是是通过一门现代化、内存安全的语言实现的。</p>
<blockquote>
<p>不过据我仔细观察redox目前的开发进度不是很活跃不知道发生了什么未来若有新的发现会在这里进行更新 - Sunface</p>
</blockquote>
<img alt="redox1 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/redox1.jpg?raw=true" class="center" />
<img alt="redox2 screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/redox2.jpeg?raw=true" class="center" />
<h3 id="tock"><a class="header" href="#tock">tock</a></h3>
<p><a href="https://github.com/tock/tock">tock</a> 是一个嵌入式操作系统,设计用于在低内存和低功耗的微控制器上运行多个并发的、相互不信任的应用程序,例如它可在 <code>Cortex-M</code><code>RISC-V</code> 平台上运行。</p>
<p><code>Tock</code> 使用两个核心机制保护操作系统中不同组件的安全运行:</p>
<ul>
<li>内核和设备驱动全部使用Rust编写提供了很好安全性的同时还将内核和设备进行了隔离</li>
<li>使用了内存保护单元技术,让应用之间、应用和内核之间实现了安全隔离</li>
</ul>
<p>具体可通过这本书了解: <a href="https://book.tockos.org/introduction.html">The Tock Book</a>.</p>
<img alt="tock screenshot" width="100%" src="https://book.tockos.org/imgs/imix.svg" class="center" />
<h3 id="theseus"><a class="header" href="#theseus">Theseus</a></h3>
<p><a href="https://github.com/theseus-os/Theseus">Theseus</a> 是从零开始构建的操作系统完全使用Rust进行开发。它使用了新的操作系统结构、更好的状态管理以及利用语言内设计原则将操作系统的职责(如资源管理)转移到编译器中。</p>
<p>该OS目前尚处于早期阶段但是看上去作者很有信心未来可以落地如果想要了解可以通过官方提供的<a href="https://theseus-os.github.io/Theseus/book/index.html">在线书籍</a>进行学习。</p>
<h3 id="writing-an-os-in-rust"><a class="header" href="#writing-an-os-in-rust">Writing an OS in Rust</a></h3>
<p><a href="https://os.phil-opp.com">Writing an OS in Rust</a> 是非常有名的博客系列专门讲解如何使用Rust来写一个简单的操作系统配套源码在<a href="https://github.com/phil-opp/blog_os">这里</a>,目前已经发布了第二版。</p>
<p>以下是<code>async/await</code>的目录截图:
<img alt="writing-os screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/writing-os.jpg?raw=true" class="center" /></p>
<h3 id="rust-raspberrypi-os-tutorials"><a class="header" href="#rust-raspberrypi-os-tutorials">rust-raspberrypi-OS-tutorials</a></h3>
<p><a href="https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials">rust-raspberrypi-OS-tutorials</a> 教大家如何用Rust开发一个嵌入式操作系统可以运行在树莓派上。这个教程讲得很细号称手把手教学而且是从零实现因此很值得学习。</p>
<img alt="rrot screenshot" width="50%" height="400px" src="https://github.com/studyrs/cookbook-images/blob/main/os/rrot.jpg?raw=true" class="center" />
<img alt="rrot1 screenshot" width="49%" height="400px" src="https://github.com/studyrs/cookbook-images/blob/main/os/rrot1.gif?raw=true" class="center" />
<h3 id="rcore-os"><a class="header" href="#rcore-os">rcore-os</a></h3>
<p><a href="https://github.com/rcore-os">rcore-os</a> 是由清华大学开发的操作系统,用 Rus t实现, 与 <code>linux</code> 相兼容,主要目的目前还是用于教学,因为还有相关的配套教程,非常值得学习。目前支持的功能不完全列表如下:<code>linux</code> 兼容的 <code>syscall</code> 接口、网络协议栈、简单的文件系统、信号系统、异步IO、内核模块化。</p>
<ul>
<li><a href="https://github.com/rcore-os/rCore">内核实现</a></li>
<li><a href="https://github.com/rcore-os/rCore-Tutorial-Book-v3">配套教程</a> </li>
</ul>
<p>以下是在树莓派上运行的图:
<img alt="rcore screenshot" width="100%" src="https://github.com/studyrs/cookbook-images/blob/main/os/rcore.jpg?raw=true" class="center" /></p>
<h3 id="edu-os"><a class="header" href="#edu-os">edu-os</a></h3>
<p><a href="https://github.com/RWTH-OS/eduOS-rs">edu-os</a><code>Unix</code> 风格的操作系统用于教学目的它是亚琛工业大学RWTH Aachen University)操作系统课程的配套大项目,但是我并没有找到对应的课程资料,根据作者的描述,上面部分的<strong>Writing an OS in Rust</strong>对他有很大的启发。</p>
<img alt="eduos screenshot" width="100%" src="https://github.com/RWTH-OS/eduOS-rs/raw/master/img/demo.gif?raw=true" class="center" /><div style="break-before: page; page-break-before: always;"></div><h1 id="处理器"><a class="header" href="#处理器">处理器</a></h1>
<h3 id="获取逻辑cpu的核心数"><a class="header" href="#获取逻辑cpu的核心数">获取逻辑CPU的核心数</a></h3>
<p><a href="https://docs.rs/num_cpus/latest/num_cpus/">num_cpus</a> 可以用于获取逻辑和物理的 CPU 核心数,下面的例子是获取逻辑核心数。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
println!(&quot;Number of logical cores is {}&quot;, num_cpus::get());
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="调用系统命令"><a class="header" href="#调用系统命令">调用系统命令</a></h1>
<h3 id="调用一个外部命令并处理输出内容"><a class="header" href="#调用一个外部命令并处理输出内容">调用一个外部命令并处理输出内容</a></h3>
<p>下面的代码将调用操作系统中的 <code>git log --oneline</code> 命令,然后使用 <a href="https://docs.rs/regex/*/regex/struct.Regex.html">regex</a> 对它输出到 <code>stdout</code> 上的调用结果进行解析,以获取哈希值和最后 5 条提交信息( commit )。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use std::process::Command;
use regex::Regex;
<span class="boring">error_chain!{
</span><span class="boring"> foreign_links {
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> Regex(regex::Error);
</span><span class="boring"> Utf8(std::string::FromUtf8Error);
</span><span class="boring"> }
</span><span class="boring">}
</span>
#[derive(PartialEq, Default, Clone, Debug)]
struct Commit {
hash: String,
message: String,
}
fn main() -&gt; Result&lt;()&gt; {
let output = Command::new(&quot;git&quot;).arg(&quot;log&quot;).arg(&quot;--oneline&quot;).output()?;
if !output.status.success() {
error_chain::bail!(&quot;Command executed with failing error code&quot;);
}
let pattern = Regex::new(r&quot;(?x)
([0-9a-fA-F]+) # commit hash
(.*) # The commit message&quot;)?;
String::from_utf8(output.stdout)?
.lines()
.filter_map(|line| pattern.captures(line))
.map(|cap| {
Commit {
hash: cap[1].to_string(),
message: cap[2].trim().to_string(),
}
})
.take(5)
.for_each(|x| println!(&quot;{:?}&quot;, x));
Ok(())
}
</code></pre></pre>
<h3 id="调用-python-解释器运行代码并检查返回的错误码"><a class="header" href="#调用-python-解释器运行代码并检查返回的错误码">调用 python 解释器运行代码并检查返回的错误码</a></h3>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use std::collections::HashSet;
use std::io::Write;
use std::process::{Command, Stdio};
<span class="boring">error_chain!{
</span><span class="boring"> errors { CmdError }
</span><span class="boring"> foreign_links {
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> Utf8(std::string::FromUtf8Error);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
let mut child = Command::new(&quot;python&quot;).stdin(Stdio::piped())
.stderr(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
child.stdin
.as_mut()
.ok_or(&quot;Child process stdin has not been captured!&quot;)?
.write_all(b&quot;import this; copyright(); credits(); exit()&quot;)?;
let output = child.wait_with_output()?;
if output.status.success() {
let raw_output = String::from_utf8(output.stdout)?;
let words = raw_output.split_whitespace()
.map(|s| s.to_lowercase())
.collect::&lt;HashSet&lt;_&gt;&gt;();
println!(&quot;Found {} unique words:&quot;, words.len());
println!(&quot;{:#?}&quot;, words);
Ok(())
} else {
let err = String::from_utf8(output.stderr)?;
error_chain::bail!(&quot;External command failed:\n {}&quot;, err)
}
}
</code></pre></pre>
<h3 id="通过管道来运行外部命令"><a class="header" href="#通过管道来运行外部命令">通过管道来运行外部命令</a></h3>
<p>下面的例子将显示当前目录中大小排名前十的文件和子目录,效果等效于命令 <code>du -ah . | sort -hr | head -n 10</code></p>
<p><a href="https://doc.rust-lang.org/std/process/struct.Command.html"><code>Command</code></a> 命令代表一个进程,其中父进程通过 <a href="https://doc.rust-lang.org/std/process/struct.Stdio.html">Stdio::piped</a> 来捕获子进程的输出。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use std::process::{Command, Stdio};
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> Utf8(std::string::FromUtf8Error);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
let directory = std::env::current_dir()?;
let mut du_output_child = Command::new(&quot;du&quot;)
.arg(&quot;-ah&quot;)
.arg(&amp;directory)
.stdout(Stdio::piped())
.spawn()?;
if let Some(du_output) = du_output_child.stdout.take() {
let mut sort_output_child = Command::new(&quot;sort&quot;)
.arg(&quot;-hr&quot;)
.stdin(du_output)
.stdout(Stdio::piped())
.spawn()?;
du_output_child.wait()?;
if let Some(sort_output) = sort_output_child.stdout.take() {
let head_output_child = Command::new(&quot;head&quot;)
.args(&amp;[&quot;-n&quot;, &quot;10&quot;])
.stdin(sort_output)
.stdout(Stdio::piped())
.spawn()?;
let head_stdout = head_output_child.wait_with_output()?;
sort_output_child.wait()?;
println!(
&quot;Top 10 biggest files and directories in '{}':\n{}&quot;,
directory.display(),
String::from_utf8(head_stdout.stdout).unwrap()
);
}
}
Ok(())
}
</code></pre></pre>
<h3 id="将子进程的-stdout-和-stderr-重定向到同一个文件"><a class="header" href="#将子进程的-stdout-和-stderr-重定向到同一个文件">将子进程的 stdout 和 stderr 重定向到同一个文件</a></h3>
<p>下面的例子将生成一个子进程,然后将它的标准输出和标准错误输出都输出到同一个文件中。最终的效果跟 Unix 命令 <code>ls . oops &gt;out.txt 2&gt;&amp;1</code> 相同。</p>
<p><a href="https://doc.rust-lang.org/std/fs/struct.File.html#method.try_clone">File::try_clone</a> 会克隆一份文件句柄的引用,然后保证这两个句柄在写的时候会使用相同的游标位置。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::fs::File;
use std::io::Error;
use std::process::{Command, Stdio};
fn main() -&gt; Result&lt;(), Error&gt; {
let outputs = File::create(&quot;out.txt&quot;)?;
let errors = outputs.try_clone()?;
Command::new(&quot;ls&quot;)
.args(&amp;[&quot;.&quot;, &quot;oops&quot;])
.stdout(Stdio::from(outputs))
.stderr(Stdio::from(errors))
.spawn()?
.wait_with_output()?;
Ok(())
}
</code></pre></pre>
<h3 id="持续处理子进程的输出"><a class="header" href="#持续处理子进程的输出">持续处理子进程的输出</a></h3>
<p>下面的代码会创建一个管道,然后当 <code>BufReader</code> 更新时,就持续从 <code>stdout</code> 中读取数据。最终效果等同于 Unix 命令 <code>journalctl | grep usb</code></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::process::{Command, Stdio};
use std::io::{BufRead, BufReader, Error, ErrorKind};
fn main() -&gt; Result&lt;(), Error&gt; {
let stdout = Command::new(&quot;journalctl&quot;)
.stdout(Stdio::piped())
.spawn()?
.stdout
.ok_or_else(|| Error::new(ErrorKind::Other,&quot;Could not capture standard output.&quot;))?;
let reader = BufReader::new(stdout);
reader
.lines()
.filter_map(|line| line.ok())
.filter(|line| line.find(&quot;usb&quot;).is_some())
.for_each(|line| println!(&quot;{}&quot;, line));
Ok(())
}
</code></pre></pre>
<h3 id="读取环境变量"><a class="header" href="#读取环境变量">读取环境变量</a></h3>
<p>使用 <a href="https://doc.rust-lang.org/std/env/fn.var.html">std::env::var</a> 可以读取系统中的环境变量。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::env;
use std::fs;
use std::io::Error;
fn main() -&gt; Result&lt;(), Error&gt; {
// 读取环境变量 `CONFIG` 的值并写入到 `config_path` 中。
// 若 `CONFIG` 环境变量没有设置,则使用一个默认的值 &quot;/etc/myapp/config&quot;
let config_path = env::var(&quot;CONFIG&quot;)
.unwrap_or(&quot;/etc/myapp/config&quot;.to_string());
let config: String = fs::read_to_string(config_path)?;
println!(&quot;Config: {}&quot;, config);
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="线程"><a class="header" href="#线程">线程</a></h1>
<h3 id="生成一个临时性的线程"><a class="header" href="#生成一个临时性的线程">生成一个临时性的线程</a></h3>
<p>下面例子用到了 <a href="cocurrency/cookbook/cocurrency/intro.html">crossbeam</a> 包,它提供了非常实用的、用于并发和并行编程的数据结构和函数。</p>
<p><a href="https://docs.rs/crossbeam/*/crossbeam/thread/struct.Scope.html#method.spawn">Scope::spawn</a> 会生成一个被限定了作用域的线程,该线程最大的特点就是:它会在传给 <a href="https://docs.rs/crossbeam/0.8.1/crossbeam/fn.scope.html">crossbeam::scope</a> 的闭包函数返回前先行结束。得益于这个特点,子线程的创建使用就像是本地闭包函数调用,因此生成的线程内部可以使用外部环境中的变量!</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">fn main() {
let arr = &amp;[1, 25, -4, 10];
let max = find_max(arr);
assert_eq!(max, Some(25));
}
// 将数组分成两个部分,并使用新的线程对它们进行处理
fn find_max(arr: &amp;[i32]) -&gt; Option&lt;i32&gt; {
const THRESHOLD: usize = 2;
if arr.len() &lt;= THRESHOLD {
return arr.iter().cloned().max();
}
let mid = arr.len() / 2;
let (left, right) = arr.split_at(mid);
crossbeam::scope(|s| {
let thread_l = s.spawn(|_| find_max(left));
let thread_r = s.spawn(|_| find_max(right));
let max_l = thread_l.join().unwrap()?;
let max_r = thread_r.join().unwrap()?;
Some(max_l.max(max_r))
}).unwrap()
}
</code></pre></pre>
<h3 id="创建并行流水线"><a class="header" href="#创建并行流水线">创建并行流水线</a></h3>
<p>下面我们使用 <a href="https://docs.rs/crossbeam/latest/crossbeam/">crossbeam</a><a href="https://docs.rs/crossbeam-channel/*/crossbeam_channel/index.html">crossbeam-channel</a> 来创建一个并行流水线:流水线的两端分别是数据源和数据下沉( sink ),在流水线中间,有两个工作线程会从源头接收数据,对数据进行并行处理,最后将数据下沉。</p>
<ul>
<li>消息通道( channel )是 <a href="https://docs.rs/crossbeam-channel/0.5.4/crossbeam_channel/fn.bounded.html">crossbeam_channel::bounded</a>,它只能缓存一条消息。当缓存满后,发送者继续调用 [crossbeam_channel::Sender::send] 发送消息时会阻塞,直到一个工作线程( 消费者 ) 拿走这条消息</li>
<li>消费者获取消息时先到先得的策略,因此两个工作线程只有一个能取到消息,保证消息不会被重复消费、处理</li>
<li>通过迭代器 <a href="https://docs.rs/crossbeam-channel/*/crossbeam_channel/struct.Receiver.html#method.iter">crossbeam_channel::Receiver::iter</a> 读取消息会阻塞当前线程,直到新消息的到来或 channel 关闭</li>
<li>channel 只有在所有的发送者或消费者关闭后,才能被关闭。而其中一个消费者 <code>rcv2</code> 处于阻塞读取状态,无比被关闭,因此我们必须要关闭所有发送者: <code>drop(snd1);</code> <code>drop(snd2)</code> ,这样 channel 关闭后,主线程的 <code>rcv2</code> 才能从阻塞状态退出,最后整个程序结束。大家还是迷惑的话,可以看看这篇<a href="https://course.rs/practice/pitfalls/main-with-channel-blocked.html">文章</a></li>
</ul>
<pre><pre class="playground"><code class="language-rust editable edition2021">extern crate crossbeam;
extern crate crossbeam_channel;
use std::thread;
use std::time::Duration;
use crossbeam_channel::bounded;
fn main() {
let (snd1, rcv1) = bounded(1);
let (snd2, rcv2) = bounded(1);
let n_msgs = 4;
let n_workers = 2;
crossbeam::scope(|s| {
// 生产者线程
s.spawn(|_| {
for i in 0..n_msgs {
snd1.send(i).unwrap();
println!(&quot;Source sent {}&quot;, i);
}
// 关闭其中一个发送者 snd1
// 该关闭操作对于结束最后的循环是必须的
drop(snd1);
});
// 通过两个线程并行处理
for _ in 0..n_workers {
// 从数据源接收数据,然后发送到下沉端
let (sendr, recvr) = (snd2.clone(), rcv1.clone());
// 生成单独的工作线程
s.spawn(move |_| {
thread::sleep(Duration::from_millis(500));
// 等待通道的关闭
for msg in recvr.iter() {
println!(&quot;Worker {:?} received {}.&quot;,
thread::current().id(), msg);
sendr.send(msg * 2).unwrap();
}
});
}
// 关闭通道,如果不关闭,下沉端将永远无法结束循环
drop(snd2);
// 下沉端
for msg in rcv2.iter() {
println!(&quot;Sink received {}&quot;, msg);
}
}).unwrap();
}
</code></pre></pre>
<h3 id="线程间传递数据"><a class="header" href="#线程间传递数据">线程间传递数据</a></h3>
<p>下面我们来看看 <a href="https://docs.rs/crossbeam-channel/*/crossbeam_channel/index.html">crossbeam-channel</a> 的单生产者单消费者( SPSC ) 使用场景。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::{thread, time};
use crossbeam_channel::unbounded;
fn main() {
// unbounded 意味着 channel 可以存储任意多的消息
let (snd, rcv) = unbounded();
let n_msgs = 5;
crossbeam::scope(|s| {
s.spawn(|_| {
for i in 0..n_msgs {
snd.send(i).unwrap();
thread::sleep(time::Duration::from_millis(100));
}
});
}).unwrap();
for _ in 0..n_msgs {
let msg = rcv.recv().unwrap();
println!(&quot;Received {}&quot;, msg);
}
}
</code></pre></pre>
<h3 id="维护全局可变的状态"><a class="header" href="#维护全局可变的状态">维护全局可变的状态</a></h3>
<p><a href="cocurrency/">lazy_static</a> 会创建一个全局的静态引用( static ref ),该引用使用了 <code>Mutex</code> 以支持可变性,因此我们可以在代码中对其进行修改。<code>Mutex</code> 能保证该全局状态同时只能被一个线程所访问。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use error_chain::error_chain;
use lazy_static::lazy_static;
use std::sync::Mutex;
error_chain!{ }
lazy_static! {
static ref FRUIT: Mutex&lt;Vec&lt;String&gt;&gt; = Mutex::new(Vec::new());
}
fn insert(fruit: &amp;str) -&gt; Result&lt;()&gt; {
let mut db = FRUIT.lock().map_err(|_| &quot;Failed to acquire MutexGuard&quot;)?;
db.push(fruit.to_string());
Ok(())
}
fn main() -&gt; Result&lt;()&gt; {
insert(&quot;apple&quot;)?;
insert(&quot;orange&quot;)?;
insert(&quot;peach&quot;)?;
{
let db = FRUIT.lock().map_err(|_| &quot;Failed to acquire MutexGuard&quot;)?;
db.iter().enumerate().for_each(|(i, item)| println!(&quot;{}: {}&quot;, i, item));
}
insert(&quot;grape&quot;)?;
Ok(())
}
</code></pre></pre>
<h3 id="并行计算-iso-文件的-sha256"><a class="header" href="#并行计算-iso-文件的-sha256">并行计算 iso 文件的 SHA256</a></h3>
<p>下面的示例将为当前目录中的每一个 .iso 文件都计算一个 SHA256 sum。其中线程池中会初始化和 CPU 核心数一致的线程数,其中核心数是通过 <a href="https://docs.rs/num_cpus/*/num_cpus/fn.get.html">num_cpus::get</a> 函数获取。</p>
<p><code>Walkdir::new</code> 可以遍历当前的目录,然后调用 <code>execute</code> 来执行读操作和 SHA256 哈希计算。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">
use walkdir::WalkDir;
use std::fs::File;
use std::io::{BufReader, Read, Error};
use std::path::Path;
use threadpool::ThreadPool;
use std::sync::mpsc::channel;
use ring::digest::{Context, Digest, SHA256};
// Verify the iso extension
fn is_iso(entry: &amp;Path) -&gt; bool {
match entry.extension() {
Some(e) if e.to_string_lossy().to_lowercase() == &quot;iso&quot; =&gt; true,
_ =&gt; false,
}
}
fn compute_digest&lt;P: AsRef&lt;Path&gt;&gt;(filepath: P) -&gt; Result&lt;(Digest, P), Error&gt; {
let mut buf_reader = BufReader::new(File::open(&amp;filepath)?);
let mut context = Context::new(&amp;SHA256);
let mut buffer = [0; 1024];
loop {
let count = buf_reader.read(&amp;mut buffer)?;
if count == 0 {
break;
}
context.update(&amp;buffer[..count]);
}
Ok((context.finish(), filepath))
}
fn main() -&gt; Result&lt;(), Error&gt; {
let pool = ThreadPool::new(num_cpus::get());
let (tx, rx) = channel();
for entry in WalkDir::new(&quot;/home/user/Downloads&quot;)
.follow_links(true)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| !e.path().is_dir() &amp;&amp; is_iso(e.path())) {
let path = entry.path().to_owned();
let tx = tx.clone();
pool.execute(move || {
let digest = compute_digest(path);
tx.send(digest).expect(&quot;Could not send data!&quot;);
});
}
drop(tx);
for t in rx.iter() {
let (sha, path) = t?;
println!(&quot;{:?} {:?}&quot;, sha, path);
}
Ok(())
}
</code></pre></pre>
<h3 id="使用线程池来绘制分形"><a class="header" href="#使用线程池来绘制分形">使用线程池来绘制分形</a></h3>
<p>下面例子中将基于 <a href="cocurrency/">Julia Set</a> 来绘制一个分形图片,其中使用到了线程池来做分布式计算。</p>
<img src="https://cloud.githubusercontent.com/assets/221000/26546700/9be34e80-446b-11e7-81dc-dd9871614ea1.png" />
<pre><pre class="playground"><code class="language-rust edtiable edition2021"><span class="boring">use error_chain::error_chain;
</span>use std::sync::mpsc::{channel, RecvError};
use threadpool::ThreadPool;
use num::complex::Complex;
use image::{ImageBuffer, Pixel, Rgb};
<span class="boring">
</span><span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> MpscRecv(RecvError);
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">// Function converting intensity values to RGB
</span><span class="boring">// Based on http://www.efg2.com/Lab/ScienceAndEngineering/Spectra.htm
</span><span class="boring">fn wavelength_to_rgb(wavelength: u32) -&gt; Rgb&lt;u8&gt; {
</span><span class="boring"> let wave = wavelength as f32;
</span><span class="boring">
</span><span class="boring"> let (r, g, b) = match wavelength {
</span><span class="boring"> 380..=439 =&gt; ((440. - wave) / (440. - 380.), 0.0, 1.0),
</span><span class="boring"> 440..=489 =&gt; (0.0, (wave - 440.) / (490. - 440.), 1.0),
</span><span class="boring"> 490..=509 =&gt; (0.0, 1.0, (510. - wave) / (510. - 490.)),
</span><span class="boring"> 510..=579 =&gt; ((wave - 510.) / (580. - 510.), 1.0, 0.0),
</span><span class="boring"> 580..=644 =&gt; (1.0, (645. - wave) / (645. - 580.), 0.0),
</span><span class="boring"> 645..=780 =&gt; (1.0, 0.0, 0.0),
</span><span class="boring"> _ =&gt; (0.0, 0.0, 0.0),
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> let factor = match wavelength {
</span><span class="boring"> 380..=419 =&gt; 0.3 + 0.7 * (wave - 380.) / (420. - 380.),
</span><span class="boring"> 701..=780 =&gt; 0.3 + 0.7 * (780. - wave) / (780. - 700.),
</span><span class="boring"> _ =&gt; 1.0,
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> let (r, g, b) = (normalize(r, factor), normalize(g, factor), normalize(b, factor));
</span><span class="boring"> Rgb::from_channels(r, g, b, 0)
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">// Maps Julia set distance estimation to intensity values
</span><span class="boring">fn julia(c: Complex&lt;f32&gt;, x: u32, y: u32, width: u32, height: u32, max_iter: u32) -&gt; u32 {
</span><span class="boring"> let width = width as f32;
</span><span class="boring"> let height = height as f32;
</span><span class="boring">
</span><span class="boring"> let mut z = Complex {
</span><span class="boring"> // scale and translate the point to image coordinates
</span><span class="boring"> re: 3.0 * (x as f32 - 0.5 * width) / width,
</span><span class="boring"> im: 2.0 * (y as f32 - 0.5 * height) / height,
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> let mut i = 0;
</span><span class="boring"> for t in 0..max_iter {
</span><span class="boring"> if z.norm() &gt;= 2.0 {
</span><span class="boring"> break;
</span><span class="boring"> }
</span><span class="boring"> z = z * z + c;
</span><span class="boring"> i = t;
</span><span class="boring"> }
</span><span class="boring"> i
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">// Normalizes color intensity values within RGB range
</span><span class="boring">fn normalize(color: f32, factor: f32) -&gt; u8 {
</span><span class="boring"> ((color * factor).powf(0.8) * 255.) as u8
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
let (width, height) = (1920, 1080);
// 为指定宽高的输出图片分配内存
let mut img = ImageBuffer::new(width, height);
let iterations = 300;
let c = Complex::new(-0.8, 0.156);
let pool = ThreadPool::new(num_cpus::get());
let (tx, rx) = channel();
for y in 0..height {
let tx = tx.clone();
// execute 将每个像素作为单独的作业接收
pool.execute(move || for x in 0..width {
let i = julia(c, x, y, width, height, iterations);
let pixel = wavelength_to_rgb(380 + i * 400 / iterations);
tx.send((x, y, pixel)).expect(&quot;Could not send data!&quot;);
});
}
for _ in 0..(width * height) {
let (x, y, pixel) = rx.recv()?;
// 使用数据来设置像素的颜色
img.put_pixel(x, y, pixel);
}
// 输出图片内容到指定文件中
let _ = img.save(&quot;output.png&quot;)?;
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="任务并行处理"><a class="header" href="#任务并行处理">任务并行处理</a></h1>
<h3 id="并行修改数组中的元素"><a class="header" href="#并行修改数组中的元素">并行修改数组中的元素</a></h3>
<p><a href="https://docs.rs/rayon/1.5.1/rayon/index.html">rayon</a> 提供了一个 <a href="https://docs.rs/rayon/*/rayon/iter/trait.IntoParallelRefMutIterator.html#tymethod.par_iter_mut">par_iter_mut</a> 方法用于并行化迭代一个数据集合。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rayon::prelude::*;
fn main() {
let mut arr = [0, 7, 9, 11];
arr.par_iter_mut().for_each(|p| *p -= 1);
println!(&quot;{:?}&quot;, arr);
}
</code></pre></pre>
<h3 id="并行测试集合中的元素是否满足给定的条件"><a class="header" href="#并行测试集合中的元素是否满足给定的条件">并行测试集合中的元素是否满足给定的条件</a></h3>
<p><a href="https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.any">rayon::any</a><a href="https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.all">rayon::all</a> 类似于 <a href="https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.any">std::any</a> / <a href="https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.all">std::all</a> ,但是是并行版本的。</p>
<ul>
<li><code>rayon::any</code> 并行检查迭代器中是否有任何元素满足给定的条件,一旦发现符合条件的元素,就立即返回</li>
<li><code>rayon::all</code> 并行检查迭代器中的所有元素是否满足给定的条件,一旦发现不满足条件的元素,就立即返回</li>
</ul>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rayon::prelude::*;
fn main() {
let mut vec = vec![2, 4, 6, 8];
assert!(!vec.par_iter().any(|n| (*n % 2) != 0));
assert!(vec.par_iter().all(|n| (*n % 2) == 0));
assert!(!vec.par_iter().any(|n| *n &gt; 8 ));
assert!(vec.par_iter().all(|n| *n &lt;= 8 ));
vec.push(9);
assert!(vec.par_iter().any(|n| (*n % 2) != 0));
assert!(!vec.par_iter().all(|n| (*n % 2) == 0));
assert!(vec.par_iter().any(|n| *n &gt; 8 ));
assert!(!vec.par_iter().all(|n| *n &lt;= 8 ));
}
</code></pre></pre>
<h3 id="使用给定条件并行搜索"><a class="header" href="#使用给定条件并行搜索">使用给定条件并行搜索</a></h3>
<p>下面例子使用 <a href="https://docs.rs/rayon/*/rayon/iter/trait.IntoParallelRefIterator.html#tymethod.par_iter">par_iter</a><a href="https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.find_any">rayon::find_any</a> 来并行搜索一个数组,直到找到任意一个满足条件的元素。</p>
<p>如果有多个元素满足条件,<code>rayon</code> 会返回第一个找到的元素,注意:第一个找到的元素未必是数组中的顺序最靠前的那个。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rayon::prelude::*;
fn main() {
let v = vec![6, 2, 1, 9, 3, 8, 11];
// 这里使用了 `&amp;&amp;x` 的形式,大家可以在以下链接阅读更多 https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find
let f1 = v.par_iter().find_any(|&amp;&amp;x| x == 9);
let f2 = v.par_iter().find_any(|&amp;&amp;x| x % 2 == 0 &amp;&amp; x &gt; 6);
let f3 = v.par_iter().find_any(|&amp;&amp;x| x &gt; 8);
assert_eq!(f1, Some(&amp;9));
assert_eq!(f2, Some(&amp;8));
assert!(f3 &gt; Some(&amp;8));
}
</code></pre></pre>
<h3 id="对数组进行并行排序"><a class="header" href="#对数组进行并行排序">对数组进行并行排序</a></h3>
<p>下面的例子将对字符串数组进行并行排序。</p>
<p><a href="https://docs.rs/rayon/*/rayon/slice/trait.ParallelSliceMut.html#method.par_sort_unstable">par_sort_unstable</a> 方法的排序性能往往要比<a href="https://docs.rs/rayon/1.5.1/rayon/slice/trait.ParallelSliceMut.html#method.par_sort">稳定的排序算法</a>更高。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rand::{Rng, thread_rng};
use rand::distributions::Alphanumeric;
use rayon::prelude::*;
fn main() {
let mut vec = vec![String::new(); 100_000];
// 并行生成数组中的字符串
vec.par_iter_mut().for_each(|p| {
let mut rng = thread_rng();
*p = (0..5).map(|_| rng.sample(&amp;Alphanumeric)).collect()
});
//
vec.par_sort_unstable();
}
</code></pre></pre>
<h3 id="并行化-map-reuduce"><a class="header" href="#并行化-map-reuduce">并行化 Map-Reuduce</a></h3>
<p>下面例子使用 <a href="https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.filter">rayon::filter</a>, <a href="https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.map">rayon::map</a>, 和 <a href="https://docs.rs/rayon/*/rayon/iter/trait.ParallelIterator.html#method.reduce">rayon::reduce</a> 来超过 30 岁的 <code>Person</code> 的平均年龄。</p>
<ul>
<li><code>rayon::filter</code> 返回集合中所有满足给定条件的元素</li>
<li><code>rayon::map</code> 对集合中的每一个元素执行一个操作,创建并返回新的迭代器,类似于<a href="https://course.rs/advance/functional-programing/iterator.html#%E8%BF%AD%E4%BB%A3%E5%99%A8%E9%80%82%E9%85%8D%E5%99%A8">迭代器适配器</a></li>
<li><code>rayon::reduce</code> 则迭代器的元素进行不停的聚合运算,直到获取一个最终结果,这个结果跟例子中 <code>rayon::sum</code> 获取的结果是相同的</li>
</ul>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rayon::prelude::*;
struct Person {
age: u32,
}
fn main() {
let v: Vec&lt;Person&gt; = vec![
Person { age: 23 },
Person { age: 19 },
Person { age: 42 },
Person { age: 17 },
Person { age: 17 },
Person { age: 31 },
Person { age: 30 },
];
let num_over_30 = v.par_iter().filter(|&amp;x| x.age &gt; 30).count() as f32;
let sum_over_30 = v.par_iter()
.map(|x| x.age)
.filter(|&amp;x| x &gt; 30)
.reduce(|| 0, |x, y| x + y);
let alt_sum_30: u32 = v.par_iter()
.map(|x| x.age)
.filter(|&amp;x| x &gt; 30)
.sum();
let avg_over_30 = sum_over_30 as f32 / num_over_30;
let alt_avg_over_30 = alt_sum_30 as f32/ num_over_30;
assert!((avg_over_30 - alt_avg_over_30).abs() &lt; std::f32::EPSILON);
println!(&quot;The average age of people older than 30 is {}&quot;, avg_over_30);
}
</code></pre></pre>
<h3 id="并行生成缩略图"><a class="header" href="#并行生成缩略图">并行生成缩略图</a></h3>
<p>下面例子将为目录中的所有图片并行生成缩略图,然后将结果存到新的目录 <code>thumbnails</code> 中。</p>
<p><a href="https://docs.rs/glob/*/glob/fn.glob_with.html">glob::glob_with</a> 可以找出当前目录下的所有 <code>.jpg</code> 文件,<code>rayon</code> 通过 <a href="https://docs.rs/image/*/image/enum.DynamicImage.html#method.resize">DynamicImage::resize</a> 来并行调整图片的大小。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use std::path::Path;
use std::fs::create_dir_all;
<span class="boring">use error_chain::ChainedError;
</span>use glob::{glob_with, MatchOptions};
use image::{FilterType, ImageError};
use rayon::prelude::*;
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> Image(ImageError);
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> Glob(glob::PatternError);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
let options: MatchOptions = Default::default();
// 找到当前目录中的所有 `jpg` 文件
let files: Vec&lt;_&gt; = glob_with(&quot;*.jpg&quot;, options)?
.filter_map(|x| x.ok())
.collect();
if files.len() == 0 {
error_chain::bail!(&quot;No .jpg files found in current directory&quot;);
}
let thumb_dir = &quot;thumbnails&quot;;
create_dir_all(thumb_dir)?;
println!(&quot;Saving {} thumbnails into '{}'...&quot;, files.len(), thumb_dir);
let image_failures: Vec&lt;_&gt; = files
.par_iter()
.map(|path| {
make_thumbnail(path, thumb_dir, 300)
.map_err(|e| e.chain_err(|| path.display().to_string()))
})
.filter_map(|x| x.err())
.collect();
image_failures.iter().for_each(|x| println!(&quot;{}&quot;, x.display_chain()));
println!(&quot;{} thumbnails saved successfully&quot;, files.len() - image_failures.len());
Ok(())
}
fn make_thumbnail&lt;PA, PB&gt;(original: PA, thumb_dir: PB, longest_edge: u32) -&gt; Result&lt;()&gt;
where
PA: AsRef&lt;Path&gt;,
PB: AsRef&lt;Path&gt;,
{
let img = image::open(original.as_ref())?;
let file_path = thumb_dir.as_ref().join(original);
Ok(img.resize(longest_edge, longest_edge, FilterType::Nearest)
.save(file_path)?)
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="sqlite"><a class="header" href="#sqlite">SQLite</a></h1>
<h3 id="创建-sqlite-数据库"><a class="header" href="#创建-sqlite-数据库">创建 SQLite 数据库</a></h3>
<p>使用 <code>rusqlite</code> 可以创建 SQLite 数据库,<a href="https://docs.rs/rusqlite/*/rusqlite/struct.Connection.html#method.open">Connection::open</a> 会尝试打开一个数据库,若不存在,则创建新的数据库。</p>
<blockquote>
<p>这里创建的 <code>cats.db</code> 数据库将被后面的例子所使用</p>
</blockquote>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rusqlite::{Connection, Result};
use rusqlite::NO_PARAMS;
fn main() -&gt; Result&lt;()&gt; {
let conn = Connection::open(&quot;cats.db&quot;)?;
conn.execute(
&quot;create table if not exists cat_colors (
id integer primary key,
name text not null unique
)&quot;,
NO_PARAMS,
)?;
conn.execute(
&quot;create table if not exists cats (
id integer primary key,
name text not null,
color_id integer not null references cat_colors(id)
)&quot;,
NO_PARAMS,
)?;
Ok(())
}
</code></pre></pre>
<h3 id="插入和查询"><a class="header" href="#插入和查询">插入和查询</a></h3>
<pre><pre class="playground"><code class="language-rust editable edition2021">
use rusqlite::NO_PARAMS;
use rusqlite::{Connection, Result};
use std::collections::HashMap;
#[derive(Debug)]
struct Cat {
name: String,
color: String,
}
fn main() -&gt; Result&lt;()&gt; {
// 打开第一个例子所创建的数据库
let conn = Connection::open(&quot;cats.db&quot;)?;
let mut cat_colors = HashMap::new();
cat_colors.insert(String::from(&quot;Blue&quot;), vec![&quot;Tigger&quot;, &quot;Sammy&quot;]);
cat_colors.insert(String::from(&quot;Black&quot;), vec![&quot;Oreo&quot;, &quot;Biscuit&quot;]);
for (color, catnames) in &amp;cat_colors {
// 插入一条数据行
conn.execute(
&quot;INSERT INTO cat_colors (name) values (?1)&quot;,
&amp;[&amp;color.to_string()],
)?;
// 获取最近插入数据行的 id
let last_id: String = conn.last_insert_rowid().to_string();
for cat in catnames {
conn.execute(
&quot;INSERT INTO cats (name, color_id) values (?1, ?2)&quot;,
&amp;[&amp;cat.to_string(), &amp;last_id],
)?;
}
}
let mut stmt = conn.prepare(
&quot;SELECT c.name, cc.name from cats c
INNER JOIN cat_colors cc
ON cc.id = c.color_id;&quot;,
)?;
let cats = stmt.query_map(NO_PARAMS, |row| {
Ok(Cat {
name: row.get(0)?,
color: row.get(1)?,
})
})?;
for cat in cats {
println!(&quot;Found cat {:?}&quot;, cat);
}
Ok(())
}
</code></pre></pre>
<h3 id="使用事务"><a class="header" href="#使用事务">使用事务</a></h3>
<p>使用 <a href="https://docs.rs/rusqlite/*/rusqlite/struct.Connection.html#method.transaction">Connection::transaction</a> 可以开始新的事务,若没有对事务进行显式地提交 <a href="https://docs.rs/rusqlite/0.27.0/rusqlite/struct.Transaction.html#method.commit">Transaction::commit</a>,则会进行回滚。</p>
<p>下面的例子中,<code>rolled_back_tx</code> 插入了重复的颜色名称,会发生回滚。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use rusqlite::{Connection, Result, NO_PARAMS};
fn main() -&gt; Result&lt;()&gt; {
// 打开第一个例子所创建的数据库
let mut conn = Connection::open(&quot;cats.db&quot;)?;
successful_tx(&amp;mut conn)?;
let res = rolled_back_tx(&amp;mut conn);
assert!(res.is_err());
Ok(())
}
fn successful_tx(conn: &amp;mut Connection) -&gt; Result&lt;()&gt; {
let tx = conn.transaction()?;
tx.execute(&quot;delete from cat_colors&quot;, NO_PARAMS)?;
tx.execute(&quot;insert into cat_colors (name) values (?1)&quot;, &amp;[&amp;&quot;lavender&quot;])?;
tx.execute(&quot;insert into cat_colors (name) values (?1)&quot;, &amp;[&amp;&quot;blue&quot;])?;
tx.commit()
}
fn rolled_back_tx(conn: &amp;mut Connection) -&gt; Result&lt;()&gt; {
let tx = conn.transaction()?;
tx.execute(&quot;delete from cat_colors&quot;, NO_PARAMS)?;
tx.execute(&quot;insert into cat_colors (name) values (?1)&quot;, &amp;[&amp;&quot;lavender&quot;])?;
tx.execute(&quot;insert into cat_colors (name) values (?1)&quot;, &amp;[&amp;&quot;blue&quot;])?;
tx.execute(&quot;insert into cat_colors (name) values (?1)&quot;, &amp;[&amp;&quot;lavender&quot;])?;
tx.commit()
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="postgres"><a class="header" href="#postgres">Postgres</a></h1>
<h3 id="在数据库中创建表格"><a class="header" href="#在数据库中创建表格">在数据库中创建表格</a></h3>
<p>我们通过 <a href="https://docs.rs/postgres/0.17.2/postgres/">postgres</a> 来操作数据库。下面的例子有一个前提:数据库 <code>library</code> 已经存在,其中用户名和密码都是 <code>postgres</code></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use postgres::{Client, NoTls, Error};
fn main() -&gt; Result&lt;(), Error&gt; {
// 连接到数据库 library
let mut client = Client::connect(&quot;postgresql://postgres:postgres@localhost/library&quot;, NoTls)?;
client.batch_execute(&quot;
CREATE TABLE IF NOT EXISTS author (
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
country VARCHAR NOT NULL
)
&quot;)?;
client.batch_execute(&quot;
CREATE TABLE IF NOT EXISTS book (
id SERIAL PRIMARY KEY,
title VARCHAR NOT NULL,
author_id INTEGER NOT NULL REFERENCES author
)
&quot;)?;
Ok(())
}
</code></pre></pre>
<h3 id="插入和查询-1"><a class="header" href="#插入和查询-1">插入和查询</a></h3>
<pre><pre class="playground"><code class="language-rust editable edition2021">use postgres::{Client, NoTls, Error};
use std::collections::HashMap;
struct Author {
_id: i32,
name: String,
country: String
}
fn main() -&gt; Result&lt;(), Error&gt; {
let mut client = Client::connect(&quot;postgresql://postgres:postgres@localhost/library&quot;,
NoTls)?;
let mut authors = HashMap::new();
authors.insert(String::from(&quot;Chinua Achebe&quot;), &quot;Nigeria&quot;);
authors.insert(String::from(&quot;Rabindranath Tagore&quot;), &quot;India&quot;);
authors.insert(String::from(&quot;Anita Nair&quot;), &quot;India&quot;);
for (key, value) in &amp;authors {
let author = Author {
_id: 0,
name: key.to_string(),
country: value.to_string()
};
// 插入数据
client.execute(
&quot;INSERT INTO author (name, country) VALUES ($1, $2)&quot;,
&amp;[&amp;author.name, &amp;author.country],
)?;
}
// 查询数据
for row in client.query(&quot;SELECT id, name, country FROM author&quot;, &amp;[])? {
let author = Author {
_id: row.get(0),
name: row.get(1),
country: row.get(2),
};
println!(&quot;Author {} is from {}&quot;, author.name, author.country);
}
Ok(())
}
</code></pre></pre>
<h3 id="聚合数据"><a class="header" href="#聚合数据">聚合数据</a></h3>
<p>下面代码将使用降序的方式列出 <a href="database/">Museum of Modern Art</a> 数据库中的前 7999 名艺术家的国籍分布.</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use postgres::{Client, Error, NoTls};
struct Nation {
nationality: String,
count: i64,
}
fn main() -&gt; Result&lt;(), Error&gt; {
let mut client = Client::connect(
&quot;postgresql://postgres:postgres@127.0.0.1/moma&quot;,
NoTls,
)?;
for row in client.query
(&quot;SELECT nationality, COUNT(nationality) AS count
FROM artists GROUP BY nationality ORDER BY count DESC&quot;, &amp;[])? {
let (nationality, count) : (Option&lt;String&gt;, Option&lt;i64&gt;)
= (row.get (0), row.get (1));
if nationality.is_some () &amp;&amp; count.is_some () {
let nation = Nation{
nationality: nationality.unwrap(),
count: count.unwrap(),
};
println!(&quot;{} {}&quot;, nation.nationality, nation.count);
}
}
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="时间计算和转换"><a class="header" href="#时间计算和转换">时间计算和转换</a></h1>
<h3 id="测量某段代码的耗时"><a class="header" href="#测量某段代码的耗时">测量某段代码的耗时</a></h3>
<p>测量从 <a href="https://doc.rust-lang.org/std/time/struct.Instant.html#method.now">time::Instant::now</a> 开始所经过的时间 <a href="https://doc.rust-lang.org/std/time/struct.Instant.html#method.elapsed">time::Instant::elapsed</a>.</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::time::{Duration, Instant};
fn main() {
let start = Instant::now();
expensive_function();
let duration = start.elapsed();
println!(&quot;Time elapsed in expensive_function() is: {:?}&quot;, duration);
}
</code></pre></pre>
<h3 id="对日期和时间进行计算"><a class="header" href="#对日期和时间进行计算">对日期和时间进行计算</a></h3>
<p>使用 <a href="https://docs.rs/chrono/*/chrono/struct.Date.html#method.checked_add_signed">DateTime::checked_add_signed</a> 计算和显示从现在开始两周后的日期和时间,然后再计算一天前的日期 <a href="https://docs.rs/chrono/*/chrono/struct.Date.html#method.checked_sub_signed">DateTime::checked_sub_signed</a></p>
<p><a href="https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.format">DateTime::format</a> 所支持的转义序列可以在 <a href="https://docs.rs/chrono/*/chrono/format/strftime/index.html">chrono::format::strftime</a> 找到.</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use chrono::{DateTime, Duration, Utc};
fn day_earlier(date_time: DateTime&lt;Utc&gt;) -&gt; Option&lt;DateTime&lt;Utc&gt;&gt; {
date_time.checked_sub_signed(Duration::days(1))
}
fn main() {
let now = Utc::now();
println!(&quot;{}&quot;, now);
let almost_three_weeks_from_now = now.checked_add_signed(Duration::weeks(2))
.and_then(|in_2weeks| in_2weeks.checked_add_signed(Duration::weeks(1)))
.and_then(day_earlier);
match almost_three_weeks_from_now {
Some(x) =&gt; println!(&quot;{}&quot;, x),
None =&gt; eprintln!(&quot;Almost three weeks from now overflows!&quot;),
}
match now.checked_add_signed(Duration::max_value()) {
Some(x) =&gt; println!(&quot;{}&quot;, x),
None =&gt; eprintln!(&quot;We can't use chrono to tell the time for the Solar System to complete more than one full orbit around the galactic center.&quot;),
}
}
</code></pre></pre>
<h3 id="将本地时间转换成其它时区"><a class="header" href="#将本地时间转换成其它时区">将本地时间转换成其它时区</a></h3>
<p>使用 <a href="https://docs.rs/chrono/*/chrono/offset/struct.Local.html#method.now">offset::Local::now</a> 获取本地时间并进行显示,接着,使用 <a href="https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.from_utc">DateTime::from_utc</a> 将它转换成 UTC 标准时间。最后,再使用 <a href="https://docs.rs/chrono/*/chrono/offset/struct.FixedOffset.html">offset::FixedOffset</a> 将 UTC 时间转换成 UTC+8 和 UTC-2 的时间。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use chrono::{DateTime, FixedOffset, Local, Utc};
fn main() {
let local_time = Local::now();
let utc_time = DateTime::&lt;Utc&gt;::from_utc(local_time.naive_utc(), Utc);
let china_timezone = FixedOffset::east(8 * 3600);
let rio_timezone = FixedOffset::west(2 * 3600);
println!(&quot;Local time now is {}&quot;, local_time);
println!(&quot;UTC time now is {}&quot;, utc_time);
println!(
&quot;Time in Hong Kong now is {}&quot;,
utc_time.with_timezone(&amp;china_timezone)
);
println!(&quot;Time in Rio de Janeiro now is {}&quot;, utc_time.with_timezone(&amp;rio_timezone));
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="解析和显示"><a class="header" href="#解析和显示">解析和显示</a></h1>
<h3 id="检查日期和时间"><a class="header" href="#检查日期和时间">检查日期和时间</a></h3>
<p>通过 <a href="https://docs.rs/chrono/*/chrono/struct.DateTime.html">DateTime</a> 获取当前的 UTC 时间:</p>
<ul>
<li><a href="https://docs.rs/chrono/*/chrono/trait.Timelike.html">Timelike</a>, 时/分/秒</li>
<li><a href="https://docs.rs/chrono/*/chrono/trait.Datelike.html">Datelike</a>, 年/月/日</li>
</ul>
<pre><pre class="playground"><code class="language-rust editable edition2021">use chrono::{Datelike, Timelike, Utc};
fn main() {
let now = Utc::now();
let (is_pm, hour) = now.hour12();
println!(
&quot;The current UTC time is {:02}:{:02}:{:02} {}&quot;,
hour,
now.minute(),
now.second(),
if is_pm { &quot;PM&quot; } else { &quot;AM&quot; }
);
println!(
&quot;And there have been {} seconds since midnight&quot;,
now.num_seconds_from_midnight()
);
let (is_common_era, year) = now.year_ce();
println!(
&quot;The current UTC date is {}-{:02}-{:02} {:?} ({})&quot;,
year,
now.month(),
now.day(),
now.weekday(),
if is_common_era { &quot;CE&quot; } else { &quot;BCE&quot; }
);
println!(
&quot;And the Common Era began {} days ago&quot;,
now.num_days_from_ce()
);
}
</code></pre></pre>
<h3 id="日期和时间戳的相互转换"><a class="header" href="#日期和时间戳的相互转换">日期和时间戳的相互转换</a></h3>
<pre><pre class="playground"><code class="language-rust editable edition2021">use chrono::{NaiveDate, NaiveDateTime};
fn main() {
// 生成一个具体的日期时间
let date_time: NaiveDateTime = NaiveDate::from_ymd(2017, 11, 12).and_hms(17, 33, 44);
println!(
&quot;Number of seconds between 1970-01-01 00:00:00 and {} is {}.&quot;,
// 打印日期和日期对应的时间戳
date_time, date_time.timestamp());
// 计算从 1970 1月1日 0:00:00 UTC 开始10亿秒后是什么日期时间
let date_time_after_a_billion_seconds = NaiveDateTime::from_timestamp(1_000_000_000, 0);
println!(
&quot;Date after a billion seconds since 1970-01-01 00:00:00 was {}.&quot;,
date_time_after_a_billion_seconds);
}
</code></pre></pre>
<h3 id="显示格式化的日期和时间"><a class="header" href="#显示格式化的日期和时间">显示格式化的日期和时间</a></h3>
<p>通过 <a href="https://docs.rs/chrono/*/chrono/offset/struct.Utc.html#method.now">Utc::now</a> 可以获取当前的 UTC 时间。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use chrono::{DateTime, Utc};
fn main() {
let now: DateTime&lt;Utc&gt; = Utc::now();
println!(&quot;UTC now is: {}&quot;, now);
// 使用 RFC 2822 格式显示当前时间
println!(&quot;UTC now in RFC 2822 is: {}&quot;, now.to_rfc2822());
// 使用 RFC 3339 格式显示当前时间
println!(&quot;UTC now in RFC 3339 is: {}&quot;, now.to_rfc3339());
// 使用自定义格式显示当前时间
println!(&quot;UTC now in a custom format is: {}&quot;, now.format(&quot;%a %b %e %T %Y&quot;));
}
</code></pre></pre>
<h3 id="将字符串解析为-datetime-结构体"><a class="header" href="#将字符串解析为-datetime-结构体">将字符串解析为 DateTime 结构体</a></h3>
<p>我们可以将多种格式的日期时间字符串转换成 <a href="https://docs.rs/chrono/*/chrono/struct.DateTime.html">DateTime</a> 结构体。<a href="https://docs.rs/chrono/*/chrono/struct.DateTime.html#method.parse_from_str">DateTime::parse_from_str</a> 使用的转义序列可以在 <a href="https://docs.rs/chrono/0.4.19/chrono/format/strftime/index.html">chrono::format::strftime</a> 找到.</p>
<p>只有当能唯一的标识出日期和时间时,才能创建 <code>DateTime</code>。如果要在没有时区的情况下解析日期或时间,你需要使用 <a href="https://docs.rs/chrono/*/chrono/naive/struct.NaiveDate.html"><code>NativeDate</code></a> 等函数。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime};
use chrono::format::ParseError;
fn main() -&gt; Result&lt;(), ParseError&gt; {
let rfc2822 = DateTime::parse_from_rfc2822(&quot;Tue, 1 Jul 2003 10:52:37 +0200&quot;)?;
println!(&quot;{}&quot;, rfc2822);
let rfc3339 = DateTime::parse_from_rfc3339(&quot;1996-12-19T16:39:57-08:00&quot;)?;
println!(&quot;{}&quot;, rfc3339);
let custom = DateTime::parse_from_str(&quot;5.8.1994 8:00 am +0000&quot;, &quot;%d.%m.%Y %H:%M %P %z&quot;)?;
println!(&quot;{}&quot;, custom);
let time_only = NaiveTime::parse_from_str(&quot;23:56:04&quot;, &quot;%H:%M:%S&quot;)?;
println!(&quot;{}&quot;, time_only);
let date_only = NaiveDate::parse_from_str(&quot;2015-09-05&quot;, &quot;%Y-%m-%d&quot;)?;
println!(&quot;{}&quot;, date_only);
let no_timezone = NaiveDateTime::parse_from_str(&quot;2015-09-05 23:56:04&quot;, &quot;%Y-%m-%d %H:%M:%S&quot;)?;
println!(&quot;{}&quot;, no_timezone);
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="日志"><a class="header" href="#日志">日志</a></h1>
<h2 id="log-包"><a class="header" href="#log-包">log 包</a></h2>
<p><a href="https://docs.rs/crate/log/0.4.16">log</a> 提供了日志相关的实用工具。</p>
<h3 id="在控制台打印-debug-信息"><a class="header" href="#在控制台打印-debug-信息">在控制台打印 debug 信息</a></h3>
<p><code>env_logger</code> 通过环境变量来配置日志。<a href="https://docs.rs/log/0.4.16/log/macro.debug.html">log::debug!</a> 使用起来跟 <a href="https://doc.rust-lang.org/std/fmt/">std::fmt</a> 中的格式化字符串很像。</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn execute_query(query: &amp;str) {
log::debug!(&quot;Executing query: {}&quot;, query);
}
fn main() {
env_logger::init();
execute_query(&quot;DROP TABLE students&quot;);
}
</code></pre></pre>
<p>如果大家运行代码,会发现没有任何日志输出,原因是默认的日志级别是 <code>error</code>,因此我们需要通过 <code>RUST_LOG</code> 环境变量来设置下新的日志级别:</p>
<pre><code class="language-shell">$ RUST_LOG=debug cargo run
</code></pre>
<p>然后你将成功看到以下输出:</p>
<pre><code class="language-shell">DEBUG:main: Executing query: DROP TABLE students
</code></pre>
<h3 id="将错误日志输出到控制台"><a class="header" href="#将错误日志输出到控制台">将错误日志输出到控制台</a></h3>
<p>下面我们通过 <a href="https://docs.rs/log/0.4.16/log/macro.error.html">log::error!</a> 将错误日志输出到标准错误 <code>stderr</code></p>
<pre><pre class="playground"><code class="language-rust edition2021">fn execute_query(_query: &amp;str) -&gt; Result&lt;(), &amp;'static str&gt; {
Err(&quot;I'm afraid I can't do that&quot;)
}
fn main() {
env_logger::init();
let response = execute_query(&quot;DROP TABLE students&quot;);
if let Err(err) = response {
log::error!(&quot;Failed to execute query: {}&quot;, err);
}
}
</code></pre></pre>
<h3 id="将错误输出到标准输出-stdout"><a class="header" href="#将错误输出到标准输出-stdout">将错误输出到标准输出 stdout</a></h3>
<p>默认的错误会输出到标准错误输出 <code>stderr</code>,下面我们通过自定的配置来让错误输出到标准输出 <code>stdout</code></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use env_logger::{Builder, Target};
fn main() {
Builder::new()
.target(Target::Stdout)
.init();
log::error!(&quot;This error has been printed to Stdout&quot;);
}
</code></pre></pre>
<h3 id="使用自定义-logger"><a class="header" href="#使用自定义-logger">使用自定义 logger</a></h3>
<p>下面的代码将实现一个自定义 logger <code>ConsoleLogger</code>,输出到标准输出 <code>stdout</code>。为了使用日志宏,<code>ConsoleLogger</code> 需要实现 <a href="https://docs.rs/log/*/log/trait.Log.html">log::Log</a> 特征,然后使用 <a href="https://docs.rs/log/*/log/fn.set_logger.html">log::set_logger</a> 来安装使用。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use log::{Record, Level, Metadata, LevelFilter, SetLoggerError};
static CONSOLE_LOGGER: ConsoleLogger = ConsoleLogger;
struct ConsoleLogger;
impl log::Log for ConsoleLogger {
fn enabled(&amp;self, metadata: &amp;Metadata) -&gt; bool {
metadata.level() &lt;= Level::Info
}
fn log(&amp;self, record: &amp;Record) {
if self.enabled(record.metadata()) {
println!(&quot;Rust says: {} - {}&quot;, record.level(), record.args());
}
}
fn flush(&amp;self) {}
}
fn main() -&gt; Result&lt;(), SetLoggerError&gt; {
log::set_logger(&amp;CONSOLE_LOGGER)?;
log::set_max_level(LevelFilter::Info);
log::info!(&quot;hello log&quot;);
log::warn!(&quot;warning&quot;);
log::error!(&quot;oops&quot;);
Ok(())
}
</code></pre></pre>
<h3 id="输出到-unix-syslog"><a class="header" href="#输出到-unix-syslog">输出到 Unix syslog</a></h3>
<p>下面的代码将使用 <a href="https://docs.rs/crate/syslog/6.0.1">syslog</a> 包将日志输出到 <a href="https://www.gnu.org/software/libc/manual/html_node/Overview-of-Syslog.html">Unix Syslog</a>.</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">#[cfg(target_os = &quot;linux&quot;)]
#[cfg(target_os = &quot;linux&quot;)]
use syslog::{Facility, Error};
#[cfg(target_os = &quot;linux&quot;)]
fn main() -&gt; Result&lt;(), Error&gt; {
// 初始化 logger
syslog::init(Facility::LOG_USER,
log::LevelFilter::Debug,
// 可选的应用名称
Some(&quot;My app name&quot;))?;
log::debug!(&quot;this is a debug {}&quot;, &quot;message&quot;);
log::error!(&quot;this is an error!&quot;);
Ok(())
}
#[cfg(not(target_os = &quot;linux&quot;))]
fn main() {
println!(&quot;So far, only Linux systems are supported.&quot;);
}
</code></pre></pre>
<h2 id="tracing"><a class="header" href="#tracing">tracing</a></h2>
<p>@todo</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="配置日志"><a class="header" href="#配置日志">配置日志</a></h1>
<h3 id="为每个模块开启独立的日志级别"><a class="header" href="#为每个模块开启独立的日志级别">为每个模块开启独立的日志级别</a></h3>
<p>下面代码创建了模块 <code>foo</code> 和嵌套模块 <code>foo::bar</code>,并通过 <a href="https://docs.rs/env_logger/*/env_logger/#enabling-logging">RUST_LOG</a> 环境变量对各自的日志级别进行了控制。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">mod foo {
mod bar {
pub fn run() {
log::warn!(&quot;[bar] warn&quot;);
log::info!(&quot;[bar] info&quot;);
log::debug!(&quot;[bar] debug&quot;);
}
}
pub fn run() {
log::warn!(&quot;[foo] warn&quot;);
log::info!(&quot;[foo] info&quot;);
log::debug!(&quot;[foo] debug&quot;);
bar::run();
}
}
fn main() {
env_logger::init();
log::warn!(&quot;[root] warn&quot;);
log::info!(&quot;[root] info&quot;);
log::debug!(&quot;[root] debug&quot;);
foo::run();
}
</code></pre></pre>
<p>要让环境变量生效,首先需要通过 <code>env_logger::init()</code> 开启相关的支持。然后通过以下命令来运行程序:</p>
<pre><code class="language-shell">RUST_LOG=&quot;warn,test::foo=info,test::foo::bar=debug&quot; ./test
</code></pre>
<p>此时的默认日志级别被设置为 <code>warn</code>,但我们还将 <code>foo</code> 模块级别设置为 <code>info</code>, <code>foo::bar</code> 模块日志级别设置为 <code>debug</code></p>
<pre><code class="language-bash">WARN:test: [root] warn
WARN:test::foo: [foo] warn
INFO:test::foo: [foo] info
WARN:test::foo::bar: [bar] warn
INFO:test::foo::bar: [bar] info
DEBUG:test::foo::bar: [bar] debug
</code></pre>
<h3 id="使用自定义环境变量来设置日志"><a class="header" href="#使用自定义环境变量来设置日志">使用自定义环境变量来设置日志</a></h3>
<p><a href="https://docs.rs/env_logger/*/env_logger/struct.Builder.html">Builder</a> 将对日志进行配置,以下代码使用 <code>MY_APP_LOG</code> 来替代 <code>RUST_LOG</code> 环境变量:</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::env;
use env_logger::Builder;
fn main() {
Builder::new()
.parse(&amp;env::var(&quot;MY_APP_LOG&quot;).unwrap_or_default())
.init();
log::info!(&quot;informational message&quot;);
log::warn!(&quot;warning message&quot;);
log::error!(&quot;this is an error {}&quot;, &quot;message&quot;);
}
</code></pre></pre>
<h3 id="在日志中包含时间戳"><a class="header" href="#在日志中包含时间戳">在日志中包含时间戳</a></h3>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::io::Write;
use chrono::Local;
use env_logger::Builder;
use log::LevelFilter;
fn main() {
Builder::new()
.format(|buf, record| {
writeln!(buf,
&quot;{} [{}] - {}&quot;,
Local::now().format(&quot;%Y-%m-%dT%H:%M:%S&quot;),
record.level(),
record.args()
)
})
.filter(None, LevelFilter::Info)
.init();
log::warn!(&quot;warn&quot;);
log::info!(&quot;info&quot;);
log::debug!(&quot;debug&quot;);
}
</code></pre></pre>
<p>以下是 <code>stderr</code> 的输出:</p>
<pre><code class="language-shell">2022-03-22T21:57:06 [WARN] - warn
2022-03-22T21:57:06 [INFO] - info
</code></pre>
<h3 id="将日志输出到指定文件"><a class="header" href="#将日志输出到指定文件">将日志输出到指定文件</a></h3>
<p><a href="https://docs.rs/log4rs/">log4rs</a> 可以帮我们将日志输出指定的位置,它可以使用外部 YAML 文件或 <code>builder</code> 的方式进行配置。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use log::LevelFilter;
use log4rs::append::file::FileAppender;
use log4rs::encode::pattern::PatternEncoder;
use log4rs::config::{Appender, Config, Root};
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> LogConfig(log4rs::config::Errors);
</span><span class="boring"> SetLogger(log::SetLoggerError);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
// 创建日志配置,并指定输出的位置
let logfile = FileAppender::builder()
// 编码模式的详情参见: https://docs.rs/log4rs/1.0.0/log4rs/encode/pattern/index.html
.encoder(Box::new(PatternEncoder::new(&quot;{l} - {m}\n&quot;)))
.build(&quot;log/output.log&quot;)?;
let config = Config::builder()
.appender(Appender::builder().build(&quot;logfile&quot;, Box::new(logfile)))
.build(Root::builder()
.appender(&quot;logfile&quot;)
.build(LevelFilter::Info))?;
log4rs::init_config(config)?;
log::info!(&quot;Hello, world!&quot;);
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="版本号"><a class="header" href="#版本号">版本号</a></h1>
<h3 id="解析并增加版本号"><a class="header" href="#解析并增加版本号">解析并增加版本号</a></h3>
<p>下面例子使用 <a href="https://docs.rs/semver/*/semver/struct.Version.html#method.parse">Version::parse</a> 将一个字符串转换成 <a href="https://docs.rs/semver/*/semver/struct.Version.html">semver::Version</a> 版本号,然后将它的 patch, minor, major 版本号都增加 1。</p>
<p>注意,为了符合<a href="http://semver.org">语义化版本的说明</a>,增加 <code>minor</code> 版本时,<code>patch</code> 版本会被重设为 <code>0</code>,当增加 <code>major</code> 版本时,<code>minor</code><code>patch</code> 都将被重设为 <code>0</code></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use semver::{Version, SemVerError};
fn main() -&gt; Result&lt;(), SemVerError&gt; {
let mut parsed_version = Version::parse(&quot;0.2.6&quot;)?;
assert_eq!(
parsed_version,
Version {
major: 0,
minor: 2,
patch: 6,
pre: vec![],
build: vec![],
}
);
parsed_version.increment_patch();
assert_eq!(parsed_version.to_string(), &quot;0.2.7&quot;);
println!(&quot;New patch release: v{}&quot;, parsed_version);
parsed_version.increment_minor();
assert_eq!(parsed_version.to_string(), &quot;0.3.0&quot;);
println!(&quot;New minor release: v{}&quot;, parsed_version);
parsed_version.increment_major();
assert_eq!(parsed_version.to_string(), &quot;1.0.0&quot;);
println!(&quot;New major release: v{}&quot;, parsed_version);
Ok(())
}
</code></pre></pre>
<h3 id="解析一个复杂的版本号字符串"><a class="header" href="#解析一个复杂的版本号字符串">解析一个复杂的版本号字符串</a></h3>
<p>这里的版本号字符串还将包含 <code>SemVer</code> 中定义的预发布和构建元信息。</p>
<p>值得注意的是,为了符合 <code>SemVer</code> 的规则,构建元信息虽然会被解析,但是在做版本号比较时,该信息会被忽略。换而言之,即使两个版本号的构建字符串不同,它们的版本号依然可能相同。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use semver::{Identifier, Version, SemVerError};
fn main() -&gt; Result&lt;(), SemVerError&gt; {
let version_str = &quot;1.0.49-125+g72ee7853&quot;;
let parsed_version = Version::parse(version_str)?;
assert_eq!(
parsed_version,
Version {
major: 1,
minor: 0,
patch: 49,
pre: vec![Identifier::Numeric(125)],
build: vec![],
}
);
assert_eq!(
parsed_version.build,
vec![Identifier::AlphaNumeric(String::from(&quot;g72ee7853&quot;))]
);
let serialized_version = parsed_version.to_string();
assert_eq!(&amp;serialized_version, version_str);
Ok(())
}
</code></pre></pre>
<h3 id="检查给定的版本号是否是预发布"><a class="header" href="#检查给定的版本号是否是预发布">检查给定的版本号是否是预发布</a></h3>
<p>下面例子给出两个版本号,然后通过 <a href="https://docs.rs/semver/1.0.7/semver/struct.Version.html#method.is_prerelease">is_prerelease</a> 判断哪个是预发布的版本号。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use semver::{Version, SemVerError};
fn main() -&gt; Result&lt;(), SemVerError&gt; {
let version_1 = Version::parse(&quot;1.0.0-alpha&quot;)?;
let version_2 = Version::parse(&quot;1.0.0&quot;)?;
assert!(version_1.is_prerelease());
assert!(!version_2.is_prerelease());
Ok(())
}
</code></pre></pre>
<h3 id="找出给定范围内的最新版本"><a class="header" href="#找出给定范围内的最新版本">找出给定范围内的最新版本</a></h3>
<p>下面例子给出了一个版本号列表,我们需要找到其中最新的版本。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use semver::{Version, VersionReq};
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> SemVer(semver::SemVerError);
</span><span class="boring"> SemVerReq(semver::ReqParseError);
</span><span class="boring"> }
</span>3}
fn find_max_matching_version&lt;'a, I&gt;(version_req_str: &amp;str, iterable: I) -&gt; Result&lt;Option&lt;Version&gt;&gt;
where
I: IntoIterator&lt;Item = &amp;'a str&gt;,
{
let vreq = VersionReq::parse(version_req_str)?;
Ok(
iterable
.into_iter()
.filter_map(|s| Version::parse(s).ok())
.filter(|s| vreq.matches(s))
.max(),
)
}
fn main() -&gt; Result&lt;()&gt; {
assert_eq!(
find_max_matching_version(&quot;&lt;= 1.0.0&quot;, vec![&quot;0.9.0&quot;, &quot;1.0.0&quot;, &quot;1.0.1&quot;])?,
Some(Version::parse(&quot;1.0.0&quot;)?)
);
assert_eq!(
find_max_matching_version(
&quot;&gt;1.2.3-alpha.3&quot;,
vec![
&quot;1.2.3-alpha.3&quot;,
&quot;1.2.3-alpha.4&quot;,
&quot;1.2.3-alpha.10&quot;,
&quot;1.2.3-beta.4&quot;,
&quot;3.4.5-alpha.9&quot;,
]
)?,
Some(Version::parse(&quot;1.2.3-beta.4&quot;)?)
);
Ok(())
}
</code></pre></pre>
<h3 id="检查外部命令的版本号兼容性"><a class="header" href="#检查外部命令的版本号兼容性">检查外部命令的版本号兼容性</a></h3>
<p>下面将通过 <a href="https://doc.rust-lang.org/std/process/struct.Command.html">Command</a> 来执行系统命令 <code>git --version</code>,并对该系统命令返回的 <code>git</code> 版本号进行解析。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use std::process::Command;
use semver::{Version, VersionReq};
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> Utf8(std::string::FromUtf8Error);
</span><span class="boring"> SemVer(semver::SemVerError);
</span><span class="boring"> SemVerReq(semver::ReqParseError);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
let version_constraint = &quot;&gt; 1.12.0&quot;;
let version_test = VersionReq::parse(version_constraint)?;
let output = Command::new(&quot;git&quot;).arg(&quot;--version&quot;).output()?;
if !output.status.success() {
error_chain::bail!(&quot;Command executed with failing error code&quot;);
}
let stdout = String::from_utf8(output.stdout)?;
let version = stdout.split(&quot; &quot;).last().ok_or_else(|| {
&quot;Invalid command output&quot;
})?;
let parsed_version = Version::parse(version)?;
if !version_test.matches(&amp;parsed_version) {
error_chain::bail!(&quot;Command version lower than minimum supported version (found {}, need {})&quot;,
parsed_version, version_constraint);
}
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="构建时工具"><a class="header" href="#构建时工具">构建时工具</a></h1>
<p>本章节的内容是关于构建工具的,如果大家没有听说过 <code>build.rs</code> 文件,强烈建议先看看<a href="https://course.rs/cargo/reference/build-script/intro.html">这里</a>了解下何为构建工具。</p>
<h3 id="编译并静态链接一个-c-库"><a class="header" href="#编译并静态链接一个-c-库">编译并静态链接一个 C 库</a></h3>
<p><a href="https://docs.rs/cc/latest/cc/">cc</a> 包能帮助我们更好地跟 C/C++/汇编进行交互:它提供了简单的 API 可以将外部的库编译成静态库( .a ),然后通过 <code>rustc</code> 进行静态链接。</p>
<p>下面的例子中,我们将在 Rust 代码中使用 C 的代码: <em>src/hello.c</em>。在开始编译 Rust 的项目代码前,<code>build.rs</code> 构建脚本将先被执行。通过 cc 包,一个静态的库可以被生成( <em>libhello.a</em> ),然后该库将被 Rust的代码所使用通过 <code>extern</code> 声明外部函数签名的方式来使用。</p>
<p>由于例子中的 C 代码很简单,因此只需要将一个文件传递给 <a href="https://docs.rs/cc/*/cc/struct.Build.html">cc::Build</a>。如果大家需要更复杂的构建,<code>cc::Build</code> 还提供了通过 <a href="https://docs.rs/cc/*/cc/struct.Build.html#method.include">include</a> 来包含路径的方式,以及额外的编译标志( <a href="https://docs.rs/cc/1.0.73/cc/struct.Build.html#method.flag">flags</a> )。</p>
<p><em>Cargo.toml</em></p>
<pre><code class="language-toml">[package]
...
build = &quot;build.rs&quot;
[build-dependencies]
cc = &quot;1&quot;
[dependencies]
error-chain = &quot;0.11&quot;
</code></pre>
<p><em>build.rs</em></p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
cc::Build::new()
.file(&quot;src/hello.c&quot;)
.compile(&quot;hello&quot;); // outputs `libhello.a`
}
</code></pre></pre>
<p><em>src/hello.c</em></p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
void hello() {
printf(&quot;Hello from C!\n&quot;);
}
void greet(const char* name) {
printf(&quot;Hello, %s!\n&quot;, name);
}
</code></pre>
<p><em>src/main.rs</em></p>
<pre><pre class="playground"><code class="language-rust edition2021">use error_chain::error_chain;
use std::ffi::CString;
use std::os::raw::c_char;
error_chain! {
foreign_links {
NulError(::std::ffi::NulError);
Io(::std::io::Error);
}
}
fn prompt(s: &amp;str) -&gt; Result&lt;String&gt; {
use std::io::Write;
print!(&quot;{}&quot;, s);
std::io::stdout().flush()?;
let mut input = String::new();
std::io::stdin().read_line(&amp;mut input)?;
Ok(input.trim().to_string())
}
extern {
fn hello();
fn greet(name: *const c_char);
}
fn main() -&gt; Result&lt;()&gt; {
unsafe { hello() }
let name = prompt(&quot;What's your name? &quot;)?;
let c_name = CString::new(name)?;
unsafe { greet(c_name.as_ptr()) }
Ok(())
}
</code></pre></pre>
<h3 id="编译并静态链接一个-c-库-1"><a class="header" href="#编译并静态链接一个-c-库-1">编译并静态链接一个 C++ 库</a></h3>
<p>链接到 C++ 库跟之前的方式非常相似。主要的区别在于链接到 C++ 库时,你需要通过构建方法 <a href="https://docs.rs/cc/*/cc/struct.Build.html#method.cpp">cpp(true)</a> 来指定一个 C++ 编译器,然后在 C++ 的代码顶部添加 <code>extern &quot;C&quot;</code> 来阻止 C++ 编译器对库名进行名称重整( name mangling )。</p>
<p><em>Cargo.toml</em></p>
<pre><code class="language-toml">[package]
...
build = &quot;build.rs&quot;
[build-dependencies]
cc = &quot;1&quot;
</code></pre>
<p><em>build.rs</em></p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
cc::Build::new()
.cpp(true)
.file(&quot;src/foo.cpp&quot;)
.compile(&quot;foo&quot;);
}
</code></pre></pre>
<p><em>src/foo.cpp</em></p>
<pre><code class="language-c++">extern &quot;C&quot; {
int multiply(int x, int y);
}
int multiply(int x, int y) {
return x*y;
}
</code></pre>
<p><em>src/main.rs</em></p>
<pre><pre class="playground"><code class="language-rust edition2021">extern {
fn multiply(x : i32, y : i32) -&gt; i32;
}
fn main(){
unsafe {
println!(&quot;{}&quot;, multiply(5,7));
}
}
</code></pre></pre>
<h3 id="为-c-库创建自定义的-define"><a class="header" href="#为-c-库创建自定义的-define">为 C 库创建自定义的 define</a></h3>
<p><a href="https://docs.rs/cc/*/cc/struct.Build.html#method.define">cc::Build::define</a> 可以让我们使用自定义的 define 来构建 C 库。</p>
<p>以下示例在构建脚本 <code>build.rs</code> 中动态定义了一个 define然后在运行时打印出 <strong>Welcome to foo - version 1.0.2</strong>。Cargo 会设置一些<a href="https://doc.rust-lang.org/cargo/reference/environment-variables.html">环境变量</a>,它们对于自定义的 define 会有所帮助。</p>
<p><em>Cargo.toml</em></p>
<pre><code class="language-toml">[package]
...
version = &quot;1.0.2&quot;
build = &quot;build.rs&quot;
[build-dependencies]
cc = &quot;1&quot;
</code></pre>
<p><em>build.rs</em></p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
cc::Build::new()
.define(&quot;APP_NAME&quot;, &quot;\&quot;foo\&quot;&quot;)
.define(&quot;VERSION&quot;, format!(&quot;\&quot;{}\&quot;&quot;, env!(&quot;CARGO_PKG_VERSION&quot;)).as_str())
.define(&quot;WELCOME&quot;, None)
.file(&quot;src/foo.c&quot;)
.compile(&quot;foo&quot;);
}
</code></pre></pre>
<p><em>src/foo.c</em></p>
<pre><code class="language-C">#include &lt;stdio.h&gt;
void print_app_info() {
#ifdef WELCOME
printf(&quot;Welcome to &quot;);
#endif
printf(&quot;%s - version %s\n&quot;, APP_NAME, VERSION);
}
</code></pre>
<p><em>src/main.rs</em></p>
<pre><pre class="playground"><code class="language-rust edition2021">extern {
fn print_app_info();
}
fn main(){
unsafe {
print_app_info();
}
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="字符编码"><a class="header" href="#字符编码">字符编码</a></h1>
<h3 id="百分号编码-percent-encoding-"><a class="header" href="#百分号编码-percent-encoding-">百分号编码( Percent encoding )</a></h3>
<p><a href="https://en.wikipedia.org/wiki/Percent-encoding">百分号编码</a>又称 URL 编码。</p>
<p><a href="https://docs.rs/crate/percent-encoding/2.1.0">percent-encoding</a> 包提供了两个函数:<code>utf8_percent_encode</code> 函数用于编码、<code>percent_decode</code> 用于解码。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use percent_encoding::{utf8_percent_encode, percent_decode, AsciiSet, CONTROLS};
use std::str::Utf8Error;
/// https://url.spec.whatwg.org/#fragment-percent-encode-set
const FRAGMENT: &amp;AsciiSet = &amp;CONTROLS.add(b' ').add(b'&quot;').add(b'&lt;').add(b'&gt;').add(b'`');
fn main() -&gt; Result&lt;(), Utf8Error&gt; {
let input = &quot;confident, productive systems programming&quot;;
let iter = utf8_percent_encode(input, FRAGMENT);
// 将元素类型为 &amp;str 的迭代器收集为 String 类型
let encoded: String = iter.collect();
assert_eq!(encoded, &quot;confident,%20productive%20systems%20programming&quot;);
let iter = percent_decode(encoded.as_bytes());
let decoded = iter.decode_utf8()?;
assert_eq!(decoded, &quot;confident, productive systems programming&quot;);
Ok(())
}
</code></pre></pre>
<p>该编码集定义了哪些字符( 特别是非 ASCII 和控制字符 )需要被百分比编码。具体的选择取决于上下文,例如 <code>url</code> 会对 URL 路径中的 <code>?</code> 进行编码,但是在路径后的查询字符串中,并不会进行编码。</p>
<h3 id="将字符串编码为-applicationx-www-form-urlencoded"><a class="header" href="#将字符串编码为-applicationx-www-form-urlencoded">将字符串编码为 application/x-www-form-urlencoded</a></h3>
<p>使用 <a href="https://docs.rs/form_urlencoded/1.0.1/form_urlencoded/fn.byte_serialize.html">form_urlencoded::byte_serialize</a> 函数将一个字符串编码成 <a href="https://url.spec.whatwg.org/#application/x-www-form-urlencoded">application/x-www-form-urlencoded</a> 格式,然后再使用 <a href="https://docs.rs/form_urlencoded/1.0.1/form_urlencoded/fn.parse.html">form_urlencoded::parse</a> 对其进行解码。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use url::form_urlencoded::{byte_serialize, parse};
fn main() {
let urlencoded: String = byte_serialize(&quot;What is ❤?&quot;.as_bytes()).collect();
assert_eq!(urlencoded, &quot;What+is+%E2%9D%A4%3F&quot;);
println!(&quot;urlencoded:'{}'&quot;, urlencoded);
let decoded: String = parse(urlencoded.as_bytes())
.map(|(key, val)| [key, val].concat())
.collect();
assert_eq!(decoded, &quot;What is ❤?&quot;);
println!(&quot;decoded:'{}'&quot;, decoded);
}
</code></pre></pre>
<h3 id="十六进制编解码"><a class="header" href="#十六进制编解码">十六进制编解码</a></h3>
<p><a href="https://docs.rs/data-encoding/*/data_encoding/">data_encoding</a> 可以将一个字符串编码成十六进制字符串,反之亦然。</p>
<p>下面的例子将 <code>&amp;[u8]</code> 转换成十六进制等效形式,然后与期待的值进行比较。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use data_encoding::{HEXUPPER, DecodeError};
fn main() -&gt; Result&lt;(), DecodeError&gt; {
let original = b&quot;The quick brown fox jumps over the lazy dog.&quot;;
let expected = &quot;54686520717569636B2062726F776E20666F78206A756D7073206F76\
657220746865206C617A7920646F672E&quot;;
let encoded = HEXUPPER.encode(original);
assert_eq!(encoded, expected);
let decoded = HEXUPPER.decode(&amp;encoded.into_bytes())?;
assert_eq!(&amp;decoded[..], &amp;original[..]);
Ok(())
}
</code></pre></pre>
<h3 id="base64-编解码"><a class="header" href="#base64-编解码">Base64 编解码</a></h3>
<p><a href="https://docs.rs/base64/0.13.0/base64/index.html">base64</a> 可以把一个字节切片编码成 <code>base64</code> String。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use error_chain::error_chain;
use std::str;
use base64::{encode, decode};
error_chain! {
foreign_links {
Base64(base64::DecodeError);
Utf8Error(str::Utf8Error);
}
}
fn main() -&gt; Result&lt;()&gt; {
// 将 `&amp;str` 转换成 `&amp;[u8; N]`
let hello = b&quot;hello rustaceans&quot;;
let encoded = encode(hello);
let decoded = decode(&amp;encoded)?;
println!(&quot;origin: {}&quot;, str::from_utf8(hello)?);
println!(&quot;base64 encoded: {}&quot;, encoded);
println!(&quot;back to origin: {}&quot;, str::from_utf8(&amp;decoded)?);
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="csv"><a class="header" href="#csv">CSV</a></h1>
<h2 id="读取-csv-记录"><a class="header" href="#读取-csv-记录">读取 CSV 记录</a></h2>
<p>我们可以将标准的 CSV 记录值读取到 <a href="https://docs.rs/csv/*/csv/struct.StringRecord.html">csv::StringRecord</a> 中,但是该数据结构期待合法的 UTF8 数据行,你还可以使用 <a href="https://docs.rs/csv/1.1.6/csv/struct.ByteRecord.html">csv::ByteRecord</a> 来读取非 UTF8 数据。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use csv::Error;
fn main() -&gt; Result&lt;(), Error&gt; {
let csv = &quot;year,make,model,description
1948,Porsche,356,Luxury sports car
1967,Ford,Mustang fastback 1967,American car&quot;;
let mut reader = csv::Reader::from_reader(csv.as_bytes());
for record in reader.records() {
let record = record?;
println!(
&quot;In {}, {} built the {} model. It is a {}.&quot;,
&amp;record[0],
&amp;record[1],
&amp;record[2],
&amp;record[3]
);
}
Ok(())
}
</code></pre></pre>
<p>还可以使用 <a href="https://docs.rs/serde/1.0.136/serde/"><code>serde</code></a> 将数据反序列化成一个强类型的结构体。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use serde::Deserialize;
#[derive(Deserialize)]
struct Record {
year: u16,
make: String,
model: String,
description: String,
}
fn main() -&gt; Result&lt;(), csv::Error&gt; {
let csv = &quot;year,make,model,description
1948,Porsche,356,Luxury sports car
1967,Ford,Mustang fastback 1967,American car&quot;;
let mut reader = csv::Reader::from_reader(csv.as_bytes());
for record in reader.deserialize() {
let record: Record = record?;
println!(
&quot;In {}, {} built the {} model. It is a {}.&quot;,
record.year,
record.make,
record.model,
record.description
);
}
Ok(())
}
</code></pre></pre>
<h3 id="读取使用了不同分隔符的-csv-记录"><a class="header" href="#读取使用了不同分隔符的-csv-记录">读取使用了不同分隔符的 CSV 记录</a></h3>
<p>下面的例子将读取使用了 <code>tab</code> 作为分隔符的 CSV 记录。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use csv::Error;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Record {
name: String,
place: String,
#[serde(deserialize_with = &quot;csv::invalid_option&quot;)]
id: Option&lt;u64&gt;,
}
use csv::ReaderBuilder;
fn main() -&gt; Result&lt;(), Error&gt; {
let data = &quot;name\tplace\tid
Mark\tMelbourne\t46
Ashley\tZurich\t92&quot;;
let mut reader = ReaderBuilder::new().delimiter(b'\t').from_reader(data.as_bytes());
for result in reader.deserialize::&lt;Record&gt;() {
println!(&quot;{:?}&quot;, result?);
}
Ok(())
}
</code></pre></pre>
<h3 id="基于给定条件来过滤-csv-记录"><a class="header" href="#基于给定条件来过滤-csv-记录">基于给定条件来过滤 CSV 记录</a></h3>
<pre><pre class="playground"><code class="language-rust editable edition2021">use error_chain::error_chain;
use std::io;
error_chain!{
foreign_links {
Io(std::io::Error);
CsvError(csv::Error);
}
}
fn main() -&gt; Result&lt;()&gt; {
let query = &quot;CA&quot;;
let data = &quot;\
City,State,Population,Latitude,Longitude
Kenai,AK,7610,60.5544444,-151.2583333
Oakman,AL,,33.7133333,-87.3886111
Sandfort,AL,,32.3380556,-85.2233333
West Hollywood,CA,37031,34.0900000,-118.3608333&quot;;
let mut rdr = csv::ReaderBuilder::new().from_reader(data.as_bytes());
let mut wtr = csv::Writer::from_writer(io::stdout());
wtr.write_record(rdr.headers()?)?;
for result in rdr.records() {
let record = result?;
if record.iter().any(|field| field == query) {
wtr.write_record(&amp;record)?;
}
}
wtr.flush()?;
Ok(())
}
</code></pre></pre>
<h3 id="序列化为-csv"><a class="header" href="#序列化为-csv">序列化为 CSV</a></h3>
<p>下面例子展示了如何将 Rust 类型序列化为 CSV。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::io;
fn main() -&gt; Result&lt;()&gt; {
let mut wtr = csv::Writer::from_writer(io::stdout());
wtr.write_record(&amp;[&quot;Name&quot;, &quot;Place&quot;, &quot;ID&quot;])?;
wtr.serialize((&quot;Mark&quot;, &quot;Sydney&quot;, 87))?;
wtr.serialize((&quot;Ashley&quot;, &quot;Dublin&quot;, 32))?;
wtr.serialize((&quot;Akshat&quot;, &quot;Delhi&quot;, 11))?;
wtr.flush()?;
Ok(())
}
</code></pre></pre>
<h3 id="使用-serde-序列化为-csv"><a class="header" href="#使用-serde-序列化为-csv">使用 serde 序列化为 CSV</a></h3>
<p>下面例子将自定义数据结构通过 <code>serde</code> 序列化 CSV。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use error_chain::error_chain;
use serde::Serialize;
use std::io;
error_chain! {
foreign_links {
IOError(std::io::Error);
CSVError(csv::Error);
}
}
#[derive(Serialize)]
struct Record&lt;'a&gt; {
name: &amp;'a str,
place: &amp;'a str,
id: u64,
}
fn main() -&gt; Result&lt;()&gt; {
let mut wtr = csv::Writer::from_writer(io::stdout());
let rec1 = Record { name: &quot;Mark&quot;, place: &quot;Melbourne&quot;, id: 56};
let rec2 = Record { name: &quot;Ashley&quot;, place: &quot;Sydney&quot;, id: 64};
let rec3 = Record { name: &quot;Akshat&quot;, place: &quot;Delhi&quot;, id: 98};
wtr.serialize(rec1)?;
wtr.serialize(rec2)?;
wtr.serialize(rec3)?;
wtr.flush()?;
Ok(())
}
</code></pre></pre>
<h3 id="csv-列转换"><a class="header" href="#csv-列转换">CSV 列转换</a></h3>
<p>下面代码将包含有颜色名和十六进制颜色的 CSV 文件转换为包含颜色名和 rgb 颜色。这里使用 <code>csv</code> 包对 CSV 文件进行读写,然后用 <code>serde</code> 进行序列化和反序列化。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>use csv::{Reader, Writer};
use serde::{de, Deserialize, Deserializer};
use std::str::FromStr;
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> CsvError(csv::Error);
</span><span class="boring"> ParseInt(std::num::ParseIntError);
</span><span class="boring"> CsvInnerError(csv::IntoInnerError&lt;Writer&lt;Vec&lt;u8&gt;&gt;&gt;);
</span><span class="boring"> IO(std::fmt::Error);
</span><span class="boring"> UTF8(std::string::FromUtf8Error);
</span><span class="boring"> }
</span><span class="boring">}
</span>
#[derive(Debug)]
struct HexColor {
red: u8,
green: u8,
blue: u8,
}
#[derive(Debug, Deserialize)]
struct Row {
color_name: String,
color: HexColor,
}
impl FromStr for HexColor {
type Err = Error;
fn from_str(hex_color: &amp;str) -&gt; std::result::Result&lt;Self, Self::Err&gt; {
let trimmed = hex_color.trim_matches('#');
if trimmed.len() != 6 {
Err(&quot;Invalid length of hex string&quot;.into())
} else {
Ok(HexColor {
red: u8::from_str_radix(&amp;trimmed[..2], 16)?,
green: u8::from_str_radix(&amp;trimmed[2..4], 16)?,
blue: u8::from_str_radix(&amp;trimmed[4..6], 16)?,
})
}
}
}
impl&lt;'de&gt; Deserialize&lt;'de&gt; for HexColor {
fn deserialize&lt;D&gt;(deserializer: D) -&gt; std::result::Result&lt;Self, D::Error&gt;
where
D: Deserializer&lt;'de&gt;,
{
let s = String::deserialize(deserializer)?;
FromStr::from_str(&amp;s).map_err(de::Error::custom)
}
}
fn main() -&gt; Result&lt;()&gt; {
let data = &quot;color_name,color
red,#ff0000
green,#00ff00
blue,#0000FF
periwinkle,#ccccff
magenta,#ff00ff&quot;
.to_owned();
let mut out = Writer::from_writer(vec![]);
let mut reader = Reader::from_reader(data.as_bytes());
for result in reader.deserialize::&lt;Row&gt;() {
let res = result?;
out.serialize((
res.color_name,
res.color.red,
res.color.green,
res.color.blue,
))?;
}
let written = String::from_utf8(out.into_inner()?)?;
assert_eq!(Some(&quot;magenta,255,0,255&quot;), written.lines().last());
println!(&quot;{}&quot;, written);
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="结构化数据"><a class="header" href="#结构化数据">结构化数据</a></h1>
<h3 id="序列和反序列非结构化的json"><a class="header" href="#序列和反序列非结构化的json">序列和反序列非结构化的JSON</a></h3>
<p><a href="encoding/">serde_json</a> 是一个高性能的 JSON 包,它支持我们在不声明结构体的情况下,去解析 JSON。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use serde_json::json;
use serde_json::{Value, Error};
fn main() -&gt; Result&lt;(), Error&gt; {
let j = r#&quot;{
&quot;userid&quot;: 103609,
&quot;verified&quot;: true,
&quot;access_privileges&quot;: [
&quot;user&quot;,
&quot;admin&quot;
]
}&quot;#;
let parsed: Value = serde_json::from_str(j)?;
let expected = json!({
&quot;userid&quot;: 103609,
&quot;verified&quot;: true,
&quot;access_privileges&quot;: [
&quot;user&quot;,
&quot;admin&quot;
]
});
assert_eq!(parsed, expected);
Ok(())
}
</code></pre></pre>
<h3 id="解析-toml-文件"><a class="header" href="#解析-toml-文件">解析 TOML 文件</a></h3>
<p><a href="https://docs.rs/toml/latest/toml/">toml</a> 包可以将 TOML 文件的内容解析为一个 <code>toml::Value</code> 值,该值能代表任何合法的 TOML 数据。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use toml::{Value, de::Error};
fn main() -&gt; Result&lt;(), Error&gt; {
let toml_content = r#&quot;
[package]
name = &quot;your_package&quot;
version = &quot;0.1.0&quot;
authors = [&quot;You! &lt;you@example.org&gt;&quot;]
[dependencies]
serde = &quot;1.0&quot;
&quot;#;
let package_info: Value = toml::from_str(toml_content)?;
assert_eq!(package_info[&quot;dependencies&quot;][&quot;serde&quot;].as_str(), Some(&quot;1.0&quot;));
assert_eq!(package_info[&quot;package&quot;][&quot;name&quot;].as_str(),
Some(&quot;your_package&quot;));
Ok(())
}
</code></pre></pre>
<p>还可以配合 <a href="encoding/">serde</a> 将 TOML 解析到我们自定义的结构体中:</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use serde::Deserialize;
use toml::de::Error;
use std::collections::HashMap;
#[derive(Deserialize)]
struct Config {
package: Package,
dependencies: HashMap&lt;String, String&gt;,
}
#[derive(Deserialize)]
struct Package {
name: String,
version: String,
authors: Vec&lt;String&gt;,
}
fn main() -&gt; Result&lt;(), Error&gt; {
let toml_content = r#&quot;
[package]
name = &quot;your_package&quot;
version = &quot;0.1.0&quot;
authors = [&quot;You! &lt;you@example.org&gt;&quot;]
[dependencies]
serde = &quot;1.0&quot;
&quot;#;
let package_info: Config = toml::from_str(toml_content)?;
assert_eq!(package_info.package.name, &quot;your_package&quot;);
assert_eq!(package_info.package.version, &quot;0.1.0&quot;);
assert_eq!(package_info.package.authors, vec![&quot;You! &lt;you@example.org&gt;&quot;]);
assert_eq!(package_info.dependencies[&quot;serde&quot;], &quot;1.0&quot;);
Ok(())
}
</code></pre></pre>
<h3 id="使用小端字节序来读写整数"><a class="header" href="#使用小端字节序来读写整数">使用小端字节序来读写整数</a></h3>
<p><a href="https://docs.rs/byteorder/latest/byteorder/">byteorder</a> 在自行接收或发送网络字节流时会非常有用( 除非性能要求高,否则还是建议使用 JSON 等数据协议,不要自己做字节流解析 )。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::Error;
#[derive(Default, PartialEq, Debug)]
struct Payload {
kind: u8,
value: u16,
}
fn main() -&gt; Result&lt;(), Error&gt; {
let original_payload = Payload::default();
let encoded_bytes = encode(&amp;original_payload)?;
let decoded_payload = decode(&amp;encoded_bytes)?;
assert_eq!(original_payload, decoded_payload);
Ok(())
}
fn encode(payload: &amp;Payload) -&gt; Result&lt;Vec&lt;u8&gt;, Error&gt; {
let mut bytes = vec![];
bytes.write_u8(payload.kind)?;
bytes.write_u16::&lt;LittleEndian&gt;(payload.value)?;
Ok(bytes)
}
fn decode(mut bytes: &amp;[u8]) -&gt; Result&lt;Payload, Error&gt; {
let payload = Payload {
kind: bytes.read_u8()?,
value: bytes.read_u16::&lt;LittleEndian&gt;()?,
};
Ok(payload)
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="文件读写"><a class="header" href="#文件读写">文件读写</a></h1>
<h2 id="迭代文件中的内容行"><a class="header" href="#迭代文件中的内容行">迭代文件中的内容行</a></h2>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::fs::File;
use std::io::{Write, BufReader, BufRead, Error};
fn main() -&gt; Result&lt;(), Error&gt; {
let path = &quot;lines.txt&quot;;
// 创建文件
let mut output = File::create(path)?;
// 写入三行内容
write!(output, &quot;Rust\n💖\nFun&quot;)?;
let input = File::open(path)?;
let buffered = BufReader::new(input);
// 迭代文件中的每一行内容line 是字符串
for line in buffered.lines() {
println!(&quot;{}&quot;, line?);
}
Ok(())
}
</code></pre></pre>
<h2 id="避免对同一个文件进行读写"><a class="header" href="#避免对同一个文件进行读写">避免对同一个文件进行读写</a></h2>
<p><a href="https://docs.rs/same-file/latest/same_file/">same_file</a> 可以帮我们识别两个文件是否是相同的。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use same_file::Handle;
use std::fs::File;
use std::io::{BufRead, BufReader, Error, ErrorKind};
use std::path::Path;
fn main() -&gt; Result&lt;(), Error&gt; {
let path_to_read = Path::new(&quot;new.txt&quot;);
// 从标准输出上获取待写入的文件名
let stdout_handle = Handle::stdout()?;
// 将待写入的文件名跟待读取的文件名进行比较
let handle = Handle::from_path(path_to_read)?;
if stdout_handle == handle {
return Err(Error::new(
ErrorKind::Other,
&quot;You are reading and writing to the same file&quot;,
));
} else {
let file = File::open(&amp;path_to_read)?;
let file = BufReader::new(file);
for (num, line) in file.lines().enumerate() {
println!(&quot;{} : {}&quot;, num, line?.to_uppercase());
}
}
Ok(())
}
</code></pre></pre>
<p>以下代码会报错,因为待写入的文件名也是 <em>new.txt</em>,跟待读取的文件名相同</p>
<pre><code class="language-shell">cargo run &gt;&gt; ./new.txt
</code></pre>
<h3 id="使用内存映射访问文件"><a class="header" href="#使用内存映射访问文件">使用内存映射访问文件</a></h3>
<p><a href="https://docs.rs/memmap/">memmap</a> 能创建一个文件的内存映射( memory map ),然后模拟一些非顺序读。</p>
<p>使用内存映射,意味着你将相关的索引加载到内存中,而不是通过 <a href="https://doc.rust-lang.org/std/fs/struct.File.html#method.seek">seek</a> 的方式去访问文件。</p>
<p><a href="https://docs.rs/memmap/*/memmap/struct.Mmap.html#method.map">Mmap::map</a> 函数会假定待映射的文件不会同时被其它进程修改。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use memmap::Mmap;
use std::fs::File;
use std::io::{Write, Error};
fn main() -&gt; Result&lt;(), Error&gt; {
write!(File::create(&quot;content.txt&quot;)?, &quot;My hovercraft is full of eels!&quot;)?;
let file = File::open(&quot;content.txt&quot;)?;
let map = unsafe { Mmap::map(&amp;file)? };
let random_indexes = [0, 1, 2, 19, 22, 10, 11, 29];
assert_eq!(&amp;map[3..13], b&quot;hovercraft&quot;);
let random_bytes: Vec&lt;u8&gt; = random_indexes.iter()
.map(|&amp;idx| map[idx])
.collect();
assert_eq!(&amp;random_bytes[..], b&quot;My loaf!&quot;);
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="目录访问"><a class="header" href="#目录访问">目录访问</a></h1>
<h3 id="获取24小时内被修改过的文件"><a class="header" href="#获取24小时内被修改过的文件">获取24小时内被修改过的文件</a></h3>
<p>通过遍历读取目录中文件的 <a href="https://doc.rust-lang.org/std/fs/struct.Metadata.html#method.modified">Metadata::modified</a> 属性,来获取目标文件名列表。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use error_chain::error_chain;
use std::{env, fs};
error_chain! {
foreign_links {
Io(std::io::Error);
SystemTimeError(std::time::SystemTimeError);
}
}
fn main() -&gt; Result&lt;()&gt; {
let current_dir = env::current_dir()?;
println!(
&quot;Entries modified in the last 24 hours in {:?}:&quot;,
current_dir
);
for entry in fs::read_dir(current_dir)? {
let entry = entry?;
let path = entry.path();
let metadata = fs::metadata(&amp;path)?;
let last_modified = metadata.modified()?.elapsed()?.as_secs();
if last_modified &lt; 24 * 3600 &amp;&amp; metadata.is_file() {
println!(
&quot;Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}&quot;,
last_modified,
metadata.permissions().readonly(),
metadata.len(),
path.file_name().ok_or(&quot;No filename&quot;)?
);
}
}
Ok(())
}
</code></pre></pre>
<h3 id="获取给定路径的-loops"><a class="header" href="#获取给定路径的-loops">获取给定路径的 loops</a></h3>
<p>使用 <a href="files/">same_file::is_same_file</a> 可以检查给定路径的 loopsloop 可以通过以下方式创建:</p>
<pre><code class="language-shell">mkdir -p /tmp/foo/bar/baz
ln -s /tmp/foo/ /tmp/foo/bar/baz/qux
</code></pre>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::io;
use std::path::{Path, PathBuf};
use same_file::is_same_file;
fn contains_loop&lt;P: AsRef&lt;Path&gt;&gt;(path: P) -&gt; io::Result&lt;Option&lt;(PathBuf, PathBuf)&gt;&gt; {
let path = path.as_ref();
let mut path_buf = path.to_path_buf();
while path_buf.pop() {
if is_same_file(&amp;path_buf, path)? {
return Ok(Some((path_buf, path.to_path_buf())));
} else if let Some(looped_paths) = contains_loop(&amp;path_buf)? {
return Ok(Some(looped_paths));
}
}
return Ok(None);
}
fn main() {
assert_eq!(
contains_loop(&quot;/tmp/foo/bar/baz/qux/bar/baz&quot;).unwrap(),
Some((
PathBuf::from(&quot;/tmp/foo&quot;),
PathBuf::from(&quot;/tmp/foo/bar/baz/qux&quot;)
))
);
}
</code></pre></pre>
<h3 id="递归查找重复的文件名"><a class="header" href="#递归查找重复的文件名">递归查找重复的文件名</a></h3>
<p><a href="https://docs.rs/walkdir/latest/walkdir/">walkdir</a> 可以帮助我们遍历指定的目录。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::collections::HashMap;
use walkdir::WalkDir;
fn main() {
let mut filenames = HashMap::new();
// 遍历当前目录
for entry in WalkDir::new(&quot;.&quot;)
.into_iter()
.filter_map(Result::ok)
.filter(|e| !e.file_type().is_dir()) {
let f_name = String::from(entry.file_name().to_string_lossy());
let counter = filenames.entry(f_name.clone()).or_insert(0);
*counter += 1;
if *counter == 2 {
println!(&quot;{}&quot;, f_name);
}
}
}
</code></pre></pre>
<h3 id="递归查找满足条件的所有文件"><a class="header" href="#递归查找满足条件的所有文件">递归查找满足条件的所有文件</a></h3>
<p>下面的代码通过 <a href="https://docs.rs/walkdir/latest/walkdir/">walkdir</a> 来查找当前目录中最近一天内发生过修改的所有文件。</p>
<p><a href="https://docs.rs/walkdir/*/walkdir/struct.WalkDir.html#method.follow_links">follow_links</a><code>true</code> 时,那软链接会被当成正常的文件或目录一样对待,也就是说软链接指向的文件或目录也会被访问和检查。若软链接指向的目标不存在或它是一个 loops就会导致错误的发生。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use walkdir::WalkDir;
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> WalkDir(walkdir::Error);
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> SystemTime(std::time::SystemTimeError);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
for entry in WalkDir::new(&quot;.&quot;)
.follow_links(true)
.into_iter()
.filter_map(|e| e.ok()) {
let f_name = entry.file_name().to_string_lossy();
let sec = entry.metadata()?.modified()?;
if f_name.ends_with(&quot;.json&quot;) &amp;&amp; sec.elapsed()?.as_secs() &lt; 86400 {
println!(&quot;{}&quot;, f_name);
}
}
Ok(())
}
</code></pre></pre>
<h3 id="遍历目录跳过隐藏文件"><a class="header" href="#遍历目录跳过隐藏文件">遍历目录跳过隐藏文件</a></h3>
<p>下面例子使用 <a href="https://docs.rs/walkdir/latest/walkdir/">walkdir</a> 来遍历一个目录,同时跳过隐藏文件 <code>is_not_hidden</code></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use walkdir::{DirEntry, WalkDir};
fn is_not_hidden(entry: &amp;DirEntry) -&gt; bool {
entry
.file_name()
.to_str()
.map(|s| entry.depth() == 0 || !s.starts_with(&quot;.&quot;))
.unwrap_or(false)
}
fn main() {
WalkDir::new(&quot;.&quot;)
.into_iter()
.filter_entry(|e| is_not_hidden(e))
.filter_map(|v| v.ok())
.for_each(|x| println!(&quot;{}&quot;, x.path().display()));
}
</code></pre></pre>
<h3 id="递归计算给定深度的文件大小"><a class="header" href="#递归计算给定深度的文件大小">递归计算给定深度的文件大小</a></h3>
<p>递归访问的深度可以使用 <a href="https://docs.rs/walkdir/*/walkdir/struct.WalkDir.html#method.min_depth">WalkDir::min_depth</a><a href="https://docs.rs/walkdir/2.3.2/walkdir/struct.WalkDir.html#method.max_depth">WalkDir::max_depth</a> 来控制。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use walkdir::WalkDir;
fn main() {
let total_size = WalkDir::new(&quot;.&quot;)
.min_depth(1)
.max_depth(3)
.into_iter()
.filter_map(|entry| entry.ok())
.filter_map(|entry| entry.metadata().ok())
.filter(|metadata| metadata.is_file())
.fold(0, |acc, m| acc + m.len());
println!(&quot;Total size: {} bytes.&quot;, total_size);
}
</code></pre></pre>
<h3 id="递归查找所有-png-文件"><a class="header" href="#递归查找所有-png-文件">递归查找所有 png 文件</a></h3>
<p>例子中使用了 <a href="https://docs.rs/glob/">glob</a> 包,其中的 <code>**</code> 代表当前目录及其所有子目录,例如,<code>/media/**/*.png</code> 代表在 <code>media</code> 和它的所有子目录下查找 png 文件.</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use glob::glob;
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> Glob(glob::GlobError);
</span><span class="boring"> Pattern(glob::PatternError);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
for entry in glob(&quot;**/*.png&quot;)? {
println!(&quot;{}&quot;, entry?.display());
}
Ok(())
}
</code></pre></pre>
<h3 id="查找满足给定正则的所有文件且忽略文件名大小写"><a class="header" href="#查找满足给定正则的所有文件且忽略文件名大小写">查找满足给定正则的所有文件且忽略文件名大小写</a></h3>
<p><a href="https://docs.rs/glob/*/glob/fn.glob_with.html">glob_with</a> 函数可以按照给定的正则表达式进行查找,同时还能使用选项来控制一些匹配设置。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use error_chain::error_chain;
use glob::{glob_with, MatchOptions};
error_chain! {
foreign_links {
Glob(glob::GlobError);
Pattern(glob::PatternError);
}
}
fn main() -&gt; Result&lt;()&gt; {
let options = MatchOptions {
case_sensitive: false,
..Default::default()
};
for entry in glob_with(&quot;/media/img_[0-9]*.png&quot;, options)? {
println!(&quot;{}&quot;, entry?.display());
}
Ok(())
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="全局变量"><a class="header" href="#全局变量">全局变量</a></h1>
<h3 id="使用-lazy_static-在运行期初始化全局变量"><a class="header" href="#使用-lazy_static-在运行期初始化全局变量">使用 lazy_static 在运行期初始化全局变量</a></h3>
<p>下面的例子,我们将使用 <a href="https://docs.rs/lazy_static/latest/lazy_static/">lazy_static</a> 声明一个在运行期初始化( 懒求值 )的 <code>Hashmap</code>,它会被求值一次,然后保存在一个全局的 <code>static</code> 引用之后。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use lazy_static::lazy_static;
use std::collections::HashMap;
lazy_static! {
static ref PRIVILEGES: HashMap&lt;&amp;'static str, Vec&lt;&amp;'static str&gt;&gt; = {
let mut map = HashMap::new();
map.insert(&quot;James&quot;, vec![&quot;user&quot;, &quot;admin&quot;]);
map.insert(&quot;Jim&quot;, vec![&quot;user&quot;]);
map
};
}
fn show_access(name: &amp;str) {
let access = PRIVILEGES.get(name);
println!(&quot;{}: {:?}&quot;, name, access);
}
fn main() {
let access = PRIVILEGES.get(&quot;James&quot;);
println!(&quot;James: {:?}&quot;, access);
show_access(&quot;Jim&quot;);
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="tcpip"><a class="header" href="#tcpip">TCP/IP</a></h1>
<h3 id="监听-tcp-端口"><a class="header" href="#监听-tcp-端口">监听 TCP 端口</a></h3>
<p>以下代码会监听指定的 TCP 端口,并接收一条外部进入的 TCP 连接,然后将读取到的一条信息输出到标准输出( <code>println!</code> )。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::net::{SocketAddrV4, Ipv4Addr, TcpListener};
use std::io::{Read, Error};
fn main() -&gt; Result&lt;(), Error&gt; {
let loopback = Ipv4Addr::new(127, 0, 0, 1);
let socket = SocketAddrV4::new(loopback, 0);
let listener = TcpListener::bind(socket)?;
let port = listener.local_addr()?;
println!(&quot;Listening on {}, access this port to end the program&quot;, port);
let (mut tcp_stream, addr) = listener.accept()?; //block until requested
println!(&quot;Connection received! {:?} is sending data.&quot;, addr);
let mut input = String::new();
let _ = tcp_stream.read_to_string(&amp;mut input)?;
println!(&quot;{:?} says {}&quot;, addr, input);
Ok(())
}
</code></pre></pre>
<h3 id="循环接收进入的-tcp-连接"><a class="header" href="#循环接收进入的-tcp-连接">循环接收进入的 TCP 连接</a></h3>
<p>@todo</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="正则表达式"><a class="header" href="#正则表达式">正则表达式</a></h1>
<h3 id="验证邮件格式并取出--前的信息"><a class="header" href="#验证邮件格式并取出--前的信息">验证邮件格式并取出 @ 前的信息</a></h3>
<p>下面代码使用 <a href="https://docs.rs/regex/latest/regex/">regex</a> 包来验证邮件格式的正确性,然后提取出 <code>@</code> 符号前的所有内容。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use lazy_static::lazy_static;
use regex::Regex;
fn extract_login(input: &amp;str) -&gt; Option&lt;&amp;str&gt; {
lazy_static! {
static ref RE: Regex = Regex::new(r&quot;(?x)
^(?P&lt;login&gt;[^@\s]+)@
([[:word:]]+\.)*
[[:word:]]+$
&quot;).unwrap();
}
RE.captures(input).and_then(|cap| {
cap.name(&quot;login&quot;).map(|login| login.as_str())
})
}
fn main() {
assert_eq!(extract_login(r&quot;I❤email@example.com&quot;), Some(r&quot;I❤email&quot;));
assert_eq!(
extract_login(r&quot;sdf+sdsfsd.as.sdsd@jhkk.d.rl&quot;),
Some(r&quot;sdf+sdsfsd.as.sdsd&quot;)
);
assert_eq!(extract_login(r&quot;More@Than@One@at.com&quot;), None);
assert_eq!(extract_login(r&quot;Not an email@email&quot;), None);
}
</code></pre></pre>
<h3 id="从文本中提出--开头的标签"><a class="header" href="#从文本中提出--开头的标签">从文本中提出 # 开头的标签</a></h3>
<p>例子对标签进行提取、排序和去重。需要注意,下面的标签仅仅是拉丁字母的,如果你要支持更多的字母,可以参考下 <a href="https://github.com/twitter/twitter-text/blob/c9fc09782efe59af4ee82855768cfaf36273e170/java/src/com/twitter/Regex.java#L255">Twitter 的正则语法</a>,友情提示,复杂的多!</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use lazy_static::lazy_static;
use regex::Regex;
use std::collections::HashSet;
fn extract_hashtags(text: &amp;str) -&gt; HashSet&lt;&amp;str&gt; {
lazy_static! {
static ref HASHTAG_REGEX : Regex = Regex::new(
r&quot;\#[a-zA-Z][0-9a-zA-Z_]*&quot;
).unwrap();
}
HASHTAG_REGEX.find_iter(text).map(|mat| mat.as_str()).collect()
}
fn main() {
let tweet = &quot;Hey #world, I just got my new #dog, say hello to Till. #dog #forever #2 #_ &quot;;
let tags = extract_hashtags(tweet);
assert!(tags.contains(&quot;#dog&quot;) &amp;&amp; tags.contains(&quot;#forever&quot;) &amp;&amp; tags.contains(&quot;#world&quot;));
assert_eq!(tags.len(), 3);
}
</code></pre></pre>
<h3 id="从文本中提取出所有手机号"><a class="header" href="#从文本中提取出所有手机号">从文本中提取出所有手机号</a></h3>
<p>[Regex::captures_iter] 可以对字符串型文本进行处理,以获取文本中的多个手机号。下面的例子适用于美国的号码。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use regex::Regex;
use std::fmt;
<span class="boring">error_chain!{
</span><span class="boring"> foreign_links {
</span><span class="boring"> Regex(regex::Error);
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> }
</span><span class="boring">}
</span>
struct PhoneNumber&lt;'a&gt; {
area: &amp;'a str,
exchange: &amp;'a str,
subscriber: &amp;'a str,
}
impl&lt;'a&gt; fmt::Display for PhoneNumber&lt;'a&gt; {
fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
write!(f, &quot;1 ({}) {}-{}&quot;, self.area, self.exchange, self.subscriber)
}
}
fn main() -&gt; Result&lt;()&gt; {
let phone_text = &quot;
+1 505 881 9292 (v) +1 505 778 2212 (c) +1 505 881 9297 (f)
(202) 991 9534
Alex 5553920011
1 (800) 233-2010
1.299.339.1020&quot;;
let re = Regex::new(
r#&quot;(?x)
(?:\+?1)? # Country Code Optional
[\s\.]?
(([2-9]\d{2})|\(([2-9]\d{2})\)) # Area Code
[\s\.\-]?
([2-9]\d{2}) # Exchange Code
[\s\.\-]?
(\d{4}) # Subscriber Number&quot;#,
)?;
let phone_numbers = re.captures_iter(phone_text).filter_map(|cap| {
let groups = (cap.get(2).or(cap.get(3)), cap.get(4), cap.get(5));
match groups {
(Some(area), Some(ext), Some(sub)) =&gt; Some(PhoneNumber {
area: area.as_str(),
exchange: ext.as_str(),
subscriber: sub.as_str(),
}),
_ =&gt; None,
}
});
assert_eq!(
phone_numbers.map(|m| m.to_string()).collect::&lt;Vec&lt;_&gt;&gt;(),
vec![
&quot;1 (505) 881-9292&quot;,
&quot;1 (505) 778-2212&quot;,
&quot;1 (505) 881-9297&quot;,
&quot;1 (202) 991-9534&quot;,
&quot;1 (555) 392-0011&quot;,
&quot;1 (800) 233-2010&quot;,
&quot;1 (299) 339-1020&quot;,
]
);
Ok(())
}
</code></pre></pre>
<h3 id="通过多个正则来过滤日志文件"><a class="header" href="#通过多个正则来过滤日志文件">通过多个正则来过滤日志文件</a></h3>
<p>例子的目标是过滤出包含 &quot;version X.X.X&quot;、以 443 结尾的 IP 地址和特别的警告的日志行。</p>
<p>值得注意的是,由于在正则中反斜杠非常常见,因此使用 <code>r#&quot;&quot;</code> 形式的原生字符串对于开发者和使用者都更加友好。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021"><span class="boring">use error_chain::error_chain;
</span>
use std::fs::File;
use std::io::{BufReader, BufRead};
use regex::RegexSetBuilder;
<span class="boring">error_chain! {
</span><span class="boring"> foreign_links {
</span><span class="boring"> Io(std::io::Error);
</span><span class="boring"> Regex(regex::Error);
</span><span class="boring"> }
</span><span class="boring">}
</span>
fn main() -&gt; Result&lt;()&gt; {
let log_path = &quot;application.log&quot;;
let buffered = BufReader::new(File::open(log_path)?);
let set = RegexSetBuilder::new(&amp;[
r#&quot;version &quot;\d\.\d\.\d&quot;&quot;#,
r#&quot;\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:443&quot;#,
r#&quot;warning.*timeout expired&quot;#,
]).case_insensitive(true)
.build()?;
buffered
.lines()
.filter_map(|line| line.ok())
.filter(|line| set.is_match(line.as_str()))
.for_each(|x| println!(&quot;{}&quot;, x));
Ok(())
}
</code></pre></pre>
<h3 id="将文本中所有的指定模式替换成另外一种模式"><a class="header" href="#将文本中所有的指定模式替换成另外一种模式">将文本中所有的指定模式替换成另外一种模式</a></h3>
<p>下面代码将标准的 ISO 8601 YYYY-MM-DD 日期模式替换成带有斜杠的美式英语日期。例如 <code>2013-01-15</code> -&gt; <code>01/15/2013</code></p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use lazy_static::lazy_static;
use std::borrow::Cow;
use regex::Regex;
fn reformat_dates(before: &amp;str) -&gt; Cow&lt;str&gt; {
lazy_static! {
static ref ISO8601_DATE_REGEX : Regex = Regex::new(
r&quot;(?P&lt;y&gt;\d{4})-(?P&lt;m&gt;\d{2})-(?P&lt;d&gt;\d{2})&quot;
).unwrap();
}
ISO8601_DATE_REGEX.replace_all(before, &quot;$m/$d/$y&quot;)
}
fn main() {
let before = &quot;2012-03-14, 2013-01-15 and 2014-07-05&quot;;
let after = reformat_dates(before);
assert_eq!(after, &quot;03/14/2012, 01/15/2013 and 07/05/2014&quot;);
}
</code></pre></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="字符串解析"><a class="header" href="#字符串解析">字符串解析</a></h1>
<h3 id="访问-unicode-字符"><a class="header" href="#访问-unicode-字符">访问 Unicode 字符</a></h3>
<p><a href="text/">unicode-segmentation</a> 包的 <a href="text/">UnicodeSegmentation::graphemes</a> 函数可以将 UTF-8 字符串收集成一个 Unicode 字符组成的数组。这样我们就可以通过索引的方式来访问对应的字符了。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use unicode_segmentation::UnicodeSegmentation;
fn main() {
let name = &quot;José Guimarães\r\n&quot;;
let graphemes = UnicodeSegmentation::graphemes(name, true)
.collect::&lt;Vec&lt;&amp;str&gt;&gt;();
assert_eq!(graphemes[3], &quot;é&quot;);
}
</code></pre></pre>
<h3 id="为自定义结构体实现-fromstr-特征"><a class="header" href="#为自定义结构体实现-fromstr-特征">为自定义结构体实现 FromStr 特征</a></h3>
<p>为我们的 RGB 结构体实现 <code>FromStr</code> 特征后,就可以将一个十六进制的颜色表示字符串转换成 RGB 结构体。</p>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::str::FromStr;
#[derive(Debug, PartialEq)]
struct RGB {
r: u8,
g: u8,
b: u8,
}
impl FromStr for RGB {
type Err = std::num::ParseIntError;
// 将十六进制的颜色码解析为 `RGB` 的实例
fn from_str(hex_code: &amp;str) -&gt; Result&lt;Self, Self::Err&gt; {
// u8::from_str_radix(src: &amp;str, radix: u32) 将一个字符串切片按照指定的基数转换为 u8 类型
let r: u8 = u8::from_str_radix(&amp;hex_code[1..3], 16)?;
let g: u8 = u8::from_str_radix(&amp;hex_code[3..5], 16)?;
let b: u8 = u8::from_str_radix(&amp;hex_code[5..7], 16)?;
Ok(RGB { r, g, b })
}
}
fn main() {
let code: &amp;str = &amp;r&quot;#fa7268&quot;;
match RGB::from_str(code) {
Ok(rgb) =&gt; {
println!(
r&quot;The RGB color code is: R: {} G: {} B: {}&quot;,
rgb.r, rgb.g, rgb.b
);
}
Err(_) =&gt; {
println!(&quot;{} is not a valid color hex code!&quot;, code);
}
}
// 测试 from_str 是否按照预期工作
assert_eq!(
RGB::from_str(&amp;r&quot;#fa7268&quot;).unwrap(),
RGB {
r: 250,
g: 114,
b: 104
}
);
}
</code></pre></pre>
<h3 id="实现-display-特征"><a class="header" href="#实现-display-特征">实现 Display 特征</a></h3>
<p>@todo</p>
<div id="giscus-container"></div>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</nav>
</div>
<script type="text/javascript">
window.playground_copyable = true;
</script>
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="book.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8">
var pagePath = "print.md"
</script>
<!-- Custom JS scripts -->
<script type="text/javascript" src="assets/custom1.js"></script>
<script type="text/javascript" src="assets/bigPicture.js"></script>
<script type="text/javascript">
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</body>
</html>