|
|
<!DOCTYPE HTML>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<title>`panic!`与不可恢复的错误 - Rust 程序设计语言 简体中文版</title>
|
|
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
|
<meta name="description" content="Rust 程序设计语言 简体中文版">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
|
|
|
<base href="">
|
|
|
|
|
|
<link rel="stylesheet" href="book.css">
|
|
|
<link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>
|
|
|
|
|
|
<link rel="shortcut icon" href="favicon.png">
|
|
|
|
|
|
<!-- Font Awesome -->
|
|
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
|
|
|
|
|
|
<link rel="stylesheet" href="highlight.css">
|
|
|
<link rel="stylesheet" href="tomorrow-night.css">
|
|
|
|
|
|
<!-- MathJax -->
|
|
|
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
|
|
|
|
|
<!-- Fetch JQuery from CDN but have a local fallback -->
|
|
|
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
|
|
|
<script>
|
|
|
if (typeof jQuery == 'undefined') {
|
|
|
document.write(unescape("%3Cscript src='jquery.js'%3E%3C/script%3E"));
|
|
|
}
|
|
|
</script>
|
|
|
</head>
|
|
|
<body class="light">
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
|
<script type="text/javascript">
|
|
|
var theme = localStorage.getItem('theme');
|
|
|
if (theme == null) { theme = 'light'; }
|
|
|
$('body').removeClass().addClass(theme);
|
|
|
</script>
|
|
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
|
<script type="text/javascript">
|
|
|
var sidebar = localStorage.getItem('sidebar');
|
|
|
if (sidebar === "hidden") { $("html").addClass("sidebar-hidden") }
|
|
|
else if (sidebar === "visible") { $("html").addClass("sidebar-visible") }
|
|
|
</script>
|
|
|
|
|
|
<div id="sidebar" class="sidebar">
|
|
|
<ul class="chapter"><li><a href="ch01-00-introduction.html"><strong>1.</strong> 介绍</a></li><li><ul class="section"><li><a href="ch01-01-installation.html"><strong>1.1.</strong> 安装</a></li><li><a href="ch01-02-hello-world.html"><strong>1.2.</strong> Hello, World!</a></li></ul></li><li><a href="ch02-00-guessing-game-tutorial.html"><strong>2.</strong> 猜猜看教程</a></li><li><a href="ch03-00-common-programming-concepts.html"><strong>3.</strong> 通用编程概念</a></li><li><ul class="section"><li><a href="ch03-01-variables-and-mutability.html"><strong>3.1.</strong> 变量和可变性</a></li><li><a href="ch03-02-data-types.html"><strong>3.2.</strong> 数据类型</a></li><li><a href="ch03-03-how-functions-work.html"><strong>3.3.</strong> 函数如何工作</a></li><li><a href="ch03-04-comments.html"><strong>3.4.</strong> 注释</a></li><li><a href="ch03-05-control-flow.html"><strong>3.5.</strong> 控制流</a></li></ul></li><li><a href="ch04-00-understanding-ownership.html"><strong>4.</strong> 认识所有权</a></li><li><ul class="section"><li><a href="ch04-01-what-is-ownership.html"><strong>4.1.</strong> 什么是所有权</a></li><li><a href="ch04-02-references-and-borrowing.html"><strong>4.2.</strong> 引用 & 借用</a></li><li><a href="ch04-03-slices.html"><strong>4.3.</strong> Slices</a></li></ul></li><li><a href="ch05-00-structs.html"><strong>5.</strong> 结构体</a></li><li><ul class="section"><li><a href="ch05-01-method-syntax.html"><strong>5.1.</strong> 方法语法</a></li></ul></li><li><a href="ch06-00-enums.html"><strong>6.</strong> 枚举和模式匹配</a></li><li><ul class="section"><li><a href="ch06-01-defining-an-enum.html"><strong>6.1.</strong> 定义枚举</a></li><li><a href="ch06-02-match.html"><strong>6.2.</strong> <code>match</code>控制流运算符</a></li><li><a href="ch06-03-if-let.html"><strong>6.3.</strong> <code>if let</code>简单控制流</a></li></ul></li><li><a href="ch07-00-modules.html"><strong>7.</strong> 模块</a></li><li><ul class="section"><li><a href="ch07-01-mod-and-the-filesystem.html"><strong>7.1.</strong> <code>mod</code>和文件系统</a></li><li><a href="ch07-02-controlling-visibility-with-pub.html"><strong>7.2.</strong> 使用<code>pub</code>控制可见性</a></li><li><a href="ch07-03-importing-names-with-use.html"><strong>7.3.</strong> 使用<code>use</code>导入命名</a></li></ul></li><li><a href="ch08-00-common-collections.html"><strong>8.</strong> 通用集合类型</a></li><li><ul class="section"><li><a href="ch08-01-vectors.html"><strong>8.1.</strong> vector</a></li><li><a href="ch08-02-strings.html"><strong>8.2.</strong> 字符串</a></li><li><a href="ch08-03-hash-maps.html"><strong>8.3.</strong> 哈希 map</a></li></ul></li><li><a href="ch09-00-error-handling.html"><strong>9.</strong> 错误处理</a></li><li><ul class="section"><li><a href="ch09-01-unrecoverable-errors-with-panic.html" class="active"><strong>9.1.</strong> <code>panic!</code>与不可恢复的错误</a></li><li><a href="ch09-02-recoverable-errors-with-result.html"><strong>9.2.</strong> <code>Result</code>与可恢复的错误</a></li><li><a href="ch09-03-to-panic-or-not-to-panic.html"><strong>9.3.</strong> <code>panic!</code>还是不<code>panic!</code></a></li></ul></li><li><a href="ch10-00-generics.html"><strong>10.</strong> 泛型、trait 和生命周期</a></li><li><ul class="section"><li><a href="ch10-01-syntax.html"><strong>10.1.</strong> 泛型数据类型</a></li><li><a href="ch10-02-traits.html"><strong>10.2.</strong> trait:定义共享的行为</a></li><li><a href="ch10-03-lifetime-syntax.html"><strong>10.3.</strong> 生命周期与引用有效性</a></li></ul></li><li><a href="ch11-00-testing.html"><strong>11.</strong> 测试</a></li><li><ul class="section"><li><a href="ch11-01-writing-tests.html"><strong>11.1.</strong> 编写测试</a></li><li><a href="ch11-02-running-tests.html"><strong>11.2.</strong> 运行测试</a></li><li><a href="ch11-03-test-organization.html"><strong>11.3.</strong> 测试的组织结构</a></li></ul></li><li><a href="ch12-00-an-io-project.html"><strong>12.</strong> 一个 I/O 项目</a></li><li><ul class="section"><li><a href="ch12-01-accepting-command-line-arguments.html"><strong>12.1.</strong> 接受命令行参数</a></li><li><a href="ch12-02-reading-a-file.html"><strong>12.2.</strong> 读取文件</a></li><li><a href="ch12-03-improving-error-handling-and-modularity.html"><strong>12.3.</strong> 增强错误处理和模块化</a></li><li><a href="ch12-04-testing-the-librarys-functionality.html"><strong>12.4.</strong> 测试库的功能</a></li><li><a href="ch12-05-working-with-environment-variables.html"><strong>12.5.</strong> 处理环境变量</a></li><li><a href="ch12-06-writing-to-stderr-instead-of-stdout.html"><strong>12.6.</strong> 输出到<code>stderr</code>而不是<code>stdout</code></a></li></ul></li><li><a href="ch13-00-functional-features.html"><strong>13.</strong> Rust 中的函数式语言功能</a></li><li><ul class="section"><li><a href="ch13-01-closures.html"><strong>13.1.</strong> 闭包</a></li><li><a href="ch13-02-iterators.html"><strong>13.2.</strong> 迭代器</a></li><li><a href="ch13-03-improving-our-io-project.html"><strong>13.3.</strong> 改进 I/O 项目</a></li><li><a href="ch13-04-performance.html"><strong>13.4.</strong> 性能</a></li></ul></li><li><a href="ch14-00-more-about-cargo.html"><strong>14.</strong> 更多关于 Cargo 和 Crates.io</a></li><li><ul class="section"><li><a href="ch14-01-release-profiles.html"><strong>14.1.</strong> 发布配置</a></li><li><a href="ch14-02-publishing-to-crates-io.html"><strong>14.2.</strong> 将 crate 发布到 Crates.io</a></li><li><a href="ch14-03-cargo-workspaces.html"><strong>14.3.</strong> Cargo 工作空间</a></li><li><a href="ch14-04-installing-binaries.html"><strong>14.4.</strong> 使用<code>cargo install</code>从 Crates.io 安装文件</a></li><li><a href="ch14-05-extending-cargo.html"><strong>14.5.</strong> Cargo 自定义扩展命令</a></li></ul></li><li><a href="ch15-00-smart-pointers.html"><strong>15.</strong> 智能指针</a></li><li><ul class="section"><li><a href="ch15-01-box.html"><strong>15.1.</strong> <code>Box<T></code>用于已知大小的堆上数据</a></li><li><a href="ch15-02-deref.html"><strong>15.2.</strong> <code>Deref</code> Trait 允许通过引用访问数据</a></li><li><a href="ch15-03-drop.html"><strong>15.3.</strong> <code>Drop</code> Trait 运行清理代码</a></li><li><a href="ch15-04-rc.html"><strong>15.4.</strong> <code>Rc<T></code> 引用计数智能指针</a></li><li><a href="ch15-05-interior-mutability.html"><strong>15.5.</strong> <code>RefCell<T></code>和内部可变性模式</a></li><li><a href="ch15-06-reference-cycles.html"><strong>15.6.</strong> 引用循环和内存泄漏是安全的</a></li></ul></li><li><a href="ch16-00-concurrency.html"><strong>16.</strong> 无畏并发</a></li><li><ul class="section"><li><a href="ch16-01-threads.html"><strong>16.1.</strong> 线程</a></li><li><a href="ch16-02-message-passing.html"><strong>16.2.</strong> 消息传递</a></li><li><a href="ch16-03-shared-state.html"><strong>16.3.</strong> 共享状态</a></li><li><a href="ch16-04-extensible-concurrency-sync-and-send.html"><strong>16.4.</strong> 可扩展的并发:<code>Sync</code>和<code>Send</code></a></li></ul></li><li><a href="ch17-00-oop.html"><strong>17.</strong> 面向对象</a></li><li><ul class="section"><li><a href="ch17-01-what-is-oo.html"><strong>17.1.</strong> 什么是面向对象?</a></li><li><a href="ch17-02-trait-objects.html"><strong>17.2.</strong> 为使用不同类型的值而设计的 trait 对象</a></li><li><a href="ch17-03-oo-design-patterns.html"><strong>17.3.</strong> 面向对象设计模式的实现</a></li></ul></li></ul>
|
|
|
</div>
|
|
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
|
|
<div class="page">
|
|
|
<div id="menu-bar" class="menu-bar">
|
|
|
<div class="left-buttons">
|
|
|
<i id="sidebar-toggle" class="fa fa-bars"></i>
|
|
|
<i id="theme-toggle" class="fa fa-paint-brush"></i>
|
|
|
</div>
|
|
|
|
|
|
<h1 class="menu-title">Rust 程序设计语言 简体中文版</h1>
|
|
|
|
|
|
<div class="right-buttons">
|
|
|
<i id="print-button" class="fa fa-print" title="Print this book"></i>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="content" class="content">
|
|
|
<a class="header" href="#panic与不可恢复的错误" name="panic与不可恢复的错误"><h2><code>panic!</code>与不可恢复的错误</h2></a>
|
|
|
<blockquote>
|
|
|
<p><a href="https://github.com/rust-lang/book/blob/master/second-edition/src/ch09-01-unrecoverable-errors-with-panic.md">ch09-01-unrecoverable-errors-with-panic.md</a>
|
|
|
<br>
|
|
|
commit e26bb338ab14b98a850c3464e821d54940a45672</p>
|
|
|
</blockquote>
|
|
|
<p>突然有一天,糟糕的事情发生了,而你对此束手无策。对于这种情况,Rust 有<code>panic!</code>宏。当执行这个宏时,程序会打印出一个错误信息,展开并清理栈数据,然后接着退出。出现这种情况的场景通常是检测到一些类型的 bug 而且程序员并不清楚该如何处理它。</p>
|
|
|
<blockquote>
|
|
|
<a class="header" href="#panic-中的栈展开与终止" name="panic-中的栈展开与终止"><h3>Panic 中的栈展开与终止</h3></a>
|
|
|
<p>当出现<code>panic!</code>时,程序默认会开始<strong>展开</strong>(<em>unwinding</em>),这意味着 Rust 会回溯栈并清理它遇到的每一个函数的数据,不过这个回溯并清理的过程有很多工作。另一种选择是直接<strong>终止</strong>(<em>abort</em>),这会不清理数据就退出程序。那么程序所使用的内存需要由操作系统来清理。如果你需要项目的最终二进制文件越小越好,可以由 panic 时展开切换为终止,通过在 <em>Cargo.toml</em> 的<code>[profile]</code>部分增加<code>panic = 'abort'</code>。例如,如果你想要在发布模式中 panic 时直接终止:</p>
|
|
|
<pre><code class="language-toml">[profile.release]
|
|
|
panic = 'abort'
|
|
|
</code></pre>
|
|
|
</blockquote>
|
|
|
<p>让我们在一个简单的程序中调用<code>panic!</code>:</p>
|
|
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|
|
<pre><code class="language-rust,should_panic">fn main() {
|
|
|
panic!("crash and burn");
|
|
|
}
|
|
|
</code></pre>
|
|
|
<p>运行程序将会出现类似这样的输出:</p>
|
|
|
<pre><code>$ cargo run
|
|
|
Compiling panic v0.1.0 (file:///projects/panic)
|
|
|
Finished debug [unoptimized + debuginfo] target(s) in 0.25 secs
|
|
|
Running `target/debug/panic`
|
|
|
thread 'main' panicked at 'crash and burn', src/main.rs:2
|
|
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
|
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
|
|
|
</code></pre>
|
|
|
<p>最后三行包含<code>panic!</code>造成的错误信息。第一行显示了 panic 提供的信息并指明了源码中 panic 出现的位置:<em>src/main.rs:2</em> 表明这是 <em>src/main.rs</em> 文件的第二行。</p>
|
|
|
<p>在这个例子中,被指明的那一行是我们代码的一部分,而且查看这一行的话就会发现<code>panic!</code>宏的调用。换句话说,<code>panic!</code>可能会出现在我们的代码调用的代码中。错误信息报告的文件名和行号可能指向别人代码中的<code>panic!</code>宏调用,而不是我们代码中最终导致<code>panic!</code>的那一行。可以使用<code>panic!</code>被调用的函数的 backtrace 来寻找(我们代码中出问题的地方)。</p>
|
|
|
<a class="header" href="#使用panic的-backtrace" name="使用panic的-backtrace"><h3>使用<code>panic!</code>的 backtrace</h3></a>
|
|
|
<p>让我们来看看另一个因为我们代码中的 bug 引起的别的库中<code>panic!</code>的例子,而不是直接的宏调用:</p>
|
|
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|
|
<pre><code class="language-rust,should_panic">fn main() {
|
|
|
let v = vec![1, 2, 3];
|
|
|
|
|
|
v[100];
|
|
|
}
|
|
|
</code></pre>
|
|
|
<p>这里尝试访问 vector 的第一百个元素,不过它只有三个元素。这种情况下 Rust 会 panic。<code>[]</code>应当返回一个元素,不过如果传递了一个无效索引,就没有可供 Rust 返回的正确的元素。</p>
|
|
|
<p>这种情况下其他像 C 这样语言会尝直接试提供所要求的值,即便这可能不是你期望的:你会得到对任何应 vector 中这个元素的内存位置的值,甚至是这些内存并不属于 vector 的情况。这被称为<strong>缓冲区溢出</strong>(<em>buffer overread</em>),并可能会导致安全漏洞,比如攻击者可以像这样操作索引来读取储存在数组后面不被允许的数据。</p>
|
|
|
<p>为了使程序远离这类漏洞,如果尝试读取一个索引不存在的元素,Rust 会停止执行并拒绝继续。尝试运行上面的程序会出现如下:</p>
|
|
|
<pre><code>$ cargo run
|
|
|
Compiling panic v0.1.0 (file:///projects/panic)
|
|
|
Finished debug [unoptimized + debuginfo] target(s) in 0.27 secs
|
|
|
Running `target/debug/panic`
|
|
|
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is
|
|
|
100', /stable-dist-rustc/build/src/libcollections/vec.rs:1362
|
|
|
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
|
|
error: Process didn't exit successfully: `target/debug/panic` (exit code: 101)
|
|
|
</code></pre>
|
|
|
<p>这指向了一个不是我们编写的文件,<em>libcollections/vec.rs</em>。这是标准库中<code>Vec<T></code>的实现。这是当对 vector <code>v</code>使用<code>[]</code>时 <em>libcollections/vec.rs</em> 中会执行的代码,也是真正出现<code>panic!</code>的地方。</p>
|
|
|
<p>接下来的几行提醒我们可以设置<code>RUST_BACKTRACE</code>环境变量来得到一个 backtrace 来调查究竟是什么导致了错误。让我们来试试看。列表 9-1 显示了其输出:</p>
|
|
|
<pre><code>$ RUST_BACKTRACE=1 cargo run
|
|
|
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
|
|
|
Running `target/debug/panic`
|
|
|
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 100', /stable-dist-rustc/build/src/libcollections/vec.rs:1392
|
|
|
stack backtrace:
|
|
|
1: 0x560ed90ec04c - std::sys::imp::backtrace::tracing::imp::write::hf33ae72d0baa11ed
|
|
|
at /stable-dist-rustc/build/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:42
|
|
|
2: 0x560ed90ee03e - std::panicking::default_hook::{{closure}}::h59672b733cc6a455
|
|
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:351
|
|
|
3: 0x560ed90edc44 - std::panicking::default_hook::h1670459d2f3f8843
|
|
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:367
|
|
|
4: 0x560ed90ee41b - std::panicking::rust_panic_with_hook::hcf0ddb069e7abcd7
|
|
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:555
|
|
|
5: 0x560ed90ee2b4 - std::panicking::begin_panic::hd6eb68e27bdf6140
|
|
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:517
|
|
|
6: 0x560ed90ee1d9 - std::panicking::begin_panic_fmt::abcd5965948b877f8
|
|
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:501
|
|
|
7: 0x560ed90ee167 - rust_begin_unwind
|
|
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:477
|
|
|
8: 0x560ed911401d - core::panicking::panic_fmt::hc0f6d7b2c300cdd9
|
|
|
at /stable-dist-rustc/build/src/libcore/panicking.rs:69
|
|
|
9: 0x560ed9113fc8 - core::panicking::panic_bounds_check::h02a4af86d01b3e96
|
|
|
at /stable-dist-rustc/build/src/libcore/panicking.rs:56
|
|
|
10: 0x560ed90e71c5 - <collections::vec::Vec<T> as core::ops::Index<usize>>::index::h98abcd4e2a74c41
|
|
|
at /stable-dist-rustc/build/src/libcollections/vec.rs:1392
|
|
|
11: 0x560ed90e727a - panic::main::h5d6b77c20526bc35
|
|
|
at /home/you/projects/panic/src/main.rs:4
|
|
|
12: 0x560ed90f5d6a - __rust_maybe_catch_panic
|
|
|
at /stable-dist-rustc/build/src/libpanic_unwind/lib.rs:98
|
|
|
13: 0x560ed90ee926 - std::rt::lang_start::hd7c880a37a646e81
|
|
|
at /stable-dist-rustc/build/src/libstd/panicking.rs:436
|
|
|
at /stable-dist-rustc/build/src/libstd/panic.rs:361
|
|
|
at /stable-dist-rustc/build/src/libstd/rt.rs:57
|
|
|
14: 0x560ed90e7302 - main
|
|
|
15: 0x7f0d53f16400 - __libc_start_main
|
|
|
16: 0x560ed90e6659 - _start
|
|
|
17: 0x0 - <unknown>
|
|
|
</code></pre>
|
|
|
<p><span class="caption">Listing 9-1: The backtrace generated by a call to
|
|
|
<code>panic!</code> displayed when the environment variable <code>RUST_BACKTRACE</code> is set</span></p>
|
|
|
<p>这里有大量的输出!backtrace 第 11 行指向了我们程序中引起错误的行:<em>src/main.rs</em> 的第四行。backtrace 是一个执行到目前位置所有被调用的函数的列表。Rust 的 backtrace 跟其他语言中的一样:阅读 backtrace 的关键是从头开始读直到发现你编写的文件。这就是问题的发源地。这一行往上是你的代码调用的代码;往下则是调用你的代码的代码。这些行可能包含核心 Rust 代码,标准库代码或用到的 crate 代码。</p>
|
|
|
<p>如果你不希望我们的程序 panic,第一个提到我们编写的代码行的位置是你应该开始调查的,以便查明是什么值如何在这个地方引起了 panic。在上面的例子中,我们故意编写会 panic 的代码来演示如何使用 backtrace,修复这个 panic 的方法就是不要尝试在一个只包含三个项的 vector 中请求索引是 100 的元素。当将来你得代码出现了 panic,你需要搞清楚在这特定的场景下代码中执行了什么操作和什么值导致了 panic,以及应当如何处理才能避免这个问题。</p>
|
|
|
<p>本章的后面会再次回到<code>panic!</code>并讲到何时应该何时不应该使用这个方式。接下来,我们来看看如何使用<code>Result</code>来从错误中恢复。</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- Mobile navigation buttons -->
|
|
|
|
|
|
<a href="ch09-00-error-handling.html" class="mobile-nav-chapters previous">
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
<a href="ch09-02-recoverable-errors-with-result.html" class="mobile-nav-chapters next">
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
</a>
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
<a href="ch09-00-error-handling.html" class="nav-chapters previous" title="You can navigate through the chapters using the arrow keys">
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
</a>
|
|
|
|
|
|
|
|
|
|
|
|
<a href="ch09-02-recoverable-errors-with-result.html" class="nav-chapters next" title="You can navigate through the chapters using the arrow keys">
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
</a>
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
<!-- Local fallback for Font Awesome -->
|
|
|
<script>
|
|
|
if ($(".fa").css("font-family") !== "FontAwesome") {
|
|
|
$('<link rel="stylesheet" type="text/css" href="_FontAwesome/css/font-awesome.css">').prependTo('head');
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<!-- Livereload script (if served using the cli tool) -->
|
|
|
|
|
|
|
|
|
<script src="highlight.js"></script>
|
|
|
<script src="book.js"></script>
|
|
|
</body>
|
|
|
</html>
|