You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trpl-zh-cn/ch05-03-method-syntax.html

457 lines
45 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE HTML>
<html lang="zh-CN" class="light" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>方法语法 - Rust 程序设计语言 简体中文版</title>
<!-- Custom HTML head -->
<meta name="description" content="Rust 程序设计语言 简体中文版">
<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" 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 class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
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>
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('light')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var 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';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="title-page.html">Rust 程序设计语言</a></li><li class="chapter-item expanded affix "><a href="foreword.html">前言</a></li><li class="chapter-item expanded affix "><a href="ch00-00-introduction.html">简介</a></li><li class="chapter-item expanded "><a href="ch01-00-getting-started.html"><strong aria-hidden="true">1.</strong> 入门指南</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch01-01-installation.html"><strong aria-hidden="true">1.1.</strong> 安装</a></li><li class="chapter-item expanded "><a href="ch01-02-hello-world.html"><strong aria-hidden="true">1.2.</strong> Hello, World!</a></li><li class="chapter-item expanded "><a href="ch01-03-hello-cargo.html"><strong aria-hidden="true">1.3.</strong> Hello, Cargo!</a></li></ol></li><li class="chapter-item expanded "><a href="ch02-00-guessing-game-tutorial.html"><strong aria-hidden="true">2.</strong> 写个猜数字游戏</a></li><li class="chapter-item expanded "><a href="ch03-00-common-programming-concepts.html"><strong aria-hidden="true">3.</strong> 常见编程概念</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch03-01-variables-and-mutability.html"><strong aria-hidden="true">3.1.</strong> 变量与可变性</a></li><li class="chapter-item expanded "><a href="ch03-02-data-types.html"><strong aria-hidden="true">3.2.</strong> 数据类型</a></li><li class="chapter-item expanded "><a href="ch03-03-how-functions-work.html"><strong aria-hidden="true">3.3.</strong> 函数</a></li><li class="chapter-item expanded "><a href="ch03-04-comments.html"><strong aria-hidden="true">3.4.</strong> 注释</a></li><li class="chapter-item expanded "><a href="ch03-05-control-flow.html"><strong aria-hidden="true">3.5.</strong> 控制流</a></li></ol></li><li class="chapter-item expanded "><a href="ch04-00-understanding-ownership.html"><strong aria-hidden="true">4.</strong> 认识所有权</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch04-01-what-is-ownership.html"><strong aria-hidden="true">4.1.</strong> 什么是所有权?</a></li><li class="chapter-item expanded "><a href="ch04-02-references-and-borrowing.html"><strong aria-hidden="true">4.2.</strong> 引用与借用</a></li><li class="chapter-item expanded "><a href="ch04-03-slices.html"><strong aria-hidden="true">4.3.</strong> Slice 类型</a></li></ol></li><li class="chapter-item expanded "><a href="ch05-00-structs.html"><strong aria-hidden="true">5.</strong> 使用结构体组织相关联的数据</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch05-01-defining-structs.html"><strong aria-hidden="true">5.1.</strong> 结构体的定义和实例化</a></li><li class="chapter-item expanded "><a href="ch05-02-example-structs.html"><strong aria-hidden="true">5.2.</strong> 结构体示例程序</a></li><li class="chapter-item expanded "><a href="ch05-03-method-syntax.html" class="active"><strong aria-hidden="true">5.3.</strong> 方法语法</a></li></ol></li><li class="chapter-item expanded "><a href="ch06-00-enums.html"><strong aria-hidden="true">6.</strong> 枚举和模式匹配</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch06-01-defining-an-enum.html"><strong aria-hidden="true">6.1.</strong> 枚举的定义</a></li><li class="chapter-item expanded "><a href="ch06-02-match.html"><strong aria-hidden="true">6.2.</strong> match 控制流结构</a></li><li class="chapter-item expanded "><a href="ch06-03-if-let.html"><strong aria-hidden="true">6.3.</strong> if let 简洁控制流</a></li></ol></li><li class="chapter-item expanded "><a href="ch07-00-managing-growing-projects-with-packages-crates-and-modules.html"><strong aria-hidden="true">7.</strong> 使用包、Crate 和模块管理不断增长的项目</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch07-01-packages-and-crates.html"><strong aria-hidden="true">7.1.</strong> 包和 Crate</a></li><li class="chapter-item expanded "><a href="ch07-02-defining-modules-to-control-scope-and-privacy.html"><strong aria-hidden="true">7.2.</strong> 定义模块来控制作用域与私有性</a></li><li class="chapter-item expanded "><a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html"><strong aria-hidden="true">7.3.</strong> 引用模块项目的路径</a></li><li class="chapter-item expanded "><a href="ch07-04-bringing-paths-into-scope-with-the-use-keyword.html"><strong aria-hidden="true">7.4.</strong> 使用 use 关键字将路径引入作用域</a></li><li class="chapter-item expanded "><a href="ch07-05-separating-modules-into-different-files.html"><strong aria-hidden="true">7.5.</strong> 将模块拆分成多个文件</a></li></ol></li><li class="chapter-item expanded "><a href="ch08-00-common-collections.html"><strong aria-hidden="true">8.</strong> 常见集合</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch08-01-vectors.html"><strong aria-hidden="true">8.1.</strong> 使用 Vector 储存列表</a></li><li class="chapter-item expanded "><a href="ch08-02-strings.html"><strong aria-hidden="true">8.2.</strong> 使用字符串储存 UTF-8 编码的文本</a></li><li class="chapter-item expanded "><a href="ch08-03-hash-maps.html"><strong aria-hidden="true">8.3.</strong> 使用 Hash Map 储存键值对</a></li></ol></li><li class="chapter-item expanded "><a href="ch09-00-error-handling.html"><strong aria-hidden="true">9.</strong> 错误处理</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch09-01-unrecoverable-errors-with-panic.html"><strong aria-hidden="true">9.1.</strong> 用 panic! 处理不可恢复的错误</a></li><li class="chapter-item expanded "><a href="ch09-02-recoverable-errors-with-result.html"><strong aria-hidden="true">9.2.</strong> 用 Result 处理可恢复的错误</a></li><li class="chapter-item expanded "><a href="ch09-03-to-panic-or-not-to-panic.html"><strong aria-hidden="true">9.3.</strong> 要不要 panic!</a></li></ol></li><li class="chapter-item expanded "><a href="ch10-00-generics.html"><strong aria-hidden="true">10.</strong> 泛型、Trait 和生命周期</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch10-01-syntax.html"><strong aria-hidden="true">10.1.</strong> 泛型数据类型</a></li><li class="chapter-item expanded "><a href="ch10-02-traits.html"><strong aria-hidden="true">10.2.</strong> Trait定义共同行为</a></li><li class="chapter-item expanded "><a href="ch10-03-lifetime-syntax.html"><strong aria-hidden="true">10.3.</strong> 生命周期确保引用有效</a></li></ol></li><li class="chapter-item expanded "><a href="ch11-00-testing.html"><strong aria-hidden="true">11.</strong> 编写自动化测试</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch11-01-writing-tests.html"><strong aria-hidden="true">11.1.</strong> 如何编写测试</a></li><li class="chapter-item expanded "><a href="ch11-02-running-tests.html"><strong aria-hidden="true">11.2.</strong> 控制测试如何运行</a></li><li class="chapter-item expanded "><a href="ch11-03-test-organization.html"><strong aria-hidden="true">11.3.</strong> 测试的组织结构</a></li></ol></li><li class="chapter-item expanded "><a href="ch12-00-an-io-project.html"><strong aria-hidden="true">12.</strong> 一个 I/O 项目:构建命令行程序</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch12-01-accepting-command-line-arguments.html"><strong aria-hidden="true">12.1.</strong> 接受命令行参数</a></li><li class="chapter-item expanded "><a href="ch12-02-reading-a-file.html"><strong aria-hidden="true">12.2.</strong> 读取文件</a></li><li class="chapter-item expanded "><a href="ch12-03-improving-error-handling-and-modularity.html"><strong aria-hidden="true">12.3.</strong> 重构以改进模块化与错误处理</a></li><li class="chapter-item expanded "><a href="ch12-04-testing-the-librarys-functionality.html"><strong aria-hidden="true">12.4.</strong> 采用测试驱动开发完善库的功能</a></li><li class="chapter-item expanded "><a href="ch12-05-working-with-environment-variables.html"><strong aria-hidden="true">12.5.</strong> 处理环境变量</a></li><li class="chapter-item expanded "><a href="ch12-06-writing-to-stderr-instead-of-stdout.html"><strong aria-hidden="true">12.6.</strong> 将错误信息输出到标准错误而不是标准输出</a></li></ol></li><li class="chapter-item expanded "><a href="ch13-00-functional-features.html"><strong aria-hidden="true">13.</strong> Rust 中的函数式语言功能:迭代器与闭包</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch13-01-closures.html"><strong aria-hidden="true">13.1.</strong> 闭包:可以捕获其环境的匿名函数</a></li><li class="chapter-item expanded "><a href="ch13-02-iterators.html"><strong aria-hidden="true">13.2.</strong> 使用迭代器处理元素序列</a></li><li class="chapter-item expanded "><a href="ch13-03-improving-our-io-project.html"><strong aria-hidden="true">13.3.</strong> 改进之前的 I/O 项目</a></li><li class="chapter-item expanded "><a href="ch13-04-performance.html"><strong aria-hidden="true">13.4.</strong> 性能比较:循环对迭代器</a></li></ol></li><li class="chapter-item expanded "><a href="ch14-00-more-about-cargo.html"><strong aria-hidden="true">14.</strong> 更多关于 Cargo 和 Crates.io 的内容</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch14-01-release-profiles.html"><strong aria-hidden="true">14.1.</strong> 采用发布配置自定义构建</a></li><li class="chapter-item expanded "><a href="ch14-02-publishing-to-crates-io.html"><strong aria-hidden="true">14.2.</strong> 将 crate 发布到 Crates.io</a></li><li class="chapter-item expanded "><a href="ch14-03-cargo-workspaces.html"><strong aria-hidden="true">14.3.</strong> Cargo 工作空间</a></li><li class="chapter-item expanded "><a href="ch14-04-installing-binaries.html"><strong aria-hidden="true">14.4.</strong> 使用 cargo install 安装二进制文件</a></li><li class="chapter-item expanded "><a href="ch14-05-extending-cargo.html"><strong aria-hidden="true">14.5.</strong> Cargo 自定义扩展命令</a></li></ol></li><li class="chapter-item expanded "><a href="ch15-00-smart-pointers.html"><strong aria-hidden="true">15.</strong> 智能指针</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch15-01-box.html"><strong aria-hidden="true">15.1.</strong> 使用 Box&lt;T&gt; 指向堆上数据</a></li><li class="chapter-item expanded "><a href="ch15-02-deref.html"><strong aria-hidden="true">15.2.</strong> 使用 Deref Trait 将智能指针当作常规引用处理</a></li><li class="chapter-item expanded "><a href="ch15-03-drop.html"><strong aria-hidden="true">15.3.</strong> 使用 Drop Trait 运行清理代码</a></li><li class="chapter-item expanded "><a href="ch15-04-rc.html"><strong aria-hidden="true">15.4.</strong> Rc&lt;T&gt; 引用计数智能指针</a></li><li class="chapter-item expanded "><a href="ch15-05-interior-mutability.html"><strong aria-hidden="true">15.5.</strong> RefCell&lt;T&gt; 与内部可变性模式</a></li><li class="chapter-item expanded "><a href="ch15-06-reference-cycles.html"><strong aria-hidden="true">15.6.</strong> 引用循环会导致内存泄漏</a></li></ol></li><li class="chapter-item expanded "><a href="ch16-00-concurrency.html"><strong aria-hidden="true">16.</strong> 无畏并发</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch16-01-threads.html"><strong aria-hidden="true">16.1.</strong> 使用线程同时地运行代码</a></li><li class="chapter-item expanded "><a href="ch16-02-message-passing.html"><strong aria-hidden="true">16.2.</strong> 使用消息传递在线程间通信</a></li><li class="chapter-item expanded "><a href="ch16-03-shared-state.html"><strong aria-hidden="true">16.3.</strong> 共享状态并发</a></li><li class="chapter-item expanded "><a href="ch16-04-extensible-concurrency-sync-and-send.html"><strong aria-hidden="true">16.4.</strong> 使用 Sync 与 Send Traits 的可扩展并发</a></li></ol></li><li class="chapter-item expanded "><a href="ch17-00-oop.html"><strong aria-hidden="true">17.</strong> Rust 的面向对象编程特性</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch17-01-what-is-oo.html"><strong aria-hidden="true">17.1.</strong> 面向对象语言的特点</a></li><li class="chapter-item expanded "><a href="ch17-02-trait-objects.html"><strong aria-hidden="true">17.2.</strong> 顾及不同类型值的 trait 对象</a></li><li class="chapter-item expanded "><a href="ch17-03-oo-design-patterns.html"><strong aria-hidden="true">17.3.</strong> 面向对象设计模式的实现</a></li></ol></li><li class="chapter-item expanded "><a href="ch18-00-patterns.html"><strong aria-hidden="true">18.</strong> 模式与模式匹配</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch18-01-all-the-places-for-patterns.html"><strong aria-hidden="true">18.1.</strong> 所有可能会用到模式的位置</a></li><li class="chapter-item expanded "><a href="ch18-02-refutability.html"><strong aria-hidden="true">18.2.</strong> Refutability可反驳性: 模式是否会匹配失效</a></li><li class="chapter-item expanded "><a href="ch18-03-pattern-syntax.html"><strong aria-hidden="true">18.3.</strong> 模式语法</a></li></ol></li><li class="chapter-item expanded "><a href="ch19-00-advanced-features.html"><strong aria-hidden="true">19.</strong> 高级特征</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch19-01-unsafe-rust.html"><strong aria-hidden="true">19.1.</strong> 不安全的 Rust</a></li><li class="chapter-item expanded "><a href="ch19-03-advanced-traits.html"><strong aria-hidden="true">19.2.</strong> 高级 trait</a></li><li class="chapter-item expanded "><a href="ch19-04-advanced-types.html"><strong aria-hidden="true">19.3.</strong> 高级类型</a></li><li class="chapter-item expanded "><a href="ch19-05-advanced-functions-and-closures.html"><strong aria-hidden="true">19.4.</strong> 高级函数与闭包</a></li><li class="chapter-item expanded "><a href="ch19-06-macros.html"><strong aria-hidden="true">19.5.</strong></a></li></ol></li><li class="chapter-item expanded "><a href="ch20-00-final-project-a-web-server.html"><strong aria-hidden="true">20.</strong> 最后的项目:构建多线程 web server</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch20-01-single-threaded.html"><strong aria-hidden="true">20.1.</strong> 建立单线程 web server</a></li><li class="chapter-item expanded "><a href="ch20-02-multithreaded.html"><strong aria-hidden="true">20.2.</strong> 将单线程 server 变为多线程 server</a></li><li class="chapter-item expanded "><a href="ch20-03-graceful-shutdown-and-cleanup.html"><strong aria-hidden="true">20.3.</strong> 优雅停机与清理</a></li></ol></li><li class="chapter-item expanded "><a href="appendix-00.html"><strong aria-hidden="true">21.</strong> 附录</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="appendix-01-keywords.html"><strong aria-hidden="true">21.1.</strong> A - 关键字</a></li><li class="chapter-item expanded "><a href="appendix-02-operators.html"><strong aria-hidden="true">21.2.</strong> B - 运算符与符号</a></li><li class="chapter-item expanded "><a href="appendix-03-derivable-traits.html"><strong aria-hidden="true">21.3.</strong> C - 可派生的 trait</a></li><li class="chapter-item expanded "><a href="appendix-04-useful-development-tools.html"><strong aria-hidden="true">21.4.</strong> D - 实用开发工具</a></li><li class="chapter-item expanded "><a href="appendix-05-editions.html"><strong aria-hidden="true">21.5.</strong> E - 版本</a></li><li class="chapter-item expanded "><a href="appendix-06-translation.html"><strong aria-hidden="true">21.6.</strong> F - 本书译本</a></li><li class="chapter-item expanded "><a href="appendix-07-nightly-rust.html"><strong aria-hidden="true">21.7.</strong> G - Rust 是如何开发的与 “Nightly Rust”</a></li></ol></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<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="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 程序设计语言 简体中文版</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/KaiserY/trpl-zh-cn/tree/main" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/KaiserY/trpl-zh-cn/edit/main/src/ch05-03-method-syntax.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>
<h2 id="方法语法"><a class="header" href="#方法语法">方法语法</a></h2>
<blockquote>
<p><a href="https://github.com/rust-lang/book/blob/main/src/ch05-03-method-syntax.md">ch05-03-method-syntax.md</a>
<br>
commit d339373a838fd312a8a9bcc9487e1ffbc9e1582f</p>
</blockquote>
<p><strong>方法</strong>method与函数类似它们使用 <code>fn</code> 关键字和名称声明,可以拥有参数和返回值,同时包含在某处调用该方法时会执行的代码。不过方法与函数是不同的,因为它们在结构体的上下文中被定义(或者是枚举或 trait 对象的上下文,将分别在<a href="ch06-00-enums.html">第六章</a><a href="ch17-02-trait-objects.html">第十七章</a>讲解),并且它们第一个参数总是 <code>self</code>,它代表调用该方法的结构体实例。</p>
<h3 id="定义方法"><a class="header" href="#定义方法">定义方法</a></h3>
<p>让我们把前面实现的获取一个 <code>Rectangle</code> 实例作为参数的 <code>area</code> 函数,改写成一个定义于 <code>Rectangle</code> 结构体上的 <code>area</code> 方法,如示例 5-13 所示:</p>
<p><span class="filename">文件名src/main.rs</span></p>
<pre><pre class="playground"><code class="language-rust">#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
impl Rectangle {
fn area(&amp;self) -&gt; u32 {
self.width * self.height
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
&quot;The area of the rectangle is {} square pixels.&quot;,
rect1.area()
);
}</code></pre></pre>
<p><span class="caption">示例 5-13<code>Rectangle</code> 结构体上定义 <code>area</code> 方法</span></p>
<p>为了使函数定义于 <code>Rectangle</code> 的上下文中,我们开始了一个 <code>impl</code> 块(<code>impl</code><em>implementation</em> 的缩写),这个 <code>impl</code> 块中的所有内容都将与 <code>Rectangle</code> 类型相关联。接着将 <code>area</code> 函数移动到 <code>impl</code> 大括号中,并将签名中的第一个(在这里也是唯一一个)参数和函数体中其他地方的对应参数改成 <code>self</code>。然后在 <code>main</code> 中将我们先前调用 <code>area</code> 方法并传递 <code>rect1</code> 作为参数的地方,改成使用 <strong>方法语法</strong><em>method syntax</em>)在 <code>Rectangle</code> 实例上调用 <code>area</code> 方法。方法语法获取一个实例并加上一个点号,后跟方法名、圆括号以及任何参数。</p>
<p><code>area</code> 的签名中,使用 <code>&amp;self</code> 来替代 <code>rectangle: &amp;Rectangle</code><code>&amp;self</code> 实际上是 <code>self: &amp;Self</code> 的缩写。在一个 <code>impl</code> 块中,<code>Self</code> 类型是 <code>impl</code> 块的类型的别名。方法的第一个参数必须有一个名为 <code>self</code><code>Self</code> 类型的参数,所以 Rust 让你在第一个参数位置上只用 <code>self</code> 这个名字来简化。注意,我们仍然需要在 <code>self</code> 前面使用 <code>&amp;</code> 来表示这个方法借用了 <code>Self</code> 实例,就像我们在 <code>rectangle: &amp;Rectangle</code> 中做的那样。方法可以选择获得 <code>self</code> 的所有权,或者像我们这里一样不可变地借用 <code>self</code>,或者可变地借用 <code>self</code>,就跟其他参数一样。</p>
<p>这里选择 <code>&amp;self</code> 的理由跟在函数版本中使用 <code>&amp;Rectangle</code> 是相同的:我们并不想获取所有权,只希望能够读取结构体中的数据,而不是写入。如果想要在方法中改变调用方法的实例,需要将第一个参数改为 <code>&amp;mut self</code>。通过仅仅使用 <code>self</code> 作为第一个参数来使方法获取实例的所有权是很少见的;这种技术通常用在当方法将 <code>self</code> 转换成别的实例的时候,这时我们想要防止调用者在转换之后使用原始的实例。</p>
<p>使用方法替代函数,除了可使用方法语法和不需要在每个函数签名中重复 <code>self</code> 的类型之外,其主要好处在于组织性。我们将某个类型实例能做的所有事情都一起放入 <code>impl</code> 块中,而不是让将来的用户在我们的库中到处寻找 <code>Rectangle</code> 的功能。</p>
<p>请注意,我们可以选择将方法的名称与结构中的一个字段相同。例如,我们可以在 <code>Rectangle</code> 上定义一个方法,并命名为 <code>width</code></p>
<p><span class="filename">文件名src/main.rs</span></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Rectangle {
</span><span class="boring"> width: u32,
</span><span class="boring"> height: u32,
</span><span class="boring">}
</span><span class="boring">
</span>impl Rectangle {
fn width(&amp;self) -&gt; bool {
self.width &gt; 0
}
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
if rect1.width() {
println!(&quot;The rectangle has a nonzero width; it is {}&quot;, rect1.width);
}
}</code></pre></pre>
<p>在这里,我们选择让 <code>width</code> 方法在实例的 <code>width</code> 字段的值大于 <code>0</code> 时返回 <code>true</code>,等于 <code>0</code> 时则返回 <code>false</code>:我们可以出于任何目的,在同名的方法中使用同名的字段。在 <code>main</code> 中,当我们在 <code>rect1.width</code> 后面加上括号时。Rust 知道我们指的是方法 <code>width</code>。当我们不使用圆括号时Rust 知道我们指的是字段 <code>width</code></p>
<p>通常,但并不总是如此,与字段同名的方法将被定义为只返回字段中的值,而不做其他事情。这样的方法被称为 <em>getters</em>Rust 并不像其他一些语言那样为结构字段自动实现它们。Getters 很有用,因为你可以把字段变成私有的,但方法是公共的,这样就可以把对字段的只读访问作为该类型公共 API 的一部分。我们将在<a href="ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#%E4%BD%BF%E7%94%A8-pub-%E5%85%B3%E9%94%AE%E5%AD%97%E6%9A%B4%E9%9C%B2%E8%B7%AF%E5%BE%84">第七章</a>中讨论什么是公有和私有,以及如何将一个字段或方法指定为公有或私有。</p>
<blockquote>
<h3 id="--运算符到哪去了"><a class="header" href="#--运算符到哪去了"><code>-&gt;</code> 运算符到哪去了?</a></h3>
<p>在 C/C++ 语言中,有两个不同的运算符来调用方法:<code>.</code> 直接在对象上调用方法,而 <code>-&gt;</code> 在一个对象的指针上调用方法这时需要先解引用dereference指针。换句话说如果 <code>object</code> 是一个指针,那么 <code>object-&gt;something()</code> 就像 <code>(*object).something()</code> 一样。</p>
<p>Rust 并没有一个与 <code>-&gt;</code> 等效的运算符相反Rust 有一个叫 <strong>自动引用和解引用</strong><em>automatic referencing and dereferencing</em>)的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。</p>
<p>它是这样工作的:当使用 <code>object.something()</code> 调用方法时Rust 会自动为 <code>object</code> 添加 <code>&amp;</code><code>&amp;mut</code><code>*</code> 以便使 <code>object</code> 与方法签名匹配。也就是说,这些代码是等价的:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span><span class="boring">#[derive(Debug,Copy,Clone)]
</span><span class="boring">struct Point {
</span><span class="boring"> x: f64,
</span><span class="boring"> y: f64,
</span><span class="boring">}
</span><span class="boring">
</span><span class="boring">impl Point {
</span><span class="boring"> fn distance(&amp;self, other: &amp;Point) -&gt; f64 {
</span><span class="boring"> let x_squared = f64::powi(other.x - self.x, 2);
</span><span class="boring"> let y_squared = f64::powi(other.y - self.y, 2);
</span><span class="boring">
</span><span class="boring"> f64::sqrt(x_squared + y_squared)
</span><span class="boring"> }
</span><span class="boring">}
</span><span class="boring">let p1 = Point { x: 0.0, y: 0.0 };
</span><span class="boring">let p2 = Point { x: 5.0, y: 6.5 };
</span>p1.distance(&amp;p2);
(&amp;p1).distance(&amp;p2);
<span class="boring">}</span></code></pre></pre>
<p>第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者———— <code>self</code> 的类型。在给出接收者和方法名的前提下Rust 可以明确地计算出方法是仅仅读取(<code>&amp;self</code>),做出修改(<code>&amp;mut self</code>)或者是获取所有权(<code>self</code>。事实上Rust 对方法接收者的隐式借用让所有权在实践中更友好。</p>
</blockquote>
<h3 id="带有更多参数的方法"><a class="header" href="#带有更多参数的方法">带有更多参数的方法</a></h3>
<p>让我们通过实现 <code>Rectangle</code> 结构体上的另一方法来练习使用方法。这回,我们让一个 <code>Rectangle</code> 的实例获取另一个 <code>Rectangle</code> 实例,如果 <code>self</code> (第一个 <code>Rectangle</code>)能完全包含第二个长方形则返回 <code>true</code>;否则返回 <code>false</code>。一旦我们定义了 <code>can_hold</code> 方法,就可以编写示例 5-14 中的代码。</p>
<p><span class="filename">文件名src/main.rs</span></p>
<pre><code class="language-rust ignore">fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
let rect2 = Rectangle {
width: 10,
height: 40,
};
let rect3 = Rectangle {
width: 60,
height: 45,
};
println!(&quot;Can rect1 hold rect2? {}&quot;, rect1.can_hold(&amp;rect2));
println!(&quot;Can rect1 hold rect3? {}&quot;, rect1.can_hold(&amp;rect3));
}</code></pre>
<p><span class="caption">示例 5-14使用还未实现的 <code>can_hold</code> 方法</span></p>
<p>同时我们希望看到如下输出,因为 <code>rect2</code> 的两个维度都小于 <code>rect1</code>,而 <code>rect3</code><code>rect1</code> 要宽:</p>
<pre><code class="language-text">Can rect1 hold rect2? true
Can rect1 hold rect3? false
</code></pre>
<p>因为我们想定义一个方法,所以它应该位于 <code>impl Rectangle</code> 块中。方法名是 <code>can_hold</code>,并且它会获取另一个 <code>Rectangle</code> 的不可变借用作为参数。通过观察调用方法的代码可以看出参数是什么类型的:<code>rect1.can_hold(&amp;rect2)</code> 传入了 <code>&amp;rect2</code>,它是一个 <code>Rectangle</code> 的实例 <code>rect2</code> 的不可变借用。这是可以理解的,因为我们只需要读取 <code>rect2</code>(而不是写入,这意味着我们需要一个不可变借用),而且希望 <code>main</code> 保持 <code>rect2</code> 的所有权,这样就可以在调用这个方法后继续使用它。<code>can_hold</code> 的返回值是一个布尔值,其实现会分别检查 <code>self</code> 的宽高是否都大于另一个 <code>Rectangle</code>。让我们在示例 5-13 的 <code>impl</code> 块中增加这个新的 <code>can_hold</code> 方法,如示例 5-15 所示:</p>
<p><span class="filename">文件名src/main.rs</span></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Rectangle {
</span><span class="boring"> width: u32,
</span><span class="boring"> height: u32,
</span><span class="boring">}
</span><span class="boring">
</span>impl Rectangle {
fn area(&amp;self) -&gt; u32 {
self.width * self.height
}
fn can_hold(&amp;self, other: &amp;Rectangle) -&gt; bool {
self.width &gt; other.width &amp;&amp; self.height &gt; other.height
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let rect1 = Rectangle {
</span><span class="boring"> width: 30,
</span><span class="boring"> height: 50,
</span><span class="boring"> };
</span><span class="boring"> let rect2 = Rectangle {
</span><span class="boring"> width: 10,
</span><span class="boring"> height: 40,
</span><span class="boring"> };
</span><span class="boring"> let rect3 = Rectangle {
</span><span class="boring"> width: 60,
</span><span class="boring"> height: 45,
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> println!(&quot;Can rect1 hold rect2? {}&quot;, rect1.can_hold(&amp;rect2));
</span><span class="boring"> println!(&quot;Can rect1 hold rect3? {}&quot;, rect1.can_hold(&amp;rect3));
</span><span class="boring">}</span></code></pre></pre>
<p><span class="caption">示例 5-15<code>Rectangle</code> 上实现 <code>can_hold</code> 方法,它获取另一个 <code>Rectangle</code> 实例作为参数</span></p>
<p>如果结合示例 5-14 的 <code>main</code> 函数来运行,就会看到期望的输出。在方法签名中,可以在 <code>self</code> 后增加多个参数,而且这些参数就像函数中的参数一样工作。</p>
<h3 id="关联函数"><a class="header" href="#关联函数">关联函数</a></h3>
<p>所有在 <code>impl</code> 块中定义的函数被称为 <strong>关联函数</strong><em>associated functions</em>),因为它们与 <code>impl</code> 后面命名的类型相关。我们可以定义不以 <code>self</code> 为第一参数的关联函数(因此不是方法),因为它们并不作用于一个结构体的实例。我们已经使用了一个这样的函数:在 <code>String</code> 类型上定义的 <code>String::from</code> 函数。</p>
<p>不是方法的关联函数经常被用作返回一个结构体新实例的构造函数。这些函数的名称通常为 <code>new</code> ,但 <code>new</code> 并不是一个关键字。例如我们可以提供一个叫做 <code>square</code> 关联函数,它接受一个维度参数并且同时作为宽和高,这样可以更轻松的创建一个正方形 <code>Rectangle</code> 而不必指定两次同样的值:</p>
<p><span class="filename">文件名src/main.rs</span></p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Rectangle {
</span><span class="boring"> width: u32,
</span><span class="boring"> height: u32,
</span><span class="boring">}
</span><span class="boring">
</span>impl Rectangle {
fn square(size: u32) -&gt; Self {
Self {
width: size,
height: size,
}
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let sq = Rectangle::square(3);
</span><span class="boring">}</span></code></pre></pre>
<p>关键字 <code>Self</code> 在函数的返回类型中代指在 <code>impl</code> 关键字后出现的类型,在这里是 <code>Rectangle</code></p>
<p>使用结构体名和 <code>::</code> 语法来调用这个关联函数:比如 <code>let sq = Rectangle::square(3);</code>。这个函数位于结构体的命名空间中:<code>::</code> 语法用于关联函数和模块创建的命名空间。<a href="ch07-02-defining-modules-to-control-scope-and-privacy.html">第七章</a>会讲到模块。</p>
<h3 id="多个-impl-块"><a class="header" href="#多个-impl-块">多个 <code>impl</code></a></h3>
<p>每个结构体都允许拥有多个 <code>impl</code> 块。例如,示例 5-16 中的代码等同于示例 5-15但每个方法有其自己的 <code>impl</code> 块。</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#[derive(Debug)]
</span><span class="boring">struct Rectangle {
</span><span class="boring"> width: u32,
</span><span class="boring"> height: u32,
</span><span class="boring">}
</span><span class="boring">
</span>impl Rectangle {
fn area(&amp;self) -&gt; u32 {
self.width * self.height
}
}
impl Rectangle {
fn can_hold(&amp;self, other: &amp;Rectangle) -&gt; bool {
self.width &gt; other.width &amp;&amp; self.height &gt; other.height
}
}
<span class="boring">
</span><span class="boring">fn main() {
</span><span class="boring"> let rect1 = Rectangle {
</span><span class="boring"> width: 30,
</span><span class="boring"> height: 50,
</span><span class="boring"> };
</span><span class="boring"> let rect2 = Rectangle {
</span><span class="boring"> width: 10,
</span><span class="boring"> height: 40,
</span><span class="boring"> };
</span><span class="boring"> let rect3 = Rectangle {
</span><span class="boring"> width: 60,
</span><span class="boring"> height: 45,
</span><span class="boring"> };
</span><span class="boring">
</span><span class="boring"> println!(&quot;Can rect1 hold rect2? {}&quot;, rect1.can_hold(&amp;rect2));
</span><span class="boring"> println!(&quot;Can rect1 hold rect3? {}&quot;, rect1.can_hold(&amp;rect3));
</span><span class="boring">}</span></code></pre></pre>
<p><span class="caption">示例 5-16使用多个 <code>impl</code> 块重写示例 5-15</span></p>
<p>这里没有理由将这些方法分散在多个 <code>impl</code> 块中,不过这是有效的语法。第十章讨论泛型和 trait 时会看到实用的多 <code>impl</code> 块的用例。</p>
<h2 id="总结"><a class="header" href="#总结">总结</a></h2>
<p>结构体让你可以创建出在你的领域中有意义的自定义类型。通过结构体,我们可以将相关联的数据片段联系起来并命名它们,这样可以使得代码更加清晰。在 <code>impl</code> 块中,你可以定义与你的类型相关联的函数,而方法是一种相关联的函数,让你指定结构体的实例所具有的行为。</p>
<p>但结构体并不是创建自定义类型的唯一方法:让我们转向 Rust 的枚举功能,为你的工具箱再添一个工具。</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="ch05-02-example-structs.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="ch06-00-enums.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="ch05-02-example-structs.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="ch06-00-enums.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js"></script>
<script src="mark.min.js"></script>
<script src="searcher.js"></script>
<script src="clipboard.min.js"></script>
<script src="highlight.js"></script>
<script src="book.js"></script>
<!-- Custom JS scripts -->
<script src="ferris.js"></script>
</div>
</body>
</html>