|
|
|
|
<!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>: 今天你"锈"了吗? 你的 Rust 代码好锈啊!</p>
|
|
|
|
|
<p>而本书,就是精选了各种开源库和代码片段,帮助大家打造优"锈"的 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>鉴于以上痛点,我们决定打造一本真正的锈书:一本足够"锈"但是又不会锈的书。</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> > <code>Rocket</code> > <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工具,支持C,C++,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&o=desc&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 "\h" for help
|
|
|
|
|
qjs > const i = 1 + 2;
|
|
|
|
|
qjs > console.log("hello " + 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 >= 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("/tmp/welcome-to-sled")?;
|
|
|
|
|
|
|
|
|
|
// insert and get, similar to std's BTreeMap
|
|
|
|
|
let old_value = tree.insert("key", "value")?;
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
tree.get(&"key")?,
|
|
|
|
|
Some(sled::IVec::from("value")),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// range queries
|
|
|
|
|
for kv_result in tree.range("key_1".."key_9") {}
|
|
|
|
|
|
|
|
|
|
// deletion
|
|
|
|
|
let old_value = tree.remove(&"key")?;
|
|
|
|
|
|
|
|
|
|
// atomic compare and swap
|
|
|
|
|
tree.compare_and_swap(
|
|
|
|
|
"key",
|
|
|
|
|
Some("current_value"),
|
|
|
|
|
Some("new_value"),
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
// 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!("Hello, world!");
|
|
|
|
|
}
|
|
|
|
|
</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("koa" );
|
|
|
|
|
cost logger = require("@adesso/logger")
|
|
|
|
|
|
|
|
|
|
// deno
|
|
|
|
|
import { Application } from "https://deno.land/x/oak/mod.ts";
|
|
|
|
|
import { Logger } from "https://adesso.de/lib/logger.ts"
|
|
|
|
|
</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) -> JsResult<JsArray> {
|
|
|
|
|
// 创建一些值:
|
|
|
|
|
let n = cx.number(9000);
|
|
|
|
|
let s = cx.string("hello");
|
|
|
|
|
let b = cx.boolean(true);
|
|
|
|
|
|
|
|
|
|
// 创建一个新数组:
|
|
|
|
|
let array: Handle<JsArray> = cx.empty_array();
|
|
|
|
|
|
|
|
|
|
// 将值推入数组中
|
|
|
|
|
array.set(&mut cx, 0, n)?;
|
|
|
|
|
array.set(&mut cx, 1, s)?;
|
|
|
|
|
array.set(&mut cx, 2, b)?;
|
|
|
|
|
|
|
|
|
|
// 返回数组
|
|
|
|
|
Ok(array)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
register_module!(mut cx, {
|
|
|
|
|
cx.export_function("makeAnArray", 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">[
|
|
|
|
|
{
|
|
|
|
|
"name": "deno_lint",
|
|
|
|
|
"totalMs": 105.3750100000002,
|
|
|
|
|
"runsCount": 5,
|
|
|
|
|
"measuredRunsAvgMs": 21.07500200000004,
|
|
|
|
|
"measuredRunsMs": [
|
|
|
|
|
24.79783199999997,
|
|
|
|
|
19.563640000000078,
|
|
|
|
|
20.759051999999883,
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"name": "eslint",
|
|
|
|
|
"totalMs": 11845.073306000002,
|
|
|
|
|
"runsCount": 5,
|
|
|
|
|
"measuredRunsAvgMs": 2369.0146612000003,
|
|
|
|
|
"measuredRunsMs": [
|
|
|
|
|
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 "let a = foo.hasOwnProperty('bar');" > 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 <rules>`
|
|
|
|
|
</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: &web_sys::Document) -> Result<DelayedHelloButton, JsValue> {
|
|
|
|
|
// 创建 `<button>` 元素.
|
|
|
|
|
let button = document.create_element("button")?;
|
|
|
|
|
|
|
|
|
|
// 监听button上的`click`事件
|
|
|
|
|
let button2 = button.clone();
|
|
|
|
|
let on_click = EventListener::new(&button, "click", move |_event| {
|
|
|
|
|
// 一秒后,更新button中的文本
|
|
|
|
|
let button3 = button2.clone();
|
|
|
|
|
Timeout::new(1_000, move || {
|
|
|
|
|
button3.set_text_content(Some("Hello from one second ago!"));
|
|
|
|
|
})
|
|
|
|
|
.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 "C" {
|
|
|
|
|
fn alert(s: &str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 从Rust导出一个`greet`函数到Javascript,该函数会`alert`一条欢迎信息
|
|
|
|
|
#[wasm_bindgen]
|
|
|
|
|
pub fn greet(name: &str) {
|
|
|
|
|
alert(&format!("Hello, {}!", 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 "\h" for help
|
|
|
|
|
qjs > const i = 1 + 2;
|
|
|
|
|
qjs > console.log("hello " + 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!("Hello, world!");
|
|
|
|
|
}
|
|
|
|
|
</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&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/">"Data-Oriented Design" 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!("Random u8: {}", n1);
|
|
|
|
|
println!("Random u16: {}", n2);
|
|
|
|
|
println!("Random u32: {}", rng.gen::<u32>());
|
|
|
|
|
println!("Random i32: {}", rng.gen::<i32>());
|
|
|
|
|
println!("Random float: {}", rng.gen::<f64>());
|
|
|
|
|
}
|
|
|
|
|
</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!("Integer: {}", rng.gen_range(0..10));
|
|
|
|
|
println!("Float: {}", 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(&mut rng);
|
|
|
|
|
println!("Roll the die: {}", 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() -> Result<(), NormalError> {
|
|
|
|
|
let mut rng = thread_rng();
|
|
|
|
|
let normal = Normal::new(2.0, 3.0)?;
|
|
|
|
|
let v = normal.sample(&mut rng);
|
|
|
|
|
println!("{} is from a N(2, 9) distribution", 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<Point> for Standard {
|
|
|
|
|
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> 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!("Random Point: {:?}", rand_point);
|
|
|
|
|
|
|
|
|
|
// 通过类型暗示( hint )生成一个随机的元组
|
|
|
|
|
let rand_tuple = rng.gen::<(i32, bool, f64)>();
|
|
|
|
|
println!("Random tuple: {:?}", 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(&Alphanumeric)
|
|
|
|
|
.take(30)
|
|
|
|
|
.map(char::from)
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
println!("{}", 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: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
|
|
|
|
|
abcdefghijklmnopqrstuvwxyz\
|
|
|
|
|
0123456789)(*&^%$#@!~";
|
|
|
|
|
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!("{:?}", 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) -> Self {
|
|
|
|
|
Person {
|
|
|
|
|
name,
|
|
|
|
|
age
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let mut people = vec![
|
|
|
|
|
Person::new("Zoe".to_string(), 25),
|
|
|
|
|
Person::new("Al".to_string(), 60),
|
|
|
|
|
Person::new("John".to_string(), 1),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 通过派生后的自然顺序(Name and age)排序
|
|
|
|
|
people.sort();
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
people,
|
|
|
|
|
vec![
|
|
|
|
|
Person::new("Al".to_string(), 60),
|
|
|
|
|
Person::new("John".to_string(), 1),
|
|
|
|
|
Person::new("Zoe".to_string(), 25),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// 只通过 age 排序
|
|
|
|
|
people.sort_by(|a, b| b.age.cmp(&a.age));
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
people,
|
|
|
|
|
vec![
|
|
|
|
|
Person::new("Al".to_string(), 60),
|
|
|
|
|
Person::new("Zoe".to_string(), 25),
|
|
|
|
|
Person::new("John".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() -> Result<(), std::io::Error> {
|
|
|
|
|
let path = "archive.tar.gz";
|
|
|
|
|
|
|
|
|
|
let tar_gz = File::open(path)?;
|
|
|
|
|
let tar = GzDecoder::new(tar_gz);
|
|
|
|
|
let mut archive = Archive::new(tar);
|
|
|
|
|
archive.unpack(".")?;
|
|
|
|
|
|
|
|
|
|
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() -> Result<(), std::io::Error> {
|
|
|
|
|
let tar_gz = File::create("archive.tar.gz")?;
|
|
|
|
|
let enc = GzEncoder::new(tar_gz, Compression::default());
|
|
|
|
|
let mut tar = tar::Builder::new(enc);
|
|
|
|
|
tar.append_dir_all("backup/logs", "/var/log")?;
|
|
|
|
|
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() -> Result<()> {
|
|
|
|
|
let file = File::open("archive.tar.gz")?;
|
|
|
|
|
let mut archive = Archive::new(GzDecoder::new(file));
|
|
|
|
|
let prefix = "bundle/logs";
|
|
|
|
|
|
|
|
|
|
println!("Extracted the following files:");
|
|
|
|
|
archive
|
|
|
|
|
.entries()? // 获取压缩档案中的文件条目列表
|
|
|
|
|
.filter_map(|e| e.ok())
|
|
|
|
|
// 对每个文件条目进行 map 处理
|
|
|
|
|
.map(|mut entry| -> Result<PathBuf> {
|
|
|
|
|
// 将文件路径名中的前缀移除,获取一个新的路径名
|
|
|
|
|
let path = entry.path()?.strip_prefix(prefix)?.to_owned();
|
|
|
|
|
// 将内容解压到新的路径名中
|
|
|
|
|
entry.unpack(&path)?;
|
|
|
|
|
Ok(path)
|
|
|
|
|
})
|
|
|
|
|
.filter_map(|e| e.ok())
|
|
|
|
|
.for_each(|x| println!("> {}", 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<R: Read>(mut reader: R) -> Result<Digest> {
|
|
|
|
|
let mut context = Context::new(&SHA256);
|
|
|
|
|
let mut buffer = [0; 1024];
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let count = reader.read(&mut buffer)?;
|
|
|
|
|
if count == 0 {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
context.update(&buffer[..count]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(context.finish())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
let path = "file.txt";
|
|
|
|
|
|
|
|
|
|
let mut output = File::create(path)?;
|
|
|
|
|
write!(output, "We will generate a digest of this text")?;
|
|
|
|
|
|
|
|
|
|
let input = File::open(path)?;
|
|
|
|
|
let reader = BufReader::new(input);
|
|
|
|
|
let digest = sha256_digest(reader)?;
|
|
|
|
|
|
|
|
|
|
println!("SHA-256 digest is {}", 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() -> Result<(), Unspecified> {
|
|
|
|
|
let mut key_value = [0u8; 48];
|
|
|
|
|
let rng = rand::SystemRandom::new();
|
|
|
|
|
rng.fill(&mut key_value)?;
|
|
|
|
|
let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value);
|
|
|
|
|
|
|
|
|
|
let message = "Legitimate and important message.";
|
|
|
|
|
let signature = hmac::sign(&key, message.as_bytes());
|
|
|
|
|
hmac::verify(&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() -> Result<(), Unspecified> {
|
|
|
|
|
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(&mut salt)?;
|
|
|
|
|
|
|
|
|
|
let password = "Guess Me If You Can!";
|
|
|
|
|
let mut pbkdf2_hash = [0u8; CREDENTIAL_LEN];
|
|
|
|
|
pbkdf2::derive(
|
|
|
|
|
pbkdf2::PBKDF2_HMAC_SHA512,
|
|
|
|
|
n_iter,
|
|
|
|
|
&salt,
|
|
|
|
|
password.as_bytes(),
|
|
|
|
|
&mut pbkdf2_hash,
|
|
|
|
|
);
|
|
|
|
|
println!("Salt: {}", HEXUPPER.encode(&salt));
|
|
|
|
|
println!("PBKDF2 hash: {}", HEXUPPER.encode(&pbkdf2_hash));
|
|
|
|
|
|
|
|
|
|
// `verify` 检查哈希是否正确
|
|
|
|
|
let should_`succeed = pbkdf2::verify(
|
|
|
|
|
pbkdf2::PBKDF2_HMAC_SHA512,
|
|
|
|
|
n_iter,
|
|
|
|
|
&salt,
|
|
|
|
|
password.as_bytes(),
|
|
|
|
|
&pbkdf2_hash,
|
|
|
|
|
);
|
|
|
|
|
let wrong_password = "Definitely not the correct password";
|
|
|
|
|
let should_fail = pbkdf2::verify(
|
|
|
|
|
pbkdf2::PBKDF2_HMAC_SHA512,
|
|
|
|
|
n_iter,
|
|
|
|
|
&salt,
|
|
|
|
|
wrong_password.as_bytes(),
|
|
|
|
|
&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(&[[1, 2, 3],
|
|
|
|
|
[4, 5, 6]]);
|
|
|
|
|
|
|
|
|
|
let b = arr2(&[[6, 5, 4],
|
|
|
|
|
[3, 2, 1]]);
|
|
|
|
|
|
|
|
|
|
// 借用 a 和 b,求和后生成新的矩阵 sum
|
|
|
|
|
let sum = &a + &b;
|
|
|
|
|
|
|
|
|
|
println!("{}", a);
|
|
|
|
|
println!("+");
|
|
|
|
|
println!("{}", b);
|
|
|
|
|
println!("=");
|
|
|
|
|
println!("{}", 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(&[[1, 2, 3],
|
|
|
|
|
[4, 5, 6]]);
|
|
|
|
|
|
|
|
|
|
let b = arr2(&[[6, 3],
|
|
|
|
|
[5, 2],
|
|
|
|
|
[4, 1]]);
|
|
|
|
|
|
|
|
|
|
println!("{}", a.dot(&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(&[1, 2, 3]);
|
|
|
|
|
|
|
|
|
|
let matrix = arr2(&[[4, 5, 6],
|
|
|
|
|
[7, 8, 9]]);
|
|
|
|
|
|
|
|
|
|
let new_vector: Array1<_> = scalar * vector;
|
|
|
|
|
println!("{}", new_vector);
|
|
|
|
|
|
|
|
|
|
let new_matrix = matrix.dot(&new_vector);
|
|
|
|
|
println!("{}", 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 = "0.13", features = ["approx"] }</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 = &c + &d;
|
|
|
|
|
|
|
|
|
|
assert_abs_diff_eq!(z, Array::from(vec![6., 6., 6., 6., 6.]));
|
|
|
|
|
|
|
|
|
|
println!("c = {}", 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>&ArrayBase<S, Ix1> where S: Data</code>,特别是在你提供一个公共 API 给其它用户时,但由于咱们是内部使用,因此更精准的 <code>ArrayView1<f64></code> 会更适合。</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust editable edition2021">use ndarray::{array, Array1, ArrayView1};
|
|
|
|
|
|
|
|
|
|
fn l1_norm(x: ArrayView1<f64>) -> f64 {
|
|
|
|
|
x.fold(0., |acc, elem| acc + elem.abs())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn l2_norm(x: ArrayView1<f64>) -> f64 {
|
|
|
|
|
x.dot(&x).sqrt()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn normalize(mut x: Array1<f64>) -> Array1<f64> {
|
|
|
|
|
let norm = l2_norm(x.view());
|
|
|
|
|
x.mapv_inplace(|e| e/norm);
|
|
|
|
|
x
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let x = array![1., 2., 3., 4., 5.];
|
|
|
|
|
println!("||x||_2 = {}", l2_norm(x.view()));
|
|
|
|
|
println!("||x||_1 = {}", l1_norm(x.view()));
|
|
|
|
|
println!("Normalizing x yields {:?}", 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!("m1 = {}", m1);
|
|
|
|
|
match m1.try_inverse() {
|
|
|
|
|
Some(inv) => {
|
|
|
|
|
println!("The inverse of m1 is: {}", inv);
|
|
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
println!("m1 is not invertible!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</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() -> Result<(), std::io::Error> {
|
|
|
|
|
let row_slice: Vec<i32> = (1..5001).collect();
|
|
|
|
|
let matrix = DMatrix::from_row_slice(50, 100, &row_slice);
|
|
|
|
|
|
|
|
|
|
// 序列化矩阵
|
|
|
|
|
let serialized_matrix = serde_json::to_string(&matrix)?;
|
|
|
|
|
|
|
|
|
|
// 反序列化
|
|
|
|
|
let deserialized_matrix: DMatrix<i32> = serde_json::from_str(&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!("Hypotenuse: {}", 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!(
|
|
|
|
|
"Distance between Paris and London on the surface of Earth is {:.1} kilometers",
|
|
|
|
|
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!("Complex integer: {}", complex_integer);
|
|
|
|
|
println!("Complex float: {}", 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!("Sum: {}", 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!("e^(2i * pi) = {}", 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::<i32>() as f32;
|
|
|
|
|
let count = data.len();
|
|
|
|
|
|
|
|
|
|
let mean = match count {
|
|
|
|
|
positive if positive > 0 => Some(sum / count as f32),
|
|
|
|
|
_ => None
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
println!("Mean of the data is {:?}", 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: &[i32]) -> Option<(Vec<i32>, i32, Vec<i32>)> {
|
|
|
|
|
match data.len() {
|
|
|
|
|
0 => None,
|
|
|
|
|
_ => {
|
|
|
|
|
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) = &mut splits;
|
|
|
|
|
if next < &pivot {
|
|
|
|
|
left.push(*next);
|
|
|
|
|
} else {
|
|
|
|
|
right.push(*next);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
splits
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Some((left, pivot, right))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn select(data: &[i32], k: usize) -> Option<i32> {
|
|
|
|
|
let part = partition(data);
|
|
|
|
|
|
|
|
|
|
match part {
|
|
|
|
|
None => None,
|
|
|
|
|
Some((left, pivot, right)) => {
|
|
|
|
|
let pivot_idx = left.len();
|
|
|
|
|
|
|
|
|
|
match pivot_idx.cmp(&k) {
|
|
|
|
|
Ordering::Equal => Some(pivot),
|
|
|
|
|
Ordering::Greater => select(&left, k),
|
|
|
|
|
Ordering::Less => select(&right, k - (pivot_idx + 1)),
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn median(data: &[i32]) -> Option<f32> {
|
|
|
|
|
let size = data.len();
|
|
|
|
|
|
|
|
|
|
match size {
|
|
|
|
|
even if even % 2 == 0 => {
|
|
|
|
|
let fst_med = select(data, (even / 2) - 1);
|
|
|
|
|
let snd_med = select(data, even / 2);
|
|
|
|
|
|
|
|
|
|
match (fst_med, snd_med) {
|
|
|
|
|
(Some(fst), Some(snd)) => Some((fst + snd) as f32 / 2.0),
|
|
|
|
|
_ => None
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
odd => 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(&data);
|
|
|
|
|
println!("Partition is {:?}", part);
|
|
|
|
|
|
|
|
|
|
let sel = select(&data, 5);
|
|
|
|
|
println!("Selection at ordered index {} is {:?}", 5, sel);
|
|
|
|
|
|
|
|
|
|
let med = median(&data);
|
|
|
|
|
println!("Median is {:?}", 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(|&(_, count)| count)
|
|
|
|
|
.map(|(value, _)| *value);
|
|
|
|
|
|
|
|
|
|
println!("Mode of the data is {:?}", 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: &[i32]) -> Option<f32> {
|
|
|
|
|
let sum = data.iter().sum::<i32>() as f32;
|
|
|
|
|
let count = data.len();
|
|
|
|
|
|
|
|
|
|
match count {
|
|
|
|
|
positive if positive > 0 => Some(sum / count as f32),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn std_deviation(data: &[i32]) -> Option<f32> {
|
|
|
|
|
match (mean(data), data.len()) {
|
|
|
|
|
(Some(data_mean), count) if count > 0 => {
|
|
|
|
|
let variance = data.iter().map(|value| {
|
|
|
|
|
let diff = data_mean - (*value as f32);
|
|
|
|
|
|
|
|
|
|
diff * diff
|
|
|
|
|
}).sum::<f32>() / count as f32;
|
|
|
|
|
|
|
|
|
|
Some(variance.sqrt())
|
|
|
|
|
},
|
|
|
|
|
_ => None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
|
|
|
|
|
|
|
|
|
|
let data_mean = mean(&data);
|
|
|
|
|
println!("Mean is {:?}", data_mean);
|
|
|
|
|
|
|
|
|
|
let data_std_deviation = std_deviation(&data);
|
|
|
|
|
println!("Standard deviation is {:?}", data_std_deviation);
|
|
|
|
|
|
|
|
|
|
let zscore = match (data_mean, data_std_deviation) {
|
|
|
|
|
(Some(mean), Some(std_deviation)) => {
|
|
|
|
|
let diff = data[4] as f32 - mean;
|
|
|
|
|
|
|
|
|
|
Some(diff / std_deviation)
|
|
|
|
|
},
|
|
|
|
|
_ => None
|
|
|
|
|
};
|
|
|
|
|
println!("Z-score of data at index 4 (with value {}) is {:?}", 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) -> BigInt {
|
|
|
|
|
if let Some(mut factorial) = 1.to_bigint() {
|
|
|
|
|
for i in 1..=x {
|
|
|
|
|
factorial = factorial * i;
|
|
|
|
|
}
|
|
|
|
|
factorial
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
panic!("Failed to calculate factorial!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
println!("{}! equals {}", 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(&mut self) -> &mut MyFlags {
|
|
|
|
|
self.bits = 0;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for MyFlags {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f, "{:032b}", 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 & 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!("{}", flags), "00000000000000000000000000000111");
|
|
|
|
|
assert_eq!(format!("{}", flags.clear()), "00000000000000000000000000000000");
|
|
|
|
|
assert_eq!(format!("{:?}", MyFlags::FLAG_B), "FLAG_B");
|
|
|
|
|
assert_eq!(format!("{:?}", MyFlags::FLAG_A | MyFlags::FLAG_B), "FLAG_A | FLAG_B");
|
|
|
|
|
}
|
|
|
|
|
</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 "s/\"/\'/g" *.json >/dev/null' 'sd "\"" "\'" *.json >/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 "s/\"/'/g" *.json >/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 "s/\"/'/g" *.json >/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 "\"" "'" *.json >/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 "name from /tmp where (name = *.tmp and size = 0) or (name = *.cfg and size > 1000000)"
|
|
|
|
|
|
|
|
|
|
# 聚合函数
|
|
|
|
|
fselect "MIN(size), MAX(size), AVG(size), SUM(size), COUNT(*) from /home/user/Downloads"
|
|
|
|
|
|
|
|
|
|
# 格式化函数
|
|
|
|
|
fselect "LOWER(name), UPPER(name), LENGTH(name), YEAR(modified) from /home/user/Downloads"
|
|
|
|
|
</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>如果你曾经遇到过**"完蛋, 我这几天的工作内容丢了"**的情况,那么就可以尝试下 <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 "dura/$(git rev-parse HEAD)"
|
|
|
|
|
</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("My Test Program")
|
|
|
|
|
.version("0.1.0")
|
|
|
|
|
.author("Hackerman Jones <hckrmnjones@hack.gov>")
|
|
|
|
|
.about("Teaches argument parsing")
|
|
|
|
|
.arg(Arg::with_name("file")
|
|
|
|
|
.short("f")
|
|
|
|
|
.long("file")
|
|
|
|
|
.takes_value(true)
|
|
|
|
|
.help("A cool file"))
|
|
|
|
|
.arg(Arg::with_name("num")
|
|
|
|
|
.short("n")
|
|
|
|
|
.long("number")
|
|
|
|
|
.takes_value(true)
|
|
|
|
|
.help("Five less than your favorite number"))
|
|
|
|
|
.get_matches();
|
|
|
|
|
|
|
|
|
|
let myfile = matches.value_of("file").unwrap_or("input.txt");
|
|
|
|
|
println!("The file passed is: {}", myfile);
|
|
|
|
|
|
|
|
|
|
let num_str = matches.value_of("num");
|
|
|
|
|
match num_str {
|
|
|
|
|
None => println!("No idea what your favorite number is."),
|
|
|
|
|
Some(s) => {
|
|
|
|
|
match s.parse::<i32>() {
|
|
|
|
|
Ok(n) => println!("Your favorite number must be {}.", n + 5),
|
|
|
|
|
Err(_) => println!("That's not a number! {}", s),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</code></pre></pre>
|
|
|
|
|
<p><code>clap</code> 针对上面提供的构建样式,会自动帮我们生成相应的使用方式说明。例如,上面代码生成的使用说明如下:</p>
|
|
|
|
|
<pre><code class="language-shell">My Test Program 0.1.0
|
|
|
|
|
Hackerman Jones <hckrmnjones@hack.gov>
|
|
|
|
|
Teaches argument parsing
|
|
|
|
|
|
|
|
|
|
USAGE:
|
|
|
|
|
testing [OPTIONS]
|
|
|
|
|
|
|
|
|
|
FLAGS:
|
|
|
|
|
-h, --help Prints help information
|
|
|
|
|
-V, --version Prints version information
|
|
|
|
|
|
|
|
|
|
OPTIONS:
|
|
|
|
|
-f, --file <file> A cool file
|
|
|
|
|
-n, --number <num> 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!("This is {} in color, {} in color and {} in color",
|
|
|
|
|
Colour::Red.paint("red"),
|
|
|
|
|
Colour::Blue.paint("blue"),
|
|
|
|
|
Colour::Green.paint("green"));
|
|
|
|
|
}
|
|
|
|
|
</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!("{} and this is not",
|
|
|
|
|
Style::new().bold().paint("This is Bold"));
|
|
|
|
|
}
|
|
|
|
|
</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!("{}, {} and {}",
|
|
|
|
|
Colour::Yellow.paint("This is colored"),
|
|
|
|
|
Style::new().bold().paint("this is bold"),
|
|
|
|
|
// Colour 也可以使用 bold 方法进行加粗
|
|
|
|
|
Colour::Yellow.bold().paint("this is bold and colored"));
|
|
|
|
|
}
|
|
|
|
|
</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!("Number of logical cores is {}", 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() -> Result<()> {
|
|
|
|
|
let output = Command::new("git").arg("log").arg("--oneline").output()?;
|
|
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
error_chain::bail!("Command executed with failing error code");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let pattern = Regex::new(r"(?x)
|
|
|
|
|
([0-9a-fA-F]+) # commit hash
|
|
|
|
|
(.*) # The commit message")?;
|
|
|
|
|
|
|
|
|
|
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!("{:?}", 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() -> Result<()> {
|
|
|
|
|
let mut child = Command::new("python").stdin(Stdio::piped())
|
|
|
|
|
.stderr(Stdio::piped())
|
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
|
.spawn()?;
|
|
|
|
|
|
|
|
|
|
child.stdin
|
|
|
|
|
.as_mut()
|
|
|
|
|
.ok_or("Child process stdin has not been captured!")?
|
|
|
|
|
.write_all(b"import this; copyright(); credits(); exit()")?;
|
|
|
|
|
|
|
|
|
|
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::<HashSet<_>>();
|
|
|
|
|
println!("Found {} unique words:", words.len());
|
|
|
|
|
println!("{:#?}", words);
|
|
|
|
|
Ok(())
|
|
|
|
|
} else {
|
|
|
|
|
let err = String::from_utf8(output.stderr)?;
|
|
|
|
|
error_chain::bail!("External command failed:\n {}", 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() -> Result<()> {
|
|
|
|
|
let directory = std::env::current_dir()?;
|
|
|
|
|
let mut du_output_child = Command::new("du")
|
|
|
|
|
.arg("-ah")
|
|
|
|
|
.arg(&directory)
|
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
|
.spawn()?;
|
|
|
|
|
|
|
|
|
|
if let Some(du_output) = du_output_child.stdout.take() {
|
|
|
|
|
let mut sort_output_child = Command::new("sort")
|
|
|
|
|
.arg("-hr")
|
|
|
|
|
.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("head")
|
|
|
|
|
.args(&["-n", "10"])
|
|
|
|
|
.stdin(sort_output)
|
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
|
.spawn()?;
|
|
|
|
|
|
|
|
|
|
let head_stdout = head_output_child.wait_with_output()?;
|
|
|
|
|
|
|
|
|
|
sort_output_child.wait()?;
|
|
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
"Top 10 biggest files and directories in '{}':\n{}",
|
|
|
|
|
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 >out.txt 2>&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() -> Result<(), Error> {
|
|
|
|
|
let outputs = File::create("out.txt")?;
|
|
|
|
|
let errors = outputs.try_clone()?;
|
|
|
|
|
|
|
|
|
|
Command::new("ls")
|
|
|
|
|
.args(&[".", "oops"])
|
|
|
|
|
.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() -> Result<(), Error> {
|
|
|
|
|
let stdout = Command::new("journalctl")
|
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
|
.spawn()?
|
|
|
|
|
.stdout
|
|
|
|
|
.ok_or_else(|| Error::new(ErrorKind::Other,"Could not capture standard output."))?;
|
|
|
|
|
|
|
|
|
|
let reader = BufReader::new(stdout);
|
|
|
|
|
|
|
|
|
|
reader
|
|
|
|
|
.lines()
|
|
|
|
|
.filter_map(|line| line.ok())
|
|
|
|
|
.filter(|line| line.find("usb").is_some())
|
|
|
|
|
.for_each(|line| println!("{}", 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() -> Result<(), Error> {
|
|
|
|
|
// 读取环境变量 `CONFIG` 的值并写入到 `config_path` 中。
|
|
|
|
|
// 若 `CONFIG` 环境变量没有设置,则使用一个默认的值 "/etc/myapp/config"
|
|
|
|
|
let config_path = env::var("CONFIG")
|
|
|
|
|
.unwrap_or("/etc/myapp/config".to_string());
|
|
|
|
|
|
|
|
|
|
let config: String = fs::read_to_string(config_path)?;
|
|
|
|
|
println!("Config: {}", 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 = &[1, 25, -4, 10];
|
|
|
|
|
let max = find_max(arr);
|
|
|
|
|
assert_eq!(max, Some(25));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将数组分成两个部分,并使用新的线程对它们进行处理
|
|
|
|
|
fn find_max(arr: &[i32]) -> Option<i32> {
|
|
|
|
|
const THRESHOLD: usize = 2;
|
|
|
|
|
|
|
|
|
|
if arr.len() <= 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!("Source sent {}", 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!("Worker {:?} received {}.",
|
|
|
|
|
thread::current().id(), msg);
|
|
|
|
|
sendr.send(msg * 2).unwrap();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 关闭通道,如果不关闭,下沉端将永远无法结束循环
|
|
|
|
|
drop(snd2);
|
|
|
|
|
|
|
|
|
|
// 下沉端
|
|
|
|
|
for msg in rcv2.iter() {
|
|
|
|
|
println!("Sink received {}", 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!("Received {}", 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<Vec<String>> = Mutex::new(Vec::new());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn insert(fruit: &str) -> Result<()> {
|
|
|
|
|
let mut db = FRUIT.lock().map_err(|_| "Failed to acquire MutexGuard")?;
|
|
|
|
|
db.push(fruit.to_string());
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
insert("apple")?;
|
|
|
|
|
insert("orange")?;
|
|
|
|
|
insert("peach")?;
|
|
|
|
|
{
|
|
|
|
|
let db = FRUIT.lock().map_err(|_| "Failed to acquire MutexGuard")?;
|
|
|
|
|
|
|
|
|
|
db.iter().enumerate().for_each(|(i, item)| println!("{}: {}", i, item));
|
|
|
|
|
}
|
|
|
|
|
insert("grape")?;
|
|
|
|
|
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: &Path) -> bool {
|
|
|
|
|
match entry.extension() {
|
|
|
|
|
Some(e) if e.to_string_lossy().to_lowercase() == "iso" => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn compute_digest<P: AsRef<Path>>(filepath: P) -> Result<(Digest, P), Error> {
|
|
|
|
|
let mut buf_reader = BufReader::new(File::open(&filepath)?);
|
|
|
|
|
let mut context = Context::new(&SHA256);
|
|
|
|
|
let mut buffer = [0; 1024];
|
|
|
|
|
|
|
|
|
|
loop {
|
|
|
|
|
let count = buf_reader.read(&mut buffer)?;
|
|
|
|
|
if count == 0 {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
context.update(&buffer[..count]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok((context.finish(), filepath))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<(), Error> {
|
|
|
|
|
let pool = ThreadPool::new(num_cpus::get());
|
|
|
|
|
|
|
|
|
|
let (tx, rx) = channel();
|
|
|
|
|
|
|
|
|
|
for entry in WalkDir::new("/home/user/Downloads")
|
|
|
|
|
.follow_links(true)
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_map(|e| e.ok())
|
|
|
|
|
.filter(|e| !e.path().is_dir() && 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("Could not send data!");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
drop(tx);
|
|
|
|
|
for t in rx.iter() {
|
|
|
|
|
let (sha, path) = t?;
|
|
|
|
|
println!("{:?} {:?}", 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) -> Rgb<u8> {
|
|
|
|
|
</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 => ((440. - wave) / (440. - 380.), 0.0, 1.0),
|
|
|
|
|
</span><span class="boring"> 440..=489 => (0.0, (wave - 440.) / (490. - 440.), 1.0),
|
|
|
|
|
</span><span class="boring"> 490..=509 => (0.0, 1.0, (510. - wave) / (510. - 490.)),
|
|
|
|
|
</span><span class="boring"> 510..=579 => ((wave - 510.) / (580. - 510.), 1.0, 0.0),
|
|
|
|
|
</span><span class="boring"> 580..=644 => (1.0, (645. - wave) / (645. - 580.), 0.0),
|
|
|
|
|
</span><span class="boring"> 645..=780 => (1.0, 0.0, 0.0),
|
|
|
|
|
</span><span class="boring"> _ => (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 => 0.3 + 0.7 * (wave - 380.) / (420. - 380.),
|
|
|
|
|
</span><span class="boring"> 701..=780 => 0.3 + 0.7 * (780. - wave) / (780. - 700.),
|
|
|
|
|
</span><span class="boring"> _ => 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<f32>, x: u32, y: u32, width: u32, height: u32, max_iter: u32) -> 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() >= 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) -> u8 {
|
|
|
|
|
</span><span class="boring"> ((color * factor).powf(0.8) * 255.) as u8
|
|
|
|
|
</span><span class="boring">}
|
|
|
|
|
</span>
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
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("Could not send data!");
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _ in 0..(width * height) {
|
|
|
|
|
let (x, y, pixel) = rx.recv()?;
|
|
|
|
|
// 使用数据来设置像素的颜色
|
|
|
|
|
img.put_pixel(x, y, pixel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 输出图片内容到指定文件中
|
|
|
|
|
let _ = img.save("output.png")?;
|
|
|
|
|
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!("{:?}", 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 > 8 ));
|
|
|
|
|
assert!(vec.par_iter().all(|n| *n <= 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 > 8 ));
|
|
|
|
|
assert!(!vec.par_iter().all(|n| *n <= 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];
|
|
|
|
|
|
|
|
|
|
// 这里使用了 `&&x` 的形式,大家可以在以下链接阅读更多 https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.find
|
|
|
|
|
let f1 = v.par_iter().find_any(|&&x| x == 9);
|
|
|
|
|
let f2 = v.par_iter().find_any(|&&x| x % 2 == 0 && x > 6);
|
|
|
|
|
let f3 = v.par_iter().find_any(|&&x| x > 8);
|
|
|
|
|
|
|
|
|
|
assert_eq!(f1, Some(&9));
|
|
|
|
|
assert_eq!(f2, Some(&8));
|
|
|
|
|
assert!(f3 > Some(&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(&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<Person> = 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(|&x| x.age > 30).count() as f32;
|
|
|
|
|
let sum_over_30 = v.par_iter()
|
|
|
|
|
.map(|x| x.age)
|
|
|
|
|
.filter(|&x| x > 30)
|
|
|
|
|
.reduce(|| 0, |x, y| x + y);
|
|
|
|
|
|
|
|
|
|
let alt_sum_30: u32 = v.par_iter()
|
|
|
|
|
.map(|x| x.age)
|
|
|
|
|
.filter(|&x| x > 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() < std::f32::EPSILON);
|
|
|
|
|
println!("The average age of people older than 30 is {}", 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() -> Result<()> {
|
|
|
|
|
let options: MatchOptions = Default::default();
|
|
|
|
|
// 找到当前目录中的所有 `jpg` 文件
|
|
|
|
|
let files: Vec<_> = glob_with("*.jpg", options)?
|
|
|
|
|
.filter_map(|x| x.ok())
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
if files.len() == 0 {
|
|
|
|
|
error_chain::bail!("No .jpg files found in current directory");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let thumb_dir = "thumbnails";
|
|
|
|
|
create_dir_all(thumb_dir)?;
|
|
|
|
|
|
|
|
|
|
println!("Saving {} thumbnails into '{}'...", files.len(), thumb_dir);
|
|
|
|
|
|
|
|
|
|
let image_failures: Vec<_> = 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!("{}", x.display_chain()));
|
|
|
|
|
|
|
|
|
|
println!("{} thumbnails saved successfully", files.len() - image_failures.len());
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn make_thumbnail<PA, PB>(original: PA, thumb_dir: PB, longest_edge: u32) -> Result<()>
|
|
|
|
|
where
|
|
|
|
|
PA: AsRef<Path>,
|
|
|
|
|
PB: AsRef<Path>,
|
|
|
|
|
{
|
|
|
|
|
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() -> Result<()> {
|
|
|
|
|
let conn = Connection::open("cats.db")?;
|
|
|
|
|
|
|
|
|
|
conn.execute(
|
|
|
|
|
"create table if not exists cat_colors (
|
|
|
|
|
id integer primary key,
|
|
|
|
|
name text not null unique
|
|
|
|
|
)",
|
|
|
|
|
NO_PARAMS,
|
|
|
|
|
)?;
|
|
|
|
|
conn.execute(
|
|
|
|
|
"create table if not exists cats (
|
|
|
|
|
id integer primary key,
|
|
|
|
|
name text not null,
|
|
|
|
|
color_id integer not null references cat_colors(id)
|
|
|
|
|
)",
|
|
|
|
|
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() -> Result<()> {
|
|
|
|
|
// 打开第一个例子所创建的数据库
|
|
|
|
|
let conn = Connection::open("cats.db")?;
|
|
|
|
|
|
|
|
|
|
let mut cat_colors = HashMap::new();
|
|
|
|
|
cat_colors.insert(String::from("Blue"), vec!["Tigger", "Sammy"]);
|
|
|
|
|
cat_colors.insert(String::from("Black"), vec!["Oreo", "Biscuit"]);
|
|
|
|
|
|
|
|
|
|
for (color, catnames) in &cat_colors {
|
|
|
|
|
// 插入一条数据行
|
|
|
|
|
conn.execute(
|
|
|
|
|
"INSERT INTO cat_colors (name) values (?1)",
|
|
|
|
|
&[&color.to_string()],
|
|
|
|
|
)?;
|
|
|
|
|
// 获取最近插入数据行的 id
|
|
|
|
|
let last_id: String = conn.last_insert_rowid().to_string();
|
|
|
|
|
|
|
|
|
|
for cat in catnames {
|
|
|
|
|
conn.execute(
|
|
|
|
|
"INSERT INTO cats (name, color_id) values (?1, ?2)",
|
|
|
|
|
&[&cat.to_string(), &last_id],
|
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let mut stmt = conn.prepare(
|
|
|
|
|
"SELECT c.name, cc.name from cats c
|
|
|
|
|
INNER JOIN cat_colors cc
|
|
|
|
|
ON cc.id = c.color_id;",
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
let cats = stmt.query_map(NO_PARAMS, |row| {
|
|
|
|
|
Ok(Cat {
|
|
|
|
|
name: row.get(0)?,
|
|
|
|
|
color: row.get(1)?,
|
|
|
|
|
})
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
for cat in cats {
|
|
|
|
|
println!("Found cat {:?}", 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() -> Result<()> {
|
|
|
|
|
// 打开第一个例子所创建的数据库
|
|
|
|
|
let mut conn = Connection::open("cats.db")?;
|
|
|
|
|
|
|
|
|
|
successful_tx(&mut conn)?;
|
|
|
|
|
|
|
|
|
|
let res = rolled_back_tx(&mut conn);
|
|
|
|
|
assert!(res.is_err());
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn successful_tx(conn: &mut Connection) -> Result<()> {
|
|
|
|
|
let tx = conn.transaction()?;
|
|
|
|
|
|
|
|
|
|
tx.execute("delete from cat_colors", NO_PARAMS)?;
|
|
|
|
|
tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
|
|
|
|
|
tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?;
|
|
|
|
|
|
|
|
|
|
tx.commit()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn rolled_back_tx(conn: &mut Connection) -> Result<()> {
|
|
|
|
|
let tx = conn.transaction()?;
|
|
|
|
|
|
|
|
|
|
tx.execute("delete from cat_colors", NO_PARAMS)?;
|
|
|
|
|
tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
|
|
|
|
|
tx.execute("insert into cat_colors (name) values (?1)", &[&"blue"])?;
|
|
|
|
|
tx.execute("insert into cat_colors (name) values (?1)", &[&"lavender"])?;
|
|
|
|
|
|
|
|
|
|
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() -> Result<(), Error> {
|
|
|
|
|
// 连接到数据库 library
|
|
|
|
|
let mut client = Client::connect("postgresql://postgres:postgres@localhost/library", NoTls)?;
|
|
|
|
|
|
|
|
|
|
client.batch_execute("
|
|
|
|
|
CREATE TABLE IF NOT EXISTS author (
|
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
|
name VARCHAR NOT NULL,
|
|
|
|
|
country VARCHAR NOT NULL
|
|
|
|
|
)
|
|
|
|
|
")?;
|
|
|
|
|
|
|
|
|
|
client.batch_execute("
|
|
|
|
|
CREATE TABLE IF NOT EXISTS book (
|
|
|
|
|
id SERIAL PRIMARY KEY,
|
|
|
|
|
title VARCHAR NOT NULL,
|
|
|
|
|
author_id INTEGER NOT NULL REFERENCES author
|
|
|
|
|
)
|
|
|
|
|
")?;
|
|
|
|
|
|
|
|
|
|
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() -> Result<(), Error> {
|
|
|
|
|
let mut client = Client::connect("postgresql://postgres:postgres@localhost/library",
|
|
|
|
|
NoTls)?;
|
|
|
|
|
|
|
|
|
|
let mut authors = HashMap::new();
|
|
|
|
|
authors.insert(String::from("Chinua Achebe"), "Nigeria");
|
|
|
|
|
authors.insert(String::from("Rabindranath Tagore"), "India");
|
|
|
|
|
authors.insert(String::from("Anita Nair"), "India");
|
|
|
|
|
|
|
|
|
|
for (key, value) in &authors {
|
|
|
|
|
let author = Author {
|
|
|
|
|
_id: 0,
|
|
|
|
|
name: key.to_string(),
|
|
|
|
|
country: value.to_string()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 插入数据
|
|
|
|
|
client.execute(
|
|
|
|
|
"INSERT INTO author (name, country) VALUES ($1, $2)",
|
|
|
|
|
&[&author.name, &author.country],
|
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 查询数据
|
|
|
|
|
for row in client.query("SELECT id, name, country FROM author", &[])? {
|
|
|
|
|
let author = Author {
|
|
|
|
|
_id: row.get(0),
|
|
|
|
|
name: row.get(1),
|
|
|
|
|
country: row.get(2),
|
|
|
|
|
};
|
|
|
|
|
println!("Author {} is from {}", 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() -> Result<(), Error> {
|
|
|
|
|
let mut client = Client::connect(
|
|
|
|
|
"postgresql://postgres:postgres@127.0.0.1/moma",
|
|
|
|
|
NoTls,
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
for row in client.query
|
|
|
|
|
("SELECT nationality, COUNT(nationality) AS count
|
|
|
|
|
FROM artists GROUP BY nationality ORDER BY count DESC", &[])? {
|
|
|
|
|
|
|
|
|
|
let (nationality, count) : (Option<String>, Option<i64>)
|
|
|
|
|
= (row.get (0), row.get (1));
|
|
|
|
|
|
|
|
|
|
if nationality.is_some () && count.is_some () {
|
|
|
|
|
|
|
|
|
|
let nation = Nation{
|
|
|
|
|
nationality: nationality.unwrap(),
|
|
|
|
|
count: count.unwrap(),
|
|
|
|
|
};
|
|
|
|
|
println!("{} {}", 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!("Time elapsed in expensive_function() is: {:?}", 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<Utc>) -> Option<DateTime<Utc>> {
|
|
|
|
|
date_time.checked_sub_signed(Duration::days(1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let now = Utc::now();
|
|
|
|
|
println!("{}", 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) => println!("{}", x),
|
|
|
|
|
None => eprintln!("Almost three weeks from now overflows!"),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
match now.checked_add_signed(Duration::max_value()) {
|
|
|
|
|
Some(x) => println!("{}", x),
|
|
|
|
|
None => eprintln!("We can't use chrono to tell the time for the Solar System to complete more than one full orbit around the galactic center."),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</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::<Utc>::from_utc(local_time.naive_utc(), Utc);
|
|
|
|
|
let china_timezone = FixedOffset::east(8 * 3600);
|
|
|
|
|
let rio_timezone = FixedOffset::west(2 * 3600);
|
|
|
|
|
println!("Local time now is {}", local_time);
|
|
|
|
|
println!("UTC time now is {}", utc_time);
|
|
|
|
|
println!(
|
|
|
|
|
"Time in Hong Kong now is {}",
|
|
|
|
|
utc_time.with_timezone(&china_timezone)
|
|
|
|
|
);
|
|
|
|
|
println!("Time in Rio de Janeiro now is {}", utc_time.with_timezone(&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!(
|
|
|
|
|
"The current UTC time is {:02}:{:02}:{:02} {}",
|
|
|
|
|
hour,
|
|
|
|
|
now.minute(),
|
|
|
|
|
now.second(),
|
|
|
|
|
if is_pm { "PM" } else { "AM" }
|
|
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"And there have been {} seconds since midnight",
|
|
|
|
|
now.num_seconds_from_midnight()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let (is_common_era, year) = now.year_ce();
|
|
|
|
|
println!(
|
|
|
|
|
"The current UTC date is {}-{:02}-{:02} {:?} ({})",
|
|
|
|
|
year,
|
|
|
|
|
now.month(),
|
|
|
|
|
now.day(),
|
|
|
|
|
now.weekday(),
|
|
|
|
|
if is_common_era { "CE" } else { "BCE" }
|
|
|
|
|
);
|
|
|
|
|
println!(
|
|
|
|
|
"And the Common Era began {} days ago",
|
|
|
|
|
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!(
|
|
|
|
|
"Number of seconds between 1970-01-01 00:00:00 and {} is {}.",
|
|
|
|
|
// 打印日期和日期对应的时间戳
|
|
|
|
|
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!(
|
|
|
|
|
"Date after a billion seconds since 1970-01-01 00:00:00 was {}.",
|
|
|
|
|
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<Utc> = Utc::now();
|
|
|
|
|
|
|
|
|
|
println!("UTC now is: {}", now);
|
|
|
|
|
// 使用 RFC 2822 格式显示当前时间
|
|
|
|
|
println!("UTC now in RFC 2822 is: {}", now.to_rfc2822());
|
|
|
|
|
// 使用 RFC 3339 格式显示当前时间
|
|
|
|
|
println!("UTC now in RFC 3339 is: {}", now.to_rfc3339());
|
|
|
|
|
// 使用自定义格式显示当前时间
|
|
|
|
|
println!("UTC now in a custom format is: {}", now.format("%a %b %e %T %Y"));
|
|
|
|
|
}
|
|
|
|
|
</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() -> Result<(), ParseError> {
|
|
|
|
|
let rfc2822 = DateTime::parse_from_rfc2822("Tue, 1 Jul 2003 10:52:37 +0200")?;
|
|
|
|
|
println!("{}", rfc2822);
|
|
|
|
|
|
|
|
|
|
let rfc3339 = DateTime::parse_from_rfc3339("1996-12-19T16:39:57-08:00")?;
|
|
|
|
|
println!("{}", rfc3339);
|
|
|
|
|
|
|
|
|
|
let custom = DateTime::parse_from_str("5.8.1994 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")?;
|
|
|
|
|
println!("{}", custom);
|
|
|
|
|
|
|
|
|
|
let time_only = NaiveTime::parse_from_str("23:56:04", "%H:%M:%S")?;
|
|
|
|
|
println!("{}", time_only);
|
|
|
|
|
|
|
|
|
|
let date_only = NaiveDate::parse_from_str("2015-09-05", "%Y-%m-%d")?;
|
|
|
|
|
println!("{}", date_only);
|
|
|
|
|
|
|
|
|
|
let no_timezone = NaiveDateTime::parse_from_str("2015-09-05 23:56:04", "%Y-%m-%d %H:%M:%S")?;
|
|
|
|
|
println!("{}", 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: &str) {
|
|
|
|
|
log::debug!("Executing query: {}", query);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
|
|
execute_query("DROP TABLE students");
|
|
|
|
|
}
|
|
|
|
|
</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: &str) -> Result<(), &'static str> {
|
|
|
|
|
Err("I'm afraid I can't do that")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
|
|
let response = execute_query("DROP TABLE students");
|
|
|
|
|
if let Err(err) = response {
|
|
|
|
|
log::error!("Failed to execute query: {}", 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!("This error has been printed to Stdout");
|
|
|
|
|
}
|
|
|
|
|
</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(&self, metadata: &Metadata) -> bool {
|
|
|
|
|
metadata.level() <= Level::Info
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn log(&self, record: &Record) {
|
|
|
|
|
if self.enabled(record.metadata()) {
|
|
|
|
|
println!("Rust says: {} - {}", record.level(), record.args());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn flush(&self) {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<(), SetLoggerError> {
|
|
|
|
|
log::set_logger(&CONSOLE_LOGGER)?;
|
|
|
|
|
log::set_max_level(LevelFilter::Info);
|
|
|
|
|
|
|
|
|
|
log::info!("hello log");
|
|
|
|
|
log::warn!("warning");
|
|
|
|
|
log::error!("oops");
|
|
|
|
|
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 = "linux")]
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
use syslog::{Facility, Error};
|
|
|
|
|
|
|
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
|
fn main() -> Result<(), Error> {
|
|
|
|
|
// 初始化 logger
|
|
|
|
|
syslog::init(Facility::LOG_USER,
|
|
|
|
|
log::LevelFilter::Debug,
|
|
|
|
|
// 可选的应用名称
|
|
|
|
|
Some("My app name"))?;
|
|
|
|
|
log::debug!("this is a debug {}", "message");
|
|
|
|
|
log::error!("this is an error!");
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(target_os = "linux"))]
|
|
|
|
|
fn main() {
|
|
|
|
|
println!("So far, only Linux systems are supported.");
|
|
|
|
|
}
|
|
|
|
|
</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!("[bar] warn");
|
|
|
|
|
log::info!("[bar] info");
|
|
|
|
|
log::debug!("[bar] debug");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn run() {
|
|
|
|
|
log::warn!("[foo] warn");
|
|
|
|
|
log::info!("[foo] info");
|
|
|
|
|
log::debug!("[foo] debug");
|
|
|
|
|
bar::run();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
env_logger::init();
|
|
|
|
|
log::warn!("[root] warn");
|
|
|
|
|
log::info!("[root] info");
|
|
|
|
|
log::debug!("[root] debug");
|
|
|
|
|
foo::run();
|
|
|
|
|
}
|
|
|
|
|
</code></pre></pre>
|
|
|
|
|
<p>要让环境变量生效,首先需要通过 <code>env_logger::init()</code> 开启相关的支持。然后通过以下命令来运行程序:</p>
|
|
|
|
|
<pre><code class="language-shell">RUST_LOG="warn,test::foo=info,test::foo::bar=debug" ./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(&env::var("MY_APP_LOG").unwrap_or_default())
|
|
|
|
|
.init();
|
|
|
|
|
|
|
|
|
|
log::info!("informational message");
|
|
|
|
|
log::warn!("warning message");
|
|
|
|
|
log::error!("this is an error {}", "message");
|
|
|
|
|
}
|
|
|
|
|
</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,
|
|
|
|
|
"{} [{}] - {}",
|
|
|
|
|
Local::now().format("%Y-%m-%dT%H:%M:%S"),
|
|
|
|
|
record.level(),
|
|
|
|
|
record.args()
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.filter(None, LevelFilter::Info)
|
|
|
|
|
.init();
|
|
|
|
|
|
|
|
|
|
log::warn!("warn");
|
|
|
|
|
log::info!("info");
|
|
|
|
|
log::debug!("debug");
|
|
|
|
|
}
|
|
|
|
|
</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() -> Result<()> {
|
|
|
|
|
// 创建日志配置,并指定输出的位置
|
|
|
|
|
let logfile = FileAppender::builder()
|
|
|
|
|
// 编码模式的详情参见: https://docs.rs/log4rs/1.0.0/log4rs/encode/pattern/index.html
|
|
|
|
|
.encoder(Box::new(PatternEncoder::new("{l} - {m}\n")))
|
|
|
|
|
.build("log/output.log")?;
|
|
|
|
|
|
|
|
|
|
let config = Config::builder()
|
|
|
|
|
.appender(Appender::builder().build("logfile", Box::new(logfile)))
|
|
|
|
|
.build(Root::builder()
|
|
|
|
|
.appender("logfile")
|
|
|
|
|
.build(LevelFilter::Info))?;
|
|
|
|
|
|
|
|
|
|
log4rs::init_config(config)?;
|
|
|
|
|
|
|
|
|
|
log::info!("Hello, world!");
|
|
|
|
|
|
|
|
|
|
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() -> Result<(), SemVerError> {
|
|
|
|
|
let mut parsed_version = Version::parse("0.2.6")?;
|
|
|
|
|
|
|
|
|
|
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(), "0.2.7");
|
|
|
|
|
println!("New patch release: v{}", parsed_version);
|
|
|
|
|
|
|
|
|
|
parsed_version.increment_minor();
|
|
|
|
|
assert_eq!(parsed_version.to_string(), "0.3.0");
|
|
|
|
|
println!("New minor release: v{}", parsed_version);
|
|
|
|
|
|
|
|
|
|
parsed_version.increment_major();
|
|
|
|
|
assert_eq!(parsed_version.to_string(), "1.0.0");
|
|
|
|
|
println!("New major release: v{}", 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() -> Result<(), SemVerError> {
|
|
|
|
|
let version_str = "1.0.49-125+g72ee7853";
|
|
|
|
|
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("g72ee7853"))]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let serialized_version = parsed_version.to_string();
|
|
|
|
|
assert_eq!(&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() -> Result<(), SemVerError> {
|
|
|
|
|
let version_1 = Version::parse("1.0.0-alpha")?;
|
|
|
|
|
let version_2 = Version::parse("1.0.0")?;
|
|
|
|
|
|
|
|
|
|
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<'a, I>(version_req_str: &str, iterable: I) -> Result<Option<Version>>
|
|
|
|
|
where
|
|
|
|
|
I: IntoIterator<Item = &'a str>,
|
|
|
|
|
{
|
|
|
|
|
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() -> Result<()> {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
find_max_matching_version("<= 1.0.0", vec!["0.9.0", "1.0.0", "1.0.1"])?,
|
|
|
|
|
Some(Version::parse("1.0.0")?)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
find_max_matching_version(
|
|
|
|
|
">1.2.3-alpha.3",
|
|
|
|
|
vec![
|
|
|
|
|
"1.2.3-alpha.3",
|
|
|
|
|
"1.2.3-alpha.4",
|
|
|
|
|
"1.2.3-alpha.10",
|
|
|
|
|
"1.2.3-beta.4",
|
|
|
|
|
"3.4.5-alpha.9",
|
|
|
|
|
]
|
|
|
|
|
)?,
|
|
|
|
|
Some(Version::parse("1.2.3-beta.4")?)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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() -> Result<()> {
|
|
|
|
|
let version_constraint = "> 1.12.0";
|
|
|
|
|
let version_test = VersionReq::parse(version_constraint)?;
|
|
|
|
|
let output = Command::new("git").arg("--version").output()?;
|
|
|
|
|
|
|
|
|
|
if !output.status.success() {
|
|
|
|
|
error_chain::bail!("Command executed with failing error code");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let stdout = String::from_utf8(output.stdout)?;
|
|
|
|
|
let version = stdout.split(" ").last().ok_or_else(|| {
|
|
|
|
|
"Invalid command output"
|
|
|
|
|
})?;
|
|
|
|
|
let parsed_version = Version::parse(version)?;
|
|
|
|
|
|
|
|
|
|
if !version_test.matches(&parsed_version) {
|
|
|
|
|
error_chain::bail!("Command version lower than minimum supported version (found {}, need {})",
|
|
|
|
|
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 = "build.rs"
|
|
|
|
|
|
|
|
|
|
[build-dependencies]
|
|
|
|
|
cc = "1"
|
|
|
|
|
|
|
|
|
|
[dependencies]
|
|
|
|
|
error-chain = "0.11"
|
|
|
|
|
</code></pre>
|
|
|
|
|
<p><em>build.rs</em></p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
cc::Build::new()
|
|
|
|
|
.file("src/hello.c")
|
|
|
|
|
.compile("hello"); // outputs `libhello.a`
|
|
|
|
|
}
|
|
|
|
|
</code></pre></pre>
|
|
|
|
|
<p><em>src/hello.c</em></p>
|
|
|
|
|
<pre><code class="language-C">#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void hello() {
|
|
|
|
|
printf("Hello from C!\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void greet(const char* name) {
|
|
|
|
|
printf("Hello, %s!\n", 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: &str) -> Result<String> {
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
print!("{}", s);
|
|
|
|
|
std::io::stdout().flush()?;
|
|
|
|
|
let mut input = String::new();
|
|
|
|
|
std::io::stdin().read_line(&mut input)?;
|
|
|
|
|
Ok(input.trim().to_string())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern {
|
|
|
|
|
fn hello();
|
|
|
|
|
fn greet(name: *const c_char);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
unsafe { hello() }
|
|
|
|
|
let name = prompt("What's your name? ")?;
|
|
|
|
|
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 "C"</code> 来阻止 C++ 编译器对库名进行名称重整( name mangling )。</p>
|
|
|
|
|
<p><em>Cargo.toml</em></p>
|
|
|
|
|
<pre><code class="language-toml">[package]
|
|
|
|
|
...
|
|
|
|
|
build = "build.rs"
|
|
|
|
|
|
|
|
|
|
[build-dependencies]
|
|
|
|
|
cc = "1"
|
|
|
|
|
</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("src/foo.cpp")
|
|
|
|
|
.compile("foo");
|
|
|
|
|
}
|
|
|
|
|
</code></pre></pre>
|
|
|
|
|
<p><em>src/foo.cpp</em></p>
|
|
|
|
|
<pre><code class="language-c++">extern "C" {
|
|
|
|
|
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) -> i32;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main(){
|
|
|
|
|
unsafe {
|
|
|
|
|
println!("{}", 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 = "1.0.2"
|
|
|
|
|
build = "build.rs"
|
|
|
|
|
|
|
|
|
|
[build-dependencies]
|
|
|
|
|
cc = "1"
|
|
|
|
|
</code></pre>
|
|
|
|
|
<p><em>build.rs</em></p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
|
|
|
|
|
cc::Build::new()
|
|
|
|
|
.define("APP_NAME", "\"foo\"")
|
|
|
|
|
.define("VERSION", format!("\"{}\"", env!("CARGO_PKG_VERSION")).as_str())
|
|
|
|
|
.define("WELCOME", None)
|
|
|
|
|
.file("src/foo.c")
|
|
|
|
|
.compile("foo");
|
|
|
|
|
}
|
|
|
|
|
</code></pre></pre>
|
|
|
|
|
<p><em>src/foo.c</em></p>
|
|
|
|
|
<pre><code class="language-C">#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
void print_app_info() {
|
|
|
|
|
#ifdef WELCOME
|
|
|
|
|
printf("Welcome to ");
|
|
|
|
|
#endif
|
|
|
|
|
printf("%s - version %s\n", 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: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`');
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<(), Utf8Error> {
|
|
|
|
|
let input = "confident, productive systems programming";
|
|
|
|
|
|
|
|
|
|
let iter = utf8_percent_encode(input, FRAGMENT);
|
|
|
|
|
// 将元素类型为 &str 的迭代器收集为 String 类型
|
|
|
|
|
let encoded: String = iter.collect();
|
|
|
|
|
assert_eq!(encoded, "confident,%20productive%20systems%20programming");
|
|
|
|
|
|
|
|
|
|
let iter = percent_decode(encoded.as_bytes());
|
|
|
|
|
let decoded = iter.decode_utf8()?;
|
|
|
|
|
assert_eq!(decoded, "confident, productive systems programming");
|
|
|
|
|
|
|
|
|
|
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("What is ❤?".as_bytes()).collect();
|
|
|
|
|
assert_eq!(urlencoded, "What+is+%E2%9D%A4%3F");
|
|
|
|
|
println!("urlencoded:'{}'", urlencoded);
|
|
|
|
|
|
|
|
|
|
let decoded: String = parse(urlencoded.as_bytes())
|
|
|
|
|
.map(|(key, val)| [key, val].concat())
|
|
|
|
|
.collect();
|
|
|
|
|
assert_eq!(decoded, "What is ❤?");
|
|
|
|
|
println!("decoded:'{}'", 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>&[u8]</code> 转换成十六进制等效形式,然后与期待的值进行比较。</p>
|
|
|
|
|
<pre><pre class="playground"><code class="language-rust editable edition2021">use data_encoding::{HEXUPPER, DecodeError};
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<(), DecodeError> {
|
|
|
|
|
let original = b"The quick brown fox jumps over the lazy dog.";
|
|
|
|
|
let expected = "54686520717569636B2062726F776E20666F78206A756D7073206F76\
|
|
|
|
|
657220746865206C617A7920646F672E";
|
|
|
|
|
|
|
|
|
|
let encoded = HEXUPPER.encode(original);
|
|
|
|
|
assert_eq!(encoded, expected);
|
|
|
|
|
|
|
|
|
|
let decoded = HEXUPPER.decode(&encoded.into_bytes())?;
|
|
|
|
|
assert_eq!(&decoded[..], &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() -> Result<()> {
|
|
|
|
|
// 将 `&str` 转换成 `&[u8; N]`
|
|
|
|
|
let hello = b"hello rustaceans";
|
|
|
|
|
let encoded = encode(hello);
|
|
|
|
|
let decoded = decode(&encoded)?;
|
|
|
|
|
|
|
|
|
|
println!("origin: {}", str::from_utf8(hello)?);
|
|
|
|
|
println!("base64 encoded: {}", encoded);
|
|
|
|
|
println!("back to origin: {}", str::from_utf8(&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() -> Result<(), Error> {
|
|
|
|
|
let csv = "year,make,model,description
|
|
|
|
|
1948,Porsche,356,Luxury sports car
|
|
|
|
|
1967,Ford,Mustang fastback 1967,American car";
|
|
|
|
|
|
|
|
|
|
let mut reader = csv::Reader::from_reader(csv.as_bytes());
|
|
|
|
|
for record in reader.records() {
|
|
|
|
|
let record = record?;
|
|
|
|
|
println!(
|
|
|
|
|
"In {}, {} built the {} model. It is a {}.",
|
|
|
|
|
&record[0],
|
|
|
|
|
&record[1],
|
|
|
|
|
&record[2],
|
|
|
|
|
&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() -> Result<(), csv::Error> {
|
|
|
|
|
let csv = "year,make,model,description
|
|
|
|
|
1948,Porsche,356,Luxury sports car
|
|
|
|
|
1967,Ford,Mustang fastback 1967,American car";
|
|
|
|
|
|
|
|
|
|
let mut reader = csv::Reader::from_reader(csv.as_bytes());
|
|
|
|
|
|
|
|
|
|
for record in reader.deserialize() {
|
|
|
|
|
let record: Record = record?;
|
|
|
|
|
println!(
|
|
|
|
|
"In {}, {} built the {} model. It is a {}.",
|
|
|
|
|
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 = "csv::invalid_option")]
|
|
|
|
|
id: Option<u64>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
use csv::ReaderBuilder;
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<(), Error> {
|
|
|
|
|
let data = "name\tplace\tid
|
|
|
|
|
Mark\tMelbourne\t46
|
|
|
|
|
Ashley\tZurich\t92";
|
|
|
|
|
|
|
|
|
|
let mut reader = ReaderBuilder::new().delimiter(b'\t').from_reader(data.as_bytes());
|
|
|
|
|
for result in reader.deserialize::<Record>() {
|
|
|
|
|
println!("{:?}", 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() -> Result<()> {
|
|
|
|
|
let query = "CA";
|
|
|
|
|
let data = "\
|
|
|
|
|
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";
|
|
|
|
|
|
|
|
|
|
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(&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() -> Result<()> {
|
|
|
|
|
let mut wtr = csv::Writer::from_writer(io::stdout());
|
|
|
|
|
|
|
|
|
|
wtr.write_record(&["Name", "Place", "ID"])?;
|
|
|
|
|
|
|
|
|
|
wtr.serialize(("Mark", "Sydney", 87))?;
|
|
|
|
|
wtr.serialize(("Ashley", "Dublin", 32))?;
|
|
|
|
|
wtr.serialize(("Akshat", "Delhi", 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<'a> {
|
|
|
|
|
name: &'a str,
|
|
|
|
|
place: &'a str,
|
|
|
|
|
id: u64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
let mut wtr = csv::Writer::from_writer(io::stdout());
|
|
|
|
|
|
|
|
|
|
let rec1 = Record { name: "Mark", place: "Melbourne", id: 56};
|
|
|
|
|
let rec2 = Record { name: "Ashley", place: "Sydney", id: 64};
|
|
|
|
|
let rec3 = Record { name: "Akshat", place: "Delhi", 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<Writer<Vec<u8>>>);
|
|
|
|
|
</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: &str) -> std::result::Result<Self, Self::Err> {
|
|
|
|
|
let trimmed = hex_color.trim_matches('#');
|
|
|
|
|
if trimmed.len() != 6 {
|
|
|
|
|
Err("Invalid length of hex string".into())
|
|
|
|
|
} else {
|
|
|
|
|
Ok(HexColor {
|
|
|
|
|
red: u8::from_str_radix(&trimmed[..2], 16)?,
|
|
|
|
|
green: u8::from_str_radix(&trimmed[2..4], 16)?,
|
|
|
|
|
blue: u8::from_str_radix(&trimmed[4..6], 16)?,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for HexColor {
|
|
|
|
|
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
|
|
|
|
where
|
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
|
{
|
|
|
|
|
let s = String::deserialize(deserializer)?;
|
|
|
|
|
FromStr::from_str(&s).map_err(de::Error::custom)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
let data = "color_name,color
|
|
|
|
|
red,#ff0000
|
|
|
|
|
green,#00ff00
|
|
|
|
|
blue,#0000FF
|
|
|
|
|
periwinkle,#ccccff
|
|
|
|
|
magenta,#ff00ff"
|
|
|
|
|
.to_owned();
|
|
|
|
|
let mut out = Writer::from_writer(vec![]);
|
|
|
|
|
let mut reader = Reader::from_reader(data.as_bytes());
|
|
|
|
|
for result in reader.deserialize::<Row>() {
|
|
|
|
|
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("magenta,255,0,255"), written.lines().last());
|
|
|
|
|
println!("{}", 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() -> Result<(), Error> {
|
|
|
|
|
let j = r#"{
|
|
|
|
|
"userid": 103609,
|
|
|
|
|
"verified": true,
|
|
|
|
|
"access_privileges": [
|
|
|
|
|
"user",
|
|
|
|
|
"admin"
|
|
|
|
|
]
|
|
|
|
|
}"#;
|
|
|
|
|
|
|
|
|
|
let parsed: Value = serde_json::from_str(j)?;
|
|
|
|
|
|
|
|
|
|
let expected = json!({
|
|
|
|
|
"userid": 103609,
|
|
|
|
|
"verified": true,
|
|
|
|
|
"access_privileges": [
|
|
|
|
|
"user",
|
|
|
|
|
"admin"
|
|
|
|
|
]
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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() -> Result<(), Error> {
|
|
|
|
|
let toml_content = r#"
|
|
|
|
|
[package]
|
|
|
|
|
name = "your_package"
|
|
|
|
|
version = "0.1.0"
|
|
|
|
|
authors = ["You! <you@example.org>"]
|
|
|
|
|
|
|
|
|
|
[dependencies]
|
|
|
|
|
serde = "1.0"
|
|
|
|
|
"#;
|
|
|
|
|
|
|
|
|
|
let package_info: Value = toml::from_str(toml_content)?;
|
|
|
|
|
|
|
|
|
|
assert_eq!(package_info["dependencies"]["serde"].as_str(), Some("1.0"));
|
|
|
|
|
assert_eq!(package_info["package"]["name"].as_str(),
|
|
|
|
|
Some("your_package"));
|
|
|
|
|
|
|
|
|
|
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<String, String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
|
struct Package {
|
|
|
|
|
name: String,
|
|
|
|
|
version: String,
|
|
|
|
|
authors: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<(), Error> {
|
|
|
|
|
let toml_content = r#"
|
|
|
|
|
[package]
|
|
|
|
|
name = "your_package"
|
|
|
|
|
version = "0.1.0"
|
|
|
|
|
authors = ["You! <you@example.org>"]
|
|
|
|
|
|
|
|
|
|
[dependencies]
|
|
|
|
|
serde = "1.0"
|
|
|
|
|
"#;
|
|
|
|
|
|
|
|
|
|
let package_info: Config = toml::from_str(toml_content)?;
|
|
|
|
|
|
|
|
|
|
assert_eq!(package_info.package.name, "your_package");
|
|
|
|
|
assert_eq!(package_info.package.version, "0.1.0");
|
|
|
|
|
assert_eq!(package_info.package.authors, vec!["You! <you@example.org>"]);
|
|
|
|
|
assert_eq!(package_info.dependencies["serde"], "1.0");
|
|
|
|
|
|
|
|
|
|
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() -> Result<(), Error> {
|
|
|
|
|
let original_payload = Payload::default();
|
|
|
|
|
let encoded_bytes = encode(&original_payload)?;
|
|
|
|
|
let decoded_payload = decode(&encoded_bytes)?;
|
|
|
|
|
assert_eq!(original_payload, decoded_payload);
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn encode(payload: &Payload) -> Result<Vec<u8>, Error> {
|
|
|
|
|
let mut bytes = vec![];
|
|
|
|
|
bytes.write_u8(payload.kind)?;
|
|
|
|
|
bytes.write_u16::<LittleEndian>(payload.value)?;
|
|
|
|
|
Ok(bytes)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn decode(mut bytes: &[u8]) -> Result<Payload, Error> {
|
|
|
|
|
let payload = Payload {
|
|
|
|
|
kind: bytes.read_u8()?,
|
|
|
|
|
value: bytes.read_u16::<LittleEndian>()?,
|
|
|
|
|
};
|
|
|
|
|
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() -> Result<(), Error> {
|
|
|
|
|
let path = "lines.txt";
|
|
|
|
|
|
|
|
|
|
// 创建文件
|
|
|
|
|
let mut output = File::create(path)?;
|
|
|
|
|
// 写入三行内容
|
|
|
|
|
write!(output, "Rust\n💖\nFun")?;
|
|
|
|
|
|
|
|
|
|
let input = File::open(path)?;
|
|
|
|
|
let buffered = BufReader::new(input);
|
|
|
|
|
|
|
|
|
|
// 迭代文件中的每一行内容,line 是字符串
|
|
|
|
|
for line in buffered.lines() {
|
|
|
|
|
println!("{}", 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() -> Result<(), Error> {
|
|
|
|
|
let path_to_read = Path::new("new.txt");
|
|
|
|
|
|
|
|
|
|
// 从标准输出上获取待写入的文件名
|
|
|
|
|
let stdout_handle = Handle::stdout()?;
|
|
|
|
|
// 将待写入的文件名跟待读取的文件名进行比较
|
|
|
|
|
let handle = Handle::from_path(path_to_read)?;
|
|
|
|
|
|
|
|
|
|
if stdout_handle == handle {
|
|
|
|
|
return Err(Error::new(
|
|
|
|
|
ErrorKind::Other,
|
|
|
|
|
"You are reading and writing to the same file",
|
|
|
|
|
));
|
|
|
|
|
} else {
|
|
|
|
|
let file = File::open(&path_to_read)?;
|
|
|
|
|
let file = BufReader::new(file);
|
|
|
|
|
for (num, line) in file.lines().enumerate() {
|
|
|
|
|
println!("{} : {}", num, line?.to_uppercase());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
</code></pre></pre>
|
|
|
|
|
<p>以下代码会报错,因为待写入的文件名也是 <em>new.txt</em>,跟待读取的文件名相同</p>
|
|
|
|
|
<pre><code class="language-shell">cargo run >> ./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() -> Result<(), Error> {
|
|
|
|
|
write!(File::create("content.txt")?, "My hovercraft is full of eels!")?;
|
|
|
|
|
|
|
|
|
|
let file = File::open("content.txt")?;
|
|
|
|
|
let map = unsafe { Mmap::map(&file)? };
|
|
|
|
|
|
|
|
|
|
let random_indexes = [0, 1, 2, 19, 22, 10, 11, 29];
|
|
|
|
|
assert_eq!(&map[3..13], b"hovercraft");
|
|
|
|
|
let random_bytes: Vec<u8> = random_indexes.iter()
|
|
|
|
|
.map(|&idx| map[idx])
|
|
|
|
|
.collect();
|
|
|
|
|
assert_eq!(&random_bytes[..], b"My loaf!");
|
|
|
|
|
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() -> Result<()> {
|
|
|
|
|
let current_dir = env::current_dir()?;
|
|
|
|
|
println!(
|
|
|
|
|
"Entries modified in the last 24 hours in {:?}:",
|
|
|
|
|
current_dir
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for entry in fs::read_dir(current_dir)? {
|
|
|
|
|
let entry = entry?;
|
|
|
|
|
let path = entry.path();
|
|
|
|
|
|
|
|
|
|
let metadata = fs::metadata(&path)?;
|
|
|
|
|
let last_modified = metadata.modified()?.elapsed()?.as_secs();
|
|
|
|
|
|
|
|
|
|
if last_modified < 24 * 3600 && metadata.is_file() {
|
|
|
|
|
println!(
|
|
|
|
|
"Last modified: {:?} seconds, is read only: {:?}, size: {:?} bytes, filename: {:?}",
|
|
|
|
|
last_modified,
|
|
|
|
|
metadata.permissions().readonly(),
|
|
|
|
|
metadata.len(),
|
|
|
|
|
path.file_name().ok_or("No filename")?
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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> 可以检查给定路径的 loops,loop 可以通过以下方式创建:</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<P: AsRef<Path>>(path: P) -> io::Result<Option<(PathBuf, PathBuf)>> {
|
|
|
|
|
let path = path.as_ref();
|
|
|
|
|
let mut path_buf = path.to_path_buf();
|
|
|
|
|
while path_buf.pop() {
|
|
|
|
|
if is_same_file(&path_buf, path)? {
|
|
|
|
|
return Ok(Some((path_buf, path.to_path_buf())));
|
|
|
|
|
} else if let Some(looped_paths) = contains_loop(&path_buf)? {
|
|
|
|
|
return Ok(Some(looped_paths));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Ok(None);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
contains_loop("/tmp/foo/bar/baz/qux/bar/baz").unwrap(),
|
|
|
|
|
Some((
|
|
|
|
|
PathBuf::from("/tmp/foo"),
|
|
|
|
|
PathBuf::from("/tmp/foo/bar/baz/qux")
|
|
|
|
|
))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
</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(".")
|
|
|
|
|
.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!("{}", 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() -> Result<()> {
|
|
|
|
|
for entry in WalkDir::new(".")
|
|
|
|
|
.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(".json") && sec.elapsed()?.as_secs() < 86400 {
|
|
|
|
|
println!("{}", 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: &DirEntry) -> bool {
|
|
|
|
|
entry
|
|
|
|
|
.file_name()
|
|
|
|
|
.to_str()
|
|
|
|
|
.map(|s| entry.depth() == 0 || !s.starts_with("."))
|
|
|
|
|
.unwrap_or(false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
WalkDir::new(".")
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter_entry(|e| is_not_hidden(e))
|
|
|
|
|
.filter_map(|v| v.ok())
|
|
|
|
|
.for_each(|x| println!("{}", 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(".")
|
|
|
|
|
.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!("Total size: {} bytes.", 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() -> Result<()> {
|
|
|
|
|
for entry in glob("**/*.png")? {
|
|
|
|
|
println!("{}", 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() -> Result<()> {
|
|
|
|
|
let options = MatchOptions {
|
|
|
|
|
case_sensitive: false,
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for entry in glob_with("/media/img_[0-9]*.png", options)? {
|
|
|
|
|
println!("{}", 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<&'static str, Vec<&'static str>> = {
|
|
|
|
|
let mut map = HashMap::new();
|
|
|
|
|
map.insert("James", vec!["user", "admin"]);
|
|
|
|
|
map.insert("Jim", vec!["user"]);
|
|
|
|
|
map
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn show_access(name: &str) {
|
|
|
|
|
let access = PRIVILEGES.get(name);
|
|
|
|
|
println!("{}: {:?}", name, access);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let access = PRIVILEGES.get("James");
|
|
|
|
|
println!("James: {:?}", access);
|
|
|
|
|
|
|
|
|
|
show_access("Jim");
|
|
|
|
|
}
|
|
|
|
|
</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() -> Result<(), Error> {
|
|
|
|
|
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!("Listening on {}, access this port to end the program", port);
|
|
|
|
|
let (mut tcp_stream, addr) = listener.accept()?; //block until requested
|
|
|
|
|
println!("Connection received! {:?} is sending data.", addr);
|
|
|
|
|
let mut input = String::new();
|
|
|
|
|
let _ = tcp_stream.read_to_string(&mut input)?;
|
|
|
|
|
println!("{:?} says {}", 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: &str) -> Option<&str> {
|
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref RE: Regex = Regex::new(r"(?x)
|
|
|
|
|
^(?P<login>[^@\s]+)@
|
|
|
|
|
([[:word:]]+\.)*
|
|
|
|
|
[[:word:]]+$
|
|
|
|
|
").unwrap();
|
|
|
|
|
}
|
|
|
|
|
RE.captures(input).and_then(|cap| {
|
|
|
|
|
cap.name("login").map(|login| login.as_str())
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
assert_eq!(extract_login(r"I❤email@example.com"), Some(r"I❤email"));
|
|
|
|
|
assert_eq!(
|
|
|
|
|
extract_login(r"sdf+sdsfsd.as.sdsd@jhkk.d.rl"),
|
|
|
|
|
Some(r"sdf+sdsfsd.as.sdsd")
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(extract_login(r"More@Than@One@at.com"), None);
|
|
|
|
|
assert_eq!(extract_login(r"Not an email@email"), 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: &str) -> HashSet<&str> {
|
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref HASHTAG_REGEX : Regex = Regex::new(
|
|
|
|
|
r"\#[a-zA-Z][0-9a-zA-Z_]*"
|
|
|
|
|
).unwrap();
|
|
|
|
|
}
|
|
|
|
|
HASHTAG_REGEX.find_iter(text).map(|mat| mat.as_str()).collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let tweet = "Hey #world, I just got my new #dog, say hello to Till. #dog #forever #2 #_ ";
|
|
|
|
|
let tags = extract_hashtags(tweet);
|
|
|
|
|
assert!(tags.contains("#dog") && tags.contains("#forever") && tags.contains("#world"));
|
|
|
|
|
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<'a> {
|
|
|
|
|
area: &'a str,
|
|
|
|
|
exchange: &'a str,
|
|
|
|
|
subscriber: &'a str,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> fmt::Display for PhoneNumber<'a> {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f, "1 ({}) {}-{}", self.area, self.exchange, self.subscriber)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() -> Result<()> {
|
|
|
|
|
let phone_text = "
|
|
|
|
|
+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";
|
|
|
|
|
|
|
|
|
|
let re = Regex::new(
|
|
|
|
|
r#"(?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"#,
|
|
|
|
|
)?;
|
|
|
|
|
|
|
|
|
|
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)) => Some(PhoneNumber {
|
|
|
|
|
area: area.as_str(),
|
|
|
|
|
exchange: ext.as_str(),
|
|
|
|
|
subscriber: sub.as_str(),
|
|
|
|
|
}),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
phone_numbers.map(|m| m.to_string()).collect::<Vec<_>>(),
|
|
|
|
|
vec![
|
|
|
|
|
"1 (505) 881-9292",
|
|
|
|
|
"1 (505) 778-2212",
|
|
|
|
|
"1 (505) 881-9297",
|
|
|
|
|
"1 (202) 991-9534",
|
|
|
|
|
"1 (555) 392-0011",
|
|
|
|
|
"1 (800) 233-2010",
|
|
|
|
|
"1 (299) 339-1020",
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
</code></pre></pre>
|
|
|
|
|
<h3 id="通过多个正则来过滤日志文件"><a class="header" href="#通过多个正则来过滤日志文件">通过多个正则来过滤日志文件</a></h3>
|
|
|
|
|
<p>例子的目标是过滤出包含 "version X.X.X"、以 443 结尾的 IP 地址和特别的警告的日志行。</p>
|
|
|
|
|
<p>值得注意的是,由于在正则中反斜杠非常常见,因此使用 <code>r#""</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() -> Result<()> {
|
|
|
|
|
let log_path = "application.log";
|
|
|
|
|
let buffered = BufReader::new(File::open(log_path)?);
|
|
|
|
|
|
|
|
|
|
let set = RegexSetBuilder::new(&[
|
|
|
|
|
r#"version "\d\.\d\.\d""#,
|
|
|
|
|
r#"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:443"#,
|
|
|
|
|
r#"warning.*timeout expired"#,
|
|
|
|
|
]).case_insensitive(true)
|
|
|
|
|
.build()?;
|
|
|
|
|
|
|
|
|
|
buffered
|
|
|
|
|
.lines()
|
|
|
|
|
.filter_map(|line| line.ok())
|
|
|
|
|
.filter(|line| set.is_match(line.as_str()))
|
|
|
|
|
.for_each(|x| println!("{}", x));
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
</code></pre></pre>
|
|
|
|
|
<h3 id="将文本中所有的指定模式替换成另外一种模式"><a class="header" href="#将文本中所有的指定模式替换成另外一种模式">将文本中所有的指定模式替换成另外一种模式</a></h3>
|
|
|
|
|
<p>下面代码将标准的 ISO 8601 YYYY-MM-DD 日期模式替换成带有斜杠的美式英语日期。例如 <code>2013-01-15</code> -> <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: &str) -> Cow<str> {
|
|
|
|
|
lazy_static! {
|
|
|
|
|
static ref ISO8601_DATE_REGEX : Regex = Regex::new(
|
|
|
|
|
r"(?P<y>\d{4})-(?P<m>\d{2})-(?P<d>\d{2})"
|
|
|
|
|
).unwrap();
|
|
|
|
|
}
|
|
|
|
|
ISO8601_DATE_REGEX.replace_all(before, "$m/$d/$y")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let before = "2012-03-14, 2013-01-15 and 2014-07-05";
|
|
|
|
|
let after = reformat_dates(before);
|
|
|
|
|
assert_eq!(after, "03/14/2012, 01/15/2013 and 07/05/2014");
|
|
|
|
|
}
|
|
|
|
|
</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 = "José Guimarães\r\n";
|
|
|
|
|
let graphemes = UnicodeSegmentation::graphemes(name, true)
|
|
|
|
|
.collect::<Vec<&str>>();
|
|
|
|
|
assert_eq!(graphemes[3], "é");
|
|
|
|
|
}
|
|
|
|
|
</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: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
|
|
|
|
|
// u8::from_str_radix(src: &str, radix: u32) 将一个字符串切片按照指定的基数转换为 u8 类型
|
|
|
|
|
let r: u8 = u8::from_str_radix(&hex_code[1..3], 16)?;
|
|
|
|
|
let g: u8 = u8::from_str_radix(&hex_code[3..5], 16)?;
|
|
|
|
|
let b: u8 = u8::from_str_radix(&hex_code[5..7], 16)?;
|
|
|
|
|
|
|
|
|
|
Ok(RGB { r, g, b })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
let code: &str = &r"#fa7268";
|
|
|
|
|
match RGB::from_str(code) {
|
|
|
|
|
Ok(rgb) => {
|
|
|
|
|
println!(
|
|
|
|
|
r"The RGB color code is: R: {} G: {} B: {}",
|
|
|
|
|
rgb.r, rgb.g, rgb.b
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
Err(_) => {
|
|
|
|
|
println!("{} is not a valid color hex code!", code);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 测试 from_str 是否按照预期工作
|
|
|
|
|
assert_eq!(
|
|
|
|
|
RGB::from_str(&r"#fa7268").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>
|