Files
rustup/dev-guide/print.html
2026-04-27 01:09:39 +00:00

731 lines
52 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>The Rustup developer guide</title>
<meta name="robots" content="noindex">
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="favicon-de23e50b.svg">
<link rel="shortcut icon" href="favicon-8114d1fc.png">
<link rel="stylesheet" href="css/variables-8adf115d.css">
<link rel="stylesheet" href="css/general-2459343d.css">
<link rel="stylesheet" href="css/chrome-ae938929.css">
<link rel="stylesheet" href="css/print-9e4910d8.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="fonts/fonts-9644e21d.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="mdbook-highlight-css" href="highlight-493f70e1.css">
<link rel="stylesheet" id="mdbook-tomorrow-night-css" href="tomorrow-night-4c0ae647.css">
<link rel="stylesheet" id="mdbook-ayu-highlight-css" href="ayu-highlight-3fdfc3ac.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";
window.path_to_searchindex_js = "searchindex-ee054dd1.js";
</script>
<!-- Start loading toc.js asap -->
<script src="toc-e709a72b.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="mdbook-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="mdbook-sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("mdbook-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 = false;
}
if (sidebar === 'visible') {
sidebar_toggle.checked = true;
} else {
html.classList.remove('sidebar-visible');
}
</script>
<nav id="mdbook-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="mdbook-sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="mdbook-page-wrapper" class="page-wrapper">
<div class="page">
<div id="mdbook-menu-bar-hover-placeholder"></div>
<div id="mdbook-menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="mdbook-sidebar-toggle" class="icon-button" for="mdbook-sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="mdbook-sidebar">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M0 96C0 78.3 14.3 64 32 64H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32C14.3 128 0 113.7 0 96zM0 256c0-17.7 14.3-32 32-32H416c17.7 0 32 14.3 32 32s-14.3 32-32 32H32c-17.7 0-32-14.3-32-32zM448 416c0 17.7-14.3 32-32 32H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H416c17.7 0 32 14.3 32 32z"/></svg></span>
</label>
<button id="mdbook-theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="mdbook-theme-list">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M371.3 367.1c27.3-3.9 51.9-19.4 67.2-42.9L600.2 74.1c12.6-19.5 9.4-45.3-7.6-61.2S549.7-4.4 531.1 9.6L294.4 187.2c-24 18-38.2 46.1-38.4 76.1L371.3 367.1zm-19.6 25.4l-116-104.4C175.9 290.3 128 339.6 128 400c0 3.9 .2 7.8 .6 11.6c1.8 17.5-10.2 36.4-27.8 36.4H96c-17.7 0-32 14.3-32 32s14.3 32 32 32H240c61.9 0 112-50.1 112-112c0-2.5-.1-5-.2-7.5z"/></svg></span>
</button>
<ul id="mdbook-theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="mdbook-theme-ayu">Ayu</button></li>
</ul>
<button id="mdbook-search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="mdbook-searchbar">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352c79.5 0 144-64.5 144-144s-64.5-144-144-144S64 128.5 64 208s64.5 144 144 144z"/></svg></span>
</button>
</div>
<h1 class="menu-title">The Rustup developer guide</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<span class=fa-svg id="print-button"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M128 0C92.7 0 64 28.7 64 64v96h64V64H354.7L384 93.3V160h64V93.3c0-17-6.7-33.3-18.7-45.3L400 18.7C388 6.7 371.7 0 354.7 0H128zM384 352v32 64H128V384 368 352H384zm64 32h32c17.7 0 32-14.3 32-32V256c0-35.3-28.7-64-64-64H64c-35.3 0-64 28.7-64 64v96c0 17.7 14.3 32 32 32H64v64c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V384zm-16-88c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24z"/></svg></span>
</a>
<a href="https://github.com/rust-lang/rustup/tree/HEAD/doc/dev-guide" title="Git repository" aria-label="Git repository">
<span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg></span>
</a>
</div>
</div>
<div id="mdbook-search-wrapper" class="hidden">
<form id="mdbook-searchbar-outer" class="searchbar-outer">
<div class="search-wrapper">
<input type="search" id="mdbook-searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="mdbook-searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper">
<span class=fa-svg id="fa-spin"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M304 48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zm0 416c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM48 304c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm464-48c0-26.5-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48s48-21.5 48-48zM142.9 437c18.7-18.7 18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zm0-294.2c18.7-18.7 18.7-49.1 0-67.9S93.7 56.2 75 75s-18.7 49.1 0 67.9s49.1 18.7 67.9 0zM369.1 437c18.7 18.7 49.1 18.7 67.9 0s18.7-49.1 0-67.9s-49.1-18.7-67.9 0s-18.7 49.1 0 67.9z"/></svg></span>
</div>
</div>
</form>
<div id="mdbook-searchresults-outer" class="searchresults-outer hidden">
<div id="mdbook-searchresults-header" class="searchresults-header"></div>
<ul id="mdbook-searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('mdbook-sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('mdbook-sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#mdbook-sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="mdbook-content" class="content">
<main>
<h1 id="contributing-to-rustup"><a class="header" href="#contributing-to-rustup">Contributing to rustup</a></h1>
<ol>
<li>Fork it!</li>
<li>Create your feature branch: <code>git checkout -b my-new-feature</code></li>
<li>Test it: <code>cargo test --features=test</code></li>
<li><a href="#linting">Lint it!</a></li>
<li>Commit your changes: <code>git commit -am 'Add some feature'</code></li>
<li>Push to the branch: <code>git push origin my-new-feature</code></li>
<li>Submit a pull request :D</li>
</ol>
<p>For developing on <code>rustup</code> itself, the easiest way is to run the development
build on your current installation. This approach is best used for minor fixes
or improvements. See the documentation for <a href="#rustup_force_arg0"><code>RUSTUP_FORCE_ARG0</code></a> for more info.</p>
<p>A more formal solution involves installing rustup into a temporary directory as
your dedicated test environment.
To do so, you can run a series of commands similar to this:</p>
<pre><code class="language-bash">cargo build
mkdir home
RUSTUP_HOME=home CARGO_HOME=home target/debug/rustup-init --no-modify-path -y
</code></pre>
<p>You can then try out <code>rustup</code> with your changes by running <code>home/bin/rustup</code>, without
affecting any existing installation. Remember to keep those two environment variables
set when running your compiled <code>rustup-init</code> or the toolchains it installs, but <em>unset</em>
when rebuilding <code>rustup</code> itself.</p>
<p>If you wish to install your new build to try out longer term in your home directory
then you can run <code>cargo dev-install</code> which is an alias in <code>.cargo/config</code> which
runs <code>cargo run -- --no-modify-path -y</code> to install your build into your homedir.</p>
<p>We use <code>rustfmt</code> to keep our codebase consistently formatted. Please ensure that
you have correctly formatted your code (most editors will do this automatically
when saving) or it may not pass the CI tests.</p>
<p>If you are moving, renaming or removing an existing mdBook page, please use mdBooks
<a href="https://rust-lang.github.io/mdBook/format/configuration/renderers.html#outputhtmlredirect"><code>output.html.redirect</code></a> feature to ensure that the old URL gets redirected.</p>
<p>Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the
Apache-2.0 license, shall be dual licensed as in the README, without any
additional terms or conditions.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="linting"><a class="header" href="#linting">Linting</a></h1>
<p>We use <code>cargo clippy</code> to ensure high-quality code and to enforce a set of best practices for Rust programming.
However, not all lints provided by <code>cargo clippy</code> are relevant or applicable to our project.
We may choose to ignore some lints if they are unstable, experimental, or specific to our project.
If you are unsure about a lint, please ask us in the
<a href="https://rust-lang.zulipchat.com/#narrow/channel/490103-t-rustup">rustup Zulip channel</a>.</p>
<h2 id="manual-linting"><a class="header" href="#manual-linting">Manual linting</a></h2>
<p>When checking the codebase with <a href="https://doc.rust-lang.org/stable/clippy/index.html"><code>clippy</code></a>,
it is recommended to use the following command:</p>
<pre><code class="language-console">$ cargo clippy --all --all-targets --all-features -- -D warnings
</code></pre>
<p>Please note the <code>--all-features</code> flag: it is used because we need to enable the <code>test</code> feature
to make lints fully work, for which <code>--all-features</code> happens to be a convenient shortcut.</p>
<p>The <code>test</code> feature is required because <code>rustup</code> uses
<a href="https://doc.rust-lang.org/cargo/reference/features.html">cargo features</a> to
<a href="https://doc.rust-lang.org/reference/conditional-compilation.html">conditionally compile</a>
support code for integration tests, as <code>#[cfg(test)]</code> is only available for unit tests.</p>
<p>If you encounter an issue or wish to speed up the initial analysis, you could also try
activating only the <code>test</code> feature by replacing <code>--all-features</code> with <code>--features=test</code>.</p>
<h2 id="rust-analyzer"><a class="header" href="#rust-analyzer">Rust-Analyzer</a></h2>
<p>When checking the codebase using <code>rust-analyzer</code>, the first thing to do remains unchanged:
enabling the features.</p>
<p>This is done by setting the <code>rust-analyzer.cargo.features</code> property to <code>"all"</code>.</p>
<p>For example, if you are using <code>rust-analyzer</code> within VSCode, you would want to
add the following to your projects <code>.vscode/settings.json</code><sup class="footnote-reference" id="fr-vscode-global-cfg-1"><a href="#footnote-vscode-global-cfg">1</a></sup>:</p>
<pre><code class="language-jsonc">"rust-analyzer.cargo.features": "all",
</code></pre>
<p>Alternatively, if you want to enable the <code>test</code> feature only, you should set the
following instead:</p>
<pre><code class="language-jsonc">"rust-analyzer.cargo.features": ["test"]
</code></pre>
<p>Next, as <code>rust-analyzer</code> depends on <code>cargo check</code> by default, it is also recommended to
enable the <code>cargo clippy</code> integration by adding the following:</p>
<pre><code class="language-jsonc">"rust-analyzer.check.command": "clippy",
</code></pre>
<p>You might also want to refer to the
<a href="https://rust-analyzer.github.io/manual.html#configuration"><code>rust-analyzer</code> manual</a>
for more details on properly setting up <code>rust-analyzer</code> in your IDE of choice.</p>
<hr>
<ol class="footnote-definition">
<li id="footnote-vscode-global-cfg">
<p>Alternatively, if you want to apply the configuration to all your Rust projects,
you can add it to your global configuration at <code>~/.config/Code/User/settings.json</code> instead. <a href="#fr-vscode-global-cfg-1"></a></p>
</li>
</ol>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="coding-standards"><a class="header" href="#coding-standards">Coding standards</a></h1>
<p>Generally we just follow good sensible Rust practices, clippy and so forth.
However there are some practices weve agreed on that are not machine-enforced;
meeting those requirements in a PR will make it easier to merge.</p>
<h2 id="atomic-commits"><a class="header" href="#atomic-commits">Atomic commits</a></h2>
<p>We use atomic commits across the repo. Each commit should represent a single unit of change.
You can read more about atomic commits <a href="https://www.aleksandrhovhannisyan.com/blog/atomic-git-commits">here</a>.</p>
<h2 id="import-grouping"><a class="header" href="#import-grouping">Import grouping</a></h2>
<p>In each file the imports should be grouped into at most 4 groups in the
following order:</p>
<ol>
<li>stdlib</li>
<li>non-repository local crates</li>
<li>repository local other crates</li>
<li>this crate</li>
</ol>
<p>Separate each group with a blank line, and rustfmt will sort into a canonical
order. Any file that is not grouped like this can be rearranged whenever the
file is touched - were not precious about having it done in a separate commit,
though that is helpful.</p>
<h2 id="no-direct-use-of-process-state-outside-rustupprocess"><a class="header" href="#no-direct-use-of-process-state-outside-rustupprocess">No direct use of process state outside rustup::process</a></h2>
<p>The <code>rustup::process</code> module abstracts the global state that is
<code>std::env::args</code>, <code>std::env::vars</code>, <code>std::io::std*</code> and <code>std::env::current_dir</code>
permitting threaded tests of the CLI logic; use the relevant methods of the
<code>rustup::process::Process</code> type rather than those APIs directly.
Usually, a <code>process: &amp;Process</code> variable will be available to you in the current context.
For example, it could be in the form of a parameter of the current function,
or a field of a <code>Cfg</code> instance, etc.</p>
<h2 id="writing-tests"><a class="header" href="#writing-tests">Writing tests</a></h2>
<p>Rustup provides a number of test helpers in the <code>rustup::test</code> module
which is conditionally enabled with the <code>test</code> feature.</p>
<p>The existing tests under <code>tests/suite</code> provide good examples of how to use these
helpers, but you might also find it useful to look at the documentation for
particular APIs in the <code>rustup::test</code> module.</p>
<p>For example, for more information regarding end-to-end tests with the <code>.expect()</code>
APIs, you can refer to the documentation of the <a href="https://github.com/search?q=repo%3Arust-lang%2Frustup+symbol%3A%2F%28%3F-i%29Assert%2F&amp;type=code"><code>Assert</code></a> type.</p>
<h2 id="clippy-lints"><a class="header" href="#clippy-lints">Clippy lints</a></h2>
<p>At the time of writing, rustups CI pipeline runs clippy on both Windows and
Linux, but contributors to particularly OS-specific code should also make
sure that their clippy checking is done on that particular platform, as
OS-conditional code is a common source of unused imports and other small lints,
which can build up over time.</p>
<h2 id="writing-platform-specific-code"><a class="header" href="#writing-platform-specific-code">Writing platform-specific code</a></h2>
<p>For developers using BSD/Linux/Mac OS, there are Windows VMs suitable for such
development tasks for use with virtualbox and other hypervisors are downloadable
from
<a href="https://developer.microsoft.com/en-us/windows/downloads/virtual-machines/">Microsoft</a>.
Similarly, there are many Linux and Unix operating systems images available for
developers whose usual operating system is Windows. Currently Rustup has no Mac
OS specific code, so there should be no need to worry about Mac VM images.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="version-numbers"><a class="header" href="#version-numbers">Version numbers</a></h1>
<p>If you ever see a released version of rustup which has <code>::</code> in its version string
then something went wrong with the CI and that needs to be addressed.</p>
<p>We use <code>git-testament</code> to construct our version strings. This records, as a
struct, details of the git commit, tag description, and also an indication
of modifications to the working tree present when the binary was compiled.</p>
<p>During normal development you may get information from invoking <code>rustup --version</code>
which looks like <code>rustup-init 1.18.3+15 (a54051502 2019-05-26)</code> or even
<code>rustup-init 1.18.3+15 (a54051502 2019-05-26) dirty 1 modification</code>.</p>
<p>The first part is always the binary name as per <code>clap</code>s normal operation. The
version number is a combination of the most recent tag in the git repo, and the
number of commits since that tag. The parenthesised information is, naturally,
the SHA of the most recent commit and the date of that commit. If the indication
of a dirty tree is present, the number of changes is indicated. This combines
adds, deletes, modifies, and unknown entries.</p>
<p>You can request further information of a <code>rustup</code> binary with the
<code>rustup dump-testament</code> hidden command. It produces output of the form:</p>
<pre><code class="language-shell">$ rustup dump-testament
Rustup version renders as: 1.18.3+15 (a54051502 2019-05-26) dirty 1 modification
Current crate version: 1.18.3
Built from branch: kinnison/version-strings
Commit info: 1.18.3+15 (a54051502 2019-05-26)
Modified: CONTRIBUTING.md
</code></pre>
<p>This can be handy when you are testing development versions on your PC
and cannot remember exactly which version you had installed, or if you have given
a development copy (or instruction to build such) to a user, and wish to have them
confirm <em>exactly</em> what they are using.</p>
<p>Finally, we tell <code>git-testament</code> that we trust the <code>stable</code> branch to carry
releases. If the build is being performed when not on the <code>stable</code> branch, and
the tag and <code>CARGO_PKG_VERSION</code> differ, then the short version string will include
both, in the form <code>rustup-init 1.18.3 :: 1.18.2+99 (a54051502 2019-05-26)</code> which
indicates the crate version before the rest of the commit.
On the other hand, if the build was on the <code>stable</code> branch then regardless
of the tag information, providing the commit was clean, the version is
always replaced by the crate version. The <code>dump-testament</code> hidden command can
reveal the truth however.</p>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="making-a-release"><a class="header" href="#making-a-release">Making a release</a></h1>
<p>In rustup, there are two possible release “modes”: the beta release and the
official release. The main difference between the two is that they use
different values for the <code>RUSTUP_UPDATE_ROOT</code> environment variable:</p>
<ul>
<li>A beta release is deployed on <code>https://dev-static.rust-lang.org/rustup</code>.</li>
<li>An official release is deployed on <code>https://static.rust-lang.org/rustup</code>.</li>
</ul>
<p>By switching between those two values, rustup effectively provides two “self
update channels”, making beta testing possible with <code>rustup self update</code>.</p>
<p>Currently, rustup does one beta release followed by one official release for
each version number in the increasing order. In other words, we dont release
any <code>1.28.x</code> once the <code>1.29.0</code> beta release is out, and the latter is followed
by the <code>1.29.0</code> stable release, and so on.</p>
<h2 id="bumping-the-version-number"><a class="header" href="#bumping-the-version-number">Bumping the version number</a></h2>
<p>The version number is registered in the <code>Cargo.toml</code> file of the project.</p>
<p>The general principle for version numbers is that we always increment the
<em>minor</em> number unless:</p>
<ul>
<li>A major incompatibility has been introduced in this release:
increment the <em>major</em> number instead.</li>
<li>This release is a hotfix because the last one had a defect:
increment the <em>patch</em> number instead.</li>
</ul>
<h3 id="minor-version-bumps"><a class="header" href="#minor-version-bumps">Minor version bumps</a></h3>
<blockquote>
<p>NOTE: Rustup hasnt been doing major version bumps since a long time ago, but
if we ever do, the procedure for it should be similar to that of a minor one.</p>
</blockquote>
<p>A minor version bump should be performed immediately after the latest <code>X.Y.0</code>
(e.g. <code>1.29.0</code>) beta release, and to do so, the following steps should be
taken:</p>
<ul>
<li>In the <code>main</code> branch, note the current minor version number <code>X.Y</code> (e.g.
<code>1.29</code>) and create a new branch from <code>main</code> named <code>release/X.Y</code> (e.g.
<code>release/1.29</code>). This will be the active backport branch from now on.</li>
<li>In a separate PR targeting <code>main</code>:
<ul>
<li>Bump the minor version number in <code>Cargo.toml</code> (e.g. to <code>1.30.0</code>).</li>
<li>Run <code>cargo build</code> and review <code>Cargo.lock</code> changes.</li>
</ul>
</li>
</ul>
<h3 id="patch-version-bumps"><a class="header" href="#patch-version-bumps">Patch version bumps</a></h3>
<p>A patch version bump should be performed immediately after any latest release
other than <code>X.Y.0</code> betas if the backport branch <code>release/X.Y</code> is still
considered active (i.e. it is expected to cut new patch releases from the
branch):</p>
<ul>
<li>In a separate PR targeting that backport branch:
<ul>
<li>Bump the patch version number in <code>Cargo.toml</code> (e.g. to <code>1.29.1</code>).</li>
<li>Run <code>cargo build</code> and review <code>Cargo.lock</code> changes.</li>
</ul>
</li>
</ul>
<h2 id="maintaining-the-backport-branch"><a class="header" href="#maintaining-the-backport-branch">Maintaining the backport branch</a></h2>
<p>When the backport branch <code>release/X.Y</code> is active, you are expected to backport
to it any relevant non-breaking changes one would like to see in new patch
releases. This includes, but is not limited to:</p>
<ul>
<li>Bug fixes.</li>
<li>Patch-compatible documentation improvements.</li>
<li>Minor features if they are not expected to cause any breakage.</li>
<li>CI adjustments if relevant to the active backport branch.</li>
</ul>
<p>The backport PRs should bear the <code>backport</code> label and target the active
backport branch in a rebased, commit-preserving manner.</p>
<p>It is OK to backport multiple original PRs at once as long as the conflict
resolution is straightforward (we would expect this to be the case for the most
part otherwise it would be against the point of patch releases in the first
place).</p>
<p>The backport branches already have similar CI setup like that of <code>main</code>, but
the full CI must be manually triggered rather than scheduled. To do so, you can
use the <a href="https://cli.github.com">GitHub CLI</a> under the project directory:</p>
<pre><code class="language-console">$ gh workflow run ci.yaml --ref release/X.Y
</code></pre>
<h2 id="cutting-a-new-release"><a class="header" href="#cutting-a-new-release">Cutting a new release</a></h2>
<p>Before making a release, ensure that <code>rustup-init.sh</code> is behaving correctly,
and that youre satisfied that nothing in the ecosystem is breaking because
of the update. A useful set of things to check includes verifying that
real-world toolchains install okay, and that <code>rust-analyzer</code> isnt broken by
the release. While its not our responsibility if they depend on non-stable
APIs, we should behave well if we can.</p>
<p>The next step is to check whether you are cutting a beta or an official release,
and determine which <code>$BRANCH</code> you should be working on:</p>
<ul>
<li><code>main</code> for <code>X.Y.0</code> beta releases.</li>
<li><code>release/X.Y</code> for any other <code>X.Y.*</code> release.</li>
</ul>
<p>Producing the final release artifacts is a bit involved because of the way
rustup is distributed. Below is a list of things to be done in order to cut a
new [b]eta release or an official [r]elease:</p>
<ol>
<li>[b/r] Make sure that the desired version number for the new release
<code>$VER_NUM</code> already exists in <code>$BRANCH</code>s <code>Cargo.toml</code> file. Then in a new PR
targeting <code>$BRANCH</code>:
<ol>
<li>Update <code>CHANGELOG.md</code> accordingly if necessary.</li>
<li>Update <code>rustup-init.sh</code> so that:
<ul>
<li>The version number matches <code>$VER_NUM</code>.</li>
<li>The commit shasum matches the latest commit on <code>$BRANCH</code>.</li>
</ul>
</li>
<li>Update the test snapshot of <code>rustup-init.sh --help</code>.
At the moment of writing, this is done by running:
<pre><code class="language-console">$ SNAPSHOTS=overwrite cargo test --features=test -- cli_rustup_init_ui
</code></pre>
</li>
</ol>
</li>
<li>[b/r] After merging the PR made in the previous step:
<ol>
<li>Pull the latest remote <code>$BRANCH</code> changes to the local <code>$BRANCH</code>.</li>
<li>Hard-reset the local <code>stable</code> to <code>$BRANCH</code>s tip.</li>
<li>Double-check that the current local <code>stable</code> is indeed what is expected
for the next release (version number, commit history, etc.).</li>
<li>Force-push the local <code>stable</code> to the remote <code>stable</code>.</li>
</ol>
</li>
<li>[b/r] While you wait for green CI on <code>stable</code>, double-check the
functionality of <code>rustup-init.sh</code> and <code>rustup-init</code> just in case.</li>
<li>[b/r] Ensure all of CI is green on the <code>stable</code> branch.
Once it is, check through a representative proportion of the builds looking
for the reported version statements to ensure that we definitely built
something cleanly which reports as the right version number when run
<code>--version</code>.</li>
<li>[b] Make a new PR to the <a href="https://github.com/rust-lang/blog.rust-lang.org/tree/main/content/inside-rust">Inside Rust Blog</a> adding a new “Call for Testing”
announcement post.</li>
<li>[r] Make a new PR to the <a href="https://github.com/rust-lang/blog.rust-lang.org">Rust Blog</a> adding a new release announcement post.</li>
<li>[b/r] Ping someone in the release team to perform the actual release.
They can find instructions in <code>ci/sync-dist.py</code>.
<blockquote>
<p><strong>Note:</strong> Some manual testing occurs here, so hopefully theyll catch
anything egregious in which case abort the change and roll back.</p>
</blockquote>
</li>
<li>[b] Once the beta release has happened, post a new topic named “Seeking beta
testers for rustup $VER_NUM” on the <a href="https://internals.rust-lang.org">Internals Forum</a> to point to the blog
post made previously.</li>
<li>[r] Once the official release has happened, prepare and push a tag on the
latest <code>stable</code> commit.
<ul>
<li><code>git tag -as $VER_NUM -m $VER_NUM</code> (optionally without <code>-s</code> if not GPG
signing the tag)</li>
<li><code>git push origin $VER_NUM</code></li>
</ul>
</li>
<li>[b/r] Immediately perform the corresponding version bump for the next
release as described in the previous sections.</li>
</ol>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="developer-tips-and-tricks"><a class="header" href="#developer-tips-and-tricks">Developer tips and tricks</a></h1>
<h2 id="rustup_force_arg0"><a class="header" href="#rustup_force_arg0"><code>RUSTUP_FORCE_ARG0</code></a></h2>
<p>The environment variable <code>RUSTUP_FORCE_ARG0</code> can be used to get rustup to think
its a particular binary, rather than e.g. copying it, symlinking it or other
tricks with exec. This is handy when testing particular code paths from cargo
run.</p>
<p>For example, if you want to run <code>rustup show</code> with <code>cargo run</code>, you may execute:</p>
<pre><code class="language-console">&gt; cargo run --config env.RUSTUP_FORCE_ARG0=\'rustup\' -- show
</code></pre>
<p>This command passes the <code>RUSTUP_FORCE_ARG0</code> environment variable to the
<code>rustup-init</code> binary without influencing the <code>cargo run</code> command itself,
which is very important since <code>cargo</code> could also be a rustup proxy.</p>
<h2 id="rustup_backtrack_limit"><a class="header" href="#rustup_backtrack_limit"><code>RUSTUP_BACKTRACK_LIMIT</code></a></h2>
<p>If its necessary to alter the backtracking limit from the default of half
a release cycle for some reason, you can set the <code>RUSTUP_BACKTRACK_LIMIT</code>
environment variable. If this is unparsable as an <code>i32</code> or if its absent
then the default of 21 days (half a cycle) is used. If it parses and is less
than 1, it is clamped to 1 at minimum.</p>
<p>This is not meant for use by users, but can be suggested in diagnosing an issue
should one arise with the backtrack limits.</p>
<h2 id="rustup_max_retries"><a class="header" href="#rustup_max_retries"><code>RUSTUP_MAX_RETRIES</code></a></h2>
<p>When downloading a file, rustup will retry the download a number of times. The
default is 3 times, but if this variable is set to a valid usize then it is the
max retry count. A value of <code>0</code> means no retries, thus the default of <code>3</code> will
mean a download is tried a total of four times before failing out.</p>
<h2 id="rustup_backtrace"><a class="header" href="#rustup_backtrace"><code>RUSTUP_BACKTRACE</code></a></h2>
<p>By default while running tests, we unset some environment variables that will
break our testing (like <code>RUSTUP_TOOLCHAIN</code>, <code>SHELL</code>, <code>ZDOTDIR</code>, <code>RUST_BACKTRACE</code>).
But if you want to debug locally, you may need backtrace. <code>RUSTUP_BACKTRACE</code>
is used like <code>RUST_BACKTRACE</code> to enable backtraces of failed tests.</p>
<p><strong>NOTE</strong>: This is a backtrace for the test, not for any subprocess invocation of
rustup process running in the test</p>
<pre><code class="language-bash">$ RUSTUP_BACKTRACE=1 cargo test --release --test cli-v1 -- remove_toolchain_then_add_again
Finished release [optimized] target(s) in 0.38s
Running target\release\deps\cli_v1-1f29f824792f6dc1.exe
running 1 test
test remove_toolchain_then_add_again ... FAILED
failures:
---- remove_toolchain_then_add_again stdout ----
thread 'remove_toolchain_then_add_again' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 1142, kind: Other, message: "An attempt was made to create more links on a file than the file system supports." }', src\libcore\result.rs:999:5
stack backtrace:
0: backtrace::backtrace::trace_unsynchronized
at C:\Users\appveyor\.cargo\registry\src\github.com-1ecc6299db9ec823\backtrace-0.3.29\src\backtrace\mod.rs:66
1: std::sys_common::backtrace::_print
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libstd\sys_common\backtrace.rs:47
2: std::sys_common::backtrace::print
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libstd\sys_common\backtrace.rs:36
3: std::panicking::default_hook::{{closure}}
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libstd\panicking.rs:198
4: std::panicking::default_hook
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libstd\panicking.rs:209
5: std::panicking::rust_panic_with_hook
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libstd\panicking.rs:475
6: std::panicking::continue_panic_fmt
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libstd\panicking.rs:382
7: std::panicking::rust_begin_panic
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libstd\panicking.rs:309
8: core::panicking::panic_fmt
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libcore\panicking.rs:85
9: core::result::unwrap_failed
10: cli_v1::mock::clitools::test
11: alloc::boxed::{{impl}}::call_once&lt;(),FnOnce&lt;()&gt;&gt;
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\src\liballoc\boxed.rs:746
12: panic_unwind::__rust_maybe_catch_panic
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libpanic_unwind\lib.rs:82
13: std::panicking::try
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\src\libstd\panicking.rs:273
14: std::panic::catch_unwind
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\src\libstd\panic.rs:388
15: test::run_test::run_test_inner::{{closure}}
at /rustc/de02101e6d949c4a9040211e9ce8c488a997497e\/src\libtest\lib.rs:1466
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
failures:
remove_toolchain_then_add_again
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 26 filtered out
error: test failed, to rerun pass '--test cli-v1'
</code></pre>
<div style="break-before: page; page-break-before: always;"></div>
<h1 id="tracing"><a class="header" href="#tracing">Tracing</a></h1>
<p>Similar to other tools in the Rust ecosystem like rustc and cargo,
rustup also provides observability/logging features via the <code>tracing</code> crate.</p>
<p>The verbosity of logs is controlled via the <code>RUSTUP_LOG</code> environment
variable using <code>tracing_subscriber</code>s <a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">directive syntax</a>.</p>
<h2 id="console-based-tracing"><a class="header" href="#console-based-tracing">Console-based tracing</a></h2>
<p>A <code>tracing_subscriber</code> that prints log lines directly to <code>stderr</code> is directly
available in the prebuilt version of rustup since v1.28.0.</p>
<p>For historical reasons, if <code>RUSTUP_LOG</code> is not set, this subscriber will print
the log lines in a format that mimics the “legacy” <code>stderr</code> output in older
versions of rustup:</p>
<pre><code class="language-console">&gt; rustup default stable
info: using existing install for 'stable-aarch64-apple-darwin'
info: default toolchain set to 'stable-aarch64-apple-darwin'
stable-aarch64-apple-darwin unchanged - rustc 1.79.0 (129f3b996 2024-06-10)
</code></pre>
<p>However, once <code>RUSTUP_LOG</code> is set to any value, rustups “custom logging mode” will
be activated, and <code>tracing_subscriber</code>s builtin output format will be used instead:</p>
<pre><code class="language-console">&gt; RUSTUP_LOG=trace rustup default stable
2024-06-16T12:08:48.732894Z INFO rustup::cli::common: using existing install for 'stable-aarch64-apple-darwin'
2024-06-16T12:08:48.739232Z INFO rustup::cli::common: default toolchain set to 'stable-aarch64-apple-darwin'
stable-aarch64-apple-darwin unchanged - rustc 1.79.0 (129f3b996 2024-06-10)
</code></pre>
<p>Please note that since <code>RUSTUP_LOG=trace</code> essentially accepts log lines from
all possible sources, you might sometimes see log lines coming from rustups
dependencies, such as <code>hyper_util</code> in the following example:</p>
<pre><code class="language-console">&gt; RUSTUP_LOG=trace rustup update
[..]
2024-06-16T12:12:45.569428Z TRACE hyper_util::client::legacy::client: http1 handshake complete, spawning background dispatcher task
2024-06-16T12:12:45.648682Z TRACE hyper_util::client::legacy::pool: pool dropped, dropping pooled (("https", static.rust-lang.org))
stable-aarch64-apple-darwin unchanged - rustc 1.79.0 (129f3b996 2024-06-10)
nightly-aarch64-apple-darwin unchanged - rustc 1.81.0-nightly (3cf924b93 2024-06-15)
2024-06-16T12:12:45.693350Z INFO rustup::cli::rustup_mode: cleaning up downloads &amp; tmp directories
</code></pre>
<p>It is also possible to limit the sources of the log lines and the desired
max level for each source. For example, set <code>RUSTUP_LOG=rustup=DEBUG</code> to
receive log lines only from <code>rustup</code> itself with a max verbosity of <code>DEBUG</code>.</p>
<h2 id="opentelemetry-tracing"><a class="header" href="#opentelemetry-tracing">Opentelemetry tracing</a></h2>
<p>The feature <code>otel</code> can be used when building rustup to turn on Opentelemetry
tracing with an OLTP GRPC exporter.</p>
<p>This can be very useful for diagnosing performance or correctness issues in more
complicated scenarios.</p>
<p>The normal <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md">OTLP environment
variables</a>
can be used to customise its behaviour, but often the simplest thing is to just
run a Jaeger docker container on the same host:</p>
<pre><code class="language-sh">docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 -e COLLECTOR_OTLP_ENABLED=true -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 4317:4317 -p 4318:4318 -p 14250:14250 -p 14268:14268 -p 14269:14269 -p 9411:9411 jaegertracing/all-in-one:latest
</code></pre>
<p>Then build <code>rustup-init</code> with tracing:</p>
<pre><code class="language-sh">cargo build --features=otel
</code></pre>
<p>Run the operation you want to analyze. For example, we can now run <code>rustup show</code> with tracing:</p>
<pre><code class="language-sh">RUSTUP_FORCE_ARG0="rustup" ./target/debug/rustup-init show
</code></pre>
<p>And <a href="http://localhost:16686/search?service=rustup">look in Jaeger for a trace</a>.</p>
<p>Tracing can also be used in tests to get a trace of the operations taken during the test.
To use this feature, build the project with <code>--features=otel,test</code>.</p>
<h2 id="adding-instrumentation"><a class="header" href="#adding-instrumentation">Adding instrumentation</a></h2>
<p>Instrumenting a currently uninstrumented function is mostly simply done like so:</p>
<pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[tracing::instrument(level = "trace", err(level = "trace"), skip_all)]
<span class="boring">}</span></code></pre>
<p>Sometimes you might want to instrument a function only when the <code>otel</code> feature is enabled.
In this case, you will need to use conditional compilation with <code>cfg_attr</code>:</p>
<pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[cfg_attr(feature="otel", tracing::instrument(level = "trace", err(level = "trace"), skip_all))]
<span class="boring">}</span></code></pre>
<p><code>skip_all</code> is not required, but some core structs dont implement <code>Debug</code> yet, and
others have a lot of output in <code>Debug</code>: tracing adds some overheads, so keeping
spans lightweight can help avoid frequency bias in the results - where
parameters with large debug in frequently called functions show up as much
slower than they are.</p>
<p>Some good general heuristics:</p>
<ul>
<li>Do instrument slow blocking functions</li>
<li>Do instrument functions with many callers or that call many different things,
as these tend to help figure the puzzle of what-is-happening</li>
<li>Default to not instrumenting thin shim functions (or at least, only instrument
them temporarily while figuring out the shape of a problem)</li>
<li>Be way of debug build timing - release optimisations make a huge difference,
though debug is a lot faster to iterate on. If something isnt a problem in
release dont pay it too much heed in debug.</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
</nav>
</div>
<template id=fa-eye><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z"/></svg></span></template>
<template id=fa-eye-slash><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z"/></svg></span></template>
<template id=fa-copy><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M502.6 70.63l-61.25-61.25C435.4 3.371 427.2 0 418.7 0H255.1c-35.35 0-64 28.66-64 64l.0195 256C192 355.4 220.7 384 256 384h192c35.2 0 64-28.8 64-64V93.25C512 84.77 508.6 76.63 502.6 70.63zM464 320c0 8.836-7.164 16-16 16H255.1c-8.838 0-16-7.164-16-16L239.1 64.13c0-8.836 7.164-16 16-16h128L384 96c0 17.67 14.33 32 32 32h47.1V320zM272 448c0 8.836-7.164 16-16 16H63.1c-8.838 0-16-7.164-16-16L47.98 192.1c0-8.836 7.164-16 16-16H160V128H63.99c-35.35 0-64 28.65-64 64l.0098 256C.002 483.3 28.66 512 64 512h192c35.2 0 64-28.8 64-64v-32h-47.1L272 448z"/></svg></span></template>
<template id=fa-play><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M73 39c-14.8-9.1-33.4-9.4-48.5-.9S0 62.6 0 80V432c0 17.4 9.4 33.4 24.5 41.9s33.7 8.1 48.5-.9L361 297c14.3-8.7 23-24.2 23-41s-8.7-32.2-23-41L73 39z"/></svg></span></template>
<template id=fa-clock-rotate-left><span class=fa-svg><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2022 Fonticons, Inc. --><path d="M75 75L41 41C25.9 25.9 0 36.6 0 57.9V168c0 13.3 10.7 24 24 24H134.1c21.4 0 32.1-25.9 17-41l-30.8-30.8C155 85.5 203 64 256 64c106 0 192 86 192 192s-86 192-192 192c-40.8 0-78.6-12.7-109.7-34.4c-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6C151.2 495 201.7 512 256 512c141.4 0 256-114.6 256-256S397.4 0 256 0C185.3 0 121.3 28.7 75 75zm181 53c-13.3 0-24 10.7-24 24V256c0 6.4 2.5 12.5 7 17l72 72c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-65-65V152c0-13.3-10.7-24-24-24z"/></svg></span></template>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr-ef4e11c1.min.js"></script>
<script src="mark-09e88c2c.min.js"></script>
<script src="searcher-c2a407aa.js"></script>
<script src="clipboard-1626706a.min.js"></script>
<script src="highlight-abc7f01d.js"></script>
<script src="book-a0b12cfe.js"></script>
<!-- Custom JS scripts -->
<script>
window.addEventListener('load', function() {
window.setTimeout(window.print, 100);
});
</script>
</div>
</body>
</html>