745 lines
55 KiB
HTML
745 lines
55 KiB
HTML
|
<!DOCTYPE HTML>
|
|||
|
<html lang="en" class="sidebar-visible no-js light">
|
|||
|
<head>
|
|||
|
<!-- Book generated using mdBook -->
|
|||
|
<meta charset="UTF-8">
|
|||
|
<title>Traits: Defining Shared Behavior - 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="#traits-defining-shared-behavior" id="traits-defining-shared-behavior">Traits: Defining Shared Behavior</a></h2>
|
|||
|
<p>A <em>trait</em> tells the Rust compiler about functionality a particular type has and
|
|||
|
can share with other types. We can use traits to define shared behavior in an
|
|||
|
abstract way. We can use trait bounds to specify that a generic can be any type
|
|||
|
that has certain behavior.</p>
|
|||
|
<blockquote>
|
|||
|
<p>Note: Traits are similar to a feature often called <em>interfaces</em> in other
|
|||
|
languages, although with some differences.</p>
|
|||
|
</blockquote>
|
|||
|
<h3><a class="header" href="#defining-a-trait" id="defining-a-trait">Defining a Trait</a></h3>
|
|||
|
<p>A type’s behavior consists of the methods we can call on that type. Different
|
|||
|
types share the same behavior if we can call the same methods on all of those
|
|||
|
types. Trait definitions are a way to group method signatures together to
|
|||
|
define a set of behaviors necessary to accomplish some purpose.</p>
|
|||
|
<p>For example, let’s say we have multiple structs that hold various kinds and
|
|||
|
amounts of text: a <code>NewsArticle</code> struct that holds a news story filed in a
|
|||
|
particular location and a <code>Tweet</code> that can have at most 280 characters along
|
|||
|
with metadata that indicates whether it was a new tweet, a retweet, or a reply
|
|||
|
to another tweet.</p>
|
|||
|
<p>We want to make a media aggregator library that can display summaries of data
|
|||
|
that might be stored in a <code>NewsArticle</code> or <code>Tweet</code> instance. To do this, we
|
|||
|
need a summary from each type, and we need to request that summary by calling a
|
|||
|
<code>summarize</code> method on an instance. Listing 10-12 shows the definition of a
|
|||
|
<code>Summary</code> trait that expresses this behavior.</p>
|
|||
|
<p><span class="filename">Filename: src/lib.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>pub trait Summary {
|
|||
|
fn summarize(&self) -> String;
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 10-12: A <code>Summary</code> trait that consists of the
|
|||
|
behavior provided by a <code>summarize</code> method</span></p>
|
|||
|
<p>Here, we declare a trait using the <code>trait</code> keyword and then the trait’s name,
|
|||
|
which is <code>Summary</code> in this case. Inside the curly brackets, we declare the
|
|||
|
method signatures that describe the behaviors of the types that implement this
|
|||
|
trait, which in this case is <code>fn summarize(&self) -> String</code>.</p>
|
|||
|
<p>After the method signature, instead of providing an implementation within curly
|
|||
|
brackets, we use a semicolon. Each type implementing this trait must provide
|
|||
|
its own custom behavior for the body of the method. The compiler will enforce
|
|||
|
that any type that has the <code>Summary</code> trait will have the method <code>summarize</code>
|
|||
|
defined with this signature exactly.</p>
|
|||
|
<p>A trait can have multiple methods in its body: the method signatures are listed
|
|||
|
one per line and each line ends in a semicolon.</p>
|
|||
|
<h3><a class="header" href="#implementing-a-trait-on-a-type" id="implementing-a-trait-on-a-type">Implementing a Trait on a Type</a></h3>
|
|||
|
<p>Now that we’ve defined the desired behavior using the <code>Summary</code> trait, we can
|
|||
|
implement it on the types in our media aggregator. Listing 10-13 shows an
|
|||
|
implementation of the <code>Summary</code> trait on the <code>NewsArticle</code> struct that uses the
|
|||
|
headline, the author, and the location to create the return value of
|
|||
|
<code>summarize</code>. For the <code>Tweet</code> struct, we define <code>summarize</code> as the username
|
|||
|
followed by the entire text of the tweet, assuming that tweet content is
|
|||
|
already limited to 280 characters.</p>
|
|||
|
<p><span class="filename">Filename: src/lib.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span><span class="boring">pub trait Summary {
|
|||
|
</span><span class="boring"> fn summarize(&self) -> String;
|
|||
|
</span><span class="boring">}
|
|||
|
</span><span class="boring">
|
|||
|
</span>pub struct NewsArticle {
|
|||
|
pub headline: String,
|
|||
|
pub location: String,
|
|||
|
pub author: String,
|
|||
|
pub content: String,
|
|||
|
}
|
|||
|
|
|||
|
impl Summary for NewsArticle {
|
|||
|
fn summarize(&self) -> String {
|
|||
|
format!("{}, by {} ({})", self.headline, self.author, self.location)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
pub struct Tweet {
|
|||
|
pub username: String,
|
|||
|
pub content: String,
|
|||
|
pub reply: bool,
|
|||
|
pub retweet: bool,
|
|||
|
}
|
|||
|
|
|||
|
impl Summary for Tweet {
|
|||
|
fn summarize(&self) -> String {
|
|||
|
format!("{}: {}", self.username, self.content)
|
|||
|
}
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 10-13: Implementing the <code>Summary</code> trait on the
|
|||
|
<code>NewsArticle</code> and <code>Tweet</code> types</span></p>
|
|||
|
<p>Implementing a trait on a type is similar to implementing regular methods. The
|
|||
|
difference is that after <code>impl</code>, we put the trait name that we want to
|
|||
|
implement, then use the <code>for</code> keyword, and then specify the name of the type we
|
|||
|
want to implement the trait for. Within the <code>impl</code> block, we put the method
|
|||
|
signatures that the trait definition has defined. Instead of adding a semicolon
|
|||
|
after each signature, we use curly brackets and fill in the method body with
|
|||
|
the specific behavior that we want the methods of the trait to have for the
|
|||
|
particular type.</p>
|
|||
|
<p>After implementing the trait, we can call the methods on instances of
|
|||
|
<code>NewsArticle</code> and <code>Tweet</code> in the same way we call regular methods, like this:</p>
|
|||
|
<pre><code class="language-rust ignore">let tweet = Tweet {
|
|||
|
username: String::from("horse_ebooks"),
|
|||
|
content: String::from("of course, as you probably already know, people"),
|
|||
|
reply: false,
|
|||
|
retweet: false,
|
|||
|
};
|
|||
|
|
|||
|
println!("1 new tweet: {}", tweet.summarize());
|
|||
|
</code></pre>
|
|||
|
<p>This code prints <code>1 new tweet: horse_ebooks: of course, as you probably already know, people</code>.</p>
|
|||
|
<p>Note that because we defined the <code>Summary</code> trait and the <code>NewsArticle</code> and
|
|||
|
<code>Tweet</code> types in the same <em>lib.rs</em> in Listing 10-13, they’re all in the same
|
|||
|
scope. Let’s say this <em>lib.rs</em> is for a crate we’ve called <code>aggregator</code> and
|
|||
|
someone else wants to use our crate’s functionality to implement the <code>Summary</code>
|
|||
|
trait on a struct defined within their library’s scope. They would need to
|
|||
|
bring the trait into their scope first. They would do so by specifying <code>use aggregator::Summary;</code>, which then would enable them to implement <code>Summary</code> for
|
|||
|
their type. The <code>Summary</code> trait would also need to be a public trait for
|
|||
|
another crate to implement it, which it is because we put the <code>pub</code> keyword
|
|||
|
before <code>trait</code> in Listing 10-12.</p>
|
|||
|
<p>One restriction to note with trait implementations is that we can implement a
|
|||
|
trait on a type only if either the trait or the type is local to our crate.
|
|||
|
For example, we can implement standard library traits like <code>Display</code> on a
|
|||
|
custom type like <code>Tweet</code> as part of our <code>aggregator</code> crate functionality,
|
|||
|
because the type <code>Tweet</code> is local to our <code>aggregator</code> crate. We can also
|
|||
|
implement <code>Summary</code> on <code>Vec<T></code> in our <code>aggregator</code> crate, because the
|
|||
|
trait <code>Summary</code> is local to our <code>aggregator</code> crate.</p>
|
|||
|
<p>But we can’t implement external traits on external types. For example, we can’t
|
|||
|
implement the <code>Display</code> trait on <code>Vec<T></code> within our <code>aggregator</code> crate,
|
|||
|
because <code>Display</code> and <code>Vec<T></code> are defined in the standard library and aren’t
|
|||
|
local to our <code>aggregator</code> crate. This restriction is part of a property of
|
|||
|
programs called <em>coherence</em>, and more specifically the <em>orphan rule</em>, so named
|
|||
|
because the parent type is not present. This rule ensures that other people’s
|
|||
|
code can’t break your code and vice versa. Without the rule, two crates could
|
|||
|
implement the same trait for the same type, and Rust wouldn’t know which
|
|||
|
implementation to use.</p>
|
|||
|
<h3><a class="header" href="#default-implementations" id="default-implementations">Default Implementations</a></h3>
|
|||
|
<p>Sometimes it’s useful to have default behavior for some or all of the methods
|
|||
|
in a trait instead of requiring implementations for all methods on every type.
|
|||
|
Then, as we implement the trait on a particular type, we can keep or override
|
|||
|
each method’s default behavior.</p>
|
|||
|
<p>Listing 10-14 shows how to specify a default string for the <code>summarize</code> method
|
|||
|
of the <code>Summary</code> trait instead of only defining the method signature, as we did
|
|||
|
in Listing 10-12.</p>
|
|||
|
<p><span class="filename">Filename: src/lib.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>pub trait Summary {
|
|||
|
fn summarize(&self) -> String {
|
|||
|
String::from("(Read more...)")
|
|||
|
}
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 10-14: Definition of a <code>Summary</code> trait with a
|
|||
|
default implementation of the <code>summarize</code> method</span></p>
|
|||
|
<p>To use a default implementation to summarize instances of <code>NewsArticle</code> instead
|
|||
|
of defining a custom implementation, we specify an empty <code>impl</code> block with
|
|||
|
<code>impl Summary for NewsArticle {}</code>.</p>
|
|||
|
<p>Even though we’re no longer defining the <code>summarize</code> method on <code>NewsArticle</code>
|
|||
|
directly, we’ve provided a default implementation and specified that
|
|||
|
<code>NewsArticle</code> implements the <code>Summary</code> trait. As a result, we can still call
|
|||
|
the <code>summarize</code> method on an instance of <code>NewsArticle</code>, like this:</p>
|
|||
|
<pre><code class="language-rust ignore">let article = NewsArticle {
|
|||
|
headline: String::from("Penguins win the Stanley Cup Championship!"),
|
|||
|
location: String::from("Pittsburgh, PA, USA"),
|
|||
|
author: String::from("Iceburgh"),
|
|||
|
content: String::from("The Pittsburgh Penguins once again are the best
|
|||
|
hockey team in the NHL."),
|
|||
|
};
|
|||
|
|
|||
|
println!("New article available! {}", article.summarize());
|
|||
|
</code></pre>
|
|||
|
<p>This code prints <code>New article available! (Read more...)</code>.</p>
|
|||
|
<p>Creating a default implementation for <code>summarize</code> doesn’t require us to change
|
|||
|
anything about the implementation of <code>Summary</code> on <code>Tweet</code> in Listing 10-13. The
|
|||
|
reason is that the syntax for overriding a default implementation is the same
|
|||
|
as the syntax for implementing a trait method that doesn’t have a default
|
|||
|
implementation.</p>
|
|||
|
<p>Default implementations can call other methods in the same trait, even if those
|
|||
|
other methods don’t have a default implementation. In this way, a trait can
|
|||
|
provide a lot of useful functionality and only require implementors to specify
|
|||
|
a small part of it. For example, we could define the <code>Summary</code> trait to have a
|
|||
|
<code>summarize_author</code> method whose implementation is required, and then define a
|
|||
|
<code>summarize</code> method that has a default implementation that calls the
|
|||
|
<code>summarize_author</code> method:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>pub trait Summary {
|
|||
|
fn summarize_author(&self) -> String;
|
|||
|
|
|||
|
fn summarize(&self) -> String {
|
|||
|
format!("(Read more from {}...)", self.summarize_author())
|
|||
|
}
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>To use this version of <code>Summary</code>, we only need to define <code>summarize_author</code>
|
|||
|
when we implement the trait on a type:</p>
|
|||
|
<pre><code class="language-rust ignore">impl Summary for Tweet {
|
|||
|
fn summarize_author(&self) -> String {
|
|||
|
format!("@{}", self.username)
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p>After we define <code>summarize_author</code>, we can call <code>summarize</code> on instances of the
|
|||
|
<code>Tweet</code> struct, and the default implementation of <code>summarize</code> will call the
|
|||
|
definition of <code>summarize_author</code> that we’ve provided. Because we’ve implemented
|
|||
|
<code>summarize_author</code>, the <code>Summary</code> trait has given us the behavior of the
|
|||
|
<code>summarize</code> method without requiring us to write any more code.</p>
|
|||
|
<pre><code class="language-rust ignore">let tweet = Tweet {
|
|||
|
username: String::from("horse_ebooks"),
|
|||
|
content: String::from("of course, as you probably already know, people"),
|
|||
|
reply: false,
|
|||
|
retweet: false,
|
|||
|
};
|
|||
|
|
|||
|
println!("1 new tweet: {}", tweet.summarize());
|
|||
|
</code></pre>
|
|||
|
<p>This code prints <code>1 new tweet: (Read more from @horse_ebooks...)</code>.</p>
|
|||
|
<p>Note that it isn’t possible to call the default implementation from an
|
|||
|
overriding implementation of that same method.</p>
|
|||
|
<h3><a class="header" href="#traits-as-parameters" id="traits-as-parameters">Traits as Parameters</a></h3>
|
|||
|
<p>Now that you know how to define and implement traits, we can explore how to use
|
|||
|
traits to define functions that accept many different types.</p>
|
|||
|
<p>For example, in Listing 10-13, we implemented the <code>Summary</code> trait on the
|
|||
|
<code>NewsArticle</code> and <code>Tweet</code> types. We can define a <code>notify</code> function that calls
|
|||
|
the <code>summarize</code> method on its <code>item</code> parameter, which is of some type that
|
|||
|
implements the <code>Summary</code> trait. To do this, we can use the <code>impl Trait</code>
|
|||
|
syntax, like this:</p>
|
|||
|
<pre><code class="language-rust ignore">pub fn notify(item: impl Summary) {
|
|||
|
println!("Breaking news! {}", item.summarize());
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p>Instead of a concrete type for the <code>item</code> parameter, we specify the <code>impl</code>
|
|||
|
keyword and the trait name. This parameter accepts any type that implements the
|
|||
|
specified trait. In the body of <code>notify</code>, we can call any methods on <code>item</code>
|
|||
|
that come from the <code>Summary</code> trait, such as <code>summarize</code>. We can call <code>notify</code>
|
|||
|
and pass in any instance of <code>NewsArticle</code> or <code>Tweet</code>. Code that calls the
|
|||
|
function with any other type, such as a <code>String</code> or an <code>i32</code>, won’t compile
|
|||
|
because those types don’t implement <code>Summary</code>.</p>
|
|||
|
<h4><a class="header" href="#trait-bound-syntax" id="trait-bound-syntax">Trait Bound Syntax</a></h4>
|
|||
|
<p>The <code>impl Trait</code> syntax works for straightforward cases but is actually
|
|||
|
syntax sugar for a longer form, which is called a <em>trait bound</em>; it looks like
|
|||
|
this:</p>
|
|||
|
<pre><code class="language-rust ignore">pub fn notify<T: Summary>(item: T) {
|
|||
|
println!("Breaking news! {}", item.summarize());
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p>This longer form is equivalent to the example in the previous section but is
|
|||
|
more verbose. We place trait bounds with the declaration of the generic type
|
|||
|
parameter after a colon and inside angle brackets.</p>
|
|||
|
<p>The <code>impl Trait</code> syntax is convenient and makes for more concise code in simple
|
|||
|
cases. The trait bound syntax can express more complexity in other cases. For
|
|||
|
example, we can have two parameters that implement <code>Summary</code>. Using the <code>impl Trait</code> syntax looks like this:</p>
|
|||
|
<pre><code class="language-rust ignore">pub fn notify(item1: impl Summary, item2: impl Summary) {
|
|||
|
</code></pre>
|
|||
|
<p>If we wanted this function to allow <code>item1</code> and <code>item2</code> to have different
|
|||
|
types, using <code>impl Trait</code> would be appropriate (as long as both types implement
|
|||
|
<code>Summary</code>). If we wanted to force both parameters to have the same type, that’s
|
|||
|
only possible to express using a trait bound, like this:</p>
|
|||
|
<pre><code class="language-rust ignore">pub fn notify<T: Summary>(item1: T, item2: T) {
|
|||
|
</code></pre>
|
|||
|
<p>The generic type <code>T</code> specified as the type of the <code>item1</code> and <code>item2</code>
|
|||
|
parameters constrains the function such that the concrete type of the value
|
|||
|
passed as an argument for <code>item1</code> and <code>item2</code> must be the same.</p>
|
|||
|
<h4><a class="header" href="#specifying-multiple-trait-bounds-with-the--syntax" id="specifying-multiple-trait-bounds-with-the--syntax">Specifying Multiple Trait Bounds with the <code>+</code> Syntax</a></h4>
|
|||
|
<p>We can also specify more than one trait bound. Say we wanted <code>notify</code> to use
|
|||
|
display formatting on <code>item</code> as well as the <code>summarize</code> method: we specify in
|
|||
|
the <code>notify</code> definition that <code>item</code> must implement both <code>Display</code> and
|
|||
|
<code>Summary</code>. We can do so using the <code>+</code> syntax:</p>
|
|||
|
<pre><code class="language-rust ignore">pub fn notify(item: impl Summary + Display) {
|
|||
|
</code></pre>
|
|||
|
<p>The <code>+</code> syntax is also valid with trait bounds on generic types:</p>
|
|||
|
<pre><code class="language-rust ignore">pub fn notify<T: Summary + Display>(item: T) {
|
|||
|
</code></pre>
|
|||
|
<p>With the two trait bounds specified, the body of <code>notify</code> can call <code>summarize</code>
|
|||
|
and use <code>{}</code> to format <code>item</code>.</p>
|
|||
|
<h4><a class="header" href="#clearer-trait-bounds-with-where-clauses" id="clearer-trait-bounds-with-where-clauses">Clearer Trait Bounds with <code>where</code> Clauses</a></h4>
|
|||
|
<p>Using too many trait bounds has its downsides. Each generic has its own trait
|
|||
|
bounds, so functions with multiple generic type parameters can contain lots of
|
|||
|
trait bound information between the function’s name and its parameter list,
|
|||
|
making the function signature hard to read. For this reason, Rust has alternate
|
|||
|
syntax for specifying trait bounds inside a <code>where</code> clause after the function
|
|||
|
signature. So instead of writing this:</p>
|
|||
|
<pre><code class="language-rust ignore">fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U) -> i32 {
|
|||
|
</code></pre>
|
|||
|
<p>we can use a <code>where</code> clause, like this:</p>
|
|||
|
<pre><code class="language-rust ignore">fn some_function<T, U>(t: T, u: U) -> i32
|
|||
|
where T: Display + Clone,
|
|||
|
U: Clone + Debug
|
|||
|
{
|
|||
|
</code></pre>
|
|||
|
<p>This function’s signature is less cluttered: the function name, parameter list,
|
|||
|
and return type are close together, similar to a function without lots of trait
|
|||
|
bounds.</p>
|
|||
|
<h3><a class="header" href="#returning-types-that-implement-traits" id="returning-types-that-implement-traits">Returning Types that Implement Traits</a></h3>
|
|||
|
<p>We can also use the <code>impl Trait</code> syntax in the return position to return a
|
|||
|
value of some type that implements a trait, as shown here:</p>
|
|||
|
<pre><code class="language-rust ignore">fn returns_summarizable() -> impl Summary {
|
|||
|
Tweet {
|
|||
|
username: String::from("horse_ebooks"),
|
|||
|
content: String::from("of course, as you probably already know, people"),
|
|||
|
reply: false,
|
|||
|
retweet: false,
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p>By using <code>impl Summary</code> for the return type, we specify that the
|
|||
|
<code>returns_summarizable</code> function returns some type that implements the <code>Summary</code>
|
|||
|
trait without naming the concrete type. In this case, <code>returns_summarizable</code>
|
|||
|
returns a <code>Tweet</code>, but the code calling this function doesn’t know that.</p>
|
|||
|
<p>The ability to return a type that is only specified by the trait it implements
|
|||
|
is especially useful in the context of closures and iterators, which we cover
|
|||
|
in Chapter 13. Closures and iterators create types that only the compiler knows
|
|||
|
or types that are very long to specify. The <code>impl Trait</code> syntax lets you
|
|||
|
concisely specify that a function returns some type that implements the
|
|||
|
<code>Iterator</code> trait without needing to write out a very long type.</p>
|
|||
|
<p>However, you can only use <code>impl Trait</code> if you’re returning a single type. For
|
|||
|
example, this code that returns either a <code>NewsArticle</code> or a <code>Tweet</code> with the
|
|||
|
return type specified as <code>impl Summary</code> wouldn’t work:</p>
|
|||
|
<pre><code class="language-rust ignore does_not_compile">fn returns_summarizable(switch: bool) -> impl Summary {
|
|||
|
if switch {
|
|||
|
NewsArticle {
|
|||
|
headline: String::from("Penguins win the Stanley Cup Championship!"),
|
|||
|
location: String::from("Pittsburgh, PA, USA"),
|
|||
|
author: String::from("Iceburgh"),
|
|||
|
content: String::from("The Pittsburgh Penguins once again are the best
|
|||
|
hockey team in the NHL."),
|
|||
|
}
|
|||
|
} else {
|
|||
|
Tweet {
|
|||
|
username: String::from("horse_ebooks"),
|
|||
|
content: String::from("of course, as you probably already know, people"),
|
|||
|
reply: false,
|
|||
|
retweet: false,
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p>Returning either a <code>NewsArticle</code> or a <code>Tweet</code> isn’t allowed due to restrictions
|
|||
|
around how the <code>impl Trait</code> syntax is implemented in the compiler. We’ll cover
|
|||
|
how to write a function with this behavior in the <a href="ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types">“Using Trait Objects That
|
|||
|
Allow for Values of Different
|
|||
|
Types”</a><!--
|
|||
|
ignore --> section of Chapter 17.</p>
|
|||
|
<h3><a class="header" href="#fixing-the-largest-function-with-trait-bounds" id="fixing-the-largest-function-with-trait-bounds">Fixing the <code>largest</code> Function with Trait Bounds</a></h3>
|
|||
|
<p>Now that you know how to specify the behavior you want to use using the generic
|
|||
|
type parameter’s bounds, let’s return to Listing 10-5 to fix the definition of
|
|||
|
the <code>largest</code> function that uses a generic type parameter! Last time we tried
|
|||
|
to run that code, we received this error:</p>
|
|||
|
<pre><code class="language-text">error[E0369]: binary operation `>` cannot be applied to type `T`
|
|||
|
--> src/main.rs:5:12
|
|||
|
|
|
|||
|
5 | if item > largest {
|
|||
|
| ^^^^^^^^^^^^^^
|
|||
|
|
|
|||
|
= note: an implementation of `std::cmp::PartialOrd` might be missing for `T`
|
|||
|
</code></pre>
|
|||
|
<p>In the body of <code>largest</code> we wanted to compare two values of type <code>T</code> using the
|
|||
|
greater than (<code>></code>) operator. Because that operator is defined as a default
|
|||
|
method on the standard library trait <code>std::cmp::PartialOrd</code>, we need to specify
|
|||
|
<code>PartialOrd</code> in the trait bounds for <code>T</code> so the <code>largest</code> function can work on
|
|||
|
slices of any type that we can compare. We don’t need to bring <code>PartialOrd</code>
|
|||
|
into scope because it’s in the prelude. Change the signature of <code>largest</code> to
|
|||
|
look like this:</p>
|
|||
|
<pre><code class="language-rust ignore">fn largest<T: PartialOrd>(list: &[T]) -> T {
|
|||
|
</code></pre>
|
|||
|
<p>This time when we compile the code, we get a different set of errors:</p>
|
|||
|
<pre><code class="language-text">error[E0508]: cannot move out of type `[T]`, a non-copy slice
|
|||
|
--> src/main.rs:2:23
|
|||
|
|
|
|||
|
2 | let mut largest = list[0];
|
|||
|
| ^^^^^^^
|
|||
|
| |
|
|||
|
| cannot move out of here
|
|||
|
| help: consider using a reference instead: `&list[0]`
|
|||
|
|
|||
|
error[E0507]: cannot move out of borrowed content
|
|||
|
--> src/main.rs:4:9
|
|||
|
|
|
|||
|
4 | for &item in list.iter() {
|
|||
|
| ^----
|
|||
|
| ||
|
|||
|
| |hint: to prevent move, use `ref item` or `ref mut item`
|
|||
|
| cannot move out of borrowed content
|
|||
|
</code></pre>
|
|||
|
<p>The key line in this error is <code>cannot move out of type [T], a non-copy slice</code>.
|
|||
|
With our non-generic versions of the <code>largest</code> function, we were only trying to
|
|||
|
find the largest <code>i32</code> or <code>char</code>. As discussed in the <a href="ch04-01-what-is-ownership.html#stack-only-data-copy">“Stack-Only Data:
|
|||
|
Copy”</a><!-- ignore --> section in Chapter 4, types like
|
|||
|
<code>i32</code> and <code>char</code> that have a known size can be stored on the stack, so they
|
|||
|
implement the <code>Copy</code> trait. But when we made the <code>largest</code> function generic,
|
|||
|
it became possible for the <code>list</code> parameter to have types in it that don’t
|
|||
|
implement the <code>Copy</code> trait. Consequently, we wouldn’t be able to move the
|
|||
|
value out of <code>list[0]</code> and into the <code>largest</code> variable, resulting in this
|
|||
|
error.</p>
|
|||
|
<p>To call this code with only those types that implement the <code>Copy</code> trait, we can
|
|||
|
add <code>Copy</code> to the trait bounds of <code>T</code>! Listing 10-15 shows the complete code of
|
|||
|
a generic <code>largest</code> function that will compile as long as the types of the
|
|||
|
values in the slice that we pass into the function implement the <code>PartialOrd</code>
|
|||
|
<em>and</em> <code>Copy</code> traits, like <code>i32</code> and <code>char</code> do.</p>
|
|||
|
<p><span class="filename">Filename: src/main.rs</span></p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">fn largest<T: PartialOrd + Copy>(list: &[T]) -> T {
|
|||
|
let mut largest = list[0];
|
|||
|
|
|||
|
for &item in list.iter() {
|
|||
|
if item > largest {
|
|||
|
largest = item;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
largest
|
|||
|
}
|
|||
|
|
|||
|
fn main() {
|
|||
|
let number_list = vec![34, 50, 25, 100, 65];
|
|||
|
|
|||
|
let result = largest(&number_list);
|
|||
|
println!("The largest number is {}", result);
|
|||
|
|
|||
|
let char_list = vec!['y', 'm', 'a', 'q'];
|
|||
|
|
|||
|
let result = largest(&char_list);
|
|||
|
println!("The largest char is {}", result);
|
|||
|
}
|
|||
|
</code></pre></pre>
|
|||
|
<p><span class="caption">Listing 10-15: A working definition of the <code>largest</code>
|
|||
|
function that works on any generic type that implements the <code>PartialOrd</code> and
|
|||
|
<code>Copy</code> traits</span></p>
|
|||
|
<p>If we don’t want to restrict the <code>largest</code> function to the types that implement
|
|||
|
the <code>Copy</code> trait, we could specify that <code>T</code> has the trait bound <code>Clone</code> instead
|
|||
|
of <code>Copy</code>. Then we could clone each value in the slice when we want the
|
|||
|
<code>largest</code> function to have ownership. Using the <code>clone</code> function means we’re
|
|||
|
potentially making more heap allocations in the case of types that own heap
|
|||
|
data like <code>String</code>, and heap allocations can be slow if we’re working with
|
|||
|
large amounts of data.</p>
|
|||
|
<p>Another way we could implement <code>largest</code> is for the function to return a
|
|||
|
reference to a <code>T</code> value in the slice. If we change the return type to <code>&T</code>
|
|||
|
instead of <code>T</code>, thereby changing the body of the function to return a
|
|||
|
reference, we wouldn’t need the <code>Clone</code> or <code>Copy</code> trait bounds and we could
|
|||
|
avoid heap allocations. Try implementing these alternate solutions on your own!</p>
|
|||
|
<h3><a class="header" href="#using-trait-bounds-to-conditionally-implement-methods" id="using-trait-bounds-to-conditionally-implement-methods">Using Trait Bounds to Conditionally Implement Methods</a></h3>
|
|||
|
<p>By using a trait bound with an <code>impl</code> block that uses generic type parameters,
|
|||
|
we can implement methods conditionally for types that implement the specified
|
|||
|
traits. For example, the type <code>Pair<T></code> in Listing 10-16 always implements the
|
|||
|
<code>new</code> function. But <code>Pair<T></code> only implements the <code>cmp_display</code> method if its
|
|||
|
inner type <code>T</code> implements the <code>PartialOrd</code> trait that enables comparison <em>and</em>
|
|||
|
the <code>Display</code> trait that enables printing.</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>use std::fmt::Display;
|
|||
|
|
|||
|
struct Pair<T> {
|
|||
|
x: T,
|
|||
|
y: T,
|
|||
|
}
|
|||
|
|
|||
|
impl<T> Pair<T> {
|
|||
|
fn new(x: T, y: T) -> Self {
|
|||
|
Self {
|
|||
|
x,
|
|||
|
y,
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
impl<T: Display + PartialOrd> Pair<T> {
|
|||
|
fn cmp_display(&self) {
|
|||
|
if self.x >= self.y {
|
|||
|
println!("The largest member is x = {}", self.x);
|
|||
|
} else {
|
|||
|
println!("The largest member is y = {}", self.y);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p><span class="caption">Listing 10-16: Conditionally implement methods on a
|
|||
|
generic type depending on trait bounds</span></p>
|
|||
|
<p>We can also conditionally implement a trait for any type that implements
|
|||
|
another trait. Implementations of a trait on any type that satisfies the trait
|
|||
|
bounds are called <em>blanket implementations</em> and are extensively used in the
|
|||
|
Rust standard library. For example, the standard library implements the
|
|||
|
<code>ToString</code> trait on any type that implements the <code>Display</code> trait. The <code>impl</code>
|
|||
|
block in the standard library looks similar to this code:</p>
|
|||
|
<pre><code class="language-rust ignore">impl<T: Display> ToString for T {
|
|||
|
// --snip--
|
|||
|
}
|
|||
|
</code></pre>
|
|||
|
<p>Because the standard library has this blanket implementation, we can call the
|
|||
|
<code>to_string</code> method defined by the <code>ToString</code> trait on any type that implements
|
|||
|
the <code>Display</code> trait. For example, we can turn integers into their corresponding
|
|||
|
<code>String</code> values like this because integers implement <code>Display</code>:</p>
|
|||
|
<pre><pre class="playpen"><code class="language-rust">
|
|||
|
<span class="boring">#![allow(unused_variables)]
|
|||
|
</span><span class="boring">fn main() {
|
|||
|
</span>let s = 3.to_string();
|
|||
|
<span class="boring">}
|
|||
|
</span></code></pre></pre>
|
|||
|
<p>Blanket implementations appear in the documentation for the trait in the
|
|||
|
“Implementors” section.</p>
|
|||
|
<p>Traits and trait bounds let us write code that uses generic type parameters to
|
|||
|
reduce duplication but also specify to the compiler that we want the generic
|
|||
|
type to have particular behavior. The compiler can then use the trait bound
|
|||
|
information to check that all the concrete types used with our code provide the
|
|||
|
correct behavior. In dynamically typed languages, we would get an error at
|
|||
|
runtime if we called a method on a type which didn’t implement the type which
|
|||
|
defines the method. But Rust moves these errors to compile time so we’re forced
|
|||
|
to fix the problems before our code is even able to run. Additionally, we don’t
|
|||
|
have to write code that checks for behavior at runtime because we’ve already
|
|||
|
checked at compile time. Doing so improves performance without having to give
|
|||
|
up the flexibility of generics.</p>
|
|||
|
<p>Another kind of generic that we’ve already been using is called <em>lifetimes</em>.
|
|||
|
Rather than ensuring that a type has the behavior we want, lifetimes ensure
|
|||
|
that references are valid as long as we need them to be. Let’s look at how
|
|||
|
lifetimes do that.</p>
|
|||
|
|
|||
|
</main>
|
|||
|
|
|||
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|||
|
<!-- Mobile navigation buttons -->
|
|||
|
|
|||
|
<a rel="prev" href="ch10-01-syntax.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="ch10-03-lifetime-syntax.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="ch10-01-syntax.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="ch10-03-lifetime-syntax.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>
|