gba/docs/01-quirks/04-newtype.html

347 lines
21 KiB
HTML
Raw Normal View History

2018-12-10 19:48:06 +11:00
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Newtype - Rust GBA Guide</title>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff" />
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body class="light">
<!-- Provide site root to javascript -->
<script type="text/javascript">var path_to_root = "../";</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script type="text/javascript">
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script type="text/javascript">
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = 'light'; }
document.body.className = theme;
document.querySelector('html').className = theme + ' js';
</script>
<!-- Hide / unhide sidebar before it is displayed -->
<script type="text/javascript">
var html = document.querySelector('html');
var sidebar = 'hidden';
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
}
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<ol class="chapter"><li><a href="../00-introduction/00-index.html"><strong aria-hidden="true">1.</strong> Introduction</a></li><li><ol class="section"><li><a href="../00-introduction/01-requirements.html"><strong aria-hidden="true">1.1.</strong> Reader Requirements</a></li><li><a href="../00-introduction/02-goals_and_style.html"><strong aria-hidden="true">1.2.</strong> Book Goals and Style</a></li><li><a href="../00-introduction/03-development-setup.html"><strong aria-hidden="true">1.3.</strong> Development Setup</a></li><li><a href="../00-introduction/04-hello-magic.html"><strong aria-hidden="true">1.4.</strong> Hello, Magic</a></li><li><a href="../00-introduction/05-help_and_resources.html"><strong aria-hidden="true">1.5.</strong> Help and Resources</a></li></ol></li><li><a href="../01-quirks/00-index.html"><strong aria-hidden="true">2.</strong> Quirks</a></li><li><ol class="section"><li><a href="../01-quirks/01-no_std.html"><strong aria-hidden="true">2.1.</strong> No Std</a></li><li><a href="../01-quirks/02-fixed_only.html"><strong aria-hidden="true">2.2.</strong> Fixed Only</a></li><li><a href="../01-quirks/03-volatile_destination.html"><strong aria-hidden="true">2.3.</strong> Volatile Destination</a></li><li><a href="../01-quirks/04-newtype.html" class="active"><strong aria-hidden="true">2.4.</strong> Newtype</a></li><li><a href="../01-quirks/05-const_asserts.html"><strong aria-hidden="true">2.5.</strong> Const Asserts</a></li></ol></li><li><a href="../02-concepts/00-index.html"><strong aria-hidden="true">3.</strong> Concepts</a></li><li><ol class="section"><li><a href="../02-concepts/01-cpu.html"><strong aria-hidden="true">3.1.</strong> CPU</a></li><li><a href="../02-concepts/02-bios.html"><strong aria-hidden="true">3.2.</strong> BIOS</a></li><li><a href="../02-concepts/03-wram.html"><strong aria-hidden="true">3.3.</strong> Work RAM</a></li><li><a href="../02-concepts/04-io-registers.html"><strong aria-hidden="true">3.4.</strong> IO Registers</a></li><li><a href="../02-concepts/05-palram.html"><strong aria-hidden="true">3.5.</strong> Palette RAM</a></li><li><a href="../02-concepts/06-vram.html"><strong aria-hidden="true">3.6.</strong> Video RAM</a></li><li><a href="../02-concepts/07-oam.html"><strong aria-hidden="true">3.7.</strong> Object Attribute Memory</a></li><li><a href="../02-concepts/08-rom.html"><strong aria-hidden="true">3.8.</strong> Game Pak ROM / Flash ROM</a></li><li><a href="../02-concepts/09-sram.html"><strong aria-hidden="true">3.9.</strong> Save RAM</a></li></ol></li><li><a href="../03-video/00-index.html"><strong aria-hidden="true">4.</strong> Video</a></li><li><ol class="section"><li><a href="../03-video/01-rgb15.html"><strong aria-hidden="true">4.1.</strong> RBG15 Color</a></li><li><a href="../03-video/TODO.html"><strong aria-hidden="true">4.2.</strong> TODO</a></li></ol></li><li><a href="../04-non-video/00-index.html"><strong aria-hidden="true">5.</strong> Non-Video</a></li><li><ol class="section"><li><a href="../04-non-video/01-buttons.html"><strong aria-hidden="true">5.1.</strong> Buttons</a></li><li><a href="../04-non-video/02-timers.html"><strong aria-hidden="true">5.2.</strong> Timers</a></li><li><a href="../04-non-video/03-dma.html"><strong aria-hidden="true">5.3.</strong> Direct Memory Access</a></li><li><a href="../04-non-video/04-sound.html"><strong aria-hidden="true">5.4.</strong> Sound</a></li><li><a href="../04-non-video/05-interrupts.html"><strong aria-hidden="true">5.5.</strong> Interrupts</a></li><li><a href="../04-non-video/06-link_cable.html"><strong aria-hidden="true">5.6.</strong> Link Cable</a></li><li><a href="../04-non-video/07-game_pak.html"><strong aria-hidden="true">5.7.</strong> Game Pak</a></li></ol></li><li><a href="../05-examples/00-index.html"><strong aria-hidden="true">6.</strong> Examples</a></li><li><ol class="section"><li><a href="../05-examples/01-hello_magic.html"><strong aria-hidden="true">6.1.</strong> hello_magic</a></li><li><a href="../05-examples/02-hello_world.html"><strong aria-hidden="true">6.2.</strong> hello_world</a></li><li><a href="
2018-12-10 19:48:06 +11:00
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar" class="menu-bar">
<div id="menu-bar-sticky-container">
<div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</button>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust GBA Guide</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script type="text/javascript">
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<a class="header" href="#newtype" id="newtype"><h1>Newtype</h1></a>
<p>TODO: we've already used newtype twice by now (fixed point values and volatile
addresses), so we need to adjust how we start this section.</p>
<p>There's a great Zero Cost abstraction that we'll be using a lot that you might
not already be familiar with: we're talking about the &quot;Newtype Pattern&quot;!</p>
2018-12-10 19:48:06 +11:00
<p>Now, I told you to read the Rust Book before you read this book, and I'm sure
you're all good students who wouldn't sneak into this book without doing the
required reading, so I'm sure you all remember exactly what I'm talking about,
because they touch on the newtype concept in the book twice, in two <em>very</em> long
named sections:</p>
<ul>
<li><a href="https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types">Using the Newtype Pattern to Implement External Traits on External
Types</a></li>
<li><a href="https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction">Using the Newtype Pattern for Type Safety and
Abstraction</a></li>
</ul>
<p>...Yeah... The Rust Book doesn't know how to make a short sub-section name to
save its life. Shame.</p>
<a class="header" href="#newtype-basics" id="newtype-basics"><h2>Newtype Basics</h2></a>
<p>So, we have all these pieces of data, and we want to keep them separated, and we
don't wanna pay the cost for it at runtime. Well, we're in luck, we can pay the
cost at compile time.</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
pub struct PixelColor(u16);
#}</code></pre></pre>
<p>TODO: we've already talked about repr(transparent) by now</p>
2018-12-10 19:48:06 +11:00
<p>Ah, except that, as I'm sure you remember from <a href="https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent">The
Rustonomicon</a>
(and from the RFC too, of course), if we have a single field struct that's
sometimes different from having just the bare value, so we should be using
<code>#[repr(transparent)]</code> with our newtypes.</p>
2018-12-10 19:48:06 +11:00
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[repr(transparent)]
pub struct PixelColor(u16);
#}</code></pre></pre>
<p>And then we'll need to do that same thing for <em>every other newtype we want</em>.</p>
<p>Except there's only two tiny parts that actually differ between newtype
declarations: the new name and the base type. All the rest is just the same rote
code over and over. Generating piles and piles of boilerplate code? Sounds like
a job for a macro to me!</p>
<a class="header" href="#making-it-a-macro" id="making-it-a-macro"><h2>Making It A Macro</h2></a>
<p>If you're going to do much with macros you should definitely read through <a href="https://danielkeep.github.io/tlborm/book/index.html">The
Little Book of Rust
Macros</a>, but we won't be
doing too much so you can just follow along here a bit if you like.</p>
<p>The most basic version of a newtype macro starts like this:</p>
2018-12-10 19:48:06 +11:00
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[macro_export]
macro_rules! newtype {
($new_name:ident, $old_name:ident) =&gt; {
#[repr(transparent)]
pub struct $new_name($old_name);
};
}
#}</code></pre></pre>
<p>The <code>#[macro_export]</code> makes it exported by the current module (like <code>pub</code>
kinda), and then we have one expansion option that takes an identifier, a <code>,</code>,
and then a second identifier. The new name is the outer type we'll be using, and
the old name is the inner type that's being wrapped. You'd use our new macro
something like this:</p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
newtype! {PixelColorCurly, u16}
newtype!(PixelColorParens, u16);
newtype![PixelColorBrackets, u16];
#}</code></pre></pre>
<p>Note that you can invoke the macro with the outermost grouping as any of <code>()</code>,
<code>[]</code>, or <code>{}</code>. It makes no particular difference to the macro. Also, that space
in the first version is kinda to show off that you can put white space in
between the macro name and the grouping if you want. The difference is mostly
style, but there are some rules and considerations here:</p>
<ul>
<li>If you use curly braces then you <em>must not</em> put a <code>;</code> after the invocation.</li>
<li>If you use parentheses or brackets then you <em>must</em> put the <code>;</code> at the end.</li>
<li>Rustfmt cares which you use and formats accordingly:
<ul>
<li>Curly brace macro use mostly gets treated like a code block.</li>
<li>Parentheses macro use mostly gets treated like a function call.</li>
<li>Bracket macro use mostly gets treated like an array declaration.</li>
</ul>
</li>
</ul>
<a class="header" href="#upgrade-that-macro" id="upgrade-that-macro"><h2>Upgrade That Macro!</h2></a>
<p>We also want to be able to add <code>derive</code> stuff and doc comments to our newtype.
Within the context of <code>macro_rules!</code> definitions these are called &quot;meta&quot;. Since
we can have any number of them we wrap it all up in a &quot;zero or more&quot; matcher.
Then our macro looks like this:</p>
2018-12-10 19:48:06 +11:00
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[macro_export]
macro_rules! newtype {
($(#[$attr:meta])* $new_name:ident, $old_name:ident) =&gt; {
$(#[$attr])*
#[repr(transparent)]
pub struct $new_name($old_name);
};
}
#}</code></pre></pre>
<p>So now we can write</p>
2018-12-10 19:48:06 +11:00
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
newtype! {
/// Color on the GBA gives 5 bits for each channel, the highest bit is ignored.
#[derive(Debug, Clone, Copy)]
PixelColor, u16
2018-12-10 19:48:06 +11:00
}
#}</code></pre></pre>
<p>And that's about all we'll need for the examples.</p>
<p><strong>As a reminder:</strong> remember that <code>macro_rules</code> macros have to appear <em>before</em>
they're invoked in your source, so the <code>newtype</code> macro will always have to be at
the very top of your file, or if you put it in a module within your project
you'll need to declare the module before anything that uses it.</p>
<a class="header" href="#potential-homework" id="potential-homework"><h2>Potential Homework</h2></a>
<p>If you wanted to keep going and get really fancy with it, you could potentially
add a lot more:</p>
<ul>
<li>Make a <code>pub const fn new() -&gt; Self</code> method that outputs the base value in a
const way. Combine this with builder style &quot;setter&quot; methods that are also
const and you can get the compiler to do quite a bit of the value building
work at compile time.</li>
<li>Making the macro optionally emit a <code>From</code> impl to unwrap it back into the base
type.</li>
<li>Allow for visibility modifiers to be applied to the inner field and the newly
generated type.</li>
<li>Allowing for generic newtypes. You already saw the need for this once in the
volatile section. Unfortunately, this particular part gets really tricky if
you're using <code>macro_rules!</code>, so you might need to move up to a full
<code>proc_macro</code>. Having a <code>proc_macro</code> isn't bad except that they have to be
defined in a crate of their own and they're compiled before use. You can't
ever use them in the crate that defines them, so we won't be using them in any
of our single file examples.</li>
<li>Allowing for optional <code>Deref</code> and <code>DerefMut</code> of the inner value. This takes
away most all the safety aspect of doing the newtype, but there may be times
for it. As an example, you could make a newtype with a different form of
Display impl that you want to otherwise treat as the base type in all places.</li>
</ul>
2018-12-10 19:48:06 +11:00
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../01-quirks/03-volatile_destination.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
2018-12-10 19:48:06 +11:00
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="../01-quirks/05-const_asserts.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
2018-12-10 19:48:06 +11:00
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a href="../01-quirks/03-volatile_destination.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
2018-12-10 19:48:06 +11:00
<i class="fa fa-angle-left"></i>
</a>
<a href="../01-quirks/05-const_asserts.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
2018-12-10 19:48:06 +11:00
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
<script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
<script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
<script src="../book.js" type="text/javascript" charset="utf-8"></script>
<!-- Custom JS scripts -->
</body>
</html>