<!DOCTYPE HTML> <html lang="en"> <head> <meta charset="UTF-8"> <title>Exception Safety - </title> <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" /> <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 href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" 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"> <link rel="stylesheet" href="ayu-highlight.css"> <!-- Custom theme --> <!-- Fetch Clipboard.js from CDN but have a local fallback --> <script src="https://cdn.jsdelivr.net/clipboard.js/1.6.1/clipboard.min.js"></script> <script> if (typeof Clipboard == 'undefined') { document.write(unescape("%3Cscript src='clipboard.min.js'%3E%3C/script%3E")); } </script> </head> <body class="light"> <!-- 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 = 'light'; } document.body.className = theme; document.querySelector('html').className = theme; </script> <!-- Hide / unhide sidebar before it is displayed --> <script type="text/javascript"> var sidebar = 'hidden'; if (document.body.clientWidth >= 1080) { try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { } sidebar = sidebar || 'visible'; } document.querySelector('html').classList.add("sidebar-" + sidebar); </script> <nav id="sidebar" class="sidebar" aria-label="Table of contents"> <ol class="chapter"><li class="affix"><a href="README.html">Introduction</a></li><li><a href="meet-safe-and-unsafe.html"><strong aria-hidden="true">1.</strong> Meet Safe and Unsafe</a></li><li><ol class="section"><li><a href="safe-unsafe-meaning.html"><strong aria-hidden="true">1.1.</strong> How Safe and Unsafe Interact</a></li><li><a href="what-unsafe-does.html"><strong aria-hidden="true">1.2.</strong> What Unsafe Can Do</a></li><li><a href="working-with-unsafe.html"><strong aria-hidden="true">1.3.</strong> Working with Unsafe</a></li></ol></li><li><a href="data.html"><strong aria-hidden="true">2.</strong> Data Layout</a></li><li><ol class="section"><li><a href="repr-rust.html"><strong aria-hidden="true">2.1.</strong> repr(Rust)</a></li><li><a href="exotic-sizes.html"><strong aria-hidden="true">2.2.</strong> Exotically Sized Types</a></li><li><a href="other-reprs.html"><strong aria-hidden="true">2.3.</strong> Other reprs</a></li></ol></li><li><a href="ownership.html"><strong aria-hidden="true">3.</strong> Ownership</a></li><li><ol class="section"><li><a href="references.html"><strong aria-hidden="true">3.1.</strong> References</a></li><li><a href="aliasing.html"><strong aria-hidden="true">3.2.</strong> Aliasing</a></li><li><a href="lifetimes.html"><strong aria-hidden="true">3.3.</strong> Lifetimes</a></li><li><a href="lifetime-mismatch.html"><strong aria-hidden="true">3.4.</strong> Limits of Lifetimes</a></li><li><a href="lifetime-elision.html"><strong aria-hidden="true">3.5.</strong> Lifetime Elision</a></li><li><a href="unbounded-lifetimes.html"><strong aria-hidden="true">3.6.</strong> Unbounded Lifetimes</a></li><li><a href="hrtb.html"><strong aria-hidden="true">3.7.</strong> Higher-Rank Trait Bounds</a></li><li><a href="subtyping.html"><strong aria-hidden="true">3.8.</strong> Subtyping and Variance</a></li><li><a href="dropck.html"><strong aria-hidden="true">3.9.</strong> Drop Check</a></li><li><a href="phantom-data.html"><strong aria-hidden="true">3.10.</strong> PhantomData</a></li><li><a href="borrow-splitting.html"><strong aria-hidden="true">3.11.</strong> Splitting Borrows</a></li></ol></li><li><a href="conversions.html"><strong aria-hidden="true">4.</strong> Type Conversions</a></li><li><ol class="section"><li><a href="coercions.html"><strong aria-hidden="true">4.1.</strong> Coercions</a></li><li><a href="dot-operator.html"><strong aria-hidden="true">4.2.</strong> The Dot Operator</a></li><li><a href="casts.html"><strong aria-hidden="true">4.3.</strong> Casts</a></li><li><a href="transmutes.html"><strong aria-hidden="true">4.4.</strong> Transmutes</a></li></ol></li><li><a href="uninitialized.html"><strong aria-hidden="true">5.</strong> Uninitialized Memory</a></li><li><ol class="section"><li><a href="checked-uninit.html"><strong aria-hidden="true">5.1.</strong> Checked</a></li><li><a href="drop-flags.html"><strong aria-hidden="true">5.2.</strong> Drop Flags</a></li><li><a href="unchecked-uninit.html"><strong aria-hidden="true">5.3.</strong> Unchecked</a></li></ol></li><li><a href="obrm.html"><strong aria-hidden="true">6.</strong> Ownership Based Resource Management</a></li><li><ol class="section"><li><a href="constructors.html"><strong aria-hidden="true">6.1.</strong> Constructors</a></li><li><a href="destructors.html"><strong aria-hidden="true">6.2.</strong> Destructors</a></li><li><a href="leaking.html"><strong aria-hidden="true">6.3.</strong> Leaking</a></li></ol></li><li><a href="unwinding.html"><strong aria-hidden="true">7.</strong> Unwinding</a></li><li><ol class="section"><li><a href="exception-safety.html" class="active"><strong aria-hidden="true">7.1.</strong> Exception Safety</a></li><li><a href="poisoning.html"><strong aria-hidden="true">7.2.</strong> Poisoning</a></li></ol></li><li><a href="concurrency.html"><strong aria-hidden="true">8.</strong> Concurrency</a></li><li><ol class="section"><li><a href="races.html"><strong aria-hidden="true">8.1.</strong> Races</a></li><li><a href="send-and-sync.html"><strong aria-hidden="true">8.2.</strong> Send and Sync</a></li><li><a href="atomics.html"><strong aria-hidden="true">8.3.</strong> Atomics</a></li></ol></li><li><a href="vec.html"><strong aria-hidden="true">9.</strong> Implementing Vec</a></li><li><ol class="section"><li><a href="vec-layout.html"><strong aria-hidden="true">9.1.</strong> Layout</a></li><li><a href="vec-alloc.html"><strong aria-hidden="true">9.2.</strong> Allocating</a></li><li><a href="vec-push-pop.html"><strong aria-hidden="true">9.3.</strong> Push and Pop</a></li><li><a href="vec-dealloc.html"><strong aria-hidden="true">9.4.</strong> Deallocating</a></li><li><a href="vec-deref.html"><strong aria-hidden="true">9.5.</strong> Deref</a></li><li><a href="vec-insert-remove.html"><strong aria-hidden="true">9.6.</strong> Insert and Remove</a></li><li><a href="vec-into-iter.html"><strong aria-hidden="true">9.7.</strong> IntoIter</a></li><li><a href="vec-raw.html"><strong aria-hidden="true">9.8.</strong> RawVec</a></li><li><a href="vec-drain.html"><strong aria-hidden="true">9.9.</strong> Drain</a></li><li><a href="vec-zsts.html"><strong aria-hidden="true">9.10.</strong> Handling Zero-Sized Types</a></li><li><a href="vec-final.html"><strong aria-hidden="true">9.11.</strong> Final Code</a></li></ol></li><li><a href="arc-and-mutex.html"><strong aria-hidden="true">10.</strong> Implementing Arc and Mutex</a></li><li><a href="ffi.html"><strong aria-hidden="true">11.</strong> FFI</a></li></ol> </nav> <div id="page-wrapper" class="page-wrapper"> <div class="page"> <div id="menu-bar" class="menu-bar"> <div id="menu-bar-sticky-container"> <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="submenu"> <li><button class="theme" id="light">Light <span class="default">(default)</span></button></li> <li><button class="theme" id="rust">Rust</button></li> <li><button class="theme" id="coal">Coal</button></li> <li><button class="theme" id="navy">Navy</button></li> <li><button class="theme" id="ayu">Ayu</button></li> </ul> </div> <h1 class="menu-title"></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> </div> </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"> <main> <a class="header" href="exception-safety.html#exception-safety" id="exception-safety"><h1>Exception Safety</h1></a> <p>Although programs should use unwinding sparingly, there's a lot of code that <em>can</em> panic. If you unwrap a None, index out of bounds, or divide by 0, your program will panic. On debug builds, every arithmetic operation can panic if it overflows. Unless you are very careful and tightly control what code runs, pretty much everything can unwind, and you need to be ready for it.</p> <p>Being ready for unwinding is often referred to as <em>exception safety</em> in the broader programming world. In Rust, there are two levels of exception safety that one may concern themselves with:</p> <ul> <li> <p>In unsafe code, we <em>must</em> be exception safe to the point of not violating memory safety. We'll call this <em>minimal</em> exception safety.</p> </li> <li> <p>In safe code, it is <em>good</em> to be exception safe to the point of your program doing the right thing. We'll call this <em>maximal</em> exception safety.</p> </li> </ul> <p>As is the case in many places in Rust, Unsafe code must be ready to deal with bad Safe code when it comes to unwinding. Code that transiently creates unsound states must be careful that a panic does not cause that state to be used. Generally this means ensuring that only non-panicking code is run while these states exist, or making a guard that cleans up the state in the case of a panic. This does not necessarily mean that the state a panic witnesses is a fully coherent state. We need only guarantee that it's a <em>safe</em> state.</p> <p>Most Unsafe code is leaf-like, and therefore fairly easy to make exception-safe. It controls all the code that runs, and most of that code can't panic. However it is not uncommon for Unsafe code to work with arrays of temporarily uninitialized data while repeatedly invoking caller-provided code. Such code needs to be careful and consider exception safety.</p> <a class="header" href="exception-safety.html#vecpush_all" id="vecpush_all"><h2>Vec::push_all</h2></a> <p><code>Vec::push_all</code> is a temporary hack to get extending a Vec by a slice reliably efficient without specialization. Here's a simple implementation:</p> <pre><code class="language-rust ignore">impl<T: Clone> Vec<T> { fn push_all(&mut self, to_push: &[T]) { self.reserve(to_push.len()); unsafe { // can't overflow because we just reserved this self.set_len(self.len() + to_push.len()); for (i, x) in to_push.iter().enumerate() { self.ptr().offset(i as isize).write(x.clone()); } } } } </code></pre> <p>We bypass <code>push</code> in order to avoid redundant capacity and <code>len</code> checks on the Vec that we definitely know has capacity. The logic is totally correct, except there's a subtle problem with our code: it's not exception-safe! <code>set_len</code>, <code>offset</code>, and <code>write</code> are all fine; <code>clone</code> is the panic bomb we over-looked.</p> <p>Clone is completely out of our control, and is totally free to panic. If it does, our function will exit early with the length of the Vec set too large. If the Vec is looked at or dropped, uninitialized memory will be read!</p> <p>The fix in this case is fairly simple. If we want to guarantee that the values we <em>did</em> clone are dropped, we can set the <code>len</code> every loop iteration. If we just want to guarantee that uninitialized memory can't be observed, we can set the <code>len</code> after the loop.</p> <a class="header" href="exception-safety.html#binaryheapsift_up" id="binaryheapsift_up"><h2>BinaryHeap::sift_up</h2></a> <p>Bubbling an element up a heap is a bit more complicated than extending a Vec. The pseudocode is as follows:</p> <pre><code class="language-text">bubble_up(heap, index): while index != 0 && heap[index] < heap[parent(index)]: heap.swap(index, parent(index)) index = parent(index) </code></pre> <p>A literal transcription of this code to Rust is totally fine, but has an annoying performance characteristic: the <code>self</code> element is swapped over and over again uselessly. We would rather have the following:</p> <pre><code class="language-text">bubble_up(heap, index): let elem = heap[index] while index != 0 && elem < heap[parent(index)]: heap[index] = heap[parent(index)] index = parent(index) heap[index] = elem </code></pre> <p>This code ensures that each element is copied as little as possible (it is in fact necessary that elem be copied twice in general). However it now exposes some exception safety trouble! At all times, there exists two copies of one value. If we panic in this function something will be double-dropped. Unfortunately, we also don't have full control of the code: that comparison is user-defined!</p> <p>Unlike Vec, the fix isn't as easy here. One option is to break the user-defined code and the unsafe code into two separate phases:</p> <pre><code class="language-text">bubble_up(heap, index): let end_index = index; while end_index != 0 && heap[end_index] < heap[parent(end_index)]: end_index = parent(end_index) let elem = heap[index] while index != end_index: heap[index] = heap[parent(index)] index = parent(index) heap[index] = elem </code></pre> <p>If the user-defined code blows up, that's no problem anymore, because we haven't actually touched the state of the heap yet. Once we do start messing with the heap, we're working with only data and functions that we trust, so there's no concern of panics.</p> <p>Perhaps you're not happy with this design. Surely it's cheating! And we have to do the complex heap traversal <em>twice</em>! Alright, let's bite the bullet. Let's intermix untrusted and unsafe code <em>for reals</em>.</p> <p>If Rust had <code>try</code> and <code>finally</code> like in Java, we could do the following:</p> <pre><code class="language-text">bubble_up(heap, index): let elem = heap[index] try: while index != 0 && elem < heap[parent(index)]: heap[index] = heap[parent(index)] index = parent(index) finally: heap[index] = elem </code></pre> <p>The basic idea is simple: if the comparison panics, we just toss the loose element in the logically uninitialized index and bail out. Anyone who observes the heap will see a potentially <em>inconsistent</em> heap, but at least it won't cause any double-drops! If the algorithm terminates normally, then this operation happens to coincide precisely with the how we finish up regardless.</p> <p>Sadly, Rust has no such construct, so we're going to need to roll our own! The way to do this is to store the algorithm's state in a separate struct with a destructor for the "finally" logic. Whether we panic or not, that destructor will run and clean up after us.</p> <pre><code class="language-rust ignore">struct Hole<'a, T: 'a> { data: &'a mut [T], /// `elt` is always `Some` from new until drop. elt: Option<T>, pos: usize, } impl<'a, T> Hole<'a, T> { fn new(data: &'a mut [T], pos: usize) -> Self { unsafe { let elt = ptr::read(&data[pos]); Hole { data: data, elt: Some(elt), pos: pos, } } } fn pos(&self) -> usize { self.pos } fn removed(&self) -> &T { self.elt.as_ref().unwrap() } unsafe fn get(&self, index: usize) -> &T { &self.data[index] } unsafe fn move_to(&mut self, index: usize) { let index_ptr: *const _ = &self.data[index]; let hole_ptr = &mut self.data[self.pos]; ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); self.pos = index; } } impl<'a, T> Drop for Hole<'a, T> { fn drop(&mut self) { // fill the hole again unsafe { let pos = self.pos; ptr::write(&mut self.data[pos], self.elt.take().unwrap()); } } } impl<T: Ord> BinaryHeap<T> { fn sift_up(&mut self, pos: usize) { unsafe { // Take out the value at `pos` and create a hole. let mut hole = Hole::new(&mut self.data, pos); while hole.pos() != 0 { let parent = parent(hole.pos()); if hole.removed() <= hole.get(parent) { break } hole.move_to(parent); } // Hole will be unconditionally filled here; panic or not! } } } </code></pre> </main> <nav class="nav-wrapper" aria-label="Page navigation"> <!-- Mobile navigation buttons --> <a rel="prev" href="unwinding.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" href="poisoning.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 href="unwinding.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left"> <i class="fa fa-angle-left"></i> </a> <a href="poisoning.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> <!-- Local fallback for Font Awesome --> <script> if (getComputedStyle(document.querySelector(".fa")).fontFamily !== "FontAwesome") { var link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = '_FontAwesome/css/font-awesome.css'; document.head.insertBefore(link, document.head.firstChild) } </script> <script src="highlight.js"></script> <script src="book.js"></script> <!-- Custom JS script --> </body> </html>