mirror of
https://github.com/Noratrieb/blog.git
synced 2026-01-14 12:35:00 +01:00
deploy: 22299f5cf4
This commit is contained in:
parent
d0d172c9e7
commit
e0f0a1d45e
15 changed files with 765 additions and 5 deletions
|
|
@ -1,3 +1,5 @@
|
||||||
<!doctype html><html lang=en><head><meta name=generator content="Hugo 0.101.0"><title>nilstriebs blog</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content><meta name=keywords content><meta name=robots content="noodp"><link rel=canonical href=http://nilstrieb.github.io/><link rel=stylesheet href=http://nilstrieb.github.io/assets/style.css><link rel=stylesheet href=assets/%25!s%28%3cnil%3e%29.css><link rel=apple-touch-icon href=http://nilstrieb.github.io/img/apple-touch-icon-192x192.png><link rel="shortcut icon" href=http://nilstrieb.github.io/img/favicon/orange.png><meta name=twitter:card content="summary"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta property="og:title" content="nilstriebs blog"><meta property="og:description" content><meta property="og:url" content="http://nilstrieb.github.io/"><meta property="og:site_name" content="nilstriebs blog"><meta property="og:image" content="img/favicon/%!s().png"><meta property="og:image:width" content="2048"><meta property="og:image:height" content="1024"><link href=/index.xml rel=alternate type=application/rss+xml title="nilstriebs blog"></head><body><div class="container headings--one-size"><header class=header><div class=header__inner><div class=header__logo><a href=http://nilstrieb.github.io/><div class=logo>Terminal</div></a></div></div></header><div class=content><div class=posts><div class=pagination><div class=pagination__buttons></div></div></div></div><footer class=footer><div class=footer__inner><div class=copyright><span>© 2022 Powered by <a href=http://gohugo.io>Hugo</a></span>
|
<!doctype html><html lang=en><head><meta name=generator content="Hugo 0.101.0"><title>nilstriebs blog</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content><meta name=keywords content><meta name=robots content="noodp"><link rel=canonical href=http://nilstrieb.github.io/><link rel=stylesheet href=http://nilstrieb.github.io/assets/style.css><link rel=stylesheet href=assets/%25!s%28%3cnil%3e%29.css><link rel=apple-touch-icon href=http://nilstrieb.github.io/img/apple-touch-icon-192x192.png><link rel="shortcut icon" href=http://nilstrieb.github.io/img/favicon/orange.png><meta name=twitter:card content="summary"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta property="og:title" content="nilstriebs blog"><meta property="og:description" content><meta property="og:url" content="http://nilstrieb.github.io/"><meta property="og:site_name" content="nilstriebs blog"><meta property="og:image" content="img/favicon/%!s().png"><meta property="og:image:width" content="2048"><meta property="og:image:height" content="1024"><link href=/index.xml rel=alternate type=application/rss+xml title="nilstriebs blog"></head><body><div class="container headings--one-size"><header class=header><div class=header__inner><div class=header__logo><a href=http://nilstrieb.github.io/><div class=logo>Terminal</div></a></div></div></header><div class=content><div class=posts><div class="post on-list"><h1 class=post-title><a href=http://nilstrieb.github.io/posts/box-is-a-unique-type/>Box Is a Unique Type</a></h1><div class=post-meta><span class=post-date>2022-07-22</span>
|
||||||
|
<span class=post-author>:: Nilstrieb</span></div><span class=post-tags>#<a href=http://nilstrieb.github.io/tags/rust/>rust</a>
|
||||||
|
#<a href=http://nilstrieb.github.io/tags/unsafe-code/>unsafe code</a> </span><div class=post-content>The current and future aliasing semantics of Box</div><div><a class="read-more button" href=/posts/box-is-a-unique-type/>→</a></div></div><div class=pagination><div class=pagination__buttons></div></div></div></div><footer class=footer><div class=footer__inner><div class=copyright><span>© 2022 Powered by <a href=http://gohugo.io>Hugo</a></span>
|
||||||
<span>:: Theme made by <a href=https://twitter.com/panr>panr</a></span></div></div></footer><script src=http://nilstrieb.github.io/assets/main.js></script>
|
<span>:: Theme made by <a href=https://twitter.com/panr>panr</a></span></div></div></footer><script src=http://nilstrieb.github.io/assets/main.js></script>
|
||||||
<script src=http://nilstrieb.github.io/assets/prism.js></script></div></body></html>
|
<script src=http://nilstrieb.github.io/assets/prism.js></script></div></body></html>
|
||||||
156
index.xml
156
index.xml
|
|
@ -1 +1,155 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>nilstriebs blog</title><link>http://nilstrieb.github.io/</link><description>Recent content on nilstriebs blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="http://nilstrieb.github.io/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>nilstriebs blog</title><link>http://nilstrieb.github.io/</link><description>Recent content on nilstriebs blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 22 Jul 2022 00:00:00 +0000</lastBuildDate><atom:link href="http://nilstrieb.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Box Is a Unique Type</title><link>http://nilstrieb.github.io/posts/box-is-a-unique-type/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>http://nilstrieb.github.io/posts/box-is-a-unique-type/</guid><description>We have all used Box&lt;T&gt; before in our Rust code. It&rsquo;s a glorious type, with great ergonomics and flexibitility. We can use it to put our values on the heap, but it can do even more than that!
|
||||||
|
struct Fields { a: String, b: String, } let fields = Box::new(Fields { a: &#34;a&#34;.to_string(), b: &#34;b&#34;.to_string() }); let a = fields.a; let b = fields.b; This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve, and they exist for good reason: They are very useful.</description><content><p>We have all used <code>Box&lt;T&gt;</code> before in our Rust code. It&rsquo;s a glorious type, with great ergonomics
|
||||||
|
and flexibitility. We can use it to put our values on the heap, but it can do even more
|
||||||
|
than that!</p>
|
||||||
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Fields</span> {
|
||||||
|
</span></span><span style="display:flex;"><span> a: String,
|
||||||
|
</span></span><span style="display:flex;"><span> b: String,
|
||||||
|
</span></span><span style="display:flex;"><span>}
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> fields <span style="color:#f92672">=</span> Box::new(Fields {
|
||||||
|
</span></span><span style="display:flex;"><span> a: <span style="color:#e6db74">&#34;a&#34;</span>.to_string(),
|
||||||
|
</span></span><span style="display:flex;"><span> b: <span style="color:#e6db74">&#34;b&#34;</span>.to_string()
|
||||||
|
</span></span><span style="display:flex;"><span>});
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> a <span style="color:#f92672">=</span> fields.a;
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> b <span style="color:#f92672">=</span> fields.b;
|
||||||
|
</span></span></code></pre></div><p>This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve,
|
||||||
|
and they exist for good reason: They are very useful. Sadly we have not yet found a way to generalize all
|
||||||
|
of these to user types as well. Too bad!</p>
|
||||||
|
<p>Anyways, this post is about one particularly subtle magic aspect of box. For this, we need to dive
|
||||||
|
deep into unsafe code, so let&rsquo;s get our hazmat suits on and jump in!</p>
|
||||||
|
<h1 id="an-interesting-optimization">An interesting optimization</h1>
|
||||||
|
<p>We have this code here:</p>
|
||||||
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">takes_box_and_ptr_to_it</span>(<span style="color:#66d9ef">mut</span> b: Box<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">u8</span><span style="color:#f92672">&gt;</span>, ptr: <span style="color:#f92672">*</span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">u8</span>) {
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> value <span style="color:#f92672">=</span> <span style="color:#66d9ef">unsafe</span> { <span style="color:#f92672">*</span>ptr };
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#f92672">*</span>b <span style="color:#f92672">=</span> <span style="color:#ae81ff">5</span>;
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> value2 <span style="color:#f92672">=</span> <span style="color:#66d9ef">unsafe</span> { <span style="color:#f92672">*</span>ptr };
|
||||||
|
</span></span><span style="display:flex;"><span> assert_ne!(value, value2);
|
||||||
|
</span></span><span style="display:flex;"><span>}
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> b <span style="color:#f92672">=</span> Box::new(<span style="color:#ae81ff">0</span>);
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> ptr: <span style="color:#f92672">*</span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">u8</span> <span style="color:#f92672">=</span> <span style="color:#f92672">&amp;*</span>b;
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span>takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
</span></span></code></pre></div><p>There&rsquo;s a function, <code>takes_box_and_ptr_to_it</code>, that takes a box and a pointer as parameters. Then,
|
||||||
|
it reads a value from the pointer, writes to the box, and reads a value again. It then asserts that
|
||||||
|
the two values aren&rsquo;t equal. How can they not be equal? If our box and pointer point to the same
|
||||||
|
location in memory, writing to the box will cause the pointer to read the new value.</p>
|
||||||
|
<p>Now construct a box, get a pointer to it, and pass the two to the function. Run the program&hellip;</p>
|
||||||
|
<p>&hellip; and everything is fine. Let&rsquo;s run it in release mode. This should work as well, since the optimizer
|
||||||
|
isn&rsquo;t allowed to change observable behaviour, and an assert is very observable. Run the progrm&hellip;</p>
|
||||||
|
<pre tabindex="0"><code>thread &#39;main&#39; panicked at &#39;assertion failed: `(left != right)`
|
||||||
|
left: `0`,
|
||||||
|
right: `0`&#39;, src/main.rs:5:5
|
||||||
|
</code></pre><p>Hmm. That&rsquo;s not what I&rsquo;ve told would happen. Is the compiler broken? Is this a miscompilation?
|
||||||
|
I&rsquo;ve heard that those do sometimes happen, right?</p>
|
||||||
|
<p>Trusting our instincts that &ldquo;it&rsquo;s never a miscompilation until it is one&rdquo;, we assume that LLVM behaved
|
||||||
|
well here. But what allows it to make this optimization? Taking a look at the generated LLVM-IR (by using
|
||||||
|
<code>--emit llvm-ir -O</code>, the <code>-O</code> is important since rustc only emits these attributes with optimizations on)
|
||||||
|
reveals the solution: (severely shortened to only show the relevant parts)</p>
|
||||||
|
<pre tabindex="0"><code class="language-llvmir" data-lang="llvmir">define void @takes_box_and_ptr_to_it(i8* noalias %0, i8* %ptr) {
|
||||||
|
</code></pre><p>See the little attribute on the first parameter called <code>noalias</code>? That&rsquo;s what&rsquo;s doing the magic here.
|
||||||
|
<code>noalias</code> is quite complex, but for our case here, it says that no other pointers point to (alias) the same location.
|
||||||
|
This allows the optimizer to assume that writing to the box pointer doesn&rsquo;t affect the other pointer - they are
|
||||||
|
not allowed to alias (it&rsquo;s like if they used <code>restrict</code> in C).</p>
|
||||||
|
<p>This might sound familiar to you if you&rsquo;re a viewer of <a href="https://twitter.com/jonhoo">Jon Gjengset</a>&rsquo;s content (which I can highly recommend). Jon has made an entire video about this before, since his crate <code>left-right</code>
|
||||||
|
was affected by this (<a href="https://youtu.be/EY7Wi9fV5bk)">https://youtu.be/EY7Wi9fV5bk)</a>.</p>
|
||||||
|
<p>If you&rsquo;re looking for <em>any</em> hint that using box emits <code>noalias</code>, you have to look no further than the documentation
|
||||||
|
for <a href="https://doc.rust-lang.org/nightly/std/boxed/index.html#considerations-for-unsafe-code"><code>std::boxed</code></a>. Well, the nightly or beta docs, because I only added this section very recently. For years, this behaviour was not really documented, and you had to
|
||||||
|
belong to the arcane circles of the select few who were aware of it. So lots of code was written thinking that box was &ldquo;just a RAII pointer&rdquo; (a pointer that allocates the value in the constructor, and deallocates it in the destructor on drop) for all
|
||||||
|
pointers are concerned.</p>
|
||||||
|
<h1 id="stacked-borrows-and-miri">Stacked Borrows and Miri</h1>
|
||||||
|
<p><a href="https://github.com/rust-lang/miri">Miri</a> is an interpreter for Rust code with the goal of finding undefinde behaviour.
|
||||||
|
Undefined behaviour, UB for short, is behaviour of a program upon which no restrictions are imposed. If UB is executed,
|
||||||
|
<em>anything</em> can happen, including segmentation faults, silent memory corruption, leakage of private keys or exactly
|
||||||
|
what you intended to happen. Examples of UB include use-after-free, out of bounds reads or data races.</p>
|
||||||
|
<p>I cannot recommend Miri highly enough for all unsafe code you&rsquo;re writing (sadly support for some IO functions
|
||||||
|
and FFI is still lacking, and it&rsquo;s still very slow).</p>
|
||||||
|
<p>So, let&rsquo;s see whether our code contains UB. It has to, since otherwise the optimizer wouldn&rsquo;t be allowed to change
|
||||||
|
observable behaviour (since the assert doesn&rsquo;t fail in debug mode). <code>$ cargo miri run</code>&hellip;</p>
|
||||||
|
<pre tabindex="0"><code class="language-rust,ignore" data-lang="rust,ignore">error: Undefined Behavior: attempting a read access using &lt;3314&gt; at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
--&gt; src/main.rs:2:26
|
||||||
|
|
|
||||||
|
2 | let value = unsafe { *ptr };
|
||||||
|
| ^^^^
|
||||||
|
| |
|
||||||
|
| attempting a read access using &lt;3314&gt; at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
| this error occurs as part of an access at alloc1722[0x0..0x1]
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
|
||||||
|
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||||
|
help: &lt;3314&gt; was created by a retag at offsets [0x0..0x1]
|
||||||
|
--&gt; src/main.rs:10:26
|
||||||
|
|
|
||||||
|
10 | let ptr: *const u8 = &amp;*b;
|
||||||
|
| ^^^
|
||||||
|
help: &lt;3314&gt; was later invalidated at offsets [0x0..0x1]
|
||||||
|
--&gt; src/main.rs:12:29
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^
|
||||||
|
= note: backtrace:
|
||||||
|
= note: inside `takes_box_and_ptr_to_it` at src/main.rs:2:26
|
||||||
|
note: inside `main` at src/main.rs:12:5
|
||||||
|
--&gt; src/main.rs:12:5
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
</code></pre><p>This behaviour does indeed not look very defined at all. But what went wrong? There&rsquo;s a lot of information here.</p>
|
||||||
|
<p>First of all, it says that we attempted a read access, and that this access failed because the tag does not exist in the
|
||||||
|
borrow stack. This is something about stacked borrows, the experimental memory model for Rust that is implemented
|
||||||
|
in Miri. For an excellent introduction, see this part of the great book <a href="https://rust-unofficial.github.io/too-many-lists/fifth-stacked-borrows.html">Learning Rust With Entirely Too Many Linked Lists</a>.</p>
|
||||||
|
<p>In short: each pointer has a unique tag attacked to it. Bytes in memory have a stack of such tags, and only the pointers
|
||||||
|
that have their tag in the stack are allowed to access it. Tags can be pushed and popped from the stack through various
|
||||||
|
operations, for example borrowing.</p>
|
||||||
|
<p>In the code example above, we get a nice little hint where the tag was created. When we created a reference (that was then
|
||||||
|
coerced into a raw pointer) from our box, it got a new tag called <code>&lt;3314&gt;</code>. Then, when we moved the box into the function,
|
||||||
|
something happened: The tag was invalidated and popped off the borrow stack. That&rsquo;s because box invalidates all tags when it&rsquo;s
|
||||||
|
moved. The tag was popped off the borrow stack and we tried to read from it anyways - undefined behaviour happened!</p>
|
||||||
|
<p>And that&rsquo;s how our code wasn&rsquo;t a miscompilation, but undefined behaviour. Quite surprising, isn&rsquo;t it?</p>
|
||||||
|
<h1 id="noalias-nothanks">noalias, nothanks</h1>
|
||||||
|
<p>Many people, myself included, don&rsquo;t think that this is a good thing.</p>
|
||||||
|
<p>First of all, it introduces more UB that could have been defined behaviour instead. This is true for almost all UB, but usually,
|
||||||
|
there is something gained from the UB that justifies it. We will look at this later. But allowing such behaviour is fairly easy:
|
||||||
|
If box didn&rsquo;t invalidate pointers on move and instead behaved like a normal raw pointer, the code above would be sound.</p>
|
||||||
|
<p>But more importantly, this is not behaviour generally expected by users. While it can be argued that box is like a <code>T</code>, but on
|
||||||
|
the heap, and therefore moving it should invalidate pointers, since moving <code>T</code> definitely has to invalidate pointers to it,
|
||||||
|
this comparison doesn&rsquo;t make sense to me. While <code>Box&lt;T&gt;</code> usually behaves like a <code>T</code>, it&rsquo;s just a pointer. Writers of unsafe
|
||||||
|
code <em>know</em> that box is just a pointer, and will abuse that knowledge, accidentally causing UB with it. While this can be
|
||||||
|
mitigated with better docs and teaching, like how no one questions the uniqueness of <code>&amp;mut T</code> (maybe that&rsquo;s also because that
|
||||||
|
one makes intuitive sense, &ldquo;shared xor mutable&rdquo; is a simple concept), I think it will always be a problem,
|
||||||
|
because in my opinion, box being unique and invalidating pointers on move is simply not intiutive.</p>
|
||||||
|
<p>When a box is moved, the pointer bytes change their location in memory. But the bytes the box points to stay the same. They don&rsquo;t
|
||||||
|
move in memory. This is the fundamental missing intuition about the box behaviour.</p>
|
||||||
|
<p>There are also other reasons why the box behaviour is not desirable. Even people who know about the behaviour of box will want
|
||||||
|
to write code that goes directly against this behaviour at some point. But usually, fixing it is pretty simple: Storing a raw
|
||||||
|
pointer (or <code>NonNull&lt;T&gt;</code>) instead of a box, and using the constructor and drop to allocate and deallocate the backing box.
|
||||||
|
This is fairly inconvenient, but totally acceptable. There are bigger problems though. There are crates like <code>owning_ref</code>
|
||||||
|
that want to expose a generic interface over any type. Users like to choose box, and sometimes <em>have</em> to chose box because of
|
||||||
|
other box-exclusive features it offers. Even worse is <code>string_cache</code>, which is extremely hard to fix.</p>
|
||||||
|
<p>Then last but not least, there&rsquo;s the opinionated fact that <code>Box&lt;T&gt;</code> shall be implementable entirely in user code. While we are
|
||||||
|
many missing language features away from this being the case, the <code>noalias</code> case is also magic descended upon box itself, with no
|
||||||
|
user code ever having access to it.</p>
|
||||||
|
<h1 id="noalias-noslow">noalias, noslow</h1>
|
||||||
|
<p>There are also several arguments in favour of box being unique and special cased here. To negate the last argument above, it can
|
||||||
|
be said that <code>Box&lt;T&gt;</code> <em>is</em> a very special type. It&rsquo;s just like a <code>T</code>, but on the heap. Using this mental model, it&rsquo;s very easy to
|
||||||
|
justify all the box magic and its unique behaviour.</p>
|
||||||
|
<p>This mental model is one that many people have, but what does this bring us? This is just one mental model of box, and
|
||||||
|
there are other mental models of it (like &ldquo;a reference that manages its lifetime itself&rdquo; or &ldquo;a safe RAII pointer&rdquo;).</p>
|
||||||
|
<p>There is one clear potential benefit from this box behaviour. ✨Optimizations✨. <code>noalias</code> doesn&rsquo;t exist for fun, it&rsquo;s something
|
||||||
|
that can bring clear performance wins (for <code>noalias</code> on <code>&amp;mut T</code>, those were measureable). So the only question remains:
|
||||||
|
How much performance does <code>noalias</code> on <code>Box&lt;T&gt;</code> give us now, and how much potential performance improvements could we get in the
|
||||||
|
future? For the latter, there is no simple answer. For the former, there is. <code>rustc</code> has <a href="https://github.com/rust-lang/rust/pull/99527"><em>no</em> performance improvements</a> from being compiled with <code>noalias</code> on <code>Box&lt;T&gt;</code>.</p>
|
||||||
|
<p>I have not yet benchmarked ecosystem crates without box noalias and don&rsquo;t have the capacity to do so right now, so I would be very
|
||||||
|
grateful if anyone wanted to pick that up and report the results.</p>
|
||||||
|
<h1 id="a-way-forward">a way forward</h1>
|
||||||
|
<p>Based on all of this, I do have a solution that, in opinion, will fix all of this, even potential performance regressions with
|
||||||
|
box. First of all, I think that even if there are some performance regressions in ecosystem crates, the overall tradeoff goes
|
||||||
|
against the current box behaviour. Unsafe code wants to use box, and it is reasonable to do so. Therefore I propose to completely
|
||||||
|
remove all uniqueness from <code>Box&lt;T&gt;</code>, and treat it just like a <code>*const T</code> for the purposes of aliasing. This will make it more
|
||||||
|
predictable for unsafe code, and comes at none or only a minor performance cost.</p>
|
||||||
|
<p>But this performance cost may be real, and especially the future optimization value can&rsquo;t be certain. I do think that there
|
||||||
|
should be a way to get the uniqueness guarantees in some other way than through box. One possibility would be to use a <code>&amp;'static mut T</code> that is unleaked for drop, but the semantics of this are still <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/316">unclear</a>. If that is not possible, maybe exposing <code>std::ptr::Unique</code> (with it getting boxes aliasing semantics) could be desirable. For this, all existing usages of <code>Unique</code> inside the standard library would have to be removed though.</p>
|
||||||
|
<p>I guess what I am wishing for are some good and flexible raw pointer types. That&rsquo;s still in the stars&hellip;</p>
|
||||||
|
<p>For more information about this topic, see <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/326">https://github.com/rust-lang/unsafe-code-guidelines/issues/326</a></p></content></item></channel></rss>
|
||||||
121
posts/box-is-a-unique-type/index.html
Normal file
121
posts/box-is-a-unique-type/index.html
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
<!doctype html><html lang=en><head><title>Box Is a Unique Type :: nilstriebs blog</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content="The current and future aliasing semantics of Box"><meta name=keywords content="box,noalias"><meta name=robots content="noodp"><link rel=canonical href=http://nilstrieb.github.io/posts/box-is-a-unique-type/><link rel=stylesheet href=http://nilstrieb.github.io/assets/style.css><link rel=stylesheet href=assets/%25!s%28%3cnil%3e%29.css><link rel=apple-touch-icon href=http://nilstrieb.github.io/img/apple-touch-icon-192x192.png><link rel="shortcut icon" href=http://nilstrieb.github.io/img/favicon/orange.png><meta name=twitter:card content="summary"><meta property="og:locale" content="en"><meta property="og:type" content="article"><meta property="og:title" content="Box Is a Unique Type"><meta property="og:description" content="The current and future aliasing semantics of Box"><meta property="og:url" content="http://nilstrieb.github.io/posts/box-is-a-unique-type/"><meta property="og:site_name" content="nilstriebs blog"><meta property="og:image" content="http://nilstrieb.github.io/"><meta property="og:image:width" content="2048"><meta property="og:image:height" content="1024"><meta property="article:published_time" content="2022-07-22 00:00:00 +0000 UTC"></head><body><div class="container headings--one-size"><header class=header><div class=header__inner><div class=header__logo><a href=http://nilstrieb.github.io/><div class=logo>Terminal</div></a></div></div></header><div class=content><div class=post><h1 class=post-title><a href=http://nilstrieb.github.io/posts/box-is-a-unique-type/>Box Is a Unique Type</a></h1><div class=post-meta><span class=post-date>2022-07-22</span>
|
||||||
|
<span class=post-author>:: Nilstrieb</span>
|
||||||
|
<span class=post-reading-time>:: 10 min read (2052 words)</span></div><span class=post-tags>#<a href=http://nilstrieb.github.io/tags/rust/>rust</a>
|
||||||
|
#<a href=http://nilstrieb.github.io/tags/unsafe-code/>unsafe code</a> </span><div class=post-content><div><p>We have all used <code>Box<T></code> before in our Rust code. It’s a glorious type, with great ergonomics
|
||||||
|
and flexibitility. We can use it to put our values on the heap, but it can do even more
|
||||||
|
than that!</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-rust data-lang=rust><span style=display:flex><span><span style=color:#66d9ef>struct</span> <span style=color:#a6e22e>Fields</span> {
|
||||||
|
</span></span><span style=display:flex><span> a: String,
|
||||||
|
</span></span><span style=display:flex><span> b: String,
|
||||||
|
</span></span><span style=display:flex><span>}
|
||||||
|
</span></span><span style=display:flex><span>
|
||||||
|
</span></span><span style=display:flex><span><span style=color:#66d9ef>let</span> fields <span style=color:#f92672>=</span> Box::new(Fields {
|
||||||
|
</span></span><span style=display:flex><span> a: <span style=color:#e6db74>"a"</span>.to_string(),
|
||||||
|
</span></span><span style=display:flex><span> b: <span style=color:#e6db74>"b"</span>.to_string()
|
||||||
|
</span></span><span style=display:flex><span>});
|
||||||
|
</span></span><span style=display:flex><span>
|
||||||
|
</span></span><span style=display:flex><span><span style=color:#66d9ef>let</span> a <span style=color:#f92672>=</span> fields.a;
|
||||||
|
</span></span><span style=display:flex><span><span style=color:#66d9ef>let</span> b <span style=color:#f92672>=</span> fields.b;
|
||||||
|
</span></span></code></pre></div><p>This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve,
|
||||||
|
and they exist for good reason: They are very useful. Sadly we have not yet found a way to generalize all
|
||||||
|
of these to user types as well. Too bad!</p><p>Anyways, this post is about one particularly subtle magic aspect of box. For this, we need to dive
|
||||||
|
deep into unsafe code, so let’s get our hazmat suits on and jump in!</p><h1 id=an-interesting-optimization>An interesting optimization<a href=#an-interesting-optimization class=hanchor arialabel=Anchor>⌗</a></h1><p>We have this code here:</p><div class=highlight><pre tabindex=0 style=color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-rust data-lang=rust><span style=display:flex><span><span style=color:#66d9ef>fn</span> <span style=color:#a6e22e>takes_box_and_ptr_to_it</span>(<span style=color:#66d9ef>mut</span> b: Box<span style=color:#f92672><</span><span style=color:#66d9ef>u8</span><span style=color:#f92672>></span>, ptr: <span style=color:#f92672>*</span><span style=color:#66d9ef>const</span> <span style=color:#66d9ef>u8</span>) {
|
||||||
|
</span></span><span style=display:flex><span> <span style=color:#66d9ef>let</span> value <span style=color:#f92672>=</span> <span style=color:#66d9ef>unsafe</span> { <span style=color:#f92672>*</span>ptr };
|
||||||
|
</span></span><span style=display:flex><span> <span style=color:#f92672>*</span>b <span style=color:#f92672>=</span> <span style=color:#ae81ff>5</span>;
|
||||||
|
</span></span><span style=display:flex><span> <span style=color:#66d9ef>let</span> value2 <span style=color:#f92672>=</span> <span style=color:#66d9ef>unsafe</span> { <span style=color:#f92672>*</span>ptr };
|
||||||
|
</span></span><span style=display:flex><span> assert_ne!(value, value2);
|
||||||
|
</span></span><span style=display:flex><span>}
|
||||||
|
</span></span><span style=display:flex><span>
|
||||||
|
</span></span><span style=display:flex><span><span style=color:#66d9ef>let</span> b <span style=color:#f92672>=</span> Box::new(<span style=color:#ae81ff>0</span>);
|
||||||
|
</span></span><span style=display:flex><span><span style=color:#66d9ef>let</span> ptr: <span style=color:#f92672>*</span><span style=color:#66d9ef>const</span> <span style=color:#66d9ef>u8</span> <span style=color:#f92672>=</span> <span style=color:#f92672>&*</span>b;
|
||||||
|
</span></span><span style=display:flex><span>
|
||||||
|
</span></span><span style=display:flex><span>takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
</span></span></code></pre></div><p>There’s a function, <code>takes_box_and_ptr_to_it</code>, that takes a box and a pointer as parameters. Then,
|
||||||
|
it reads a value from the pointer, writes to the box, and reads a value again. It then asserts that
|
||||||
|
the two values aren’t equal. How can they not be equal? If our box and pointer point to the same
|
||||||
|
location in memory, writing to the box will cause the pointer to read the new value.</p><p>Now construct a box, get a pointer to it, and pass the two to the function. Run the program…</p><p>… and everything is fine. Let’s run it in release mode. This should work as well, since the optimizer
|
||||||
|
isn’t allowed to change observable behaviour, and an assert is very observable. Run the progrm…</p><pre tabindex=0><code>thread 'main' panicked at 'assertion failed: `(left != right)`
|
||||||
|
left: `0`,
|
||||||
|
right: `0`', src/main.rs:5:5
|
||||||
|
</code></pre><p>Hmm. That’s not what I’ve told would happen. Is the compiler broken? Is this a miscompilation?
|
||||||
|
I’ve heard that those do sometimes happen, right?</p><p>Trusting our instincts that “it’s never a miscompilation until it is one”, we assume that LLVM behaved
|
||||||
|
well here. But what allows it to make this optimization? Taking a look at the generated LLVM-IR (by using
|
||||||
|
<code>--emit llvm-ir -O</code>, the <code>-O</code> is important since rustc only emits these attributes with optimizations on)
|
||||||
|
reveals the solution: (severely shortened to only show the relevant parts)</p><pre tabindex=0><code class=language-llvmir data-lang=llvmir>define void @takes_box_and_ptr_to_it(i8* noalias %0, i8* %ptr) {
|
||||||
|
</code></pre><p>See the little attribute on the first parameter called <code>noalias</code>? That’s what’s doing the magic here.
|
||||||
|
<code>noalias</code> is quite complex, but for our case here, it says that no other pointers point to (alias) the same location.
|
||||||
|
This allows the optimizer to assume that writing to the box pointer doesn’t affect the other pointer - they are
|
||||||
|
not allowed to alias (it’s like if they used <code>restrict</code> in C).</p><p>This might sound familiar to you if you’re a viewer of <a href=https://twitter.com/jonhoo>Jon Gjengset</a>’s content (which I can highly recommend). Jon has made an entire video about this before, since his crate <code>left-right</code>
|
||||||
|
was affected by this (<a href=https://youtu.be/EY7Wi9fV5bk)>https://youtu.be/EY7Wi9fV5bk)</a>.</p><p>If you’re looking for <em>any</em> hint that using box emits <code>noalias</code>, you have to look no further than the documentation
|
||||||
|
for <a href=https://doc.rust-lang.org/nightly/std/boxed/index.html#considerations-for-unsafe-code><code>std::boxed</code></a>. Well, the nightly or beta docs, because I only added this section very recently. For years, this behaviour was not really documented, and you had to
|
||||||
|
belong to the arcane circles of the select few who were aware of it. So lots of code was written thinking that box was “just a RAII pointer” (a pointer that allocates the value in the constructor, and deallocates it in the destructor on drop) for all
|
||||||
|
pointers are concerned.</p><h1 id=stacked-borrows-and-miri>Stacked Borrows and Miri<a href=#stacked-borrows-and-miri class=hanchor arialabel=Anchor>⌗</a></h1><p><a href=https://github.com/rust-lang/miri>Miri</a> is an interpreter for Rust code with the goal of finding undefinde behaviour.
|
||||||
|
Undefined behaviour, UB for short, is behaviour of a program upon which no restrictions are imposed. If UB is executed,
|
||||||
|
<em>anything</em> can happen, including segmentation faults, silent memory corruption, leakage of private keys or exactly
|
||||||
|
what you intended to happen. Examples of UB include use-after-free, out of bounds reads or data races.</p><p>I cannot recommend Miri highly enough for all unsafe code you’re writing (sadly support for some IO functions
|
||||||
|
and FFI is still lacking, and it’s still very slow).</p><p>So, let’s see whether our code contains UB. It has to, since otherwise the optimizer wouldn’t be allowed to change
|
||||||
|
observable behaviour (since the assert doesn’t fail in debug mode). <code>$ cargo miri run</code>…</p><pre tabindex=0><code class=language-rust,ignore data-lang=rust,ignore>error: Undefined Behavior: attempting a read access using <3314> at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
--> src/main.rs:2:26
|
||||||
|
|
|
||||||
|
2 | let value = unsafe { *ptr };
|
||||||
|
| ^^^^
|
||||||
|
| |
|
||||||
|
| attempting a read access using <3314> at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
| this error occurs as part of an access at alloc1722[0x0..0x1]
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
|
||||||
|
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||||
|
help: <3314> was created by a retag at offsets [0x0..0x1]
|
||||||
|
--> src/main.rs:10:26
|
||||||
|
|
|
||||||
|
10 | let ptr: *const u8 = &*b;
|
||||||
|
| ^^^
|
||||||
|
help: <3314> was later invalidated at offsets [0x0..0x1]
|
||||||
|
--> src/main.rs:12:29
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^
|
||||||
|
= note: backtrace:
|
||||||
|
= note: inside `takes_box_and_ptr_to_it` at src/main.rs:2:26
|
||||||
|
note: inside `main` at src/main.rs:12:5
|
||||||
|
--> src/main.rs:12:5
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
</code></pre><p>This behaviour does indeed not look very defined at all. But what went wrong? There’s a lot of information here.</p><p>First of all, it says that we attempted a read access, and that this access failed because the tag does not exist in the
|
||||||
|
borrow stack. This is something about stacked borrows, the experimental memory model for Rust that is implemented
|
||||||
|
in Miri. For an excellent introduction, see this part of the great book <a href=https://rust-unofficial.github.io/too-many-lists/fifth-stacked-borrows.html>Learning Rust With Entirely Too Many Linked Lists</a>.</p><p>In short: each pointer has a unique tag attacked to it. Bytes in memory have a stack of such tags, and only the pointers
|
||||||
|
that have their tag in the stack are allowed to access it. Tags can be pushed and popped from the stack through various
|
||||||
|
operations, for example borrowing.</p><p>In the code example above, we get a nice little hint where the tag was created. When we created a reference (that was then
|
||||||
|
coerced into a raw pointer) from our box, it got a new tag called <code><3314></code>. Then, when we moved the box into the function,
|
||||||
|
something happened: The tag was invalidated and popped off the borrow stack. That’s because box invalidates all tags when it’s
|
||||||
|
moved. The tag was popped off the borrow stack and we tried to read from it anyways - undefined behaviour happened!</p><p>And that’s how our code wasn’t a miscompilation, but undefined behaviour. Quite surprising, isn’t it?</p><h1 id=noalias-nothanks>noalias, nothanks<a href=#noalias-nothanks class=hanchor arialabel=Anchor>⌗</a></h1><p>Many people, myself included, don’t think that this is a good thing.</p><p>First of all, it introduces more UB that could have been defined behaviour instead. This is true for almost all UB, but usually,
|
||||||
|
there is something gained from the UB that justifies it. We will look at this later. But allowing such behaviour is fairly easy:
|
||||||
|
If box didn’t invalidate pointers on move and instead behaved like a normal raw pointer, the code above would be sound.</p><p>But more importantly, this is not behaviour generally expected by users. While it can be argued that box is like a <code>T</code>, but on
|
||||||
|
the heap, and therefore moving it should invalidate pointers, since moving <code>T</code> definitely has to invalidate pointers to it,
|
||||||
|
this comparison doesn’t make sense to me. While <code>Box<T></code> usually behaves like a <code>T</code>, it’s just a pointer. Writers of unsafe
|
||||||
|
code <em>know</em> that box is just a pointer, and will abuse that knowledge, accidentally causing UB with it. While this can be
|
||||||
|
mitigated with better docs and teaching, like how no one questions the uniqueness of <code>&mut T</code> (maybe that’s also because that
|
||||||
|
one makes intuitive sense, “shared xor mutable” is a simple concept), I think it will always be a problem,
|
||||||
|
because in my opinion, box being unique and invalidating pointers on move is simply not intiutive.</p><p>When a box is moved, the pointer bytes change their location in memory. But the bytes the box points to stay the same. They don’t
|
||||||
|
move in memory. This is the fundamental missing intuition about the box behaviour.</p><p>There are also other reasons why the box behaviour is not desirable. Even people who know about the behaviour of box will want
|
||||||
|
to write code that goes directly against this behaviour at some point. But usually, fixing it is pretty simple: Storing a raw
|
||||||
|
pointer (or <code>NonNull<T></code>) instead of a box, and using the constructor and drop to allocate and deallocate the backing box.
|
||||||
|
This is fairly inconvenient, but totally acceptable. There are bigger problems though. There are crates like <code>owning_ref</code>
|
||||||
|
that want to expose a generic interface over any type. Users like to choose box, and sometimes <em>have</em> to chose box because of
|
||||||
|
other box-exclusive features it offers. Even worse is <code>string_cache</code>, which is extremely hard to fix.</p><p>Then last but not least, there’s the opinionated fact that <code>Box<T></code> shall be implementable entirely in user code. While we are
|
||||||
|
many missing language features away from this being the case, the <code>noalias</code> case is also magic descended upon box itself, with no
|
||||||
|
user code ever having access to it.</p><h1 id=noalias-noslow>noalias, noslow<a href=#noalias-noslow class=hanchor arialabel=Anchor>⌗</a></h1><p>There are also several arguments in favour of box being unique and special cased here. To negate the last argument above, it can
|
||||||
|
be said that <code>Box<T></code> <em>is</em> a very special type. It’s just like a <code>T</code>, but on the heap. Using this mental model, it’s very easy to
|
||||||
|
justify all the box magic and its unique behaviour.</p><p>This mental model is one that many people have, but what does this bring us? This is just one mental model of box, and
|
||||||
|
there are other mental models of it (like “a reference that manages its lifetime itself” or “a safe RAII pointer”).</p><p>There is one clear potential benefit from this box behaviour. ✨Optimizations✨. <code>noalias</code> doesn’t exist for fun, it’s something
|
||||||
|
that can bring clear performance wins (for <code>noalias</code> on <code>&mut T</code>, those were measureable). So the only question remains:
|
||||||
|
How much performance does <code>noalias</code> on <code>Box<T></code> give us now, and how much potential performance improvements could we get in the
|
||||||
|
future? For the latter, there is no simple answer. For the former, there is. <code>rustc</code> has <a href=https://github.com/rust-lang/rust/pull/99527><em>no</em> performance improvements</a> from being compiled with <code>noalias</code> on <code>Box<T></code>.</p><p>I have not yet benchmarked ecosystem crates without box noalias and don’t have the capacity to do so right now, so I would be very
|
||||||
|
grateful if anyone wanted to pick that up and report the results.</p><h1 id=a-way-forward>a way forward<a href=#a-way-forward class=hanchor arialabel=Anchor>⌗</a></h1><p>Based on all of this, I do have a solution that, in opinion, will fix all of this, even potential performance regressions with
|
||||||
|
box. First of all, I think that even if there are some performance regressions in ecosystem crates, the overall tradeoff goes
|
||||||
|
against the current box behaviour. Unsafe code wants to use box, and it is reasonable to do so. Therefore I propose to completely
|
||||||
|
remove all uniqueness from <code>Box<T></code>, and treat it just like a <code>*const T</code> for the purposes of aliasing. This will make it more
|
||||||
|
predictable for unsafe code, and comes at none or only a minor performance cost.</p><p>But this performance cost may be real, and especially the future optimization value can’t be certain. I do think that there
|
||||||
|
should be a way to get the uniqueness guarantees in some other way than through box. One possibility would be to use a <code>&'static mut T</code> that is unleaked for drop, but the semantics of this are still <a href=https://github.com/rust-lang/unsafe-code-guidelines/issues/316>unclear</a>. If that is not possible, maybe exposing <code>std::ptr::Unique</code> (with it getting boxes aliasing semantics) could be desirable. For this, all existing usages of <code>Unique</code> inside the standard library would have to be removed though.</p><p>I guess what I am wishing for are some good and flexible raw pointer types. That’s still in the stars…</p><p>For more information about this topic, see <a href=https://github.com/rust-lang/unsafe-code-guidelines/issues/326>https://github.com/rust-lang/unsafe-code-guidelines/issues/326</a></p></div></div></div></div><footer class=footer><div class=footer__inner><div class=copyright><span>© 2022 Powered by <a href=http://gohugo.io>Hugo</a></span>
|
||||||
|
<span>:: Theme made by <a href=https://twitter.com/panr>panr</a></span></div></div></footer><script src=http://nilstrieb.github.io/assets/main.js></script>
|
||||||
|
<script src=http://nilstrieb.github.io/assets/prism.js></script></div></body></html>
|
||||||
5
posts/index.html
Normal file
5
posts/index.html
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<!doctype html><html lang=en><head><title>Posts :: nilstriebs blog</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content><meta name=keywords content><meta name=robots content="noodp"><link rel=canonical href=http://nilstrieb.github.io/posts/><link rel=stylesheet href=http://nilstrieb.github.io/assets/style.css><link rel=stylesheet href=assets/%25!s%28%3cnil%3e%29.css><link rel=apple-touch-icon href=http://nilstrieb.github.io/img/apple-touch-icon-192x192.png><link rel="shortcut icon" href=http://nilstrieb.github.io/img/favicon/orange.png><meta name=twitter:card content="summary"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta property="og:title" content="Posts"><meta property="og:description" content><meta property="og:url" content="http://nilstrieb.github.io/posts/"><meta property="og:site_name" content="nilstriebs blog"><meta property="og:image" content="img/favicon/%!s().png"><meta property="og:image:width" content="2048"><meta property="og:image:height" content="1024"><link href=/posts/index.xml rel=alternate type=application/rss+xml title="nilstriebs blog"></head><body><div class="container headings--one-size"><header class=header><div class=header__inner><div class=header__logo><a href=http://nilstrieb.github.io/><div class=logo>Terminal</div></a></div></div></header><div class=content><div class=posts><div class="post on-list"><h1 class=post-title><a href=http://nilstrieb.github.io/posts/box-is-a-unique-type/>Box Is a Unique Type</a></h1><div class=post-meta><span class=post-date>2022-07-22</span>
|
||||||
|
<span class=post-author>:: Nilstrieb</span></div><span class=post-tags>#<a href=http://nilstrieb.github.io/tags/rust/>rust</a>
|
||||||
|
#<a href=http://nilstrieb.github.io/tags/unsafe-code/>unsafe code</a> </span><div class=post-content>The current and future aliasing semantics of Box</div><div><a class="read-more button" href=/posts/box-is-a-unique-type/>→</a></div></div><div class=pagination><div class=pagination__buttons></div></div></div></div><footer class=footer><div class=footer__inner><div class=copyright><span>© 2022 Powered by <a href=http://gohugo.io>Hugo</a></span>
|
||||||
|
<span>:: Theme made by <a href=https://twitter.com/panr>panr</a></span></div></div></footer><script src=http://nilstrieb.github.io/assets/main.js></script>
|
||||||
|
<script src=http://nilstrieb.github.io/assets/prism.js></script></div></body></html>
|
||||||
155
posts/index.xml
Normal file
155
posts/index.xml
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on nilstriebs blog</title><link>http://nilstrieb.github.io/posts/</link><description>Recent content in Posts on nilstriebs blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 22 Jul 2022 00:00:00 +0000</lastBuildDate><atom:link href="http://nilstrieb.github.io/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Box Is a Unique Type</title><link>http://nilstrieb.github.io/posts/box-is-a-unique-type/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>http://nilstrieb.github.io/posts/box-is-a-unique-type/</guid><description>We have all used Box&lt;T&gt; before in our Rust code. It&rsquo;s a glorious type, with great ergonomics and flexibitility. We can use it to put our values on the heap, but it can do even more than that!
|
||||||
|
struct Fields { a: String, b: String, } let fields = Box::new(Fields { a: &#34;a&#34;.to_string(), b: &#34;b&#34;.to_string() }); let a = fields.a; let b = fields.b; This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve, and they exist for good reason: They are very useful.</description><content><p>We have all used <code>Box&lt;T&gt;</code> before in our Rust code. It&rsquo;s a glorious type, with great ergonomics
|
||||||
|
and flexibitility. We can use it to put our values on the heap, but it can do even more
|
||||||
|
than that!</p>
|
||||||
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Fields</span> {
|
||||||
|
</span></span><span style="display:flex;"><span> a: String,
|
||||||
|
</span></span><span style="display:flex;"><span> b: String,
|
||||||
|
</span></span><span style="display:flex;"><span>}
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> fields <span style="color:#f92672">=</span> Box::new(Fields {
|
||||||
|
</span></span><span style="display:flex;"><span> a: <span style="color:#e6db74">&#34;a&#34;</span>.to_string(),
|
||||||
|
</span></span><span style="display:flex;"><span> b: <span style="color:#e6db74">&#34;b&#34;</span>.to_string()
|
||||||
|
</span></span><span style="display:flex;"><span>});
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> a <span style="color:#f92672">=</span> fields.a;
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> b <span style="color:#f92672">=</span> fields.b;
|
||||||
|
</span></span></code></pre></div><p>This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve,
|
||||||
|
and they exist for good reason: They are very useful. Sadly we have not yet found a way to generalize all
|
||||||
|
of these to user types as well. Too bad!</p>
|
||||||
|
<p>Anyways, this post is about one particularly subtle magic aspect of box. For this, we need to dive
|
||||||
|
deep into unsafe code, so let&rsquo;s get our hazmat suits on and jump in!</p>
|
||||||
|
<h1 id="an-interesting-optimization">An interesting optimization</h1>
|
||||||
|
<p>We have this code here:</p>
|
||||||
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">takes_box_and_ptr_to_it</span>(<span style="color:#66d9ef">mut</span> b: Box<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">u8</span><span style="color:#f92672">&gt;</span>, ptr: <span style="color:#f92672">*</span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">u8</span>) {
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> value <span style="color:#f92672">=</span> <span style="color:#66d9ef">unsafe</span> { <span style="color:#f92672">*</span>ptr };
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#f92672">*</span>b <span style="color:#f92672">=</span> <span style="color:#ae81ff">5</span>;
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> value2 <span style="color:#f92672">=</span> <span style="color:#66d9ef">unsafe</span> { <span style="color:#f92672">*</span>ptr };
|
||||||
|
</span></span><span style="display:flex;"><span> assert_ne!(value, value2);
|
||||||
|
</span></span><span style="display:flex;"><span>}
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> b <span style="color:#f92672">=</span> Box::new(<span style="color:#ae81ff">0</span>);
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> ptr: <span style="color:#f92672">*</span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">u8</span> <span style="color:#f92672">=</span> <span style="color:#f92672">&amp;*</span>b;
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span>takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
</span></span></code></pre></div><p>There&rsquo;s a function, <code>takes_box_and_ptr_to_it</code>, that takes a box and a pointer as parameters. Then,
|
||||||
|
it reads a value from the pointer, writes to the box, and reads a value again. It then asserts that
|
||||||
|
the two values aren&rsquo;t equal. How can they not be equal? If our box and pointer point to the same
|
||||||
|
location in memory, writing to the box will cause the pointer to read the new value.</p>
|
||||||
|
<p>Now construct a box, get a pointer to it, and pass the two to the function. Run the program&hellip;</p>
|
||||||
|
<p>&hellip; and everything is fine. Let&rsquo;s run it in release mode. This should work as well, since the optimizer
|
||||||
|
isn&rsquo;t allowed to change observable behaviour, and an assert is very observable. Run the progrm&hellip;</p>
|
||||||
|
<pre tabindex="0"><code>thread &#39;main&#39; panicked at &#39;assertion failed: `(left != right)`
|
||||||
|
left: `0`,
|
||||||
|
right: `0`&#39;, src/main.rs:5:5
|
||||||
|
</code></pre><p>Hmm. That&rsquo;s not what I&rsquo;ve told would happen. Is the compiler broken? Is this a miscompilation?
|
||||||
|
I&rsquo;ve heard that those do sometimes happen, right?</p>
|
||||||
|
<p>Trusting our instincts that &ldquo;it&rsquo;s never a miscompilation until it is one&rdquo;, we assume that LLVM behaved
|
||||||
|
well here. But what allows it to make this optimization? Taking a look at the generated LLVM-IR (by using
|
||||||
|
<code>--emit llvm-ir -O</code>, the <code>-O</code> is important since rustc only emits these attributes with optimizations on)
|
||||||
|
reveals the solution: (severely shortened to only show the relevant parts)</p>
|
||||||
|
<pre tabindex="0"><code class="language-llvmir" data-lang="llvmir">define void @takes_box_and_ptr_to_it(i8* noalias %0, i8* %ptr) {
|
||||||
|
</code></pre><p>See the little attribute on the first parameter called <code>noalias</code>? That&rsquo;s what&rsquo;s doing the magic here.
|
||||||
|
<code>noalias</code> is quite complex, but for our case here, it says that no other pointers point to (alias) the same location.
|
||||||
|
This allows the optimizer to assume that writing to the box pointer doesn&rsquo;t affect the other pointer - they are
|
||||||
|
not allowed to alias (it&rsquo;s like if they used <code>restrict</code> in C).</p>
|
||||||
|
<p>This might sound familiar to you if you&rsquo;re a viewer of <a href="https://twitter.com/jonhoo">Jon Gjengset</a>&rsquo;s content (which I can highly recommend). Jon has made an entire video about this before, since his crate <code>left-right</code>
|
||||||
|
was affected by this (<a href="https://youtu.be/EY7Wi9fV5bk)">https://youtu.be/EY7Wi9fV5bk)</a>.</p>
|
||||||
|
<p>If you&rsquo;re looking for <em>any</em> hint that using box emits <code>noalias</code>, you have to look no further than the documentation
|
||||||
|
for <a href="https://doc.rust-lang.org/nightly/std/boxed/index.html#considerations-for-unsafe-code"><code>std::boxed</code></a>. Well, the nightly or beta docs, because I only added this section very recently. For years, this behaviour was not really documented, and you had to
|
||||||
|
belong to the arcane circles of the select few who were aware of it. So lots of code was written thinking that box was &ldquo;just a RAII pointer&rdquo; (a pointer that allocates the value in the constructor, and deallocates it in the destructor on drop) for all
|
||||||
|
pointers are concerned.</p>
|
||||||
|
<h1 id="stacked-borrows-and-miri">Stacked Borrows and Miri</h1>
|
||||||
|
<p><a href="https://github.com/rust-lang/miri">Miri</a> is an interpreter for Rust code with the goal of finding undefinde behaviour.
|
||||||
|
Undefined behaviour, UB for short, is behaviour of a program upon which no restrictions are imposed. If UB is executed,
|
||||||
|
<em>anything</em> can happen, including segmentation faults, silent memory corruption, leakage of private keys or exactly
|
||||||
|
what you intended to happen. Examples of UB include use-after-free, out of bounds reads or data races.</p>
|
||||||
|
<p>I cannot recommend Miri highly enough for all unsafe code you&rsquo;re writing (sadly support for some IO functions
|
||||||
|
and FFI is still lacking, and it&rsquo;s still very slow).</p>
|
||||||
|
<p>So, let&rsquo;s see whether our code contains UB. It has to, since otherwise the optimizer wouldn&rsquo;t be allowed to change
|
||||||
|
observable behaviour (since the assert doesn&rsquo;t fail in debug mode). <code>$ cargo miri run</code>&hellip;</p>
|
||||||
|
<pre tabindex="0"><code class="language-rust,ignore" data-lang="rust,ignore">error: Undefined Behavior: attempting a read access using &lt;3314&gt; at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
--&gt; src/main.rs:2:26
|
||||||
|
|
|
||||||
|
2 | let value = unsafe { *ptr };
|
||||||
|
| ^^^^
|
||||||
|
| |
|
||||||
|
| attempting a read access using &lt;3314&gt; at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
| this error occurs as part of an access at alloc1722[0x0..0x1]
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
|
||||||
|
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||||
|
help: &lt;3314&gt; was created by a retag at offsets [0x0..0x1]
|
||||||
|
--&gt; src/main.rs:10:26
|
||||||
|
|
|
||||||
|
10 | let ptr: *const u8 = &amp;*b;
|
||||||
|
| ^^^
|
||||||
|
help: &lt;3314&gt; was later invalidated at offsets [0x0..0x1]
|
||||||
|
--&gt; src/main.rs:12:29
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^
|
||||||
|
= note: backtrace:
|
||||||
|
= note: inside `takes_box_and_ptr_to_it` at src/main.rs:2:26
|
||||||
|
note: inside `main` at src/main.rs:12:5
|
||||||
|
--&gt; src/main.rs:12:5
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
</code></pre><p>This behaviour does indeed not look very defined at all. But what went wrong? There&rsquo;s a lot of information here.</p>
|
||||||
|
<p>First of all, it says that we attempted a read access, and that this access failed because the tag does not exist in the
|
||||||
|
borrow stack. This is something about stacked borrows, the experimental memory model for Rust that is implemented
|
||||||
|
in Miri. For an excellent introduction, see this part of the great book <a href="https://rust-unofficial.github.io/too-many-lists/fifth-stacked-borrows.html">Learning Rust With Entirely Too Many Linked Lists</a>.</p>
|
||||||
|
<p>In short: each pointer has a unique tag attacked to it. Bytes in memory have a stack of such tags, and only the pointers
|
||||||
|
that have their tag in the stack are allowed to access it. Tags can be pushed and popped from the stack through various
|
||||||
|
operations, for example borrowing.</p>
|
||||||
|
<p>In the code example above, we get a nice little hint where the tag was created. When we created a reference (that was then
|
||||||
|
coerced into a raw pointer) from our box, it got a new tag called <code>&lt;3314&gt;</code>. Then, when we moved the box into the function,
|
||||||
|
something happened: The tag was invalidated and popped off the borrow stack. That&rsquo;s because box invalidates all tags when it&rsquo;s
|
||||||
|
moved. The tag was popped off the borrow stack and we tried to read from it anyways - undefined behaviour happened!</p>
|
||||||
|
<p>And that&rsquo;s how our code wasn&rsquo;t a miscompilation, but undefined behaviour. Quite surprising, isn&rsquo;t it?</p>
|
||||||
|
<h1 id="noalias-nothanks">noalias, nothanks</h1>
|
||||||
|
<p>Many people, myself included, don&rsquo;t think that this is a good thing.</p>
|
||||||
|
<p>First of all, it introduces more UB that could have been defined behaviour instead. This is true for almost all UB, but usually,
|
||||||
|
there is something gained from the UB that justifies it. We will look at this later. But allowing such behaviour is fairly easy:
|
||||||
|
If box didn&rsquo;t invalidate pointers on move and instead behaved like a normal raw pointer, the code above would be sound.</p>
|
||||||
|
<p>But more importantly, this is not behaviour generally expected by users. While it can be argued that box is like a <code>T</code>, but on
|
||||||
|
the heap, and therefore moving it should invalidate pointers, since moving <code>T</code> definitely has to invalidate pointers to it,
|
||||||
|
this comparison doesn&rsquo;t make sense to me. While <code>Box&lt;T&gt;</code> usually behaves like a <code>T</code>, it&rsquo;s just a pointer. Writers of unsafe
|
||||||
|
code <em>know</em> that box is just a pointer, and will abuse that knowledge, accidentally causing UB with it. While this can be
|
||||||
|
mitigated with better docs and teaching, like how no one questions the uniqueness of <code>&amp;mut T</code> (maybe that&rsquo;s also because that
|
||||||
|
one makes intuitive sense, &ldquo;shared xor mutable&rdquo; is a simple concept), I think it will always be a problem,
|
||||||
|
because in my opinion, box being unique and invalidating pointers on move is simply not intiutive.</p>
|
||||||
|
<p>When a box is moved, the pointer bytes change their location in memory. But the bytes the box points to stay the same. They don&rsquo;t
|
||||||
|
move in memory. This is the fundamental missing intuition about the box behaviour.</p>
|
||||||
|
<p>There are also other reasons why the box behaviour is not desirable. Even people who know about the behaviour of box will want
|
||||||
|
to write code that goes directly against this behaviour at some point. But usually, fixing it is pretty simple: Storing a raw
|
||||||
|
pointer (or <code>NonNull&lt;T&gt;</code>) instead of a box, and using the constructor and drop to allocate and deallocate the backing box.
|
||||||
|
This is fairly inconvenient, but totally acceptable. There are bigger problems though. There are crates like <code>owning_ref</code>
|
||||||
|
that want to expose a generic interface over any type. Users like to choose box, and sometimes <em>have</em> to chose box because of
|
||||||
|
other box-exclusive features it offers. Even worse is <code>string_cache</code>, which is extremely hard to fix.</p>
|
||||||
|
<p>Then last but not least, there&rsquo;s the opinionated fact that <code>Box&lt;T&gt;</code> shall be implementable entirely in user code. While we are
|
||||||
|
many missing language features away from this being the case, the <code>noalias</code> case is also magic descended upon box itself, with no
|
||||||
|
user code ever having access to it.</p>
|
||||||
|
<h1 id="noalias-noslow">noalias, noslow</h1>
|
||||||
|
<p>There are also several arguments in favour of box being unique and special cased here. To negate the last argument above, it can
|
||||||
|
be said that <code>Box&lt;T&gt;</code> <em>is</em> a very special type. It&rsquo;s just like a <code>T</code>, but on the heap. Using this mental model, it&rsquo;s very easy to
|
||||||
|
justify all the box magic and its unique behaviour.</p>
|
||||||
|
<p>This mental model is one that many people have, but what does this bring us? This is just one mental model of box, and
|
||||||
|
there are other mental models of it (like &ldquo;a reference that manages its lifetime itself&rdquo; or &ldquo;a safe RAII pointer&rdquo;).</p>
|
||||||
|
<p>There is one clear potential benefit from this box behaviour. ✨Optimizations✨. <code>noalias</code> doesn&rsquo;t exist for fun, it&rsquo;s something
|
||||||
|
that can bring clear performance wins (for <code>noalias</code> on <code>&amp;mut T</code>, those were measureable). So the only question remains:
|
||||||
|
How much performance does <code>noalias</code> on <code>Box&lt;T&gt;</code> give us now, and how much potential performance improvements could we get in the
|
||||||
|
future? For the latter, there is no simple answer. For the former, there is. <code>rustc</code> has <a href="https://github.com/rust-lang/rust/pull/99527"><em>no</em> performance improvements</a> from being compiled with <code>noalias</code> on <code>Box&lt;T&gt;</code>.</p>
|
||||||
|
<p>I have not yet benchmarked ecosystem crates without box noalias and don&rsquo;t have the capacity to do so right now, so I would be very
|
||||||
|
grateful if anyone wanted to pick that up and report the results.</p>
|
||||||
|
<h1 id="a-way-forward">a way forward</h1>
|
||||||
|
<p>Based on all of this, I do have a solution that, in opinion, will fix all of this, even potential performance regressions with
|
||||||
|
box. First of all, I think that even if there are some performance regressions in ecosystem crates, the overall tradeoff goes
|
||||||
|
against the current box behaviour. Unsafe code wants to use box, and it is reasonable to do so. Therefore I propose to completely
|
||||||
|
remove all uniqueness from <code>Box&lt;T&gt;</code>, and treat it just like a <code>*const T</code> for the purposes of aliasing. This will make it more
|
||||||
|
predictable for unsafe code, and comes at none or only a minor performance cost.</p>
|
||||||
|
<p>But this performance cost may be real, and especially the future optimization value can&rsquo;t be certain. I do think that there
|
||||||
|
should be a way to get the uniqueness guarantees in some other way than through box. One possibility would be to use a <code>&amp;'static mut T</code> that is unleaked for drop, but the semantics of this are still <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/316">unclear</a>. If that is not possible, maybe exposing <code>std::ptr::Unique</code> (with it getting boxes aliasing semantics) could be desirable. For this, all existing usages of <code>Unique</code> inside the standard library would have to be removed though.</p>
|
||||||
|
<p>I guess what I am wishing for are some good and flexible raw pointer types. That&rsquo;s still in the stars&hellip;</p>
|
||||||
|
<p>For more information about this topic, see <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/326">https://github.com/rust-lang/unsafe-code-guidelines/issues/326</a></p></content></item></channel></rss>
|
||||||
1
posts/page/1/index.html
Normal file
1
posts/page/1/index.html
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<!doctype html><html lang=en-us><head><title>http://nilstrieb.github.io/posts/</title><link rel=canonical href=http://nilstrieb.github.io/posts/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://nilstrieb.github.io/posts/"></head></html>
|
||||||
|
|
@ -1 +1 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"><url><loc>http://nilstrieb.github.io/categories/</loc></url><url><loc>http://nilstrieb.github.io/</loc></url><url><loc>http://nilstrieb.github.io/tags/</loc></url></urlset>
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"><url><loc>http://nilstrieb.github.io/posts/box-is-a-unique-type/</loc><lastmod>2022-07-22T00:00:00+00:00</lastmod></url><url><loc>http://nilstrieb.github.io/</loc><lastmod>2022-07-22T00:00:00+00:00</lastmod></url><url><loc>http://nilstrieb.github.io/posts/</loc><lastmod>2022-07-22T00:00:00+00:00</lastmod></url><url><loc>http://nilstrieb.github.io/tags/rust/</loc><lastmod>2022-07-22T00:00:00+00:00</lastmod></url><url><loc>http://nilstrieb.github.io/tags/</loc><lastmod>2022-07-22T00:00:00+00:00</lastmod></url><url><loc>http://nilstrieb.github.io/tags/unsafe-code/</loc><lastmod>2022-07-22T00:00:00+00:00</lastmod></url><url><loc>http://nilstrieb.github.io/categories/</loc></url></urlset>
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
<!doctype html><html lang=en><head><title>Tags :: nilstriebs blog</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content><meta name=keywords content><meta name=robots content="noodp"><link rel=canonical href=http://nilstrieb.github.io/tags/><link rel=stylesheet href=http://nilstrieb.github.io/assets/style.css><link rel=stylesheet href=assets/%25!s%28%3cnil%3e%29.css><link rel=apple-touch-icon href=http://nilstrieb.github.io/img/apple-touch-icon-192x192.png><link rel="shortcut icon" href=http://nilstrieb.github.io/img/favicon/orange.png><meta name=twitter:card content="summary"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta property="og:title" content="Tags"><meta property="og:description" content><meta property="og:url" content="http://nilstrieb.github.io/tags/"><meta property="og:site_name" content="nilstriebs blog"><meta property="og:image" content="img/favicon/%!s().png"><meta property="og:image:width" content="2048"><meta property="og:image:height" content="1024"><link href=/tags/index.xml rel=alternate type=application/rss+xml title="nilstriebs blog"></head><body><div class="container headings--one-size"><header class=header><div class=header__inner><div class=header__logo><a href=http://nilstrieb.github.io/><div class=logo>Terminal</div></a></div></div></header><div class=content><div class=terms><h1>Tags</h1><ul></ul></div></div><footer class=footer><div class=footer__inner><div class=copyright><span>© 2022 Powered by <a href=http://gohugo.io>Hugo</a></span>
|
<!doctype html><html lang=en><head><title>Tags :: nilstriebs blog</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content><meta name=keywords content><meta name=robots content="noodp"><link rel=canonical href=http://nilstrieb.github.io/tags/><link rel=stylesheet href=http://nilstrieb.github.io/assets/style.css><link rel=stylesheet href=assets/%25!s%28%3cnil%3e%29.css><link rel=apple-touch-icon href=http://nilstrieb.github.io/img/apple-touch-icon-192x192.png><link rel="shortcut icon" href=http://nilstrieb.github.io/img/favicon/orange.png><meta name=twitter:card content="summary"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta property="og:title" content="Tags"><meta property="og:description" content><meta property="og:url" content="http://nilstrieb.github.io/tags/"><meta property="og:site_name" content="nilstriebs blog"><meta property="og:image" content="img/favicon/%!s().png"><meta property="og:image:width" content="2048"><meta property="og:image:height" content="1024"><link href=/tags/index.xml rel=alternate type=application/rss+xml title="nilstriebs blog"></head><body><div class="container headings--one-size"><header class=header><div class=header__inner><div class=header__logo><a href=http://nilstrieb.github.io/><div class=logo>Terminal</div></a></div></div></header><div class=content><div class=terms><h1>Tags</h1><ul><li><a class=terms-title href=http://nilstrieb.github.io/tags/rust/>rust (1)</a></li><li><a class=terms-title href=http://nilstrieb.github.io/tags/unsafe-code/>unsafe code (1)</a></li></ul></div></div><footer class=footer><div class=footer__inner><div class=copyright><span>© 2022 Powered by <a href=http://gohugo.io>Hugo</a></span>
|
||||||
<span>:: Theme made by <a href=https://twitter.com/panr>panr</a></span></div></div></footer><script src=http://nilstrieb.github.io/assets/main.js></script>
|
<span>:: Theme made by <a href=https://twitter.com/panr>panr</a></span></div></div></footer><script src=http://nilstrieb.github.io/assets/main.js></script>
|
||||||
<script src=http://nilstrieb.github.io/assets/prism.js></script></div></body></html>
|
<script src=http://nilstrieb.github.io/assets/prism.js></script></div></body></html>
|
||||||
|
|
@ -1 +1 @@
|
||||||
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tags on nilstriebs blog</title><link>http://nilstrieb.github.io/tags/</link><description>Recent content in Tags on nilstriebs blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><atom:link href="http://nilstrieb.github.io/tags/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tags on nilstriebs blog</title><link>http://nilstrieb.github.io/tags/</link><description>Recent content in Tags on nilstriebs blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 22 Jul 2022 00:00:00 +0000</lastBuildDate><atom:link href="http://nilstrieb.github.io/tags/index.xml" rel="self" type="application/rss+xml"/><item><title>rust</title><link>http://nilstrieb.github.io/tags/rust/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>http://nilstrieb.github.io/tags/rust/</guid><description/><content/></item><item><title>unsafe code</title><link>http://nilstrieb.github.io/tags/unsafe-code/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>http://nilstrieb.github.io/tags/unsafe-code/</guid><description/><content/></item></channel></rss>
|
||||||
5
tags/rust/index.html
Normal file
5
tags/rust/index.html
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<!doctype html><html lang=en><head><title>rust :: nilstriebs blog</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content><meta name=keywords content><meta name=robots content="noodp"><link rel=canonical href=http://nilstrieb.github.io/tags/rust/><link rel=stylesheet href=http://nilstrieb.github.io/assets/style.css><link rel=stylesheet href=assets/%25!s%28%3cnil%3e%29.css><link rel=apple-touch-icon href=http://nilstrieb.github.io/img/apple-touch-icon-192x192.png><link rel="shortcut icon" href=http://nilstrieb.github.io/img/favicon/orange.png><meta name=twitter:card content="summary"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta property="og:title" content="rust"><meta property="og:description" content><meta property="og:url" content="http://nilstrieb.github.io/tags/rust/"><meta property="og:site_name" content="nilstriebs blog"><meta property="og:image" content="img/favicon/%!s().png"><meta property="og:image:width" content="2048"><meta property="og:image:height" content="1024"><link href=/tags/rust/index.xml rel=alternate type=application/rss+xml title="nilstriebs blog"></head><body><div class="container headings--one-size"><header class=header><div class=header__inner><div class=header__logo><a href=http://nilstrieb.github.io/><div class=logo>Terminal</div></a></div></div></header><div class=content><div class=posts><div class="post on-list"><h1 class=post-title><a href=http://nilstrieb.github.io/posts/box-is-a-unique-type/>Box Is a Unique Type</a></h1><div class=post-meta><span class=post-date>2022-07-22</span>
|
||||||
|
<span class=post-author>:: Nilstrieb</span></div><span class=post-tags>#<a href=http://nilstrieb.github.io/tags/rust/>rust</a>
|
||||||
|
#<a href=http://nilstrieb.github.io/tags/unsafe-code/>unsafe code</a> </span><div class=post-content>The current and future aliasing semantics of Box</div><div><a class="read-more button" href=/posts/box-is-a-unique-type/>→</a></div></div><div class=pagination><div class=pagination__buttons></div></div></div></div><footer class=footer><div class=footer__inner><div class=copyright><span>© 2022 Powered by <a href=http://gohugo.io>Hugo</a></span>
|
||||||
|
<span>:: Theme made by <a href=https://twitter.com/panr>panr</a></span></div></div></footer><script src=http://nilstrieb.github.io/assets/main.js></script>
|
||||||
|
<script src=http://nilstrieb.github.io/assets/prism.js></script></div></body></html>
|
||||||
155
tags/rust/index.xml
Normal file
155
tags/rust/index.xml
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>rust on nilstriebs blog</title><link>http://nilstrieb.github.io/tags/rust/</link><description>Recent content in rust on nilstriebs blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 22 Jul 2022 00:00:00 +0000</lastBuildDate><atom:link href="http://nilstrieb.github.io/tags/rust/index.xml" rel="self" type="application/rss+xml"/><item><title>Box Is a Unique Type</title><link>http://nilstrieb.github.io/posts/box-is-a-unique-type/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>http://nilstrieb.github.io/posts/box-is-a-unique-type/</guid><description>We have all used Box&lt;T&gt; before in our Rust code. It&rsquo;s a glorious type, with great ergonomics and flexibitility. We can use it to put our values on the heap, but it can do even more than that!
|
||||||
|
struct Fields { a: String, b: String, } let fields = Box::new(Fields { a: &#34;a&#34;.to_string(), b: &#34;b&#34;.to_string() }); let a = fields.a; let b = fields.b; This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve, and they exist for good reason: They are very useful.</description><content><p>We have all used <code>Box&lt;T&gt;</code> before in our Rust code. It&rsquo;s a glorious type, with great ergonomics
|
||||||
|
and flexibitility. We can use it to put our values on the heap, but it can do even more
|
||||||
|
than that!</p>
|
||||||
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Fields</span> {
|
||||||
|
</span></span><span style="display:flex;"><span> a: String,
|
||||||
|
</span></span><span style="display:flex;"><span> b: String,
|
||||||
|
</span></span><span style="display:flex;"><span>}
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> fields <span style="color:#f92672">=</span> Box::new(Fields {
|
||||||
|
</span></span><span style="display:flex;"><span> a: <span style="color:#e6db74">&#34;a&#34;</span>.to_string(),
|
||||||
|
</span></span><span style="display:flex;"><span> b: <span style="color:#e6db74">&#34;b&#34;</span>.to_string()
|
||||||
|
</span></span><span style="display:flex;"><span>});
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> a <span style="color:#f92672">=</span> fields.a;
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> b <span style="color:#f92672">=</span> fields.b;
|
||||||
|
</span></span></code></pre></div><p>This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve,
|
||||||
|
and they exist for good reason: They are very useful. Sadly we have not yet found a way to generalize all
|
||||||
|
of these to user types as well. Too bad!</p>
|
||||||
|
<p>Anyways, this post is about one particularly subtle magic aspect of box. For this, we need to dive
|
||||||
|
deep into unsafe code, so let&rsquo;s get our hazmat suits on and jump in!</p>
|
||||||
|
<h1 id="an-interesting-optimization">An interesting optimization</h1>
|
||||||
|
<p>We have this code here:</p>
|
||||||
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">takes_box_and_ptr_to_it</span>(<span style="color:#66d9ef">mut</span> b: Box<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">u8</span><span style="color:#f92672">&gt;</span>, ptr: <span style="color:#f92672">*</span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">u8</span>) {
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> value <span style="color:#f92672">=</span> <span style="color:#66d9ef">unsafe</span> { <span style="color:#f92672">*</span>ptr };
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#f92672">*</span>b <span style="color:#f92672">=</span> <span style="color:#ae81ff">5</span>;
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> value2 <span style="color:#f92672">=</span> <span style="color:#66d9ef">unsafe</span> { <span style="color:#f92672">*</span>ptr };
|
||||||
|
</span></span><span style="display:flex;"><span> assert_ne!(value, value2);
|
||||||
|
</span></span><span style="display:flex;"><span>}
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> b <span style="color:#f92672">=</span> Box::new(<span style="color:#ae81ff">0</span>);
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> ptr: <span style="color:#f92672">*</span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">u8</span> <span style="color:#f92672">=</span> <span style="color:#f92672">&amp;*</span>b;
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span>takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
</span></span></code></pre></div><p>There&rsquo;s a function, <code>takes_box_and_ptr_to_it</code>, that takes a box and a pointer as parameters. Then,
|
||||||
|
it reads a value from the pointer, writes to the box, and reads a value again. It then asserts that
|
||||||
|
the two values aren&rsquo;t equal. How can they not be equal? If our box and pointer point to the same
|
||||||
|
location in memory, writing to the box will cause the pointer to read the new value.</p>
|
||||||
|
<p>Now construct a box, get a pointer to it, and pass the two to the function. Run the program&hellip;</p>
|
||||||
|
<p>&hellip; and everything is fine. Let&rsquo;s run it in release mode. This should work as well, since the optimizer
|
||||||
|
isn&rsquo;t allowed to change observable behaviour, and an assert is very observable. Run the progrm&hellip;</p>
|
||||||
|
<pre tabindex="0"><code>thread &#39;main&#39; panicked at &#39;assertion failed: `(left != right)`
|
||||||
|
left: `0`,
|
||||||
|
right: `0`&#39;, src/main.rs:5:5
|
||||||
|
</code></pre><p>Hmm. That&rsquo;s not what I&rsquo;ve told would happen. Is the compiler broken? Is this a miscompilation?
|
||||||
|
I&rsquo;ve heard that those do sometimes happen, right?</p>
|
||||||
|
<p>Trusting our instincts that &ldquo;it&rsquo;s never a miscompilation until it is one&rdquo;, we assume that LLVM behaved
|
||||||
|
well here. But what allows it to make this optimization? Taking a look at the generated LLVM-IR (by using
|
||||||
|
<code>--emit llvm-ir -O</code>, the <code>-O</code> is important since rustc only emits these attributes with optimizations on)
|
||||||
|
reveals the solution: (severely shortened to only show the relevant parts)</p>
|
||||||
|
<pre tabindex="0"><code class="language-llvmir" data-lang="llvmir">define void @takes_box_and_ptr_to_it(i8* noalias %0, i8* %ptr) {
|
||||||
|
</code></pre><p>See the little attribute on the first parameter called <code>noalias</code>? That&rsquo;s what&rsquo;s doing the magic here.
|
||||||
|
<code>noalias</code> is quite complex, but for our case here, it says that no other pointers point to (alias) the same location.
|
||||||
|
This allows the optimizer to assume that writing to the box pointer doesn&rsquo;t affect the other pointer - they are
|
||||||
|
not allowed to alias (it&rsquo;s like if they used <code>restrict</code> in C).</p>
|
||||||
|
<p>This might sound familiar to you if you&rsquo;re a viewer of <a href="https://twitter.com/jonhoo">Jon Gjengset</a>&rsquo;s content (which I can highly recommend). Jon has made an entire video about this before, since his crate <code>left-right</code>
|
||||||
|
was affected by this (<a href="https://youtu.be/EY7Wi9fV5bk)">https://youtu.be/EY7Wi9fV5bk)</a>.</p>
|
||||||
|
<p>If you&rsquo;re looking for <em>any</em> hint that using box emits <code>noalias</code>, you have to look no further than the documentation
|
||||||
|
for <a href="https://doc.rust-lang.org/nightly/std/boxed/index.html#considerations-for-unsafe-code"><code>std::boxed</code></a>. Well, the nightly or beta docs, because I only added this section very recently. For years, this behaviour was not really documented, and you had to
|
||||||
|
belong to the arcane circles of the select few who were aware of it. So lots of code was written thinking that box was &ldquo;just a RAII pointer&rdquo; (a pointer that allocates the value in the constructor, and deallocates it in the destructor on drop) for all
|
||||||
|
pointers are concerned.</p>
|
||||||
|
<h1 id="stacked-borrows-and-miri">Stacked Borrows and Miri</h1>
|
||||||
|
<p><a href="https://github.com/rust-lang/miri">Miri</a> is an interpreter for Rust code with the goal of finding undefinde behaviour.
|
||||||
|
Undefined behaviour, UB for short, is behaviour of a program upon which no restrictions are imposed. If UB is executed,
|
||||||
|
<em>anything</em> can happen, including segmentation faults, silent memory corruption, leakage of private keys or exactly
|
||||||
|
what you intended to happen. Examples of UB include use-after-free, out of bounds reads or data races.</p>
|
||||||
|
<p>I cannot recommend Miri highly enough for all unsafe code you&rsquo;re writing (sadly support for some IO functions
|
||||||
|
and FFI is still lacking, and it&rsquo;s still very slow).</p>
|
||||||
|
<p>So, let&rsquo;s see whether our code contains UB. It has to, since otherwise the optimizer wouldn&rsquo;t be allowed to change
|
||||||
|
observable behaviour (since the assert doesn&rsquo;t fail in debug mode). <code>$ cargo miri run</code>&hellip;</p>
|
||||||
|
<pre tabindex="0"><code class="language-rust,ignore" data-lang="rust,ignore">error: Undefined Behavior: attempting a read access using &lt;3314&gt; at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
--&gt; src/main.rs:2:26
|
||||||
|
|
|
||||||
|
2 | let value = unsafe { *ptr };
|
||||||
|
| ^^^^
|
||||||
|
| |
|
||||||
|
| attempting a read access using &lt;3314&gt; at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
| this error occurs as part of an access at alloc1722[0x0..0x1]
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
|
||||||
|
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||||
|
help: &lt;3314&gt; was created by a retag at offsets [0x0..0x1]
|
||||||
|
--&gt; src/main.rs:10:26
|
||||||
|
|
|
||||||
|
10 | let ptr: *const u8 = &amp;*b;
|
||||||
|
| ^^^
|
||||||
|
help: &lt;3314&gt; was later invalidated at offsets [0x0..0x1]
|
||||||
|
--&gt; src/main.rs:12:29
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^
|
||||||
|
= note: backtrace:
|
||||||
|
= note: inside `takes_box_and_ptr_to_it` at src/main.rs:2:26
|
||||||
|
note: inside `main` at src/main.rs:12:5
|
||||||
|
--&gt; src/main.rs:12:5
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
</code></pre><p>This behaviour does indeed not look very defined at all. But what went wrong? There&rsquo;s a lot of information here.</p>
|
||||||
|
<p>First of all, it says that we attempted a read access, and that this access failed because the tag does not exist in the
|
||||||
|
borrow stack. This is something about stacked borrows, the experimental memory model for Rust that is implemented
|
||||||
|
in Miri. For an excellent introduction, see this part of the great book <a href="https://rust-unofficial.github.io/too-many-lists/fifth-stacked-borrows.html">Learning Rust With Entirely Too Many Linked Lists</a>.</p>
|
||||||
|
<p>In short: each pointer has a unique tag attacked to it. Bytes in memory have a stack of such tags, and only the pointers
|
||||||
|
that have their tag in the stack are allowed to access it. Tags can be pushed and popped from the stack through various
|
||||||
|
operations, for example borrowing.</p>
|
||||||
|
<p>In the code example above, we get a nice little hint where the tag was created. When we created a reference (that was then
|
||||||
|
coerced into a raw pointer) from our box, it got a new tag called <code>&lt;3314&gt;</code>. Then, when we moved the box into the function,
|
||||||
|
something happened: The tag was invalidated and popped off the borrow stack. That&rsquo;s because box invalidates all tags when it&rsquo;s
|
||||||
|
moved. The tag was popped off the borrow stack and we tried to read from it anyways - undefined behaviour happened!</p>
|
||||||
|
<p>And that&rsquo;s how our code wasn&rsquo;t a miscompilation, but undefined behaviour. Quite surprising, isn&rsquo;t it?</p>
|
||||||
|
<h1 id="noalias-nothanks">noalias, nothanks</h1>
|
||||||
|
<p>Many people, myself included, don&rsquo;t think that this is a good thing.</p>
|
||||||
|
<p>First of all, it introduces more UB that could have been defined behaviour instead. This is true for almost all UB, but usually,
|
||||||
|
there is something gained from the UB that justifies it. We will look at this later. But allowing such behaviour is fairly easy:
|
||||||
|
If box didn&rsquo;t invalidate pointers on move and instead behaved like a normal raw pointer, the code above would be sound.</p>
|
||||||
|
<p>But more importantly, this is not behaviour generally expected by users. While it can be argued that box is like a <code>T</code>, but on
|
||||||
|
the heap, and therefore moving it should invalidate pointers, since moving <code>T</code> definitely has to invalidate pointers to it,
|
||||||
|
this comparison doesn&rsquo;t make sense to me. While <code>Box&lt;T&gt;</code> usually behaves like a <code>T</code>, it&rsquo;s just a pointer. Writers of unsafe
|
||||||
|
code <em>know</em> that box is just a pointer, and will abuse that knowledge, accidentally causing UB with it. While this can be
|
||||||
|
mitigated with better docs and teaching, like how no one questions the uniqueness of <code>&amp;mut T</code> (maybe that&rsquo;s also because that
|
||||||
|
one makes intuitive sense, &ldquo;shared xor mutable&rdquo; is a simple concept), I think it will always be a problem,
|
||||||
|
because in my opinion, box being unique and invalidating pointers on move is simply not intiutive.</p>
|
||||||
|
<p>When a box is moved, the pointer bytes change their location in memory. But the bytes the box points to stay the same. They don&rsquo;t
|
||||||
|
move in memory. This is the fundamental missing intuition about the box behaviour.</p>
|
||||||
|
<p>There are also other reasons why the box behaviour is not desirable. Even people who know about the behaviour of box will want
|
||||||
|
to write code that goes directly against this behaviour at some point. But usually, fixing it is pretty simple: Storing a raw
|
||||||
|
pointer (or <code>NonNull&lt;T&gt;</code>) instead of a box, and using the constructor and drop to allocate and deallocate the backing box.
|
||||||
|
This is fairly inconvenient, but totally acceptable. There are bigger problems though. There are crates like <code>owning_ref</code>
|
||||||
|
that want to expose a generic interface over any type. Users like to choose box, and sometimes <em>have</em> to chose box because of
|
||||||
|
other box-exclusive features it offers. Even worse is <code>string_cache</code>, which is extremely hard to fix.</p>
|
||||||
|
<p>Then last but not least, there&rsquo;s the opinionated fact that <code>Box&lt;T&gt;</code> shall be implementable entirely in user code. While we are
|
||||||
|
many missing language features away from this being the case, the <code>noalias</code> case is also magic descended upon box itself, with no
|
||||||
|
user code ever having access to it.</p>
|
||||||
|
<h1 id="noalias-noslow">noalias, noslow</h1>
|
||||||
|
<p>There are also several arguments in favour of box being unique and special cased here. To negate the last argument above, it can
|
||||||
|
be said that <code>Box&lt;T&gt;</code> <em>is</em> a very special type. It&rsquo;s just like a <code>T</code>, but on the heap. Using this mental model, it&rsquo;s very easy to
|
||||||
|
justify all the box magic and its unique behaviour.</p>
|
||||||
|
<p>This mental model is one that many people have, but what does this bring us? This is just one mental model of box, and
|
||||||
|
there are other mental models of it (like &ldquo;a reference that manages its lifetime itself&rdquo; or &ldquo;a safe RAII pointer&rdquo;).</p>
|
||||||
|
<p>There is one clear potential benefit from this box behaviour. ✨Optimizations✨. <code>noalias</code> doesn&rsquo;t exist for fun, it&rsquo;s something
|
||||||
|
that can bring clear performance wins (for <code>noalias</code> on <code>&amp;mut T</code>, those were measureable). So the only question remains:
|
||||||
|
How much performance does <code>noalias</code> on <code>Box&lt;T&gt;</code> give us now, and how much potential performance improvements could we get in the
|
||||||
|
future? For the latter, there is no simple answer. For the former, there is. <code>rustc</code> has <a href="https://github.com/rust-lang/rust/pull/99527"><em>no</em> performance improvements</a> from being compiled with <code>noalias</code> on <code>Box&lt;T&gt;</code>.</p>
|
||||||
|
<p>I have not yet benchmarked ecosystem crates without box noalias and don&rsquo;t have the capacity to do so right now, so I would be very
|
||||||
|
grateful if anyone wanted to pick that up and report the results.</p>
|
||||||
|
<h1 id="a-way-forward">a way forward</h1>
|
||||||
|
<p>Based on all of this, I do have a solution that, in opinion, will fix all of this, even potential performance regressions with
|
||||||
|
box. First of all, I think that even if there are some performance regressions in ecosystem crates, the overall tradeoff goes
|
||||||
|
against the current box behaviour. Unsafe code wants to use box, and it is reasonable to do so. Therefore I propose to completely
|
||||||
|
remove all uniqueness from <code>Box&lt;T&gt;</code>, and treat it just like a <code>*const T</code> for the purposes of aliasing. This will make it more
|
||||||
|
predictable for unsafe code, and comes at none or only a minor performance cost.</p>
|
||||||
|
<p>But this performance cost may be real, and especially the future optimization value can&rsquo;t be certain. I do think that there
|
||||||
|
should be a way to get the uniqueness guarantees in some other way than through box. One possibility would be to use a <code>&amp;'static mut T</code> that is unleaked for drop, but the semantics of this are still <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/316">unclear</a>. If that is not possible, maybe exposing <code>std::ptr::Unique</code> (with it getting boxes aliasing semantics) could be desirable. For this, all existing usages of <code>Unique</code> inside the standard library would have to be removed though.</p>
|
||||||
|
<p>I guess what I am wishing for are some good and flexible raw pointer types. That&rsquo;s still in the stars&hellip;</p>
|
||||||
|
<p>For more information about this topic, see <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/326">https://github.com/rust-lang/unsafe-code-guidelines/issues/326</a></p></content></item></channel></rss>
|
||||||
1
tags/rust/page/1/index.html
Normal file
1
tags/rust/page/1/index.html
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<!doctype html><html lang=en-us><head><title>http://nilstrieb.github.io/tags/rust/</title><link rel=canonical href=http://nilstrieb.github.io/tags/rust/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://nilstrieb.github.io/tags/rust/"></head></html>
|
||||||
5
tags/unsafe-code/index.html
Normal file
5
tags/unsafe-code/index.html
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<!doctype html><html lang=en><head><title>unsafe code :: nilstriebs blog</title><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><meta name=description content><meta name=keywords content><meta name=robots content="noodp"><link rel=canonical href=http://nilstrieb.github.io/tags/unsafe-code/><link rel=stylesheet href=http://nilstrieb.github.io/assets/style.css><link rel=stylesheet href=assets/%25!s%28%3cnil%3e%29.css><link rel=apple-touch-icon href=http://nilstrieb.github.io/img/apple-touch-icon-192x192.png><link rel="shortcut icon" href=http://nilstrieb.github.io/img/favicon/orange.png><meta name=twitter:card content="summary"><meta property="og:locale" content="en"><meta property="og:type" content="website"><meta property="og:title" content="unsafe code"><meta property="og:description" content><meta property="og:url" content="http://nilstrieb.github.io/tags/unsafe-code/"><meta property="og:site_name" content="nilstriebs blog"><meta property="og:image" content="img/favicon/%!s().png"><meta property="og:image:width" content="2048"><meta property="og:image:height" content="1024"><link href=/tags/unsafe-code/index.xml rel=alternate type=application/rss+xml title="nilstriebs blog"></head><body><div class="container headings--one-size"><header class=header><div class=header__inner><div class=header__logo><a href=http://nilstrieb.github.io/><div class=logo>Terminal</div></a></div></div></header><div class=content><div class=posts><div class="post on-list"><h1 class=post-title><a href=http://nilstrieb.github.io/posts/box-is-a-unique-type/>Box Is a Unique Type</a></h1><div class=post-meta><span class=post-date>2022-07-22</span>
|
||||||
|
<span class=post-author>:: Nilstrieb</span></div><span class=post-tags>#<a href=http://nilstrieb.github.io/tags/rust/>rust</a>
|
||||||
|
#<a href=http://nilstrieb.github.io/tags/unsafe-code/>unsafe code</a> </span><div class=post-content>The current and future aliasing semantics of Box</div><div><a class="read-more button" href=/posts/box-is-a-unique-type/>→</a></div></div><div class=pagination><div class=pagination__buttons></div></div></div></div><footer class=footer><div class=footer__inner><div class=copyright><span>© 2022 Powered by <a href=http://gohugo.io>Hugo</a></span>
|
||||||
|
<span>:: Theme made by <a href=https://twitter.com/panr>panr</a></span></div></div></footer><script src=http://nilstrieb.github.io/assets/main.js></script>
|
||||||
|
<script src=http://nilstrieb.github.io/assets/prism.js></script></div></body></html>
|
||||||
155
tags/unsafe-code/index.xml
Normal file
155
tags/unsafe-code/index.xml
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>unsafe code on nilstriebs blog</title><link>http://nilstrieb.github.io/tags/unsafe-code/</link><description>Recent content in unsafe code on nilstriebs blog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Fri, 22 Jul 2022 00:00:00 +0000</lastBuildDate><atom:link href="http://nilstrieb.github.io/tags/unsafe-code/index.xml" rel="self" type="application/rss+xml"/><item><title>Box Is a Unique Type</title><link>http://nilstrieb.github.io/posts/box-is-a-unique-type/</link><pubDate>Fri, 22 Jul 2022 00:00:00 +0000</pubDate><guid>http://nilstrieb.github.io/posts/box-is-a-unique-type/</guid><description>We have all used Box&lt;T&gt; before in our Rust code. It&rsquo;s a glorious type, with great ergonomics and flexibitility. We can use it to put our values on the heap, but it can do even more than that!
|
||||||
|
struct Fields { a: String, b: String, } let fields = Box::new(Fields { a: &#34;a&#34;.to_string(), b: &#34;b&#34;.to_string() }); let a = fields.a; let b = fields.b; This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve, and they exist for good reason: They are very useful.</description><content><p>We have all used <code>Box&lt;T&gt;</code> before in our Rust code. It&rsquo;s a glorious type, with great ergonomics
|
||||||
|
and flexibitility. We can use it to put our values on the heap, but it can do even more
|
||||||
|
than that!</p>
|
||||||
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">struct</span> <span style="color:#a6e22e">Fields</span> {
|
||||||
|
</span></span><span style="display:flex;"><span> a: String,
|
||||||
|
</span></span><span style="display:flex;"><span> b: String,
|
||||||
|
</span></span><span style="display:flex;"><span>}
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> fields <span style="color:#f92672">=</span> Box::new(Fields {
|
||||||
|
</span></span><span style="display:flex;"><span> a: <span style="color:#e6db74">&#34;a&#34;</span>.to_string(),
|
||||||
|
</span></span><span style="display:flex;"><span> b: <span style="color:#e6db74">&#34;b&#34;</span>.to_string()
|
||||||
|
</span></span><span style="display:flex;"><span>});
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> a <span style="color:#f92672">=</span> fields.a;
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> b <span style="color:#f92672">=</span> fields.b;
|
||||||
|
</span></span></code></pre></div><p>This kind of partial deref move is just one of the spectacular magic tricks box has up its sleeve,
|
||||||
|
and they exist for good reason: They are very useful. Sadly we have not yet found a way to generalize all
|
||||||
|
of these to user types as well. Too bad!</p>
|
||||||
|
<p>Anyways, this post is about one particularly subtle magic aspect of box. For this, we need to dive
|
||||||
|
deep into unsafe code, so let&rsquo;s get our hazmat suits on and jump in!</p>
|
||||||
|
<h1 id="an-interesting-optimization">An interesting optimization</h1>
|
||||||
|
<p>We have this code here:</p>
|
||||||
|
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-rust" data-lang="rust"><span style="display:flex;"><span><span style="color:#66d9ef">fn</span> <span style="color:#a6e22e">takes_box_and_ptr_to_it</span>(<span style="color:#66d9ef">mut</span> b: Box<span style="color:#f92672">&lt;</span><span style="color:#66d9ef">u8</span><span style="color:#f92672">&gt;</span>, ptr: <span style="color:#f92672">*</span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">u8</span>) {
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> value <span style="color:#f92672">=</span> <span style="color:#66d9ef">unsafe</span> { <span style="color:#f92672">*</span>ptr };
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#f92672">*</span>b <span style="color:#f92672">=</span> <span style="color:#ae81ff">5</span>;
|
||||||
|
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">let</span> value2 <span style="color:#f92672">=</span> <span style="color:#66d9ef">unsafe</span> { <span style="color:#f92672">*</span>ptr };
|
||||||
|
</span></span><span style="display:flex;"><span> assert_ne!(value, value2);
|
||||||
|
</span></span><span style="display:flex;"><span>}
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> b <span style="color:#f92672">=</span> Box::new(<span style="color:#ae81ff">0</span>);
|
||||||
|
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">let</span> ptr: <span style="color:#f92672">*</span><span style="color:#66d9ef">const</span> <span style="color:#66d9ef">u8</span> <span style="color:#f92672">=</span> <span style="color:#f92672">&amp;*</span>b;
|
||||||
|
</span></span><span style="display:flex;"><span>
|
||||||
|
</span></span><span style="display:flex;"><span>takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
</span></span></code></pre></div><p>There&rsquo;s a function, <code>takes_box_and_ptr_to_it</code>, that takes a box and a pointer as parameters. Then,
|
||||||
|
it reads a value from the pointer, writes to the box, and reads a value again. It then asserts that
|
||||||
|
the two values aren&rsquo;t equal. How can they not be equal? If our box and pointer point to the same
|
||||||
|
location in memory, writing to the box will cause the pointer to read the new value.</p>
|
||||||
|
<p>Now construct a box, get a pointer to it, and pass the two to the function. Run the program&hellip;</p>
|
||||||
|
<p>&hellip; and everything is fine. Let&rsquo;s run it in release mode. This should work as well, since the optimizer
|
||||||
|
isn&rsquo;t allowed to change observable behaviour, and an assert is very observable. Run the progrm&hellip;</p>
|
||||||
|
<pre tabindex="0"><code>thread &#39;main&#39; panicked at &#39;assertion failed: `(left != right)`
|
||||||
|
left: `0`,
|
||||||
|
right: `0`&#39;, src/main.rs:5:5
|
||||||
|
</code></pre><p>Hmm. That&rsquo;s not what I&rsquo;ve told would happen. Is the compiler broken? Is this a miscompilation?
|
||||||
|
I&rsquo;ve heard that those do sometimes happen, right?</p>
|
||||||
|
<p>Trusting our instincts that &ldquo;it&rsquo;s never a miscompilation until it is one&rdquo;, we assume that LLVM behaved
|
||||||
|
well here. But what allows it to make this optimization? Taking a look at the generated LLVM-IR (by using
|
||||||
|
<code>--emit llvm-ir -O</code>, the <code>-O</code> is important since rustc only emits these attributes with optimizations on)
|
||||||
|
reveals the solution: (severely shortened to only show the relevant parts)</p>
|
||||||
|
<pre tabindex="0"><code class="language-llvmir" data-lang="llvmir">define void @takes_box_and_ptr_to_it(i8* noalias %0, i8* %ptr) {
|
||||||
|
</code></pre><p>See the little attribute on the first parameter called <code>noalias</code>? That&rsquo;s what&rsquo;s doing the magic here.
|
||||||
|
<code>noalias</code> is quite complex, but for our case here, it says that no other pointers point to (alias) the same location.
|
||||||
|
This allows the optimizer to assume that writing to the box pointer doesn&rsquo;t affect the other pointer - they are
|
||||||
|
not allowed to alias (it&rsquo;s like if they used <code>restrict</code> in C).</p>
|
||||||
|
<p>This might sound familiar to you if you&rsquo;re a viewer of <a href="https://twitter.com/jonhoo">Jon Gjengset</a>&rsquo;s content (which I can highly recommend). Jon has made an entire video about this before, since his crate <code>left-right</code>
|
||||||
|
was affected by this (<a href="https://youtu.be/EY7Wi9fV5bk)">https://youtu.be/EY7Wi9fV5bk)</a>.</p>
|
||||||
|
<p>If you&rsquo;re looking for <em>any</em> hint that using box emits <code>noalias</code>, you have to look no further than the documentation
|
||||||
|
for <a href="https://doc.rust-lang.org/nightly/std/boxed/index.html#considerations-for-unsafe-code"><code>std::boxed</code></a>. Well, the nightly or beta docs, because I only added this section very recently. For years, this behaviour was not really documented, and you had to
|
||||||
|
belong to the arcane circles of the select few who were aware of it. So lots of code was written thinking that box was &ldquo;just a RAII pointer&rdquo; (a pointer that allocates the value in the constructor, and deallocates it in the destructor on drop) for all
|
||||||
|
pointers are concerned.</p>
|
||||||
|
<h1 id="stacked-borrows-and-miri">Stacked Borrows and Miri</h1>
|
||||||
|
<p><a href="https://github.com/rust-lang/miri">Miri</a> is an interpreter for Rust code with the goal of finding undefinde behaviour.
|
||||||
|
Undefined behaviour, UB for short, is behaviour of a program upon which no restrictions are imposed. If UB is executed,
|
||||||
|
<em>anything</em> can happen, including segmentation faults, silent memory corruption, leakage of private keys or exactly
|
||||||
|
what you intended to happen. Examples of UB include use-after-free, out of bounds reads or data races.</p>
|
||||||
|
<p>I cannot recommend Miri highly enough for all unsafe code you&rsquo;re writing (sadly support for some IO functions
|
||||||
|
and FFI is still lacking, and it&rsquo;s still very slow).</p>
|
||||||
|
<p>So, let&rsquo;s see whether our code contains UB. It has to, since otherwise the optimizer wouldn&rsquo;t be allowed to change
|
||||||
|
observable behaviour (since the assert doesn&rsquo;t fail in debug mode). <code>$ cargo miri run</code>&hellip;</p>
|
||||||
|
<pre tabindex="0"><code class="language-rust,ignore" data-lang="rust,ignore">error: Undefined Behavior: attempting a read access using &lt;3314&gt; at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
--&gt; src/main.rs:2:26
|
||||||
|
|
|
||||||
|
2 | let value = unsafe { *ptr };
|
||||||
|
| ^^^^
|
||||||
|
| |
|
||||||
|
| attempting a read access using &lt;3314&gt; at alloc1722[0x0], but that tag does not exist in the borrow stack for this location
|
||||||
|
| this error occurs as part of an access at alloc1722[0x0..0x1]
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
|
||||||
|
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
|
||||||
|
help: &lt;3314&gt; was created by a retag at offsets [0x0..0x1]
|
||||||
|
--&gt; src/main.rs:10:26
|
||||||
|
|
|
||||||
|
10 | let ptr: *const u8 = &amp;*b;
|
||||||
|
| ^^^
|
||||||
|
help: &lt;3314&gt; was later invalidated at offsets [0x0..0x1]
|
||||||
|
--&gt; src/main.rs:12:29
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^
|
||||||
|
= note: backtrace:
|
||||||
|
= note: inside `takes_box_and_ptr_to_it` at src/main.rs:2:26
|
||||||
|
note: inside `main` at src/main.rs:12:5
|
||||||
|
--&gt; src/main.rs:12:5
|
||||||
|
|
|
||||||
|
12 | takes_box_and_ptr_to_it(b, ptr);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
</code></pre><p>This behaviour does indeed not look very defined at all. But what went wrong? There&rsquo;s a lot of information here.</p>
|
||||||
|
<p>First of all, it says that we attempted a read access, and that this access failed because the tag does not exist in the
|
||||||
|
borrow stack. This is something about stacked borrows, the experimental memory model for Rust that is implemented
|
||||||
|
in Miri. For an excellent introduction, see this part of the great book <a href="https://rust-unofficial.github.io/too-many-lists/fifth-stacked-borrows.html">Learning Rust With Entirely Too Many Linked Lists</a>.</p>
|
||||||
|
<p>In short: each pointer has a unique tag attacked to it. Bytes in memory have a stack of such tags, and only the pointers
|
||||||
|
that have their tag in the stack are allowed to access it. Tags can be pushed and popped from the stack through various
|
||||||
|
operations, for example borrowing.</p>
|
||||||
|
<p>In the code example above, we get a nice little hint where the tag was created. When we created a reference (that was then
|
||||||
|
coerced into a raw pointer) from our box, it got a new tag called <code>&lt;3314&gt;</code>. Then, when we moved the box into the function,
|
||||||
|
something happened: The tag was invalidated and popped off the borrow stack. That&rsquo;s because box invalidates all tags when it&rsquo;s
|
||||||
|
moved. The tag was popped off the borrow stack and we tried to read from it anyways - undefined behaviour happened!</p>
|
||||||
|
<p>And that&rsquo;s how our code wasn&rsquo;t a miscompilation, but undefined behaviour. Quite surprising, isn&rsquo;t it?</p>
|
||||||
|
<h1 id="noalias-nothanks">noalias, nothanks</h1>
|
||||||
|
<p>Many people, myself included, don&rsquo;t think that this is a good thing.</p>
|
||||||
|
<p>First of all, it introduces more UB that could have been defined behaviour instead. This is true for almost all UB, but usually,
|
||||||
|
there is something gained from the UB that justifies it. We will look at this later. But allowing such behaviour is fairly easy:
|
||||||
|
If box didn&rsquo;t invalidate pointers on move and instead behaved like a normal raw pointer, the code above would be sound.</p>
|
||||||
|
<p>But more importantly, this is not behaviour generally expected by users. While it can be argued that box is like a <code>T</code>, but on
|
||||||
|
the heap, and therefore moving it should invalidate pointers, since moving <code>T</code> definitely has to invalidate pointers to it,
|
||||||
|
this comparison doesn&rsquo;t make sense to me. While <code>Box&lt;T&gt;</code> usually behaves like a <code>T</code>, it&rsquo;s just a pointer. Writers of unsafe
|
||||||
|
code <em>know</em> that box is just a pointer, and will abuse that knowledge, accidentally causing UB with it. While this can be
|
||||||
|
mitigated with better docs and teaching, like how no one questions the uniqueness of <code>&amp;mut T</code> (maybe that&rsquo;s also because that
|
||||||
|
one makes intuitive sense, &ldquo;shared xor mutable&rdquo; is a simple concept), I think it will always be a problem,
|
||||||
|
because in my opinion, box being unique and invalidating pointers on move is simply not intiutive.</p>
|
||||||
|
<p>When a box is moved, the pointer bytes change their location in memory. But the bytes the box points to stay the same. They don&rsquo;t
|
||||||
|
move in memory. This is the fundamental missing intuition about the box behaviour.</p>
|
||||||
|
<p>There are also other reasons why the box behaviour is not desirable. Even people who know about the behaviour of box will want
|
||||||
|
to write code that goes directly against this behaviour at some point. But usually, fixing it is pretty simple: Storing a raw
|
||||||
|
pointer (or <code>NonNull&lt;T&gt;</code>) instead of a box, and using the constructor and drop to allocate and deallocate the backing box.
|
||||||
|
This is fairly inconvenient, but totally acceptable. There are bigger problems though. There are crates like <code>owning_ref</code>
|
||||||
|
that want to expose a generic interface over any type. Users like to choose box, and sometimes <em>have</em> to chose box because of
|
||||||
|
other box-exclusive features it offers. Even worse is <code>string_cache</code>, which is extremely hard to fix.</p>
|
||||||
|
<p>Then last but not least, there&rsquo;s the opinionated fact that <code>Box&lt;T&gt;</code> shall be implementable entirely in user code. While we are
|
||||||
|
many missing language features away from this being the case, the <code>noalias</code> case is also magic descended upon box itself, with no
|
||||||
|
user code ever having access to it.</p>
|
||||||
|
<h1 id="noalias-noslow">noalias, noslow</h1>
|
||||||
|
<p>There are also several arguments in favour of box being unique and special cased here. To negate the last argument above, it can
|
||||||
|
be said that <code>Box&lt;T&gt;</code> <em>is</em> a very special type. It&rsquo;s just like a <code>T</code>, but on the heap. Using this mental model, it&rsquo;s very easy to
|
||||||
|
justify all the box magic and its unique behaviour.</p>
|
||||||
|
<p>This mental model is one that many people have, but what does this bring us? This is just one mental model of box, and
|
||||||
|
there are other mental models of it (like &ldquo;a reference that manages its lifetime itself&rdquo; or &ldquo;a safe RAII pointer&rdquo;).</p>
|
||||||
|
<p>There is one clear potential benefit from this box behaviour. ✨Optimizations✨. <code>noalias</code> doesn&rsquo;t exist for fun, it&rsquo;s something
|
||||||
|
that can bring clear performance wins (for <code>noalias</code> on <code>&amp;mut T</code>, those were measureable). So the only question remains:
|
||||||
|
How much performance does <code>noalias</code> on <code>Box&lt;T&gt;</code> give us now, and how much potential performance improvements could we get in the
|
||||||
|
future? For the latter, there is no simple answer. For the former, there is. <code>rustc</code> has <a href="https://github.com/rust-lang/rust/pull/99527"><em>no</em> performance improvements</a> from being compiled with <code>noalias</code> on <code>Box&lt;T&gt;</code>.</p>
|
||||||
|
<p>I have not yet benchmarked ecosystem crates without box noalias and don&rsquo;t have the capacity to do so right now, so I would be very
|
||||||
|
grateful if anyone wanted to pick that up and report the results.</p>
|
||||||
|
<h1 id="a-way-forward">a way forward</h1>
|
||||||
|
<p>Based on all of this, I do have a solution that, in opinion, will fix all of this, even potential performance regressions with
|
||||||
|
box. First of all, I think that even if there are some performance regressions in ecosystem crates, the overall tradeoff goes
|
||||||
|
against the current box behaviour. Unsafe code wants to use box, and it is reasonable to do so. Therefore I propose to completely
|
||||||
|
remove all uniqueness from <code>Box&lt;T&gt;</code>, and treat it just like a <code>*const T</code> for the purposes of aliasing. This will make it more
|
||||||
|
predictable for unsafe code, and comes at none or only a minor performance cost.</p>
|
||||||
|
<p>But this performance cost may be real, and especially the future optimization value can&rsquo;t be certain. I do think that there
|
||||||
|
should be a way to get the uniqueness guarantees in some other way than through box. One possibility would be to use a <code>&amp;'static mut T</code> that is unleaked for drop, but the semantics of this are still <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/316">unclear</a>. If that is not possible, maybe exposing <code>std::ptr::Unique</code> (with it getting boxes aliasing semantics) could be desirable. For this, all existing usages of <code>Unique</code> inside the standard library would have to be removed though.</p>
|
||||||
|
<p>I guess what I am wishing for are some good and flexible raw pointer types. That&rsquo;s still in the stars&hellip;</p>
|
||||||
|
<p>For more information about this topic, see <a href="https://github.com/rust-lang/unsafe-code-guidelines/issues/326">https://github.com/rust-lang/unsafe-code-guidelines/issues/326</a></p></content></item></channel></rss>
|
||||||
1
tags/unsafe-code/page/1/index.html
Normal file
1
tags/unsafe-code/page/1/index.html
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<!doctype html><html lang=en-us><head><title>http://nilstrieb.github.io/tags/unsafe-code/</title><link rel=canonical href=http://nilstrieb.github.io/tags/unsafe-code/><meta name=robots content="noindex"><meta charset=utf-8><meta http-equiv=refresh content="0; url=http://nilstrieb.github.io/tags/unsafe-code/"></head></html>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue