RustBook/ch18-01-all-the-places-for-patterns.html

457 lines
39 KiB
HTML
Raw Permalink Normal View History

2020-01-06 20:57:15 +00:00
<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>All the Places Patterns Can Be Used - The Rust Programming Language</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="googleFonts/css.css" 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 -->
<link rel="stylesheet" href="ferris.css">
<link rel="stylesheet" href="theme/2018-edition.css">
</head>
<body>
<!-- Provide site root to javascript -->
<script type="text/javascript">
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
</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 = default_theme; }
var html = document.querySelector('html');
html.classList.remove('no-js')
html.classList.remove('light')
html.classList.add(theme);
html.classList.add('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">
<div id="sidebar-scrollbox" class="sidebar-scrollbox">
<ol class="chapter"><li class="expanded affix "><a href="title-page.html">The Rust Programming Language</a></li><li class="expanded affix "><a href="foreword.html">Foreword</a></li><li class="expanded affix "><a href="ch00-00-introduction.html">Introduction</a></li><li class="expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> Installation</a></li><li class="expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Hello, Cargo!</a></li></ol></li><li class="expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> Programming a Guessing Game</a></li><li class="expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> Common Programming Concepts</a></li><li><ol class="section"><li class="expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> Variables and Mutability</a></li><li class="expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> Data Types</a></li><li class="expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> Functions</a></li><li class="expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> Comments</a></li><li class="expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> Control Flow</a></li></ol></li><li class="expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> Understanding Ownership</a></li><li><ol class="section"><li class="expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> What is Ownership?</a></li><li class="expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> References and Borrowing</a></li><li class="expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> The Slice Type</a></li></ol></li><li class="expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> Using Structs to Structure Related Data</a></li><li><ol class="section"><li class="expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> Defining and Instantiating Structs</a></li><li class="expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> An Example Program Using Structs</a></li><li class="expanded "><a href="ch05-03-method-syntax.html"><strong aria-hidden="true">5.3.</strong> Method Syntax</a></li></ol></li><li class="expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> Enums and Pattern Matching</a></li><li><ol class="section"><li class="expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> Defining an Enum</a></li><li class="expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> The match Control Flow Operator</a></li><li class="expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> Concise Control Flow with if let</a></li></ol></li><li class="expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> Managing Growing Projects with Packages, Crates, and Modules</a></li><li><ol class="section"><li class="expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> Packages and Crates</a></li><li class="expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> Defining Modules to Control Scope and Privacy</a></li><li class="expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
</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 (default)</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">The Rust Programming Language</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>
<h2><a class="header" href="#all-the-places-patterns-can-be-used" id="all-the-places-patterns-can-be-used">All the Places Patterns Can Be Used</a></h2>
<p>Patterns pop up in a number of places in Rust, and youve been using them a lot
without realizing it! This section discusses all the places where patterns are
valid.</p>
<h3><a class="header" href="#match-arms" id="match-arms"><code>match</code> Arms</a></h3>
<p>As discussed in Chapter 6, we use patterns in the arms of <code>match</code> expressions.
Formally, <code>match</code> expressions are defined as the keyword <code>match</code>, a value to
match on, and one or more match arms that consist of a pattern and an
expression to run if the value matches that arms pattern, like this:</p>
<pre><code class="language-text">match VALUE {
PATTERN =&gt; EXPRESSION,
PATTERN =&gt; EXPRESSION,
PATTERN =&gt; EXPRESSION,
}
</code></pre>
<p>One requirement for <code>match</code> expressions is that they need to be <em>exhaustive</em> in
the sense that all possibilities for the value in the <code>match</code> expression must
be accounted for. One way to ensure youve covered every possibility is to have
a catchall pattern for the last arm: for example, a variable name matching any
value can never fail and thus covers every remaining case.</p>
<p>A particular pattern <code>_</code> will match anything, but it never binds to a variable,
so its often used in the last match arm. The <code>_</code> pattern can be useful when
you want to ignore any value not specified, for example. Well cover the <code>_</code>
pattern in more detail in the <a href="ch18-03-pattern-syntax.html#ignoring-values-in-a-pattern">“Ignoring Values in a
Pattern”</a><!-- ignore --> section later in this
chapter.</p>
<h3><a class="header" href="#conditional-if-let-expressions" id="conditional-if-let-expressions">Conditional <code>if let</code> Expressions</a></h3>
<p>In Chapter 6 we discussed how to use <code>if let</code> expressions mainly as a shorter
way to write the equivalent of a <code>match</code> that only matches one case.
Optionally, <code>if let</code> can have a corresponding <code>else</code> containing code to run if
the pattern in the <code>if let</code> doesnt match.</p>
<p>Listing 18-1 shows that its also possible to mix and match <code>if let</code>, <code>else if</code>, and <code>else if let</code> expressions. Doing so gives us more flexibility than a
<code>match</code> expression in which we can express only one value to compare with the
patterns. Also, the conditions in a series of <code>if let</code>, <code>else if</code>, <code>else if let</code> arms arent required to relate to each other.</p>
<p>The code in Listing 18-1 shows a series of checks for several conditions that
decide what the background color should be. For this example, weve created
variables with hardcoded values that a real program might receive from user
input.</p>
<p><span class="filename">Filename: src/main.rs</span></p>
<pre><pre class="playpen"><code class="language-rust">fn main() {
let favorite_color: Option&lt;&amp;str&gt; = None;
let is_tuesday = false;
let age: Result&lt;u8, _&gt; = &quot;34&quot;.parse();
if let Some(color) = favorite_color {
println!(&quot;Using your favorite color, {}, as the background&quot;, color);
} else if is_tuesday {
println!(&quot;Tuesday is green day!&quot;);
} else if let Ok(age) = age {
if age &gt; 30 {
println!(&quot;Using purple as the background color&quot;);
} else {
println!(&quot;Using orange as the background color&quot;);
}
} else {
println!(&quot;Using blue as the background color&quot;);
}
}
</code></pre></pre>
<p><span class="caption">Listing 18-1: Mixing <code>if let</code>, <code>else if</code>, <code>else if let</code>,
and <code>else</code></span></p>
<p>If the user specifies a favorite color, that color is the background color. If
today is Tuesday, the background color is green. If the user specifies
their age as a string and we can parse it as a number successfully, the color
is either purple or orange depending on the value of the number. If none of
these conditions apply, the background color is blue.</p>
<p>This conditional structure lets us support complex requirements. With the
hardcoded values we have here, this example will print <code>Using purple as the background color</code>.</p>
<p>You can see that <code>if let</code> can also introduce shadowed variables in the same way
that <code>match</code> arms can: the line <code>if let Ok(age) = age</code> introduces a new
shadowed <code>age</code> variable that contains the value inside the <code>Ok</code> variant. This
means we need to place the <code>if age &gt; 30</code> condition within that block: we cant
combine these two conditions into <code>if let Ok(age) = age &amp;&amp; age &gt; 30</code>. The
shadowed <code>age</code> we want to compare to 30 isnt valid until the new scope starts
with the curly bracket.</p>
<p>The downside of using <code>if let</code> expressions is that the compiler doesnt check
exhaustiveness, whereas with <code>match</code> expressions it does. If we omitted the
last <code>else</code> block and therefore missed handling some cases, the compiler would
not alert us to the possible logic bug.</p>
<h3><a class="header" href="#while-let-conditional-loops" id="while-let-conditional-loops"><code>while let</code> Conditional Loops</a></h3>
<p>Similar in construction to <code>if let</code>, the <code>while let</code> conditional loop allows a
<code>while</code> loop to run for as long as a pattern continues to match. The example in
Listing 18-2 shows a <code>while let</code> loop that uses a vector as a stack and prints
the values in the vector in the opposite order in which they were pushed.</p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!(&quot;{}&quot;, top);
}
<span class="boring">}
</span></code></pre></pre>
<p><span class="caption">Listing 18-2: Using a <code>while let</code> loop to print values
for as long as <code>stack.pop()</code> returns <code>Some</code></span></p>
<p>This example prints 3, 2, and then 1. The <code>pop</code> method takes the last element
out of the vector and returns <code>Some(value)</code>. If the vector is empty, <code>pop</code>
returns <code>None</code>. The <code>while</code> loop continues running the code in its block as
long as <code>pop</code> returns <code>Some</code>. When <code>pop</code> returns <code>None</code>, the loop stops. We can
use <code>while let</code> to pop every element off our stack.</p>
<h3><a class="header" href="#for-loops" id="for-loops"><code>for</code> Loops</a></h3>
<p>In Chapter 3, we mentioned that the <code>for</code> loop is the most common loop
construction in Rust code, but we havent yet discussed the pattern that <code>for</code>
takes. In a <code>for</code> loop, the pattern is the value that directly follows the
keyword <code>for</code>, so in <code>for x in y</code> the <code>x</code> is the pattern.</p>
<p>Listing 18-3 demonstrates how to use a pattern in a <code>for</code> loop to destructure,
or break apart, a tuple as part of the <code>for</code> loop.</p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!(&quot;{} is at index {}&quot;, value, index);
}
<span class="boring">}
</span></code></pre></pre>
<p><span class="caption">Listing 18-3: Using a pattern in a <code>for</code> loop to
destructure a tuple</span></p>
<p>The code in Listing 18-3 will print the following:</p>
<pre><code class="language-text">a is at index 0
b is at index 1
c is at index 2
</code></pre>
<p>We use the <code>enumerate</code> method to adapt an iterator to produce a value and that
values index in the iterator, placed into a tuple. The first call to
<code>enumerate</code> produces the tuple <code>(0, 'a')</code>. When this value is matched to the
pattern <code>(index, value)</code>, <code>index</code> will be <code>0</code> and <code>value</code> will be <code>'a'</code>,
printing the first line of the output.</p>
<h3><a class="header" href="#let-statements" id="let-statements"><code>let</code> Statements</a></h3>
<p>Prior to this chapter, we had only explicitly discussed using patterns with
<code>match</code> and <code>if let</code>, but in fact, weve used patterns in other places as well,
including in <code>let</code> statements. For example, consider this straightforward
variable assignment with <code>let</code>:</p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>let x = 5;
<span class="boring">}
</span></code></pre></pre>
<p>Throughout this book, weve used <code>let</code> like this hundreds of times, and
although you might not have realized it, you were using patterns! More
formally, a <code>let</code> statement looks like this:</p>
<pre><code class="language-text">let PATTERN = EXPRESSION;
</code></pre>
<p>In statements like <code>let x = 5;</code> with a variable name in the <code>PATTERN</code> slot, the
variable name is just a particularly simple form of a pattern. Rust compares
the expression against the pattern and assigns any names it finds. So in the
<code>let x = 5;</code> example, <code>x</code> is a pattern that means “bind what matches here to
the variable <code>x</code>.” Because the name <code>x</code> is the whole pattern, this pattern
effectively means “bind everything to the variable <code>x</code>, whatever the value is.”</p>
<p>To see the pattern matching aspect of <code>let</code> more clearly, consider Listing
18-4, which uses a pattern with <code>let</code> to destructure a tuple.</p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>let (x, y, z) = (1, 2, 3);
<span class="boring">}
</span></code></pre></pre>
<p><span class="caption">Listing 18-4: Using a pattern to destructure a tuple and
create three variables at once</span></p>
<p>Here, we match a tuple against a pattern. Rust compares the value <code>(1, 2, 3)</code>
to the pattern <code>(x, y, z)</code> and sees that the value matches the pattern, so Rust
binds <code>1</code> to <code>x</code>, <code>2</code> to <code>y</code>, and <code>3</code> to <code>z</code>. You can think of this tuple
pattern as nesting three individual variable patterns inside it.</p>
<p>If the number of elements in the pattern doesnt match the number of elements
in the tuple, the overall type wont match and well get a compiler error. For
example, Listing 18-5 shows an attempt to destructure a tuple with three
elements into two variables, which wont work.</p>
<pre><code class="language-rust ignore does_not_compile">let (x, y) = (1, 2, 3);
</code></pre>
<p><span class="caption">Listing 18-5: Incorrectly constructing a pattern whose
variables dont match the number of elements in the tuple</span></p>
<p>Attempting to compile this code results in this type error:</p>
<pre><code class="language-text">error[E0308]: mismatched types
--&gt; src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ expected a tuple with 3 elements, found one with 2 elements
|
= note: expected type `({integer}, {integer}, {integer})`
found type `(_, _)`
</code></pre>
<p>If we wanted to ignore one or more of the values in the tuple, we could use <code>_</code>
or <code>..</code>, as youll see in the <a href="ch18-03-pattern-syntax.html#ignoring-values-in-a-pattern">“Ignoring Values in a
Pattern”</a><!-- ignore --> section. If the problem
is that we have too many variables in the pattern, the solution is to make the
types match by removing variables so the number of variables equals the number
of elements in the tuple.</p>
<h3><a class="header" href="#function-parameters" id="function-parameters">Function Parameters</a></h3>
<p>Function parameters can also be patterns. The code in Listing 18-6, which
declares a function named <code>foo</code> that takes one parameter named <code>x</code> of type
<code>i32</code>, should by now look familiar.</p>
<pre><pre class="playpen"><code class="language-rust">
<span class="boring">#![allow(unused_variables)]
</span><span class="boring">fn main() {
</span>fn foo(x: i32) {
// code goes here
}
<span class="boring">}
</span></code></pre></pre>
<p><span class="caption">Listing 18-6: A function signature uses patterns in the
parameters</span></p>
<p>The <code>x</code> part is a pattern! As we did with <code>let</code>, we could match a tuple in a
functions arguments to the pattern. Listing 18-7 splits the values in a tuple
as we pass it to a function.</p>
<p><span class="filename">Filename: src/main.rs</span></p>
<pre><pre class="playpen"><code class="language-rust">fn print_coordinates(&amp;(x, y): &amp;(i32, i32)) {
println!(&quot;Current location: ({}, {})&quot;, x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&amp;point);
}
</code></pre></pre>
<p><span class="caption">Listing 18-7: A function with parameters that destructure
a tuple</span></p>
<p>This code prints <code>Current location: (3, 5)</code>. The values <code>&amp;(3, 5)</code> match the
pattern <code>&amp;(x, y)</code>, so <code>x</code> is the value <code>3</code> and <code>y</code> is the value <code>5</code>.</p>
<p>We can also use patterns in closure parameter lists in the same way as in
function parameter lists, because closures are similar to functions, as
discussed in Chapter 13.</p>
<p>At this point, youve seen several ways of using patterns, but patterns dont
work the same in every place we can use them. In some places, the patterns must
be irrefutable; in other circumstances, they can be refutable. Well discuss
these two concepts next.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="ch18-00-patterns.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next" href="ch18-02-refutability.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a href="ch18-00-patterns.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a href="ch18-02-refutability.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 type="text/javascript">
window.playpen_copyable = true;
</script>
<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 -->
<script type="text/javascript" src="ferris.js"></script>
</body>
</html>