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

340 lines
17 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE HTML>
<html lang="zh-CN" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>断言 assertion - Rust语言圣经(Rust Course)</title>
<!-- Custom HTML head -->
<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" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../theme/style.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let 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>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</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">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<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="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</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">Rust语言圣经(Rust Course)</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/sunface/rust-course" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/sunface/rust-course/edit/main/src/test/assertion.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></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>
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">
<main>
<h1 id="断言-assertion"><a class="header" href="#断言-assertion">断言 assertion</a></h1>
<p>在编写测试函数时,断言决定了我们的测试是通过还是失败,它为结果代言。在前面,大家已经见识过 <code>assert_eq!</code> 的使用,下面一起来看看 Rust 为我们提供了哪些好用的断言。</p>
<h2 id="断言列表"><a class="header" href="#断言列表">断言列表</a></h2>
<p>在正式开始前,来看看常用的断言有哪些:</p>
<ul>
<li><code>assert!</code>, <code>assert_eq!</code>, <code>assert_ne!</code>, 它们会在所有模式下运行</li>
<li><code>debug_assert!</code>, <code>debug_assert_eq!</code>, <code>debug_assert_ne!</code>, 它们只会在 <code>Debug</code> 模式下运行</li>
</ul>
<h2 id="assert_eq"><a class="header" href="#assert_eq">assert_eq!</a></h2>
<p><code>assert_eq!</code> 宏可以用于判断两个表达式返回的值是否相等 :</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let a = 3;
let b = 1 + 2;
assert_eq!(a, b);
}</code></pre></pre>
<p>当不相等时,当前线程会直接 <code>panic</code>:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let a = 3;
let b = 1 + 3;
assert_eq!(a, b, "我们在测试两个数之和{} + {},这是额外的错误信息", a, b);
}</code></pre></pre>
<p>运行后报错如下:</p>
<pre><code class="language-shell">$ cargo run
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `3`,
right: `4`: 我们在测试两个数之和3 + 4这是额外的错误信息', src/main.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</code></pre>
<p>可以看到,错误不仅按照预期发生了,我们还成功的定制了错误信息! 这种格式化输出的方式跟 <code>println!</code> 并无区别,具体参见 <a href="https://doc.rust-lang.org/std/fmt/index.html"><code>std::fmt</code></a></p>
<p>因为涉及到相等比较( <code>==</code> )和错误信息打印,因此两个表达式的值必须实现 <code>PartialEq</code><code>Debug</code> 特征,其中所有的原生类型和大多数标准库类型都实现了这些特征,而对于你自己定义的结构体、枚举,如果想要对其进行 <code>assert_eq!</code> 断言,则需要实现 <code>PartialEq</code><code>Debug</code> 特征:</p>
<ul>
<li>若希望实现个性化相等比较和错误打印,则需手动实现</li>
<li>否则可以为自定义的结构体、枚举添加 <code>#[derive(PartialEq, Debug)]</code> 注解,来<a href="https://course.rs/appendix/derive.html">自动派生</a>对应的特征</li>
</ul>
<p><strong>以上特征限制对于下面即将讲解的 <code>assert_ne!</code> 一样有效,</strong> 就不再重复讲述。</p>
<h2 id="assert_ne"><a class="header" href="#assert_ne">assert_ne!</a></h2>
<p><code>assert_ne!</code> 在使用和限制上与 <code>assert_eq!</code> 并无区别,唯一的区别就在于,前者判断的是两者的不相等性。</p>
<p>我们将之前报错的代码稍作修改:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let a = 3;
let b = 1 + 3;
assert_ne!(a, b, "我们在测试两个数之和{} + {},这是额外的错误信息", a, b);
}</code></pre></pre>
<p>由于 <code>a</code><code>b</code> 不相等,因此 <code>assert_ne!</code> 会顺利通过,不再报错。</p>
<h2 id="assert"><a class="header" href="#assert">assert!</a></h2>
<p><code>assert!</code> 用于判断传入的布尔表达式是否为 <code>true</code>:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>// 以下断言的错误信息只包含给定表达式的返回值
assert!(true);
fn some_computation() -&gt; bool { true }
assert!(some_computation());
// 使用自定义报错信息
let x = true;
assert!(x, "x wasn't true!");
// 使用格式化的自定义报错信息
let a = 3; let b = 27;
assert!(a + b == 30, "a = {}, b = {}", a, b);
<span class="boring">}</span></code></pre></pre>
<p>来看看该如何使用 <code>assert!</code> 进行单元测试 :</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn can_hold(&amp;self, other: &amp;Rectangle) -&gt; bool {
self.width &gt; other.width &amp;&amp; self.height &gt; other.height
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn larger_can_hold_smaller() {
let larger = Rectangle {
width: 8,
height: 7,
};
let smaller = Rectangle {
width: 5,
height: 1,
};
assert!(larger.can_hold(&amp;smaller));
}
}
<span class="boring">}</span></code></pre></pre>
<h2 id="debug_assert-系列"><a class="header" href="#debug_assert-系列"><code>debug_assert!</code> 系列</a></h2>
<p><code>debug_assert!</code>, <code>debug_assert_eq!</code>, <code>debug_assert_ne!</code> 这三个在功能上与之前讲解的版本并无区别,主要区别在于,<code>debug_assert!</code> 系列只能在 <code>Debug</code> 模式下输出,例如如下代码:</p>
<pre><pre class="playground"><code class="language-rust edition2021">fn main() {
let a = 3;
let b = 1 + 3;
debug_assert_eq!(a, b, "我们在测试两个数之和{} + {},这是额外的错误信息", a, b);
}</code></pre></pre>
<p><code>Debug</code> 模式下运行输出错误信息:</p>
<pre><code class="language-shell">$ cargo run
thread 'main' panicked at 'assertion failed: `(left == right)`
left: `3`,
right: `4`: 我们在测试两个数之和3 + 4这是额外的错误信息', src/main.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</code></pre>
<p>但是在 <code>Release</code> 模式下却没有任何输出:</p>
<pre><code class="language-shell">$ cargo run --release
</code></pre>
<p>若一些断言检查会影响发布版本的性能时,大家可以使用 <code>debug_assert!</code> 来避免这种情况的发生。</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../test/unit-integration-test.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../test/ci.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../test/unit-integration-test.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../test/ci.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../ace.js"></script>
<script src="../editor.js"></script>
<script src="../mode-rust.js"></script>
<script src="../theme-dawn.js"></script>
<script src="../theme-tomorrow_night.js"></script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
<script src="../assets/custom2.js"></script>
<script src="../assets/bigPicture.js"></script>
</div>
</body>
</html>