rustc-dev-guide/ty_module/generic_arguments.html

298 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>ADTs and Generic Arguments - Rust Compiler Development Guide</title>
<!-- Custom HTML head -->
<meta name="description" content="A guide to developing the Rust compiler (rustc)">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust Compiler Development 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>
<a href="https://github.com/rust-lang/rustc-dev-guide" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide/edit/master/src/ty_module/generic_arguments.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="adts-and-generic-arguments"><a class="header" href="#adts-and-generic-arguments">ADTs and Generic Arguments</a></h1>
<p>The term <code>ADT</code> stands for "Algebraic data type", in rust this refers to a struct, enum, or union.</p>
<h2 id="adts-representation"><a class="header" href="#adts-representation">ADTs Representation</a></h2>
<p>Let's consider the example of a type like <code>MyStruct&lt;u32&gt;</code>, where <code>MyStruct</code> is defined like so:</p>
<pre><code class="language-rust ignore">struct MyStruct&lt;T&gt; { x: u8, y: T }</code></pre>
<p>The type <code>MyStruct&lt;u32&gt;</code> would be an instance of <code>TyKind::Adt</code>:</p>
<pre><code class="language-rust ignore">Adt(&amp;'tcx AdtDef, GenericArgs&lt;'tcx&gt;)
// ------------ ---------------
// (1) (2)
//
// (1) represents the `MyStruct` part
// (2) represents the `&lt;u32&gt;`, or "substitutions" / generic arguments</code></pre>
<p>There are two parts:</p>
<ul>
<li>The <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.AdtDef.html"><code>AdtDef</code></a> references the struct/enum/union but without the values for its type
parameters. In our example, this is the <code>MyStruct</code> part <em>without</em> the argument <code>u32</code>.
(Note that in the HIR, structs, enums and unions are represented differently, but in <code>ty::Ty</code>,
they are all represented using <code>TyKind::Adt</code>.)</li>
<li>The <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgs.html"><code>GenericArgs</code></a> is a list of values that are to be substituted
for the generic parameters. In our example of <code>MyStruct&lt;u32&gt;</code>, we would end up with a list like
<code>[u32]</code>. Well dig more into generics and substitutions in a little bit.</li>
</ul>
<h3 id="adtdef-and-defid"><a class="header" href="#adtdef-and-defid"><strong><code>AdtDef</code> and <code>DefId</code></strong></a></h3>
<p>For every type defined in the source code, there is a unique <code>DefId</code> (see <a href="../hir.html#identifiers-in-the-hir">this
chapter</a>). This includes ADTs and generics. In the <code>MyStruct&lt;T&gt;</code>
definition we gave above, there are two <code>DefId</code>s: one for <code>MyStruct</code> and one for <code>T</code>. Notice that
the code above does not generate a new <code>DefId</code> for <code>u32</code> because it is not defined in that code (it
is only referenced).</p>
<p><code>AdtDef</code> is more or less a wrapper around <code>DefId</code> with lots of useful helper methods. There is
essentially a one-to-one relationship between <code>AdtDef</code> and <code>DefId</code>. You can get the <code>AdtDef</code> for a
<code>DefId</code> with the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.adt_def"><code>tcx.adt_def(def_id)</code> query</a>. <code>AdtDef</code>s are all interned, as shown
by the <code>'tcx</code> lifetime.</p>
<h2 id="question-why-not-substitute-inside-the-adtdef"><a class="header" href="#question-why-not-substitute-inside-the-adtdef">Question: Why not substitute “inside” the <code>AdtDef</code>?</a></h2>
<p>Recall that we represent a generic struct with <code>(AdtDef, args)</code>. So why bother with this scheme?</p>
<p>Well, the alternate way we could have chosen to represent types would be to always create a new,
fully-substituted form of the <code>AdtDef</code> where all the types are already substituted. This seems like
less of a hassle. However, the <code>(AdtDef, args)</code> scheme has some advantages over this.</p>
<p>First, <code>(AdtDef, args)</code> scheme has an efficiency win:</p>
<pre><code class="language-rust ignore">struct MyStruct&lt;T&gt; {
... 100s of fields ...
}
// Want to do: MyStruct&lt;A&gt; ==&gt; MyStruct&lt;B&gt;</code></pre>
<p>in an example like this, we can instantiate <code>MyStruct&lt;A&gt;</code> as <code>MyStruct&lt;B&gt;</code> (and so on) very cheaply,
by just replacing the one reference to <code>A</code> with <code>B</code>. But if we eagerly instantiated all the fields,
that could be a lot more work because we might have to go through all of the fields in the <code>AdtDef</code>
and update all of their types.</p>
<p>A bit more deeply, this corresponds to structs in Rust being <a href="https://en.wikipedia.org/wiki/Nominal_type_system"><em>nominal</em> types</a> — which
means that they are defined by their <em>name</em> (and that their contents are then indexed from the
definition of that name, and not carried along “within” the type itself).</p>
<h2 id="the-genericargs-type"><a class="header" href="#the-genericargs-type">The <code>GenericArgs</code> type</a></h2>
<p>Given a generic type <code>MyType&lt;A, B, …&gt;</code>, we have to store the list of generic arguments for <code>MyType</code>.</p>
<p>In rustc this is done using <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgs.html"><code>GenericArgs</code></a>. <code>GenericArgs</code> is a thin pointer to a slice of <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html"><code>GenericArg</code></a> representing a list of generic arguments for a generic item. For example, given a <code>struct HashMap&lt;K, V&gt;</code> with two type parameters, <code>K</code> and <code>V</code>, the <code>GenericArgs</code> used to represent the type <code>HashMap&lt;i32, u32&gt;</code> would be represented by <code>&amp;'tcx [tcx.types.i32, tcx.types.u32]</code>.</p>
<p><code>GenericArg</code> is conceptually an <code>enum</code> with three variants, one for type arguments, one for const arguments and one for lifetime arguments.
In practice that is actually represented by <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.GenericArgKind.html"><code>GenericArgKind</code></a> and <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.GenericArg.html"><code>GenericArg</code></a> is a more space efficient version that has a method to
turn it into a <code>GenericArgKind</code>.</p>
<p>The actual <code>GenericArg</code> struct stores the type, lifetime or const as an interned pointer with the discriminant stored in the lower 2 bits.
Unless you are working with the <code>GenericArgs</code> implementation specifically, you should generally not have to deal with <code>GenericArg</code> and instead
make use of the safe <a href="#genericargkind"><code>GenericArgKind</code></a> abstraction obtainable via the <code>GenericArg::unpack()</code> method.</p>
<p>In some cases you may have to construct a <code>GenericArg</code>, this can be done via <code>Ty/Const/Region::into()</code> or <code>GenericArgKind::pack</code>.</p>
<pre><code class="language-rust ignore">// An example of unpacking and packing a generic argument.
fn deal_with_generic_arg&lt;'tcx&gt;(generic_arg: GenericArg&lt;'tcx&gt;) -&gt; GenericArg&lt;'tcx&gt; {
// Unpack a raw `GenericArg` to deal with it safely.
let new_generic_arg: GenericArgKind&lt;'tcx&gt; = match generic_arg.unpack() {
GenericArgKind::Type(ty) =&gt; { /* ... */ }
GenericArgKind::Lifetime(lt) =&gt; { /* ... */ }
GenericArgKind::Const(ct) =&gt; { /* ... */ }
};
// Pack the `GenericArgKind` to store it in a generic args list.
new_generic_arg.pack()
}</code></pre>
<p>So pulling it all together:</p>
<pre><code class="language-rust ignore">struct MyStruct&lt;T&gt;(T);
type Foo = MyStruct&lt;u32&gt;</code></pre>
<p>For the <code>MyStruct&lt;U&gt;</code> written in the <code>Foo</code> type alias, we would represent it in the following way:</p>
<ul>
<li>There would be an <code>AdtDef</code> (and corresponding <code>DefId</code>) for <code>MyStruct</code>.</li>
<li>There would be a <code>GenericArgs</code> containing the list <code>[GenericArgKind::Type(Ty(u32))]</code></li>
<li>And finally a <code>TyKind::Adt</code> with the <code>AdtDef</code> and <code>GenericArgs</code> listed above.</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../ty.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../ty_module/param_ty_const_regions.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../ty.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../ty_module/param_ty_const_regions.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
<script src="../mermaid.min.js"></script>
<script src="../mermaid-init.js"></script>
</div>
</body>
</html>