This commit is contained in:
sunface
2025-04-25 12:48:03 +08:00
commit 987c59ed75
143 changed files with 44104 additions and 0 deletions

View File

@ -0,0 +1,487 @@
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Advanced Traits - Rust By Practice</title>
<!-- Custom HTML head -->
<meta name="description" content="Learn Rust with Example, Exercise and real Practice, written with ❤️ by https://course.rs team">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="../theme/style1.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust By Practice</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/sunface/rust-by-practice" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/sunface/rust-by-practice/edit/master/en/src/generics-traits/advanced-traits.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="advance-traits"><a class="header" href="#advance-traits">Advance Traits</a></h1>
<h2 id="associated-types"><a class="header" href="#associated-types">Associated types</a></h2>
<p>The use of "Associated types" improves the overall readability of code by moving inner types locally into a trait as output types. For example :</p>
<pre><pre class="playground"><code class="language-rust edition2021"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>pub trait CacheableItem: Clone + Default + fmt::Debug + Decodable + Encodable {
type Address: AsRef&lt;[u8]&gt; + Clone + fmt::Debug + Eq + Hash;
fn is_null(&amp;self) -&gt; bool;
}
<span class="boring">}</span></code></pre></pre>
<p>Using of <code>Address</code> is much more clearer and convenient than <code>AsRef&lt;[u8]&gt; + Clone + fmt::Debug + Eq + Hash</code>.</p>
<ol>
<li>🌟🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">
struct Container(i32, i32);
// USING associated types to re-implement trait Contains.
// trait Contains {
// type A;
// type B;
trait Contains&lt;A, B&gt; {
fn contains(&amp;self, _: &amp;A, _: &amp;B) -&gt; bool;
fn first(&amp;self) -&gt; i32;
fn last(&amp;self) -&gt; i32;
}
impl Contains&lt;i32, i32&gt; for Container {
fn contains(&amp;self, number_1: &amp;i32, number_2: &amp;i32) -&gt; bool {
(&amp;self.0 == number_1) &amp;&amp; (&amp;self.1 == number_2)
}
// Grab the first number.
fn first(&amp;self) -&gt; i32 { self.0 }
// Grab the last number.
fn last(&amp;self) -&gt; i32 { self.1 }
}
fn difference&lt;A, B, C: Contains&lt;A, B&gt;&gt;(container: &amp;C) -&gt; i32 {
container.last() - container.first()
}
fn main() {
let number_1 = 3;
let number_2 = 10;
let container = Container(number_1, number_2);
println!("Does container contain {} and {}: {}",
&amp;number_1, &amp;number_2,
container.contains(&amp;number_1, &amp;number_2));
println!("First number: {}", container.first());
println!("Last number: {}", container.last());
println!("The difference is: {}", difference(&amp;container));
}</code></pre></pre>
<h2 id="default-generic-type-parameters"><a class="header" href="#default-generic-type-parameters">Default Generic Type Parameters</a></h2>
<p>When we use generic type parameters, we can specify a default concrete type for the generic type. This eliminates the need for implementors of the trait to specify a concrete type if the default type works.</p>
<ol start="2">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">
use std::ops::Sub;
#[derive(Debug, PartialEq)]
struct Point&lt;T&gt; {
x: T,
y: T,
}
// FILL in the blank in three ways: two of them use the default generic parameters, the other one not.
// Notice that the implementation uses the associated type `Output`.
impl __ {
type Output = Self;
fn sub(self, other: Self) -&gt; Self::Output {
Point {
x: self.x - other.x,
y: self.y - other.y,
}
}
}
fn main() {
assert_eq!(Point { x: 2, y: 3 } - Point { x: 1, y: 0 },
Point { x: 1, y: 3 });
println!("Success!");
}</code></pre></pre>
<h2 id="fully-qualified-syntax"><a class="header" href="#fully-qualified-syntax">Fully Qualified Syntax</a></h2>
<p>Nothing in Rust prevents a trait from having a method with the same name as another traits method, nor does Rust prevent you from implementing both traits on one type. Its also possible to implement a method directly on the type with the same name as methods from traits.</p>
<p>When calling methods with the same name, we have to use Fully Qualified Syntax.</p>
<h4 id="example"><a class="header" href="#example">Example</a></h4>
<pre><pre class="playground"><code class="language-rust editable edition2021">trait UsernameWidget {
// Get the selected username out of this widget
fn get(&amp;self) -&gt; String;
}
trait AgeWidget {
// Get the selected age out of this widget
fn get(&amp;self) -&gt; u8;
}
// A form with both a UsernameWidget and an AgeWidget.
struct Form {
username: String,
age: u8,
}
impl UsernameWidget for Form {
fn get(&amp;self) -&gt; String {
self.username.clone()
}
}
impl AgeWidget for Form {
fn get(&amp;self) -&gt; u8 {
self.age
}
}
fn main() {
let form = Form{
username: "rustacean".to_owned(),
age: 28,
};
// If you uncomment this line, you'll get an error saying
// "multiple `get` found". Because, after all, there are multiple methods
// named `get`.
// println!("{}", form.get());
let username = UsernameWidget::get(&amp;form);
assert_eq!("rustacean".to_owned(), username);
let age = AgeWidget::get(&amp;form); // You can also use `&lt;Form as AgeWidget&gt;::get`
assert_eq!(28, age);
println!("Success!");
}</code></pre></pre>
<h4 id="exercise"><a class="header" href="#exercise">Exercise</a></h4>
<ol start="3">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">trait Pilot {
fn fly(&amp;self) -&gt; String;
}
trait Wizard {
fn fly(&amp;self) -&gt; String;
}
struct Human;
impl Pilot for Human {
fn fly(&amp;self) -&gt; String {
String::from("This is your captain speaking.")
}
}
impl Wizard for Human {
fn fly(&amp;self) -&gt; String {
String::from("Up!")
}
}
impl Human {
fn fly(&amp;self) -&gt; String {
String::from("*waving arms furiously*")
}
}
fn main() {
let person = Human;
assert_eq!(__, "This is your captain speaking.");
assert_eq!(__, "Up!");
assert_eq!(__, "*waving arms furiously*");
println!("Success!");
}</code></pre></pre>
<h2 id="supertraits"><a class="header" href="#supertraits">Supertraits</a></h2>
<p>Sometimes, you might need one trait to use another traits functionality( like the "inheritance" in other languages ). In this case, you need to rely on the dependent trait also being implemented. The trait you rely on is a <code>supertrait</code> of the trait youre implementing.</p>
<ol start="4">
<li>🌟🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">
trait Person {
fn name(&amp;self) -&gt; String;
}
// Person is a supertrait of Student.
// Implementing Student requires you to also impl Person.
trait Student: Person {
fn university(&amp;self) -&gt; String;
}
trait Programmer {
fn fav_language(&amp;self) -&gt; String;
}
// CompSciStudent (computer science student) is a subtrait of both Programmer
// and Student. Implementing CompSciStudent requires you to impl both supertraits.
trait CompSciStudent: Programmer + Student {
fn git_username(&amp;self) -&gt; String;
}
fn comp_sci_student_greeting(student: &amp;dyn CompSciStudent) -&gt; String {
format!(
"My name is {} and I attend {}. My favorite language is {}. My Git username is {}",
student.name(),
student.university(),
student.fav_language(),
student.git_username()
)
}
struct CSStudent {
name: String,
university: String,
fav_language: String,
git_username: String
}
// IMPLEMENT the necessary traits for CSStudent to make the code work
impl ...
fn main() {
let student = CSStudent {
name: "Sunfei".to_string(),
university: "XXX".to_string(),
fav_language: "Rust".to_string(),
git_username: "sunface".to_string()
};
// FILL in the blank
println!("{}", comp_sci_student_greeting(__));
}</code></pre></pre>
<h2 id="orphan-rules"><a class="header" href="#orphan-rules">Orphan Rules</a></h2>
<p>We cant implement external traits on external types. For example, we cant implement the <code>Display</code> trait on <code>Vec&lt;T&gt;</code> within our own crate, because <code>Display</code> and <code>Vec&lt;T&gt;</code> are defined in the standard library and arent local to our crate.</p>
<p>This restriction is often called the orphan rule, so named because the parent type is not present. This rule ensures that other peoples code cant break your code and vice versa.</p>
<p>Its possible to get around this restriction using the newtype pattern, which involves creating a new type in a tuple struct.</p>
<ol start="5">
<li>🌟🌟</li>
</ol>
<pre><pre class="playground"><code class="language-rust editable edition2021">use std::fmt;
// DEFINE a newtype `Pretty` here
impl fmt::Display for Pretty {
fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
write!(f, "\"{}\"", self.0.clone() + ", world")
}
}
fn main() {
let w = Pretty("hello".to_string());
println!("w = {}", w);
}</code></pre></pre>
<blockquote>
<p>You can find the solutions <a href="https://github.com/sunface/rust-by-practice">here</a>(under the solutions path), but only use it when you need it :)</p>
</blockquote>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../generics-traits/trait-object.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../collections/intro.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../generics-traits/trait-object.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../collections/intro.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../ace.js"></script>
<script src="../editor.js"></script>
<script src="../mode-rust.js"></script>
<script src="../theme-dawn.js"></script>
<script src="../theme-tomorrow_night.js"></script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
<script src="../assets/custom3.js"></script>
<script src="../assets/lang1.js"></script>
</div>
</body>
</html>