mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 00:43:59 +08:00
fix mdbook-latex related format
This commit is contained in:
parent
f96f669b06
commit
acf3e9ce77
7
.editorconfig
Normal file
7
.editorconfig
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
@ -24,7 +24,7 @@ PS:
|
|||||||
全局安装 vuepress
|
全局安装 vuepress
|
||||||
|
|
||||||
``` bash
|
``` bash
|
||||||
npm i -g vuepress
|
npm i -g vuepress
|
||||||
```
|
```
|
||||||
|
|
||||||
cd 到项目目录,然后开始构建。构建好的静态文档会出现在 "./src/.vuepress/dist" 中
|
cd 到项目目录,然后开始构建。构建好的静态文档会出现在 "./src/.vuepress/dist" 中
|
||||||
|
@ -8,4 +8,10 @@ destination = "mdbook"
|
|||||||
|
|
||||||
[output.html]
|
[output.html]
|
||||||
additional-css = ["ferris.css"]
|
additional-css = ["ferris.css"]
|
||||||
additional-js = ["ferris.js"]
|
additional-js = ["ferris.js"]
|
||||||
|
|
||||||
|
[output.latex]
|
||||||
|
latex = true # default = true
|
||||||
|
pdf = true # default = false
|
||||||
|
markdown = true # default = false
|
||||||
|
custom-template = "custom-template.tex"
|
||||||
|
216
custom-template.tex
Normal file
216
custom-template.tex
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
%% Packages and Settings
|
||||||
|
\UseRawInputEncoding
|
||||||
|
\documentclass{article}
|
||||||
|
\usepackage[english]{babel}
|
||||||
|
\usepackage{adjustbox}
|
||||||
|
\usepackage{colortbl}
|
||||||
|
\usepackage[T1]{fontenc}
|
||||||
|
\usepackage[margin=1in]{geometry}
|
||||||
|
\usepackage{graphicx}
|
||||||
|
|
||||||
|
\usepackage{fontspec}
|
||||||
|
\setmainfont[Script=Devanagari]{Courier New}
|
||||||
|
\setsansfont{DejaVu Sans}
|
||||||
|
\setmonofont{DejaVu Sans Mono}
|
||||||
|
|
||||||
|
\usepackage{xeCJK}
|
||||||
|
\setCJKmainfont[BoldFont=SimHei,ItalicFont=KaiTi]{微软雅黑}
|
||||||
|
\xeCJKsetup{PunctStyle=kaiming,CJKspace=true,CheckSingle=true}
|
||||||
|
|
||||||
|
% https://tex.stackexchange.com/questions/219174/issue-with-page-breaks-before-section-and-toc-hyperlinks?rq=1
|
||||||
|
\usepackage{titlesec}
|
||||||
|
\usepackage{hyperref}
|
||||||
|
\newcommand{\sectionbreak}{\clearpage}
|
||||||
|
|
||||||
|
\usepackage[utf8]{inputenc}
|
||||||
|
\usepackage{listings}
|
||||||
|
\usepackage{longtable}
|
||||||
|
\usepackage{tabularx}
|
||||||
|
\usepackage{tabu}
|
||||||
|
\usepackage{textcomp}
|
||||||
|
\usepackage{xcolor}
|
||||||
|
\usepackage{array}
|
||||||
|
|
||||||
|
\newcommand{\PreserveBackslash}[1]{\let\temp=\\#1\let\\=\temp}
|
||||||
|
\newcolumntype{C}[1]{>{\PreserveBackslash\centering}m{#1}}
|
||||||
|
\newcolumntype{R}[1]{>{\PreserveBackslash\raggedleft}p{#1}}
|
||||||
|
\newcolumntype{L}[1]{>{\PreserveBackslash\raggedright}p{#1}}
|
||||||
|
|
||||||
|
% https://tex.stackexchange.com/questions/143015/different-column-number-in-rows
|
||||||
|
\usepackage{booktabs,multirow,tabularx}% http://ctan.org/pkg/{booktabs,multirow,tabularx}
|
||||||
|
\newcommand{\makecell}[1]{\begin{tabular}{c}#1\end{tabular}}
|
||||||
|
|
||||||
|
% https://tex.stackexchange.com/questions/823/remove-ugly-borders-around-clickable-cross-references-and-hyperlinks
|
||||||
|
\hypersetup{
|
||||||
|
colorlinks,
|
||||||
|
linkcolor={red!50!black},
|
||||||
|
citecolor={blue!50!black},
|
||||||
|
urlcolor={blue!80!black}
|
||||||
|
}
|
||||||
|
|
||||||
|
%% Unicode rules.
|
||||||
|
% https://tex.stackexchange.com/questions/215520/output-from-tree-command-in-a-listing
|
||||||
|
\usepackage{newunicodechar}
|
||||||
|
\newunicodechar{└}{{\smash{\raisebox{0.5ex}{\rule{0.5pt}{\dimexpr\baselineskip-1.5ex}}}\raisebox{0.5ex}{\rule{1ex}{0.5pt}}}}
|
||||||
|
\newunicodechar{─}{{\raisebox{0.5ex}{\rule{1.5ex}{0.5pt}}}}
|
||||||
|
\newunicodechar{├}{{\smash{\raisebox{-1ex}{\rule{0.5pt}{\baselineskip}}}\raisebox{0.5ex}{\rule{1ex}{0.5pt}}}}
|
||||||
|
\newunicodechar{’}{{'}}
|
||||||
|
\newunicodechar{“}{{"}}
|
||||||
|
\newunicodechar{”}{{"}}
|
||||||
|
|
||||||
|
%% Code highlighting.
|
||||||
|
\definecolor{commentsColor}{rgb}{0.497495, 0.497587, 0.497464}
|
||||||
|
\definecolor{keywordsColor}{rgb}{0.000000, 0.000000, 0.635294}
|
||||||
|
\definecolor{stringColor}{rgb}{0.558215, 0.000000, 0.135316}
|
||||||
|
\lstset{ %
|
||||||
|
backgroundcolor=\color{white}, % choose the background color; you must add \usepackage{color} or \usepackage{xcolor}
|
||||||
|
basicstyle=\footnotesize\ttfamily, % the size of the fonts that are used for the code
|
||||||
|
breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace
|
||||||
|
breaklines=true, % sets automatic line breaking
|
||||||
|
captionpos=b, % sets the caption-position to bottom
|
||||||
|
commentstyle=\color{commentsColor}\textit, % comment style
|
||||||
|
deletekeywords={...}, % if you want to delete keywords from the given language
|
||||||
|
escapeinside={\%*}{*)}, % if you want to add LaTeX within your code
|
||||||
|
extendedchars=true, % lets you use non-ASCII characters; for 8-bits encodings only, does not work with UTF-8
|
||||||
|
frame=tb, % adds a frame around the code
|
||||||
|
keepspaces=true, % keeps spaces in text, useful for keeping indentation of code (possibly needs columns=flexible)
|
||||||
|
keywordstyle=\color{keywordsColor}\bfseries, % keyword style
|
||||||
|
language=Python, % the language of the code (can be overrided per snippet)
|
||||||
|
otherkeywords={*,...}, % if you want to add more keywords to the set
|
||||||
|
numbers=left, % where to put the line-numbers; possible values are (none, left, right)
|
||||||
|
numbersep=5pt, % how far the line-numbers are from the code
|
||||||
|
numberstyle=\tiny\color{commentsColor}, % the style that is used for the line-numbers
|
||||||
|
rulecolor=\color{black}, % if not set, the frame-color may be changed on line-breaks within not-black text (e.g. comments (green here))
|
||||||
|
showspaces=false, % show spaces everywhere adding particular underscores; it overrides 'showstringspaces'
|
||||||
|
showstringspaces=false, % underline spaces within strings only
|
||||||
|
showtabs=false, % show tabs within strings adding particular underscores
|
||||||
|
stepnumber=1, % the step between two line-numbers. If it's 1, each line will be numbered
|
||||||
|
stringstyle=\color{stringColor}, % string literal style
|
||||||
|
prebreak=\raisebox{0ex}[0ex][0ex]{\ensuremath{\hookrightarrow}},
|
||||||
|
tabsize=2, % sets default tabsize to 2 spaces
|
||||||
|
title=\lstname, % show the filename of files included with \lstinputlisting; also try caption instead of title
|
||||||
|
columns=fixed, % Using fixed column width (for e.g. nice alignment)
|
||||||
|
inputencoding=utf8, % https://tex.stackexchange.com/questions/24528/having-problems-with-listings-and-utf-8-can-it-be-fixed
|
||||||
|
literate={└}{{\smash{\raisebox{0.5ex}{\rule{0.5pt}{\dimexpr\baselineskip-1.5ex}}}\raisebox{0.5ex}{\rule{1ex}{0.5pt}}}}1 {─}{{\raisebox{0.5ex}{\rule{1.5ex}{0.5pt}}}}1 {├}{{\smash{\raisebox{-1ex}{\rule{0.5pt}{\baselineskip}}}\raisebox{0.5ex}{\rule{1ex}{0.5pt}}}}1,
|
||||||
|
}
|
||||||
|
|
||||||
|
%% Language definitions.
|
||||||
|
\lstdefinelanguage{rust}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{rs}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{console}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{\#},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{handlebars}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{shell}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{\#},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{json}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{yaml}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{toml}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{diff}{
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{JavaScript}{
|
||||||
|
keywords={typeof, new, true, false, catch, function, return, null, catch, switch, var, if, in, while, do, else, case, break},
|
||||||
|
ndkeywords={class, export, boolean, throw, implements, import, this},
|
||||||
|
sensitive=false,
|
||||||
|
comment=[l]{//},
|
||||||
|
morecomment=[s]{/*}{*/},
|
||||||
|
morestring=[b]',
|
||||||
|
morestring=[b]"
|
||||||
|
}
|
||||||
|
|
||||||
|
\lstdefinelanguage{text}{}
|
||||||
|
\lstdefinelanguage{hbs}{}
|
||||||
|
\lstdefinelanguage{cmd}{}
|
||||||
|
\lstdefinelanguage{powershell}{}
|
||||||
|
\lstdefinelanguage{makefile}{}
|
||||||
|
\lstdefinelanguage{markdown}{}
|
||||||
|
|
||||||
|
%% Title and Author (retreived from book.toml)
|
||||||
|
\title{}
|
||||||
|
\author{}
|
||||||
|
\date{}
|
||||||
|
|
||||||
|
%% Begin document.
|
||||||
|
\begin{document}
|
||||||
|
\maketitle
|
||||||
|
\clearpage
|
||||||
|
\tableofcontents
|
||||||
|
\clearpage
|
||||||
|
|
||||||
|
%% mdbook-latex begin
|
||||||
|
|
||||||
|
\end{document}
|
@ -1,8 +1,8 @@
|
|||||||
## 附录 A: 关键字
|
## 附录 A:关键字
|
||||||
|
|
||||||
> [appendix-01-keywords.md](https://raw.githubusercontent.com/rust-lang/book/master/src/appendix-01-keywords.md)
|
> [appendix-01-keywords.md](https://raw.githubusercontent.com/rust-lang/book/master/src/appendix-01-keywords.md)
|
||||||
> <br>
|
> <br>
|
||||||
> commit 27dd97a785794709aa87c51ab697cded41e8163a
|
> commit 27dd97a785794709aa87c51ab697cded41e8163a
|
||||||
|
|
||||||
下面的列表包含 Rust 中正在使用或者以后会用到的关键字。因此,这些关键字不能被用作标识符(除了 “[原始标识符][raw-identifiers]” 部分介绍的原始标识符),这包括函数、变量、参数、结构体字段、模块、crate、常量、宏、静态值、属性、类型、trait 或生命周期
|
下面的列表包含 Rust 中正在使用或者以后会用到的关键字。因此,这些关键字不能被用作标识符(除了 “[原始标识符][raw-identifiers]” 部分介绍的原始标识符),这包括函数、变量、参数、结构体字段、模块、crate、常量、宏、静态值、属性、类型、trait 或生命周期
|
||||||
的名字。
|
的名字。
|
||||||
@ -19,7 +19,7 @@
|
|||||||
* `continue` - 继续进入下一次循环迭代
|
* `continue` - 继续进入下一次循环迭代
|
||||||
* `crate` - 链接(link)一个外部 **crate** 或一个代表宏定义的 **crate** 的宏变量
|
* `crate` - 链接(link)一个外部 **crate** 或一个代表宏定义的 **crate** 的宏变量
|
||||||
* `dyn` - 动态分发 trait 对象
|
* `dyn` - 动态分发 trait 对象
|
||||||
* `else` - 作为 `if` 和 `if let` 控制流结构的 fallback
|
* `else` - 作为 `if` 和 `if let` 控制流结构的 fallback
|
||||||
* `enum` - 定义一个枚举
|
* `enum` - 定义一个枚举
|
||||||
* `extern` - 链接一个外部 **crate** 、函数或变量
|
* `extern` - 链接一个外部 **crate** 、函数或变量
|
||||||
* `false` - 布尔字面值 `false`
|
* `false` - 布尔字面值 `false`
|
||||||
@ -110,4 +110,4 @@ fn main() {
|
|||||||
|
|
||||||
原始标识符允许使用你选择的任何单词作为标识符,即使该单词恰好是保留关键字。 此外,原始标识符允许你使用以不同于你的 crate 使用的 Rust 版本编写的库。比如,`try` 在 2015 edition 中不是关键字,而在 2018 edition 则是。所以如果如果用 2015 edition 编写的库中带有 `try` 函数,在 2018 edition 中调用时就需要使用原始标识符语法,在这里是 `r#try`。有关版本的更多信息,请参见[附录 E][appendix-e].
|
原始标识符允许使用你选择的任何单词作为标识符,即使该单词恰好是保留关键字。 此外,原始标识符允许你使用以不同于你的 crate 使用的 Rust 版本编写的库。比如,`try` 在 2015 edition 中不是关键字,而在 2018 edition 则是。所以如果如果用 2015 edition 编写的库中带有 `try` 函数,在 2018 edition 中调用时就需要使用原始标识符语法,在这里是 `r#try`。有关版本的更多信息,请参见[附录 E][appendix-e].
|
||||||
|
|
||||||
[appendix-e]: appendix-05-editions.html
|
[appendix-e]: appendix-05-editions.html
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
例如,在使用 `assert_eq!` 宏时,`Debug` trait 是必须的。如果等式断言失败,这个宏就把给定实例的值作为参数打印出来,如此程序员可以看到两个实例为什么不相等。
|
例如,在使用 `assert_eq!` 宏时,`Debug` trait 是必须的。如果等式断言失败,这个宏就把给定实例的值作为参数打印出来,如此程序员可以看到两个实例为什么不相等。
|
||||||
|
|
||||||
## 等值比较的 `PartialEq` 和 `Eq`
|
### 等值比较的 `PartialEq` 和 `Eq`
|
||||||
|
|
||||||
`PartialEq` trait 可以比较一个类型的实例以检查是否相等,并开启了 `==` 和 `!=` 运算符的功能。
|
`PartialEq` trait 可以比较一个类型的实例以检查是否相等,并开启了 `==` 和 `!=` 运算符的功能。
|
||||||
|
|
||||||
@ -42,9 +42,9 @@
|
|||||||
|
|
||||||
例如,对于一个 `HashMap<K, V>` 中的 key 来说, `Eq` 是必须的,这样 `HashMap<K, V>` 就可以知道两个 key 是否一样了。
|
例如,对于一个 `HashMap<K, V>` 中的 key 来说, `Eq` 是必须的,这样 `HashMap<K, V>` 就可以知道两个 key 是否一样了。
|
||||||
|
|
||||||
## 次序比较的 `PartialOrd` 和 `Ord`
|
### 次序比较的 `PartialOrd` 和 `Ord`
|
||||||
|
|
||||||
`PartialOrd` trait 可以基于排序的目的而比较一个类型的实例。实现了 `PartialOrd` 的类型可以使用 `<`、 `>`、`<=` 和 `>=` 操作符。但只能在同时实现了 `PartialEq` 的类型上使用 `PartialOrd`。
|
`PartialOrd` trait 可以基于排序的目的而比较一个类型的实例。实现了 `PartialOrd` 的类型可以使用 `<`、 `>`、`<=` 和 `>=` 操作符。但只能在同时实现了 `PartialEq` 的类型上使用 `PartialOrd`。
|
||||||
|
|
||||||
派生 `PartialOrd` 实现了 `partial_cmp` 方法,其返回一个 `Option<Ordering>` ,但当给定值无法产生顺序时将返回 `None`。尽管大多数类型的值都可以比较,但一个无法产生顺序的例子是:浮点类型的非数字值。当在浮点数上调用 `partial_cmp` 时,`NaN` 的浮点数将返回 `None`。
|
派生 `PartialOrd` 实现了 `partial_cmp` 方法,其返回一个 `Option<Ordering>` ,但当给定值无法产生顺序时将返回 `None`。尽管大多数类型的值都可以比较,但一个无法产生顺序的例子是:浮点类型的非数字值。当在浮点数上调用 `partial_cmp` 时,`NaN` 的浮点数将返回 `None`。
|
||||||
|
|
||||||
@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
例如,当在 `BTreeSet<T>`(一种基于有序值存储数据的数据结构)上存值时,`Ord` 是必须的。
|
例如,当在 `BTreeSet<T>`(一种基于有序值存储数据的数据结构)上存值时,`Ord` 是必须的。
|
||||||
|
|
||||||
## 复制值的 `Clone` 和 `Copy`
|
### 复制值的 `Clone` 和 `Copy`
|
||||||
|
|
||||||
`Clone` trait 可以明确地创建一个值的深拷贝(deep copy),复制过程可能包含任意代码的执行以及堆上数据的复制。查阅第四章 [“变量和数据的交互方式:移动”][ways-variables-and-data-interact-clone] 以获取有关 `Clone` 的更多信息。
|
`Clone` trait 可以明确地创建一个值的深拷贝(deep copy),复制过程可能包含任意代码的执行以及堆上数据的复制。查阅第四章 [“变量和数据的交互方式:移动”][ways-variables-and-data-interact-clone] 以获取有关 `Clone` 的更多信息。
|
||||||
|
|
||||||
@ -74,13 +74,13 @@
|
|||||||
|
|
||||||
任何使用 `Copy` 的代码都可以通过 `Clone` 实现,但代码可能会稍慢或是要使用 `clone` 替代。
|
任何使用 `Copy` 的代码都可以通过 `Clone` 实现,但代码可能会稍慢或是要使用 `clone` 替代。
|
||||||
|
|
||||||
## 固定大小的值到值映射的 `Hash`
|
### 固定大小的值到值映射的 `Hash`
|
||||||
|
|
||||||
`Hash` trait 可以实例化一个任意大小的类型,并且能够用哈希(hash)函数将该实例映射到一个固定大小的值上。派生 `Hash` 实现了 `hash` 方法。`hash` 方法的派生实现结合了在类型的每部分调用 `hash` 的结果,这意味着所有的字段或值也必须将 `Hash` 实现为派生 `Hash`。
|
`Hash` trait 可以实例化一个任意大小的类型,并且能够用哈希(hash)函数将该实例映射到一个固定大小的值上。派生 `Hash` 实现了 `hash` 方法。`hash` 方法的派生实现结合了在类型的每部分调用 `hash` 的结果,这意味着所有的字段或值也必须将 `Hash` 实现为派生 `Hash`。
|
||||||
|
|
||||||
例如,在 `HashMap<K, V>` 的 key 上存储有效数据时,`Hash` 是必须的。
|
例如,在 `HashMap<K, V>` 的 key 上存储有效数据时,`Hash` 是必须的。
|
||||||
|
|
||||||
## 默认值的 `Default`
|
### 默认值的 `Default`
|
||||||
|
|
||||||
`Default` trait 使你创建一个类型的默认值。 派生 `Default` 实现了 `default` 函数。`default` 函数的派生实现调用了类型每部分的 `default` 函数,这意味着类型中所有的字段或值也必须把 `Default` 实现为派生 `Default` 。
|
`Default` trait 使你创建一个类型的默认值。 派生 `Default` 实现了 `default` 函数。`default` 函数的派生实现调用了类型每部分的 `default` 函数,这意味着类型中所有的字段或值也必须把 `Default` 实现为派生 `Default` 。
|
||||||
|
|
||||||
@ -94,4 +94,4 @@ ch05-01-defining-structs.html#creating-instances-from-other-instances-with-struc
|
|||||||
ch04-01-what-is-ownership.html#stack-only-data-copy
|
ch04-01-what-is-ownership.html#stack-only-data-copy
|
||||||
[ways-variables-and-data-interact-clone]:
|
[ways-variables-and-data-interact-clone]:
|
||||||
ch04-01-what-is-ownership.html#ways-variables-and-data-interact-clone
|
ch04-01-what-is-ownership.html#ways-variables-and-data-interact-clone
|
||||||
[macros]: ch19-06-macros.html#macros
|
[macros]: ch19-06-macros.html#macros
|
||||||
|
@ -1,299 +0,0 @@
|
|||||||
## 附录D - 宏
|
|
||||||
|
|
||||||
> [appendix-04-macros.md][appendix-04]
|
|
||||||
> <br>
|
|
||||||
> commit 32215c1d96c9046c0b553a05fa5ec3ede2e125c3
|
|
||||||
|
|
||||||
[appendix-04]: https://github.com/rust-lang/book/blob/master/second-edition/src/appendix-04-macros.md
|
|
||||||
|
|
||||||
我们已经在本书中像 *println!* 这样使用过宏了,但还没完全探索什么是宏以及它是如何工作的。本附录以如下方式解释宏:
|
|
||||||
|
|
||||||
* 什么是宏以及与函数有何区别
|
|
||||||
* 如何定义一个声明式宏( declarative macro )来进行元编程(metaprogramming)
|
|
||||||
* 如何定义一个过程式宏( procedural macro )来自定义 `derive` traits
|
|
||||||
|
|
||||||
因为在 Rust 里宏仍在开发中,该附录会介绍宏的细节。宏已经改变了,在不久的将来,和语言的其他部分以及从 Rust 1.0 至今的标准库相比,将会以更快的速率改变,因此,和本书其他部分相比,本部分更有可能变得过时。由于 Rust 的稳定性保证,该处的代码将会在后续版本中继续可运行,但可能会以更优的性能或更容易的方式来写那些在此次发布版中无法实现的宏。当你尝试实现该附录任何功能时,请记住这一点。
|
|
||||||
|
|
||||||
### 宏和函数的区别
|
|
||||||
|
|
||||||
从根本上来说,宏是一种为写其他代码而写代码的方式,即所谓的*元编程*。在附录C中,探讨了 `derive` 属性,其生成各种 trait 的实现。我们也在本书中使用过 `println!` 宏和 `vec!` 宏。所有的这些宏 *扩展开* 来以生成比你手写更多的代码。
|
|
||||||
|
|
||||||
元编程对于减少大量编写和维护的代码是非常有用的,它也扮演了函数的角色。但宏有一些函数所没有的附加能力。
|
|
||||||
|
|
||||||
一个函数标签必须声明函数参数个数和类型。而宏只接受一个可变参数:用一个参数调用 `println!("hello")` 或用两个参数调用 `println!("hello {}", name)` 。而且,宏可以在编译器翻译代码前展开,例如,宏可以在一个给定类型上实现 trait 。因为函数是在运行时被调用,同时 trait 需要在运行时实现,所以函数无法像宏这样。
|
|
||||||
|
|
||||||
实现一个宏而不是函数的消极面是宏定义要比函数定义更复杂,因为你正在为写 Rust 代码而写代码。由于这样的间接性,宏定义通常要比函数定义更难阅读、理解以及维护。
|
|
||||||
|
|
||||||
宏和函数的另一个区别是:宏定义无法像函数定义那样出现在模块命名空间中。当使用外部包( external crate )时,为了防止无法预料的名字冲突,在导入外部包的同时也必须明确地用 `#[macro_use]` 注解将宏导入到项目中。下面的例子将所有定义在 `serde` 包中的宏导入到当前包中:
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
#[macro_use]
|
|
||||||
extern crate serde;
|
|
||||||
```
|
|
||||||
|
|
||||||
如果 `extern crate` 能够将宏默认导入而无需使用明确的注解,则会阻止你使用同时定义相同宏名的两个包。在练习中,这样的冲突并不经常遇到,但使用的包越多,越有可能遇到。
|
|
||||||
|
|
||||||
宏和函数最重要的区别是:在一个文件中,必须在调用宏`之前`定义或导入宏,然而却可以在任意地方定义或调用函数。
|
|
||||||
|
|
||||||
### 通用元编程的声明式宏 `macro_rules!`
|
|
||||||
|
|
||||||
在 Rust 中,最广泛使用的宏形式是 *declarative macros* 。通常也指 *macros by example* 、*`macro_rules!` macros* 或简单的 *macros* 。在其核心中,声明式宏使你可以写一些和 Rust 的 `match` 表达式类似的代码。正如在第六章讨论的那样,`match` 表达式是控制结构,其接收一个表达式,与表达式的结果进行模式匹配,然后根据模式匹配执行相关代码。宏也将一个值和包含相关代码的模式进行比较;此种情况下,该值是 Rust 源代码传递给宏的常量,而模式则与源代码结构进行比较,同时每个模式的相关代码成为传递给宏的代码<!-- 这部分翻译自己不太满意-->。所有的这些都在编译时发生。
|
|
||||||
|
|
||||||
可以使用 `macro_rules!` 来定义宏。让我们通过查看 `vec!` 宏定义来探索如何使用 `macro_rules!` 结构。第八章讲述了如何使用 `vec!` 宏来生成一个给定值的 vector。例如,下面的宏用三个整数创建一个 vector `:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let v: Vec<u32> = vec![1, 2, 3];
|
|
||||||
```
|
|
||||||
|
|
||||||
也可以使用 `vec!` 宏来构造两个整数的 vector 或五个字符串切片的 vector 。但却无法使用函数做相同的事情,因为我们无法预先知道参数值的数量和类型。
|
|
||||||
|
|
||||||
在示例 D-1 中来看一个 `vec!` 稍微简化的定义。
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! vec {
|
|
||||||
( $( $x:expr ),* ) => {
|
|
||||||
{
|
|
||||||
let mut temp_vec = Vec::new();
|
|
||||||
$(
|
|
||||||
temp_vec.push($x);
|
|
||||||
)*
|
|
||||||
temp_vec
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="caption">示例 D-1: `vec!` 宏定义的一个简化版本</span>
|
|
||||||
|
|
||||||
> 注意:标准库中实际定义的 `vec!` 包括预分配适当量的内存。这部分为代码优化,为了让示例简化,此处并没有包含在内。
|
|
||||||
|
|
||||||
无论何时导入定义了宏的包,`#[macro_export]` 注解说明宏应该是可用的。 如果没有 `#[macro_export]` 注解,即使凭借包使用 `#[macro_use]` 注解,该宏也不会导入进来,
|
|
||||||
|
|
||||||
接着使用 `macro_rules!` 进行了宏定义,且所定义的宏并*不带*感叹号。名字后跟大括号表示宏定义体,在该例中是 `vec` 。
|
|
||||||
|
|
||||||
`vec!` 宏的结构和 `match` 表达式的结构类似。此处有一个单边模式 `( $( $x:expr ),* )` ,后跟 `=>` 以及和模式相关的代码块。如果模式匹配,该相关代码块将被执行。假设这只是这个宏中的模式,且只有一个有效匹配,其他任何匹配都是错误的。更复杂的宏会有多个单边模式。<!-- 此处 arm, one arm 并未找到合适的翻译-->
|
|
||||||
|
|
||||||
宏定义中有效模式语法和在第十八章提及的模式语法是不同的,因为宏模式所匹配的是 Rust 代码结构而不是值。回过头来检查下示例 D-1 中模式片段什么意思。对于全部的宏模式语法,请查阅[参考]。
|
|
||||||
|
|
||||||
[参考]: https://github.com/rust-lang/book/blob/master/reference/macros.html
|
|
||||||
|
|
||||||
首先,一对括号包含了全部模式。接下来是后跟一对括号的美元符号( `$` ),其通过替代代码捕获了符合括号内模式的值。`$()` 内则是 `$x:expr` ,其匹配 Rust 的任意表达式或给定 `$x` 名字的表达式。
|
|
||||||
|
|
||||||
`$()` 之后的逗号说明一个逗号分隔符可以有选择的出现代码之后,这段代码与在 `$()` 中所捕获的代码相匹配。紧随逗号之后的 `*` 说明该模式匹配零个或多个 `*` 之前的任何模式。
|
|
||||||
|
|
||||||
当以 `vec![1, 2, 3];` 调用宏时,`$x` 模式与三个表达式 `1`、`2` 和 `3` 进行了三次匹配。
|
|
||||||
|
|
||||||
现在让我们来看看这个出现在与此单边模式相关的代码块中的模式:在 `$()*` 部分中所生成的 `temp_vec.push()` 为在匹配到模式中的 `$()` 每一部分而生成。`$x` 由每个与之相匹配的表达式所替换。当以 `vec![1, 2, 3];` 调用该宏时,替换该宏调用所生成的代码会是下面这样:
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
let mut temp_vec = Vec::new();
|
|
||||||
temp_vec.push(1);
|
|
||||||
temp_vec.push(2);
|
|
||||||
temp_vec.push(3);
|
|
||||||
temp_vec
|
|
||||||
```
|
|
||||||
|
|
||||||
我们已经定义了一个宏,其可以接收任意数量和类型的参数,同时可以生成能够创建包含指定元素的 vector 的代码。
|
|
||||||
|
|
||||||
|
|
||||||
鉴于大多数 Rust 程序员*使用*宏而不是*写*宏,此处不再深入探讨 `macro_rules!` 。请查阅在线文档或其他资源,如 [“The Little Book of Rust Macros”][tlborm] 来更多地了解如何写宏。
|
|
||||||
|
|
||||||
[tlborm]: https://danielkeep.github.io/tlborm/book/index.html
|
|
||||||
|
|
||||||
### 自定义 `derive` 的过程式宏
|
|
||||||
|
|
||||||
第二种形式的宏叫做*过程式宏*( *procedural macros* ),因为它们更像函数(一种过程类型)。过程式宏接收 Rust 代码作为输入,在这些代码上进行操作,然后产生另一些代码作为输出,而非像声明式宏那样匹配对应模式然后以另一部分代码替换当前代码。在书写部分附录时,只能定义过程式宏来使你在一个通过 `derive` 注解来指定 trait 名的类型上实现 trait 。
|
|
||||||
|
|
||||||
我们会创建一个 `hello_macro` 包,该包定义了一个关联到 `hello_macro` 函数并以 `HelloMacro` 为名的trait。并非让包的用户为其每一个类型实现`HelloMacro` trait,我们将会提供一个过程式宏以便用户可以使用 `#[derive(HelloMacro)]` 注解他们的类型来得到 `hello_macro` 函数的默认实现。该函数的默认实现会打印 `Hello, Macro! My name is TypeName!`,其中 `TypeName` 为定义了 trait 的类型名。换言之,我们会创建一个包,让使用该包的程序员能够写类似示例 D-2 中的代码。
|
|
||||||
|
|
||||||
<span class="filename">文件名: src/main.rs</span>
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
extern crate hello_macro;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate hello_macro_derive;
|
|
||||||
|
|
||||||
use hello_macro::HelloMacro;
|
|
||||||
|
|
||||||
#[derive(HelloMacro)]
|
|
||||||
struct Pancakes;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Pancakes::hello_macro();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="caption">示例 D-2: 包用户所写的能够使用过程式宏的代码</span>
|
|
||||||
|
|
||||||
运行该代码将会打印 `Hello, Macro! My name is Pancakes!` 第一步是像下面这样新建一个库:
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ cargo new hello_macro --lib
|
|
||||||
```
|
|
||||||
|
|
||||||
接下来,会定义 `HelloMacro` trait 以及其关联函数:
|
|
||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
|
||||||
|
|
||||||
```rust
|
|
||||||
pub trait HelloMacro {
|
|
||||||
fn hello_macro();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
现在有了一个包含函数的 trait 。此时,包用户可以实现该 trait 以达到其期望的功能,像这样:
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
extern crate hello_macro;
|
|
||||||
|
|
||||||
use hello_macro::HelloMacro;
|
|
||||||
|
|
||||||
struct Pancakes;
|
|
||||||
|
|
||||||
impl HelloMacro for Pancakes {
|
|
||||||
fn hello_macro() {
|
|
||||||
println!("Hello, Macro! My name is Pancakes!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
Pancakes::hello_macro();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
然而,他们需要为每一个他们想使用 `hello_macro` 的类型编写实现的代码块。我们希望为其节约这些工作。
|
|
||||||
|
|
||||||
另外,我们也无法为 `hello_macro` 函数提供一个能够打印实现了该 trait 的类型的名字的默认实现:Rust 没有反射的能力,因此其无法在运行时获取类型名。我们需要一个在运行时生成代码的宏。
|
|
||||||
|
|
||||||
下一步是定义过程式宏。在编写该附录时,过程式宏必须在包内。该限制后面可能被取消。构造包和包中宏的惯例如下:对于一个 `foo` 的包来说,一个自定义的派生过程式宏的包被称为 `foo_derive` 。在 `hello_macro` 项目中新建名为 `hello_macro_derive` 的包。
|
|
||||||
|
|
||||||
```text
|
|
||||||
$ cargo new hello_macro_derive --lib
|
|
||||||
```
|
|
||||||
|
|
||||||
由于两个包紧密相关,因此在 `hello_macro` 包的目录下创建过程式宏的包。如果改变在 `hello_macro` 中定义的 trait ,同时也必须改变在 `hello_macro_derive` 中实现的过程式宏。这两个包需要分别发布,编程人员如果使用这些包,则需要同时添加这两个依赖并导入到代码中。我们也可以只用 `hello_macro` 包而将 `hello_macro_derive` 作为一个依赖,并重新导出过程式宏的代码。但我们组织项目的方式使编程人员使用 `hello_macro` 成为可能,即使他们无需 `derive` 的功能。
|
|
||||||
|
|
||||||
需要将 `hello_macro_derive` 声明为一个过程式宏的包。同时也需要 `syn` 和 `quote` 包中的功能,正如注释中所说,需要将其加到依赖中。为 `hello_macro_derive` 将下面的代码加入到 *Cargo.toml* 文件中。
|
|
||||||
|
|
||||||
<span class="filename">文件名: hello_macro_derive/Cargo.toml</span>
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
syn = "0.11.11"
|
|
||||||
quote = "0.3.15"
|
|
||||||
```
|
|
||||||
为定义一个过程式宏,请将示例 D-3 中的代码放在 `hello_macro_derive` 包的 *src/lib.rs* 文件里面。注意这段代码在我们添加 `impl_hello_macro` 函数的定义之前是无法编译的。
|
|
||||||
|
|
||||||
<span class="filename">文件名: hello_macro_derive/src/lib.rs</span>
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
extern crate proc_macro;
|
|
||||||
extern crate syn;
|
|
||||||
#[macro_use]
|
|
||||||
extern crate quote;
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
|
|
||||||
#[proc_macro_derive(HelloMacro)]
|
|
||||||
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
|
|
||||||
// Construct a string representation of the type definition
|
|
||||||
let s = input.to_string();
|
|
||||||
|
|
||||||
// Parse the string representation
|
|
||||||
let ast = syn::parse_derive_input(&s).unwrap();
|
|
||||||
|
|
||||||
// Build the impl
|
|
||||||
let gen = impl_hello_macro(&ast);
|
|
||||||
|
|
||||||
// Return the generated impl
|
|
||||||
gen.parse().unwrap()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<span class="caption">示例 D-3: 大多数过程式宏处理 Rust 代码的代码</span>
|
|
||||||
|
|
||||||
注意在 D-3 中分离函数的方式,这将和你几乎所见到或创建的每一个过程式宏都一样,因为这让编写一个过程式宏更加方便。在 `impl_hello_macro` 被调用的地方所选择做的什么依赖于该过程式宏的目的而有所不同。
|
|
||||||
|
|
||||||
现在,我们已经介绍了三个包:`proc_macro` 、 [`syn`] 和 [`quote`] 。Rust 自带 `proc_macro` ,因此无需将其加到 *Cargo.toml* 文件的依赖中。`proc_macro` 可以将 Rust 代码转换为相应的字符串。`syn` 则将字符串中的 Rust 代码解析成为一个可以操作的数据结构。`quote` 则将 `syn` 解析的数据结构反过来传入到 Rust 代码中。这些包让解析我们所要处理的有序 Rust 代码变得更简单:为 Rust 编写整个的解析器并不是一件简单的工作。
|
|
||||||
|
|
||||||
[`syn`]: https://crates.io/crates/syn
|
|
||||||
[`quote`]: https://crates.io/crates/quote
|
|
||||||
|
|
||||||
当用户在一个类型上指定 `#[derive(HelloMacro)]` 时,`hello_macro_derive` 函数将会被调用。
|
|
||||||
原因在于我们已经使用 `proc_macro_derive` 及其指定名称对 `hello_macro_derive` 函数进行了注解:`HelloMacro` ,其匹配到 trait 名,这是大多数过程式宏的方便之处。
|
|
||||||
|
|
||||||
该函数首先将来自 `TokenStream` 的 `输入` 转换为一个名为 `to_string` 的 `String` 类型。该 `String` 代表 派生 `HelloMacro` Rust 代码的字符串。在示例 D-2 的例子中,`s` 是 `String` 类型的 `struct Pancakes;` 值,这是因为我们加上了 `#[derive(HelloMacro)]` 注解。
|
|
||||||
|
|
||||||
> 注意:编写本附录时,只可以将 `TokenStream` 转换为字符串,将来会提供更丰富的API。
|
|
||||||
|
|
||||||
现在需要将 `String` 类型的 Rust 代码 解析为一个数据结构中,随后便可以与之交互并操作该数据结构。这正是 `syn` 所做的。`syn` 中的 `parse_derive_input` 函数以一个 `String` 作为参数并返回一个 表示解析出 Rust 代码的 `DeriveInput` 结构体。 下面的代码 展示了从字符串 `struct Pancakes;` 中解析出来的 `DeriveInput` 结构体的相关部分。
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
DeriveInput {
|
|
||||||
// --snip--
|
|
||||||
|
|
||||||
ident: Ident(
|
|
||||||
"Pancakes"
|
|
||||||
),
|
|
||||||
body: Struct(
|
|
||||||
Unit
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
该结构体的字段展示了我们解析的 Rust 代码是一个元组结构体,其 `ident` ( identifier,表示名字)为 `Pancakes` 。该结构体里面有更多字段描述了所有有序 Rust 代码,查阅 [`syn`
|
|
||||||
documentation for `DeriveInput`][syn-docs] 以获取更多信息。
|
|
||||||
|
|
||||||
[syn-docs]: https://docs.rs/syn/0.11.11/syn/struct.DeriveInput.html
|
|
||||||
|
|
||||||
此时,尚未定义 `impl_hello_macro` 函数,其用于构建所要包含在内的 Rust 新代码。但在定义之前,要注意 `hello_macro_derive` 函数的最后一部分使用了 `quote` 包中的 `parse` 函数,该函数将 `impl_hello_macro` 的输出返回给 `TokenStream` 。所返回的 `TokenStream` 会被加到我们的包用户所写的代码中,因此,当用户编译他们的包时,他们会获取到我们所提供的额外功能。
|
|
||||||
|
|
||||||
你也注意到,当调用 `parse_derive_input` 或 `parse` 失败时,我们调用 `unwrap` 来抛出异常。在过程式宏中,有必要错误上抛异常,因为 `proc_macro_derive` 函数必须返回 `TokenStream` 而不是 `Result` ,以此来符合过程式宏的 API 。我们已经选择用 `unwrap` 来简化了这个例子;在生产中的代码里,你应该通过 `panic!` 或 `expect` 来提供关于发生何种错误的更加明确的错误信息。
|
|
||||||
|
|
||||||
现在我们有了将注解的 Rust 代码从 `TokenStream` 转换为 `String` 和 `DeriveInput` 实例的代码,让我们来创建在注解类型上实现 `HelloMacro` trait 的代码。
|
|
||||||
|
|
||||||
<span class="filename">文件名: hello_macro_derive/src/lib.rs</span>
|
|
||||||
|
|
||||||
```rust,ignore
|
|
||||||
fn impl_hello_macro(ast: &syn::DeriveInput) -> quote::Tokens {
|
|
||||||
let name = &ast.ident;
|
|
||||||
quote! {
|
|
||||||
impl HelloMacro for #name {
|
|
||||||
fn hello_macro() {
|
|
||||||
println!("Hello, Macro! My name is {}", stringify!(#name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
我们得到一个包含以 `ast.ident` 作为注解类型名字(标识符)的 `Ident` 结构体实例。示例 D-2 中的代码说明 `name` 会是 `Ident("Pancakes")` 。
|
|
||||||
|
|
||||||
`quote!` 宏让我们编写我们想要返回的代码,并可以将其传入进 `quote::Tokens` 。这个宏也提供了一些非常酷的模板机制;我们可以写 `#name` ,然后 `quote!` 会以 名为 `name` 的变量值来替换它。你甚至可以做些与这个正则宏任务类似的重复事情。查阅 [the `quote` crate’s
|
|
||||||
docs][quote-docs] 来获取详尽的介绍。
|
|
||||||
|
|
||||||
[quote-docs]: https://docs.rs/quote
|
|
||||||
|
|
||||||
我们期望我们的过程式宏能够为通过 `#name` 获取到的用户注解类型生成 `HelloMacro` trait 的实现。该 trait 的实现有一个函数 `hello_macro` ,其函数体包括了我们期望提供的功能:打印 `Hello, Macro! My name is` 和注解的类型名。
|
|
||||||
|
|
||||||
此处所使用的 `stringify!` 为 Rust 内置宏。其接收一个 Rust 表达式,如 `1 + 2` , 然后在编译时将表达式转换为一个字符串常量,如 `"1 + 2"` 。这与 `format!` 或 `println!` 是不同的,它计算表达式并将结果转换为 `String` 。有一种可能的情况是,所输入的 `#name` 可能是一个需要打印的表达式,因此我们用 `stringify!` 。 `stringify!` 编译时也保留了一份将 `#name` 转换为字符串之后的内存分配。
|
|
||||||
|
|
||||||
此时,`cargo build` 应该都能成功编译 `hello_macro` 和 `hello_macro_derive` 。我们将这些 crate 连接到示例 D-2 的代码中来看看过程式宏的行为。在 *projects* 目录下用 `cargo new pancakes` 命令新建一个二进制项目。需要将 `hello_macro` 和 `hello_macro_derive` 作为依赖加到 `pancakes` 包的 *Cargo.toml* 文件中去。如果你正将 `hello_macro` 和 `hello_macro_derive` 的版本发布到 [*https://crates.io/*][crates.io] 上,其应为正规依赖;如果不是,则可以像下面这样将其指定为 `path` 依赖:
|
|
||||||
|
|
||||||
[crates.io]: https://crates.io/
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[dependencies]
|
|
||||||
hello_macro = { path = "../hello_macro" }
|
|
||||||
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }
|
|
||||||
```
|
|
||||||
|
|
||||||
把示例 D-2 中的代码放在 *src/main.rs* ,然后执行 `cargo run` : 其应该打印 `Hello, Macro! My name is Pancakes!` 。从过程式宏中实现的 `HelloMacro` trait 被包括在内,但并不包含 `pancakes` 的包,需要实现它。`#[derive(HelloMacro)]` 添加了该 trait 的实现。<!-- 中间句子翻译不是太好 -->
|
|
||||||
|
|
||||||
### 宏的前景
|
|
||||||
|
|
||||||
在将来,Rust 仍会扩展声明式宏和过程式宏。Rust会通过 `macro` 使用一个更好的声明式宏系统,以及为较之 `derive` 的更强大的任务增加更多的过程式宏类型。在本书出版时,这些系统仍然在开发中,请查阅 Rust 在线文档以获取最新信息。
|
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
本附录,我们将讨论 Rust 项目提供的用于开发 Rust 代码的工具。
|
本附录,我们将讨论 Rust 项目提供的用于开发 Rust 代码的工具。
|
||||||
|
|
||||||
## 通过 `rustfmt` 自动格式化
|
### 通过 `rustfmt` 自动格式化
|
||||||
|
|
||||||
`rustfmt` 工具根据社区代码风格格式化代码。很多项目使用 `rustfmt` 来避免编写 Rust 风格的争论:所有人都用这个工具格式化代码!
|
`rustfmt` 工具根据社区代码风格格式化代码。很多项目使用 `rustfmt` 来避免编写 Rust 风格的争论:所有人都用这个工具格式化代码!
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ $ cargo fmt
|
|||||||
|
|
||||||
[rustfmt]: https://github.com/rust-lang-nursery/rustfmt
|
[rustfmt]: https://github.com/rust-lang-nursery/rustfmt
|
||||||
|
|
||||||
## 通过 `rustfix` 修复代码
|
### 通过 `rustfix` 修复代码
|
||||||
|
|
||||||
如果你编写过 Rust 代码,那么你可能见过编译器警告。例如,考虑如下代码:
|
如果你编写过 Rust 代码,那么你可能见过编译器警告。例如,考虑如下代码:
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ fn main() {
|
|||||||
|
|
||||||
`cargo fix` 命令可以用于在不同 Rust 版本间迁移代码。版本在附录 E 中介绍。
|
`cargo fix` 命令可以用于在不同 Rust 版本间迁移代码。版本在附录 E 中介绍。
|
||||||
|
|
||||||
## 通过 `clippy` 提供更多 lint 功能
|
### 通过 `clippy` 提供更多 lint 功能
|
||||||
|
|
||||||
`clippy` 工具是一系列 lint 的集合,用于捕捉常见错误和改进 Rust 代码。
|
`clippy` 工具是一系列 lint 的集合,用于捕捉常见错误和改进 Rust 代码。
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ fn main() {
|
|||||||
|
|
||||||
[clippy]: https://github.com/rust-lang/rust-clippy
|
[clippy]: https://github.com/rust-lang/rust-clippy
|
||||||
|
|
||||||
## 使用 Rust Language Server 的 IDE 集成
|
### 使用 Rust Language Server 的 IDE 集成
|
||||||
|
|
||||||
为了帮助 IDE 集成,Rust 项目分发了 `rls`,其为 Rust Language Server 的缩写。这个工具采用 [Language Server Protocol][lsp],这是一个 IDE 与编程语言沟通的规格说明。`rls` 可以用于不同的客户端,比如 [Visual Studio: Code 的 Rust 插件][vscode]。
|
为了帮助 IDE 集成,Rust 项目分发了 `rls`,其为 Rust Language Server 的缩写。这个工具采用 [Language Server Protocol][lsp],这是一个 IDE 与编程语言沟通的规格说明。`rls` 可以用于不同的客户端,比如 [Visual Studio: Code 的 Rust 插件][vscode]。
|
||||||
|
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
## 附录E - 本书翻译
|
|
||||||
|
|
||||||
> [appendix-05-translation.md](https://github.com/rust-lang/book/blob/master/second-edition/src/appendix-05-translation.md)
|
|
||||||
> <br />
|
|
||||||
> commit 9c3756a33e6c1aa07a762ffa853fcdfcffb48ddc
|
|
||||||
|
|
||||||
一些非英语语言的资源。多数仍在翻译中;查阅[翻译标签][label]来帮助我们或使我们知道新的翻译!
|
|
||||||
|
|
||||||
[label]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations
|
|
||||||
|
|
||||||
- [Português](https://github.com/rust-br/rust-book-pt-br) (BR)
|
|
||||||
- [Português](https://github.com/nunojesus/rust-book-pt-pt) (PT)
|
|
||||||
- [Tiếng việt](https://github.com/hngnaig/rust-lang-book/tree/vi-VN)
|
|
||||||
- [简体中文](https://github.com/KaiserY/trpl-zh-cn)
|
|
||||||
- [Українська](https://github.com/pavloslav/rust-book-uk-ua)
|
|
||||||
- [Español](https://github.com/thecodix/book)
|
|
||||||
- [Italiano](https://github.com/AgeOfWar/rust-book-it)
|
|
||||||
- [Русский](https://github.com/iDeBugger/rust-book-ru)
|
|
||||||
- [한국어](https://github.com/rinthel/rust-lang-book-ko)
|
|
||||||
- [日本語](https://github.com/hazama-yuinyan/book)
|
|
||||||
- [Français](https://github.com/quadrifoglio/rust-book-fr)
|
|
||||||
- [Polski](https://github.com/paytchoo/book-pl)
|
|
||||||
- [עברית](https://github.com/idanmel/rust-book-heb)
|
|
||||||
- [Cebuano](https://github.com/agentzero1/book)
|
|
||||||
- [Tagalog](https://github.com/josephace135/book)
|
|
@ -1,113 +0,0 @@
|
|||||||
## F - 最新功能
|
|
||||||
|
|
||||||
> [appendix-06-newest-features.md](https://github.com/rust-lang/book/blob/master/second-edition/src/appendix-06-newest-features.md)
|
|
||||||
> <br />
|
|
||||||
> commit b64de01431cdf1020ad3358d2f83e46af68a39ed
|
|
||||||
|
|
||||||
自从本书的主要部分完成之后,该附录文档中的特性已经加到 Rust 的稳定版中。
|
|
||||||
|
|
||||||
### 字段初始化缩写
|
|
||||||
|
|
||||||
我们可以通过具名字段来初始化一个数据结构(结构体、枚举、联合体),如将 `fieldname: fieldname` 缩写为 `fieldname` 。这可以以更少的重复代码来完成一个复杂的初始化语法。
|
|
||||||
|
|
||||||
```rust
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Person {
|
|
||||||
name: String,
|
|
||||||
age: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let name = String::from("Peter");
|
|
||||||
let age = 27;
|
|
||||||
|
|
||||||
// Using full syntax:
|
|
||||||
let peter = Person { name: name, age: age };
|
|
||||||
|
|
||||||
let name = String::from("Portia");
|
|
||||||
let age = 27;
|
|
||||||
|
|
||||||
// Using field init shorthand:
|
|
||||||
let portia = Person { name, age };
|
|
||||||
|
|
||||||
println!("{:?}", portia);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## 从循环中返回( loop )
|
|
||||||
|
|
||||||
`loop` 的用法之一是重试一个可以操作,比如检查线程是否完成其任务。然而可能需要将该操作的结果传到其他部分代码。如果加上 `break` 表达式来停止循环,则会从循环中断中返回:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn main() {
|
|
||||||
let mut counter = 0;
|
|
||||||
|
|
||||||
let result = loop {
|
|
||||||
counter += 1;
|
|
||||||
|
|
||||||
if counter == 10 {
|
|
||||||
break counter * 2;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(result, 20);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## `use` 声明中的内置组
|
|
||||||
|
|
||||||
如果有一个包含许多不同子模块的复杂模块树,然后需要从每个子模块中引入几个特性,那么将所有导入模块放在同一声明中来保持代码清洁同时避免根模块名重复将会非常有用。
|
|
||||||
|
|
||||||
`use` 声明所支持的嵌套在这些情况下对你有帮助:简单的导入和全局导入。例如,下面的代码片段导入了 `bar` 、 `Foo` 、 `baz` 中所有项和 `Bar` :
|
|
||||||
|
|
||||||
```rust
|
|
||||||
# #![allow(unused_imports, dead_code)]
|
|
||||||
#
|
|
||||||
# mod foo {
|
|
||||||
# pub mod bar {
|
|
||||||
# pub type Foo = ();
|
|
||||||
# }
|
|
||||||
# pub mod baz {
|
|
||||||
# pub mod quux {
|
|
||||||
# pub type Bar = ();
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
use foo::{
|
|
||||||
bar::{self, Foo},
|
|
||||||
baz::{*, quux::Bar},
|
|
||||||
};
|
|
||||||
#
|
|
||||||
# fn main() {}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 范围包含
|
|
||||||
|
|
||||||
先前,当在表达式中使用范围( `..` 或 `...` )时,其必须使用排除上界的 `..` ,而在模式中,则要使用包含上界的 `...` 。而现在,`..=` 可作为语法用于表达式或范围上下文件中的包含范围中。
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn main() {
|
|
||||||
for i in 0 ..= 10 {
|
|
||||||
match i {
|
|
||||||
0 ..= 5 => println!("{}: low", i),
|
|
||||||
6 ..= 10 => println!("{}: high", i),
|
|
||||||
_ => println!("{}: out of range", i),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`...` 语法也可用在匹配语法中,但不能用于表达式中,而 `..=` 则二者皆可。
|
|
||||||
|
|
||||||
## 128 字节的整数
|
|
||||||
|
|
||||||
Rust 1.26.0 添加了 128 字节的整数基本类型:
|
|
||||||
|
|
||||||
- `u128`: 一个在 [0, 2^128 - 1] 范围内的 128 字节的无符号整数
|
|
||||||
- `i128`: 一个在 [-(2^127), 2^127 - 1] 范围内的有符号整数
|
|
||||||
|
|
||||||
这俩基本类型由 LLVM 支持高效地实现。即使在不支持 128 字节整数的平台上,它们都是可用的,且可像其他整数类型那样使用。
|
|
||||||
|
|
||||||
这俩基本类型在那些需要高效使用大整数的算法中非常有用,如某些加密算法。
|
|
@ -4,7 +4,7 @@
|
|||||||
> <br>
|
> <br>
|
||||||
> commit 0aa307c7d79d2cbf83cdf5d47780b2904e9cb03f
|
> commit 0aa307c7d79d2cbf83cdf5d47780b2904e9cb03f
|
||||||
|
|
||||||
> 注意:本书的版本与出版的 [The Rust Programming Language][nsprust]
|
> 注意:本书的版本与出版的 [The Rust Programming Language][nsprust]
|
||||||
> 和电子版的 [No Starch Press][nsp] 一致
|
> 和电子版的 [No Starch Press][nsp] 一致
|
||||||
|
|
||||||
[nsprust]: https://nostarch.com/rust
|
[nsprust]: https://nostarch.com/rust
|
||||||
|
@ -138,4 +138,4 @@ $ ./main # Windows 是 .\main.exe
|
|||||||
|
|
||||||
仅仅使用 `rustc` 编译简单程序是没问题的,不过随着项目的增长,你可能需要管理你项目的方方面面,并让代码易于分享。接下来,我们要介绍一个叫做 Cargo 的工具,它会帮助你编写真实世界中的 Rust 程序。
|
仅仅使用 `rustc` 编译简单程序是没问题的,不过随着项目的增长,你可能需要管理你项目的方方面面,并让代码易于分享。接下来,我们要介绍一个叫做 Cargo 的工具,它会帮助你编写真实世界中的 Rust 程序。
|
||||||
|
|
||||||
[troubleshooting]: ch01-01-installation.html#troubleshooting
|
[troubleshooting]: ch01-01-installation.html#troubleshooting
|
||||||
|
@ -161,4 +161,4 @@ $ cargo build
|
|||||||
|
|
||||||
是时候通过构建更实质性的程序来熟悉读写 Rust 代码了。所以在第二章我们会构建一个猜猜看游戏程序。如果你更愿意从学习 Rust 常用的编程概念开始,请阅读第三章,接着再回到第二章。
|
是时候通过构建更实质性的程序来熟悉读写 Rust 代码了。所以在第二章我们会构建一个猜猜看游戏程序。如果你更愿意从学习 Rust 常用的编程概念开始,请阅读第三章,接着再回到第二章。
|
||||||
|
|
||||||
[installation]: ch01-01-installation.html#installation
|
[installation]: ch01-01-installation.html#installation
|
||||||
|
@ -383,4 +383,4 @@ fn main() {
|
|||||||
[comparing-the-guess-to-the-secret-number]:
|
[comparing-the-guess-to-the-secret-number]:
|
||||||
ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number
|
ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number
|
||||||
[quitting-after-a-correct-guess]:
|
[quitting-after-a-correct-guess]:
|
||||||
ch02-00-guessing-game-tutorial.html#quitting-after-a-correct-guess
|
ch02-00-guessing-game-tutorial.html#quitting-after-a-correct-guess
|
||||||
|
@ -21,7 +21,7 @@ Rust 的核心功能(之一)是 **所有权**(*ownership*)。虽然该
|
|||||||
> 栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据,要改为存储在堆上。堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 **指针**(*pointer*)。这个过程称作 **在堆上分配内存**(*allocating on the heap*),有时简称为 “分配”(allocating)。将数据推入栈中并不被认为是分配。因为指针的大小是已知并且固定的,你可以将指针存储在栈上,不过当需要实际数据时,必须访问指针。
|
> 栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据,要改为存储在堆上。堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 **指针**(*pointer*)。这个过程称作 **在堆上分配内存**(*allocating on the heap*),有时简称为 “分配”(allocating)。将数据推入栈中并不被认为是分配。因为指针的大小是已知并且固定的,你可以将指针存储在栈上,不过当需要实际数据时,必须访问指针。
|
||||||
>
|
>
|
||||||
> 想象一下去餐馆就座吃饭。当进入时,你说明有几个人,餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了,他们也可以通过询问来找到你们坐在哪。
|
> 想象一下去餐馆就座吃饭。当进入时,你说明有几个人,餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了,他们也可以通过询问来找到你们坐在哪。
|
||||||
>
|
>
|
||||||
> 入栈比在堆上分配内存要快,因为(入栈时)操作系统无需为存储新数据去搜索内存空间;其位置总是在栈顶。相比之下,在堆上分配内存则需要更多的工作,这是因为操作系统必须首先找到一块足够存放数据的内存空间,并接着做一些记录为下一次分配做准备。
|
> 入栈比在堆上分配内存要快,因为(入栈时)操作系统无需为存储新数据去搜索内存空间;其位置总是在栈顶。相比之下,在堆上分配内存则需要更多的工作,这是因为操作系统必须首先找到一块足够存放数据的内存空间,并接着做一些记录为下一次分配做准备。
|
||||||
>
|
>
|
||||||
> 访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问。现代处理器在内存中跳转越少就越快(缓存)。继续类比,假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜,接着桌子 B 听一个菜,然后再桌子 A,然后再桌子 B 这样的流程会更加缓慢。出于同样原因,处理器在处理的数据彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。在堆上分配大量的空间也可能消耗时间。
|
> 访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问。现代处理器在内存中跳转越少就越快(缓存)。继续类比,假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜,接着桌子 B 听一个菜,然后再桌子 A,然后再桌子 B 这样的流程会更加缓慢。出于同样原因,处理器在处理的数据彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。在堆上分配大量的空间也可能消耗时间。
|
||||||
@ -292,7 +292,7 @@ fn main() {
|
|||||||
let s2 = String::from("hello"); // s2 进入作用域
|
let s2 = String::from("hello"); // s2 进入作用域
|
||||||
|
|
||||||
let s3 = takes_and_gives_back(s2); // s2 被移动到
|
let s3 = takes_and_gives_back(s2); // s2 被移动到
|
||||||
// takes_and_gives_back 中,
|
// takes_and_gives_back 中,
|
||||||
// 它也将返回值移给 s3
|
// 它也将返回值移给 s3
|
||||||
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
|
} // 这里, s3 移出作用域并被丢弃。s2 也移出作用域,但已被移走,
|
||||||
// 所以什么也不会发生。s1 移出作用域并被丢弃
|
// 所以什么也不会发生。s1 移出作用域并被丢弃
|
||||||
|
@ -195,4 +195,4 @@ Rust 为我们提供了很多可以通过 `derive` 注解来使用的 trait,
|
|||||||
|
|
||||||
我们的 `area` 函数是非常特殊的,它只计算长方形的面积。如果这个行为与 `Rectangle` 结构体再结合得更紧密一些就更好了,因为它不能用于其他类型。现在让我们看看如何继续重构这些代码,来将 `area` 函数协调进 `Rectangle` 类型定义的 `area` **方法** 中。
|
我们的 `area` 函数是非常特殊的,它只计算长方形的面积。如果这个行为与 `Rectangle` 结构体再结合得更紧密一些就更好了,因为它不能用于其他类型。现在让我们看看如何继续重构这些代码,来将 `area` 函数协调进 `Rectangle` 类型定义的 `area` **方法** 中。
|
||||||
|
|
||||||
[the-tuple-type]: ch03-02-data-types.html#the-tuple-type
|
[the-tuple-type]: ch03-02-data-types.html#the-tuple-type
|
||||||
|
@ -187,4 +187,4 @@ Rust 在编译时就必须准确的知道 vector 中类型的原因在于它需
|
|||||||
|
|
||||||
现在我们了解了一些使用 vector 的最常见的方式,请一定去看看标准库中 `Vec` 定义的很多其他实用方法的 API 文档。例如,除了 `push` 之外还有一个 `pop` 方法,它会移除并返回 vector 的最后一个元素。让我们继续下一个集合类型:`String`!
|
现在我们了解了一些使用 vector 的最常见的方式,请一定去看看标准库中 `Vec` 定义的很多其他实用方法的 API 文档。例如,除了 `push` 之外还有一个 `pop` 方法,它会移除并返回 vector 的最后一个元素。让我们继续下一个集合类型:`String`!
|
||||||
|
|
||||||
[deref]: ch15-02-deref.html#following-the-pointer-to-the-value-with-the-dereference-operator
|
[deref]: ch15-02-deref.html#following-the-pointer-to-the-value-with-the-dereference-operator
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
突然有一天,代码出问题了,而你对此束手无策。对于这种情况,Rust 有 `panic!`宏。当执行这个宏时,程序会打印出一个错误信息,展开并清理栈数据,然后接着退出。出现这种情况的场景通常是检测到一些类型的 bug,而且程序员并不清楚该如何处理它。
|
突然有一天,代码出问题了,而你对此束手无策。对于这种情况,Rust 有 `panic!`宏。当执行这个宏时,程序会打印出一个错误信息,展开并清理栈数据,然后接着退出。出现这种情况的场景通常是检测到一些类型的 bug,而且程序员并不清楚该如何处理它。
|
||||||
|
|
||||||
> ### 对应 panic 时的栈展开或终止
|
> ### 对应 panic 时的栈展开或终止
|
||||||
>
|
>
|
||||||
> 当出现 panic 时,程序默认会开始 **展开**(*unwinding*),这意味着 Rust 会回溯栈并清理它遇到的每一个函数的数据,不过这个回溯并清理的过程有很多工作。另一种选择是直接 **终止**(*abort*),这会不清理数据就退出程序。那么程序所使用的内存需要由操作系统来清理。如果你需要项目的最终二进制文件越小越好,panic 时通过在 *Cargo.toml* 的 `[profile]` 部分增加 `panic = 'abort'`,可以由展开切换为终止。例如,如果你想要在release模式中 panic 时直接终止:
|
> 当出现 panic 时,程序默认会开始 **展开**(*unwinding*),这意味着 Rust 会回溯栈并清理它遇到的每一个函数的数据,不过这个回溯并清理的过程有很多工作。另一种选择是直接 **终止**(*abort*),这会不清理数据就退出程序。那么程序所使用的内存需要由操作系统来清理。如果你需要项目的最终二进制文件越小越好,panic 时通过在 *Cargo.toml* 的 `[profile]` 部分增加 `panic = 'abort'`,可以由展开切换为终止。例如,如果你想要在release模式中 panic 时直接终止:
|
||||||
>
|
>
|
||||||
> ```toml
|
> ```toml
|
||||||
|
@ -133,7 +133,7 @@ println!("New article available! {}", article.summarize());
|
|||||||
|
|
||||||
为 `summarize` 创建默认实现并不要求对示例 10-13 中 `Tweet` 上的 `Summary` 实现做任何改变。其原因是重载一个默认实现的语法与实现没有默认实现的 trait 方法的语法一样。
|
为 `summarize` 创建默认实现并不要求对示例 10-13 中 `Tweet` 上的 `Summary` 实现做任何改变。其原因是重载一个默认实现的语法与实现没有默认实现的 trait 方法的语法一样。
|
||||||
|
|
||||||
默认实现允许调用相同 trait 中的其他方法,哪怕这些方法没有默认实现。如此,trait 可以提供很多有用的功能而只需要实现指定一小部分内容。例如,我们可以定义 `Summary` trait,使其具有一个需要实现的 `summarize_author` 方法,然后定义一个 `summarize` 方法,此方法的默认实现调用 `summarize_author` 方法:
|
默认实现允许调用相同 trait 中的其他方法,哪怕这些方法没有默认实现。如此,trait 可以提供很多有用的功能而只需要实现指定一小部分内容。例如,我们可以定义 `Summary` trait,使其具有一个需要实现的 `summarize_author` 方法,然后定义一个 `summarize` 方法,此方法的默认实现调用 `summarize_author` 方法:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
pub trait Summary {
|
pub trait Summary {
|
||||||
|
@ -28,4 +28,4 @@ Rust 的运行速度、安全性、单二进制文件输出和跨平台支持使
|
|||||||
[ch10]: ch10-00-generics.html
|
[ch10]: ch10-00-generics.html
|
||||||
[ch11]: ch11-00-testing.html
|
[ch11]: ch11-00-testing.html
|
||||||
[ch13]: ch13-00-functional-features.html
|
[ch13]: ch13-00-functional-features.html
|
||||||
[ch17]: ch17-00-oop.html
|
[ch17]: ch17-00-oop.html
|
||||||
|
@ -99,4 +99,4 @@ In file sample.txt
|
|||||||
好的,它可以工作!我们将所需的参数值保存进了对应的变量中。之后会增加一些错误处理来应对类似用户没有提供参数的情况,不过现在我们将忽略他们并开始增加读取文件功能。
|
好的,它可以工作!我们将所需的参数值保存进了对应的变量中。之后会增加一些错误处理来应对类似用户没有提供参数的情况,不过现在我们将忽略他们并开始增加读取文件功能。
|
||||||
|
|
||||||
[ch13]: ch13-00-functional-features.html
|
[ch13]: ch13-00-functional-features.html
|
||||||
[ch7-idiomatic-use]: ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths
|
[ch7-idiomatic-use]: ch07-04-bringing-paths-into-scope-with-the-use-keyword.html#creating-idiomatic-use-paths
|
||||||
|
@ -261,4 +261,4 @@ ch10-03-lifetime-syntax.html#validating-references-with-lifetimes
|
|||||||
[ch11-anatomy]: ch11-01-writing-tests.html#the-anatomy-of-a-test-function
|
[ch11-anatomy]: ch11-01-writing-tests.html#the-anatomy-of-a-test-function
|
||||||
[ch10-lifetimes]: ch10-03-lifetime-syntax.html
|
[ch10-lifetimes]: ch10-03-lifetime-syntax.html
|
||||||
[ch3-iter]: ch03-05-control-flow.html#looping-through-a-collection-with-for
|
[ch3-iter]: ch03-05-control-flow.html#looping-through-a-collection-with-for
|
||||||
[ch13-iterators]: ch13-02-iterators.html
|
[ch13-iterators]: ch13-02-iterators.html
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
> commit 7b23a000fc511d985069601eb5b09c6017e609eb
|
> commit 7b23a000fc511d985069601eb5b09c6017e609eb
|
||||||
|
|
||||||
在第八章中,我们谈到了 vector 只能存储同种类型元素的局限。示例 8-10 中提供了一个定义 `SpreadsheetCell` 枚举来储存整型,浮点型和文本成员的替代方案。这意味着可以在每个单元中储存不同类型的数据,并仍能拥有一个代表一排单元的 vector。这在当编译代码时就知道希望可以交替使用的类型为固定集合的情况下是完全可行的。
|
在第八章中,我们谈到了 vector 只能存储同种类型元素的局限。示例 8-10 中提供了一个定义 `SpreadsheetCell` 枚举来储存整型,浮点型和文本成员的替代方案。这意味着可以在每个单元中储存不同类型的数据,并仍能拥有一个代表一排单元的 vector。这在当编译代码时就知道希望可以交替使用的类型为固定集合的情况下是完全可行的。
|
||||||
|
|
||||||
然而有时我们希望库用户在特定情况下能够扩展有效的类型集合。为了展示如何实现这一点,这里将创建一个图形用户接口(Graphical User Interface, GUI)工具的例子,它通过遍历列表并调用每一个项目的 `draw` 方法来将其绘制到屏幕上 —— 此乃一个 GUI 工具的常见技术。我们将要创建一个叫做 `gui` 的库 crate,它含一个 GUI 库的结构。这个 GUI 库包含一些可供开发者使用的类型,比如 `Button` 或 `TextField`。在此之上,`gui` 的用户希望创建自定义的可以绘制于屏幕上的类型:比如,一个程序员可能会增加 `Image`,另一个可能会增加 `SelectBox`。
|
然而有时我们希望库用户在特定情况下能够扩展有效的类型集合。为了展示如何实现这一点,这里将创建一个图形用户接口(Graphical User Interface, GUI)工具的例子,它通过遍历列表并调用每一个项目的 `draw` 方法来将其绘制到屏幕上 —— 此乃一个 GUI 工具的常见技术。我们将要创建一个叫做 `gui` 的库 crate,它含一个 GUI 库的结构。这个 GUI 库包含一些可供开发者使用的类型,比如 `Button` 或 `TextField`。在此之上,`gui` 的用户希望创建自定义的可以绘制于屏幕上的类型:比如,一个程序员可能会增加 `Image`,另一个可能会增加 `SelectBox`。
|
||||||
|
|
||||||
这个例子中并不会实现一个功能完善的 GUI 库,不过会展示其中各个部分是如何结合在一起的。编写库的时候,我们不可能知晓并定义所有其他程序员希望创建的类型。我们所知晓的是 `gui` 需要记录一系列不同类型的值,并需要能够对其中每一个值调用 `draw` 方法。这里无需知道调用 `draw` 方法时具体会发生什么,只要该值会有那个方法可供我们调用。
|
这个例子中并不会实现一个功能完善的 GUI 库,不过会展示其中各个部分是如何结合在一起的。编写库的时候,我们不可能知晓并定义所有其他程序员希望创建的类型。我们所知晓的是 `gui` 需要记录一系列不同类型的值,并需要能够对其中每一个值调用 `draw` 方法。这里无需知道调用 `draw` 方法时具体会发生什么,只要该值会有那个方法可供我们调用。
|
||||||
|
@ -195,4 +195,4 @@ fn main() {
|
|||||||
现在我们见过了很多使用模式的方式了,不过模式在每个使用它的地方并不以相同的方式工作;在一些地方,模式必须是 *irrefutable* 的,意味着他们必须匹配所提供的任何值。在另一些情况,他们则可以是 refutable 的。接下来让我们讨论这两个概念。
|
现在我们见过了很多使用模式的方式了,不过模式在每个使用它的地方并不以相同的方式工作;在一些地方,模式必须是 *irrefutable* 的,意味着他们必须匹配所提供的任何值。在另一些情况,他们则可以是 refutable 的。接下来让我们讨论这两个概念。
|
||||||
|
|
||||||
[ignoring-values-in-a-pattern]:
|
[ignoring-values-in-a-pattern]:
|
||||||
ch18-03-pattern-syntax.html#ignoring-values-in-a-pattern
|
ch18-03-pattern-syntax.html#ignoring-values-in-a-pattern
|
||||||
|
@ -241,4 +241,4 @@ ch17-01-what-is-oo.html#encapsulation-that-hides-implementation-details
|
|||||||
ch06-02-match.html#the-match-control-flow-operator
|
ch06-02-match.html#the-match-control-flow-operator
|
||||||
[using-trait-objects-that-allow-for-values-of-different-types]:
|
[using-trait-objects-that-allow-for-values-of-different-types]:
|
||||||
ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types
|
ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types
|
||||||
[using-the-newtype-pattern]: ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
|
[using-the-newtype-pattern]: ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
|
||||||
|
@ -119,4 +119,4 @@ fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
|
|||||||
[advanced-traits]:
|
[advanced-traits]:
|
||||||
ch19-03-advanced-traits.html#advanced-traits
|
ch19-03-advanced-traits.html#advanced-traits
|
||||||
[using-trait-objects-that-allow-for-values-of-different-types]:
|
[using-trait-objects-that-allow-for-values-of-different-types]:
|
||||||
ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types
|
ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types
|
||||||
|
@ -36,7 +36,7 @@ let v: Vec<u32> = vec![1, 2, 3];
|
|||||||
|
|
||||||
也可以使用 `vec!` 宏来构造两个整数的 vector 或五个字符串 slice 的 vector 。但却无法使用函数做相同的事情,因为我们无法预先知道参数值的数量和类型。
|
也可以使用 `vec!` 宏来构造两个整数的 vector 或五个字符串 slice 的 vector 。但却无法使用函数做相同的事情,因为我们无法预先知道参数值的数量和类型。
|
||||||
|
|
||||||
在示例 19-28 中展示了一个 `vec!` 稍微简化的定义。
|
在示例 19-28 中展示了一个 `vec!` 稍微简化的定义。
|
||||||
|
|
||||||
<span class="filename">文件名: src/lib.rs</span>
|
<span class="filename">文件名: src/lib.rs</span>
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ pub fn some_name(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
### 如何编写自定义 `derive` 宏
|
### 如何编写自定义 `derive` 宏
|
||||||
|
|
||||||
让我们创建一个 `hello_macro` crate,其包含名为 `HelloMacro` 的 trait 和关联函数 `hello_macro`。不同于让 crate 的用户为其每一个类型实现 `HelloMacro` trait,我们将会提供一个过程式宏以便用户可以使用 `#[derive(HelloMacro)]` 注解他们的类型来得到 `hello_macro` 函数的默认实现。该默认实现会打印 `Hello, Macro! My name is TypeName!`,其中 `TypeName` 为定义了 trait 的类型名。换言之,我们会创建一个 crate,使程序员能够写类似示例 19-30 中的代码。
|
让我们创建一个 `hello_macro` crate,其包含名为 `HelloMacro` 的 trait 和关联函数 `hello_macro`。不同于让 crate 的用户为其每一个类型实现 `HelloMacro` trait,我们将会提供一个过程式宏以便用户可以使用 `#[derive(HelloMacro)]` 注解他们的类型来得到 `hello_macro` 函数的默认实现。该默认实现会打印 `Hello, Macro! My name is TypeName!`,其中 `TypeName` 为定义了 trait 的类型名。换言之,我们会创建一个 crate,使程序员能够写类似示例 19-30 中的代码。
|
||||||
|
|
||||||
<span class="filename">文件名: src/main.rs</span>
|
<span class="filename">文件名: src/main.rs</span>
|
||||||
|
|
||||||
|
@ -27,4 +27,4 @@ Rust 程序设计语言的 2018 Edition 包含许多的改进使得 Rust 更为
|
|||||||
|
|
||||||
[install]: ch01-01-installation.html
|
[install]: ch01-01-installation.html
|
||||||
[editions]: appendix-05-editions.html
|
[editions]: appendix-05-editions.html
|
||||||
[nsprust]: https://nostarch.com/rust
|
[nsprust]: https://nostarch.com/rust
|
||||||
|
Loading…
Reference in New Issue
Block a user