blob: 0dbff2597f37d042de2e8fc3042beac08f58e1a5 [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Collections - The Embedded Rust Book</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 -->
<!-- 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="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<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 (`/`)" 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">The Embedded Rust 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/rust-embedded/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>
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="collections"><a class="header" href="#collections">Collections</a></h1>
<p>Eventually you'll want to use dynamic data structures (AKA collections) in your
program. <code>std</code> provides a set of common collections: <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html"><code>Vec</code></a>, <a href="https://doc.rust-lang.org/std/string/struct.String.html"><code>String</code></a>,
<a href="https://doc.rust-lang.org/std/collections/struct.HashMap.html"><code>HashMap</code></a>, etc. All the collections implemented in <code>std</code> use a global dynamic
memory allocator (AKA the heap).</p>
<p>As <code>core</code> is, by definition, free of memory allocations these implementations
are not available there, but they can be found in the <code>alloc</code> crate
that's shipped with the compiler.</p>
<p>If you need collections, a heap allocated implementation is not your only
option. You can also use <em>fixed capacity</em> collections; one such implementation
can be found in the <a href="https://crates.io/crates/heapless"><code>heapless</code></a> crate.</p>
<p>In this section, we'll explore and compare these two implementations.</p>
<h2 id="using-alloc"><a class="header" href="#using-alloc">Using <code>alloc</code></a></h2>
<p>The <code>alloc</code> crate is shipped with the standard Rust distribution. To import the
crate you can directly <code>use</code> it <em>without</em> declaring it as a dependency in your
<code>Cargo.toml</code> file.</p>
<pre><code class="language-rust ignore">#![feature(alloc)]
extern crate alloc;
use alloc::vec::Vec;</code></pre>
<p>To be able to use any collection you'll first need use the <code>global_allocator</code>
attribute to declare the global allocator your program will use. It's required
that the allocator you select implements the <a href="https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html"><code>GlobalAlloc</code></a> trait.</p>
<p>For completeness and to keep this section as self-contained as possible we'll
implement a simple bump pointer allocator and use that as the global allocator.
However, we <em>strongly</em> suggest you use a battle tested allocator from crates.io
in your program instead of this allocator.</p>
<pre><code class="language-rust ignore">// Bump pointer allocator implementation
use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell;
use core::ptr;
use cortex_m::interrupt;
// Bump pointer allocator for *single* core systems
struct BumpPointerAlloc {
head: UnsafeCell&lt;usize&gt;,
end: usize,
}
unsafe impl Sync for BumpPointerAlloc {}
unsafe impl GlobalAlloc for BumpPointerAlloc {
unsafe fn alloc(&amp;self, layout: Layout) -&gt; *mut u8 {
// `interrupt::free` is a critical section that makes our allocator safe
// to use from within interrupts
interrupt::free(|_| {
let head = self.head.get();
let size = layout.size();
let align = layout.align();
let align_mask = !(align - 1);
// move start up to the next alignment boundary
let start = (*head + align - 1) &amp; align_mask;
if start + size &gt; self.end {
// a null pointer signal an Out Of Memory condition
ptr::null_mut()
} else {
*head = start + size;
start as *mut u8
}
})
}
unsafe fn dealloc(&amp;self, _: *mut u8, _: Layout) {
// this allocator never deallocates memory
}
}
// Declaration of the global memory allocator
// NOTE the user must ensure that the memory region `[0x2000_0100, 0x2000_0200]`
// is not used by other parts of the program
#[global_allocator]
static HEAP: BumpPointerAlloc = BumpPointerAlloc {
head: UnsafeCell::new(0x2000_0100),
end: 0x2000_0200,
};</code></pre>
<p>Apart from selecting a global allocator the user will also have to define how
Out Of Memory (OOM) errors are handled using the <em>unstable</em>
<code>alloc_error_handler</code> attribute.</p>
<pre><code class="language-rust ignore">#![feature(alloc_error_handler)]
use cortex_m::asm;
#[alloc_error_handler]
fn on_oom(_layout: Layout) -&gt; ! {
asm::bkpt();
loop {}
}</code></pre>
<p>Once all that is in place, the user can finally use the collections in <code>alloc</code>.</p>
<pre><code class="language-rust ignore">#[entry]
fn main() -&gt; ! {
let mut xs = Vec::new();
xs.push(42);
assert!(xs.pop(), Some(42));
loop {
// ..
}
}</code></pre>
<p>If you have used the collections in the <code>std</code> crate then these will be familiar
as they are exact same implementation.</p>
<h2 id="using-heapless"><a class="header" href="#using-heapless">Using <code>heapless</code></a></h2>
<p><code>heapless</code> requires no setup as its collections don't depend on a global memory
allocator. Just <code>use</code> its collections and proceed to instantiate them:</p>
<pre><code class="language-rust ignore">// heapless version: v0.4.x
use heapless::Vec;
use heapless::consts::*;
#[entry]
fn main() -&gt; ! {
let mut xs: Vec&lt;_, U8&gt; = Vec::new();
xs.push(42).unwrap();
assert_eq!(xs.pop(), Some(42));
loop {}
}</code></pre>
<p>You'll note two differences between these collections and the ones in <code>alloc</code>.</p>
<p>First, you have to declare upfront the capacity of the collection. <code>heapless</code>
collections never reallocate and have fixed capacities; this capacity is part of
the type signature of the collection. In this case we have declared that <code>xs</code>
has a capacity of 8 elements that is the vector can, at most, hold 8 elements.
This is indicated by the <code>U8</code> (see <a href="https://crates.io/crates/typenum"><code>typenum</code></a>) in the type signature.</p>
<p>Second, the <code>push</code> method, and many other methods, return a <code>Result</code>. Since the
<code>heapless</code> collections have fixed capacity all operations that insert elements
into the collection can potentially fail. The API reflects this problem by
returning a <code>Result</code> indicating whether the operation succeeded or not. In
contrast, <code>alloc</code> collections will reallocate themselves on the heap to increase
their capacity.</p>
<p>As of version v0.4.x all <code>heapless</code> collections store all their elements inline.
This means that an operation like <code>let x = heapless::Vec::new();</code> will allocate
the collection on the stack, but it's also possible to allocate the collection
on a <code>static</code> variable, or even on the heap (<code>Box&lt;Vec&lt;_, _&gt;&gt;</code>).</p>
<h2 id="trade-offs"><a class="header" href="#trade-offs">Trade-offs</a></h2>
<p>Keep these in mind when choosing between heap allocated, relocatable collections
and fixed capacity collections.</p>
<h3 id="out-of-memory-and-error-handling"><a class="header" href="#out-of-memory-and-error-handling">Out Of Memory and error handling</a></h3>
<p>With heap allocations Out Of Memory is always a possibility and can occur in
any place where a collection may need to grow: for example, all
<code>alloc::Vec.push</code> invocations can potentially generate an OOM condition. Thus
some operations can <em>implicitly</em> fail. Some <code>alloc</code> collections expose
<code>try_reserve</code> methods that let you check for potential OOM conditions when
growing the collection but you need be proactive about using them.</p>
<p>If you exclusively use <code>heapless</code> collections and you don't use a memory
allocator for anything else then an OOM condition is impossible. Instead, you'll
have to deal with collections running out of capacity on a case by case basis.
That is you'll have deal with <em>all</em> the <code>Result</code>s returned by methods like
<code>Vec.push</code>.</p>
<p>OOM failures can be harder to debug than say <code>unwrap</code>-ing on all <code>Result</code>s
returned by <code>heapless::Vec.push</code> because the observed location of failure may
<em>not</em> match with the location of the cause of the problem. For example, even
<code>vec.reserve(1)</code> can trigger an OOM if the allocator is nearly exhausted because
some other collection was leaking memory (memory leaks are possible in safe
Rust).</p>
<h3 id="memory-usage"><a class="header" href="#memory-usage">Memory usage</a></h3>
<p>Reasoning about memory usage of heap allocated collections is hard because the
capacity of long lived collections can change at runtime. Some operations may
implicitly reallocate the collection increasing its memory usage, and some
collections expose methods like <code>shrink_to_fit</code> that can potentially reduce the
memory used by the collection -- ultimately, it's up to the allocator to decide
whether to actually shrink the memory allocation or not. Additionally, the
allocator may have to deal with memory fragmentation which can increase the
<em>apparent</em> memory usage.</p>
<p>On the other hand if you exclusively use fixed capacity collections, store
most of them in <code>static</code> variables and set a maximum size for the call stack
then the linker will detect if you try to use more memory than what's physically
available.</p>
<p>Furthermore, fixed capacity collections allocated on the stack will be reported
by <a href="https://doc.rust-lang.org/beta/unstable-book/compiler-flags/emit-stack-sizes.html"><code>-Z emit-stack-sizes</code></a> flag which means that tools that analyze stack usage
(like <a href="https://crates.io/crates/stack-sizes"><code>stack-sizes</code></a>) will include them in their analysis.</p>
<p>However, fixed capacity collections can <em>not</em> be shrunk which can result in
lower load factors (the ratio between the size of the collection and its
capacity) than what relocatable collections can achieve.</p>
<h3 id="worst-case-execution-time-wcet"><a class="header" href="#worst-case-execution-time-wcet">Worst Case Execution Time (WCET)</a></h3>
<p>If you are building time sensitive applications or hard real time applications
then you care, maybe a lot, about the worst case execution time of the different
parts of your program.</p>
<p>The <code>alloc</code> collections can reallocate so the WCET of operations that may grow
the collection will also include the time it takes to reallocate the collection,
which itself depends on the <em>runtime</em> capacity of the collection. This makes it
hard to determine the WCET of, for example, the <code>alloc::Vec.push</code> operation as
it depends on both the allocator being used and its runtime capacity.</p>
<p>On the other hand fixed capacity collections never reallocate so all operations
have a predictable execution time. For example, <code>heapless::Vec.push</code> executes in
constant time.</p>
<h3 id="ease-of-use"><a class="header" href="#ease-of-use">Ease of use</a></h3>
<p><code>alloc</code> requires setting up a global allocator whereas <code>heapless</code> does not.
However, <code>heapless</code> requires you to pick the capacity of each collection that
you instantiate.</p>
<p>The <code>alloc</code> API will be familiar to virtually every Rust developer. The
<code>heapless</code> API tries to closely mimic the <code>alloc</code> API but it will never be
exactly the same due to its explicit error handling -- some developers may feel
the explicit error handling is excessive or too cumbersome.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../concurrency/index.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="../design-patterns/index.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="../concurrency/index.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="../design-patterns/index.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="../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 -->
</div>
</body>
</html>