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.

776 lines
28 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>无聊的组合 - 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/too-many-lists/production-unsafe-deque/boring-combinatorics.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="boring-combinatorics"><a class="header" href="#boring-combinatorics">Boring Combinatorics</a></h1>
<p>好了,回到我们的常规链接列表!</p>
<p>首先,让我们来解决 <code>Drop</code> 的问题:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl&lt;T&gt; Drop for LinkedList&lt;T&gt; {
fn drop(&amp;mut self) {
// Pop until we have to stop
while let Some(_) = self.pop_front() { }
}
}
<span class="boring">}</span></code></pre></pre>
<p>我们必须填写一堆非常无聊的组合实现,如 front、front_mut、back、back_mut、iter、iter_mut、into_iter......</p>
<p>我已经精心设计了之前的 push/pop 实现,因此我们只需前后对调,代码就能做正确的事情!痛苦的经验万岁!(对于节点来说,使用 "prev和next "是很有诱惑力的,但我发现,为了避免错误,尽量使用 "front "和 "back"才是真正对的)。</p>
<p>首先是 <code>front</code>:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn front(&amp;self) -&gt; Option&lt;&amp;T&gt; {
unsafe {
self.front.map(|node| &amp;(*node.as_ptr()).elem)
}
}
<span class="boring">}</span></code></pre></pre>
<p>接着是:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub fn front_mut(&amp;mut self) -&gt; Option&lt;&amp;mut T&gt; {
unsafe {
self.front.map(|node| &amp;mut (*node.as_ptr()).elem)
}
}
<span class="boring">}</span></code></pre></pre>
<p>我会把所有的 <code>back</code> 版本放到文章最终的代码中。</p>
<p>接下来是迭代器。与之前的所有列表不同,我们终于解锁了双端迭代器(<a href="https://doc.rust-lang.org/std/iter/trait.DoubleEndedIterator.html">DoubleEndedIterator</a>)的功能,而且如果要达到生产质量,我们还要支持精确大小迭代器( <a href="https://doc.rust-lang.org/std/iter/trait.ExactSizeIterator.html">ExactSizeIterator</a>)。</p>
<p>因此,除了 <code>next</code><code>size_hint</code>,我们还将支持 <code>next_back</code><code>len</code></p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct Iter&lt;'a, T&gt; {
front: Link&lt;T&gt;,
back: Link&lt;T&gt;,
len: usize,
_boo: PhantomData&lt;&amp;'a T&gt;,
}
impl&lt;T&gt; LinkedList&lt;T&gt; {
pub fn iter(&amp;self) -&gt; Iter&lt;T&gt; {
Iter {
front: self.front,
back: self.back,
len: self.len,
_boo: PhantomData,
}
}
}
impl&lt;'a, T&gt; IntoIterator for &amp;'a LinkedList&lt;T&gt; {
type IntoIter = Iter&lt;'a, T&gt;;
type Item = &amp;'a T;
fn into_iter(self) -&gt; Self::IntoIter {
self.iter()
}
}
impl&lt;'a, T&gt; Iterator for Iter&lt;'a, T&gt; {
type Item = &amp;'a T;
fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
// While self.front == self.back is a tempting condition to check here,
// it won't do the right for yielding the last element! That sort of
// thing only works for arrays because of "one-past-the-end" pointers.
if self.len &gt; 0 {
// We could unwrap front, but this is safer and easier
self.front.map(|node| unsafe {
self.len -= 1;
self.front = (*node.as_ptr()).back;
&amp;(*node.as_ptr()).elem
})
} else {
None
}
}
fn size_hint(&amp;self) -&gt; (usize, Option&lt;usize&gt;) {
(self.len, Some(self.len))
}
}
impl&lt;'a, T&gt; DoubleEndedIterator for Iter&lt;'a, T&gt; {
fn next_back(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
if self.len &gt; 0 {
self.back.map(|node| unsafe {
self.len -= 1;
self.back = (*node.as_ptr()).front;
&amp;(*node.as_ptr()).elem
})
} else {
None
}
}
}
impl&lt;'a, T&gt; ExactSizeIterator for Iter&lt;'a, T&gt; {
fn len(&amp;self) -&gt; usize {
self.len
}
}
<span class="boring">}</span></code></pre></pre>
<p>...这只是 <code>.iter()</code>...</p>
<p>我们将在最后粘贴 IterMut它在很多地方与 <code>mut</code> 的代码完全相同,让我们先敲掉 <code>into_iter</code>。我们仍然可以使用我们屡试不爽的解决方案,即让它包裹我们的集合,并在下一步中使用 <code>pop</code></p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub struct IntoIter&lt;T&gt; {
list: LinkedList&lt;T&gt;,
}
impl&lt;T&gt; LinkedList&lt;T&gt; {
pub fn into_iter(self) -&gt; IntoIter&lt;T&gt; {
IntoIter {
list: self
}
}
}
impl&lt;T&gt; IntoIterator for LinkedList&lt;T&gt; {
type IntoIter = IntoIter&lt;T&gt;;
type Item = T;
fn into_iter(self) -&gt; Self::IntoIter {
self.into_iter()
}
}
impl&lt;T&gt; Iterator for IntoIter&lt;T&gt; {
type Item = T;
fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
self.list.pop_front()
}
fn size_hint(&amp;self) -&gt; (usize, Option&lt;usize&gt;) {
(self.list.len, Some(self.list.len))
}
}
impl&lt;T&gt; DoubleEndedIterator for IntoIter&lt;T&gt; {
fn next_back(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
self.list.pop_back()
}
}
impl&lt;T&gt; ExactSizeIterator for IntoIter&lt;T&gt; {
fn len(&amp;self) -&gt; usize {
self.list.len
}
}
<span class="boring">}</span></code></pre></pre>
<p>仍然是一大堆模板,但至少是令人满意的模板。</p>
<p>好了,这是我们所有的代码,其中包含了所有的组合:</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>use std::ptr::NonNull;
use std::marker::PhantomData;
pub struct LinkedList&lt;T&gt; {
front: Link&lt;T&gt;,
back: Link&lt;T&gt;,
len: usize,
_boo: PhantomData&lt;T&gt;,
}
type Link&lt;T&gt; = Option&lt;NonNull&lt;Node&lt;T&gt;&gt;&gt;;
struct Node&lt;T&gt; {
front: Link&lt;T&gt;,
back: Link&lt;T&gt;,
elem: T,
}
pub struct Iter&lt;'a, T&gt; {
front: Link&lt;T&gt;,
back: Link&lt;T&gt;,
len: usize,
_boo: PhantomData&lt;&amp;'a T&gt;,
}
pub struct IterMut&lt;'a, T&gt; {
front: Link&lt;T&gt;,
back: Link&lt;T&gt;,
len: usize,
_boo: PhantomData&lt;&amp;'a mut T&gt;,
}
pub struct IntoIter&lt;T&gt; {
list: LinkedList&lt;T&gt;,
}
impl&lt;T&gt; LinkedList&lt;T&gt; {
pub fn new() -&gt; Self {
Self {
front: None,
back: None,
len: 0,
_boo: PhantomData,
}
}
pub fn push_front(&amp;mut self, elem: T) {
// SAFETY: it's a linked-list, what do you want?
unsafe {
let new = NonNull::new_unchecked(Box::into_raw(Box::new(Node {
front: None,
back: None,
elem,
})));
if let Some(old) = self.front {
// Put the new front before the old one
(*old.as_ptr()).front = Some(new);
(*new.as_ptr()).back = Some(old);
} else {
// If there's no front, then we're the empty list and need
// to set the back too.
self.back = Some(new);
}
// These things always happen!
self.front = Some(new);
self.len += 1;
}
}
pub fn push_back(&amp;mut self, elem: T) {
// SAFETY: it's a linked-list, what do you want?
unsafe {
let new = NonNull::new_unchecked(Box::into_raw(Box::new(Node {
back: None,
front: None,
elem,
})));
if let Some(old) = self.back {
// Put the new back before the old one
(*old.as_ptr()).back = Some(new);
(*new.as_ptr()).front = Some(old);
} else {
// If there's no back, then we're the empty list and need
// to set the front too.
self.front = Some(new);
}
// These things always happen!
self.back = Some(new);
self.len += 1;
}
}
pub fn pop_front(&amp;mut self) -&gt; Option&lt;T&gt; {
unsafe {
// Only have to do stuff if there is a front node to pop.
self.front.map(|node| {
// Bring the Box back to life so we can move out its value and
// Drop it (Box continues to magically understand this for us).
let boxed_node = Box::from_raw(node.as_ptr());
let result = boxed_node.elem;
// Make the next node into the new front.
self.front = boxed_node.back;
if let Some(new) = self.front {
// Cleanup its reference to the removed node
(*new.as_ptr()).front = None;
} else {
// If the front is now null, then this list is now empty!
self.back = None;
}
self.len -= 1;
result
// Box gets implicitly freed here, knows there is no T.
})
}
}
pub fn pop_back(&amp;mut self) -&gt; Option&lt;T&gt; {
unsafe {
// Only have to do stuff if there is a back node to pop.
self.back.map(|node| {
// Bring the Box front to life so we can move out its value and
// Drop it (Box continues to magically understand this for us).
let boxed_node = Box::from_raw(node.as_ptr());
let result = boxed_node.elem;
// Make the next node into the new back.
self.back = boxed_node.front;
if let Some(new) = self.back {
// Cleanup its reference to the removed node
(*new.as_ptr()).back = None;
} else {
// If the back is now null, then this list is now empty!
self.front = None;
}
self.len -= 1;
result
// Box gets implicitly freed here, knows there is no T.
})
}
}
pub fn front(&amp;self) -&gt; Option&lt;&amp;T&gt; {
unsafe {
self.front.map(|node| &amp;(*node.as_ptr()).elem)
}
}
pub fn front_mut(&amp;mut self) -&gt; Option&lt;&amp;mut T&gt; {
unsafe {
self.front.map(|node| &amp;mut (*node.as_ptr()).elem)
}
}
pub fn back(&amp;self) -&gt; Option&lt;&amp;T&gt; {
unsafe {
self.back.map(|node| &amp;(*node.as_ptr()).elem)
}
}
pub fn back_mut(&amp;mut self) -&gt; Option&lt;&amp;mut T&gt; {
unsafe {
self.back.map(|node| &amp;mut (*node.as_ptr()).elem)
}
}
pub fn len(&amp;self) -&gt; usize {
self.len
}
pub fn iter(&amp;self) -&gt; Iter&lt;T&gt; {
Iter {
front: self.front,
back: self.back,
len: self.len,
_boo: PhantomData,
}
}
pub fn iter_mut(&amp;mut self) -&gt; IterMut&lt;T&gt; {
IterMut {
front: self.front,
back: self.back,
len: self.len,
_boo: PhantomData,
}
}
pub fn into_iter(self) -&gt; IntoIter&lt;T&gt; {
IntoIter {
list: self
}
}
}
impl&lt;T&gt; Drop for LinkedList&lt;T&gt; {
fn drop(&amp;mut self) {
// Pop until we have to stop
while let Some(_) = self.pop_front() { }
}
}
impl&lt;'a, T&gt; IntoIterator for &amp;'a LinkedList&lt;T&gt; {
type IntoIter = Iter&lt;'a, T&gt;;
type Item = &amp;'a T;
fn into_iter(self) -&gt; Self::IntoIter {
self.iter()
}
}
impl&lt;'a, T&gt; Iterator for Iter&lt;'a, T&gt; {
type Item = &amp;'a T;
fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
// While self.front == self.back is a tempting condition to check here,
// it won't do the right for yielding the last element! That sort of
// thing only works for arrays because of "one-past-the-end" pointers.
if self.len &gt; 0 {
// We could unwrap front, but this is safer and easier
self.front.map(|node| unsafe {
self.len -= 1;
self.front = (*node.as_ptr()).back;
&amp;(*node.as_ptr()).elem
})
} else {
None
}
}
fn size_hint(&amp;self) -&gt; (usize, Option&lt;usize&gt;) {
(self.len, Some(self.len))
}
}
impl&lt;'a, T&gt; DoubleEndedIterator for Iter&lt;'a, T&gt; {
fn next_back(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
if self.len &gt; 0 {
self.back.map(|node| unsafe {
self.len -= 1;
self.back = (*node.as_ptr()).front;
&amp;(*node.as_ptr()).elem
})
} else {
None
}
}
}
impl&lt;'a, T&gt; ExactSizeIterator for Iter&lt;'a, T&gt; {
fn len(&amp;self) -&gt; usize {
self.len
}
}
impl&lt;'a, T&gt; IntoIterator for &amp;'a mut LinkedList&lt;T&gt; {
type IntoIter = IterMut&lt;'a, T&gt;;
type Item = &amp;'a mut T;
fn into_iter(self) -&gt; Self::IntoIter {
self.iter_mut()
}
}
impl&lt;'a, T&gt; Iterator for IterMut&lt;'a, T&gt; {
type Item = &amp;'a mut T;
fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
// While self.front == self.back is a tempting condition to check here,
// it won't do the right for yielding the last element! That sort of
// thing only works for arrays because of "one-past-the-end" pointers.
if self.len &gt; 0 {
// We could unwrap front, but this is safer and easier
self.front.map(|node| unsafe {
self.len -= 1;
self.front = (*node.as_ptr()).back;
&amp;mut (*node.as_ptr()).elem
})
} else {
None
}
}
fn size_hint(&amp;self) -&gt; (usize, Option&lt;usize&gt;) {
(self.len, Some(self.len))
}
}
impl&lt;'a, T&gt; DoubleEndedIterator for IterMut&lt;'a, T&gt; {
fn next_back(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
if self.len &gt; 0 {
self.back.map(|node| unsafe {
self.len -= 1;
self.back = (*node.as_ptr()).front;
&amp;mut (*node.as_ptr()).elem
})
} else {
None
}
}
}
impl&lt;'a, T&gt; ExactSizeIterator for IterMut&lt;'a, T&gt; {
fn len(&amp;self) -&gt; usize {
self.len
}
}
impl&lt;T&gt; IntoIterator for LinkedList&lt;T&gt; {
type IntoIter = IntoIter&lt;T&gt;;
type Item = T;
fn into_iter(self) -&gt; Self::IntoIter {
self.into_iter()
}
}
impl&lt;T&gt; Iterator for IntoIter&lt;T&gt; {
type Item = T;
fn next(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
self.list.pop_front()
}
fn size_hint(&amp;self) -&gt; (usize, Option&lt;usize&gt;) {
(self.list.len, Some(self.list.len))
}
}
impl&lt;T&gt; DoubleEndedIterator for IntoIter&lt;T&gt; {
fn next_back(&amp;mut self) -&gt; Option&lt;Self::Item&gt; {
self.list.pop_back()
}
}
impl&lt;T&gt; ExactSizeIterator for IntoIter&lt;T&gt; {
fn len(&amp;self) -&gt; usize {
self.list.len
}
}
#[cfg(test)]
mod test {
use super::LinkedList;
#[test]
fn test_basic_front() {
let mut list = LinkedList::new();
// Try to break an empty list
assert_eq!(list.len(), 0);
assert_eq!(list.pop_front(), None);
assert_eq!(list.len(), 0);
// Try to break a one item list
list.push_front(10);
assert_eq!(list.len(), 1);
assert_eq!(list.pop_front(), Some(10));
assert_eq!(list.len(), 0);
assert_eq!(list.pop_front(), None);
assert_eq!(list.len(), 0);
// Mess around
list.push_front(10);
assert_eq!(list.len(), 1);
list.push_front(20);
assert_eq!(list.len(), 2);
list.push_front(30);
assert_eq!(list.len(), 3);
assert_eq!(list.pop_front(), Some(30));
assert_eq!(list.len(), 2);
list.push_front(40);
assert_eq!(list.len(), 3);
assert_eq!(list.pop_front(), Some(40));
assert_eq!(list.len(), 2);
assert_eq!(list.pop_front(), Some(20));
assert_eq!(list.len(), 1);
assert_eq!(list.pop_front(), Some(10));
assert_eq!(list.len(), 0);
assert_eq!(list.pop_front(), None);
assert_eq!(list.len(), 0);
assert_eq!(list.pop_front(), None);
assert_eq!(list.len(), 0);
}
}
<span class="boring">}</span></code></pre></pre>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../too-many-lists/production-unsafe-deque/drop-and-panic-safety.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="../../too-many-lists/production-unsafe-deque/filling-in-random-bits.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="../../too-many-lists/production-unsafe-deque/drop-and-panic-safety.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="../../too-many-lists/production-unsafe-deque/filling-in-random-bits.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>