diff --git a/md/ch11.md b/md/ch11.md index 9c9e9d1..09ffcd2 100644 --- a/md/ch11.md +++ b/md/ch11.md @@ -47,7 +47,7 @@ assert_eq!(bytes, b"hello world\n"); ``` -这一章首先展示trait怎么使用、怎么工作、怎么定义自己的trait。但trait的用途比我们目前提到的更多。我们将使用它们给现有类型添加扩展的方法,甚至像`str`和`bool`这种内建类型也可以。我们将会解释为什么给一个类型添加trait不会消耗多余的内存,以及如何在没有虚方法开销的情况下使用trait。我们将看到一些Rust提供的用于操作符重载和其他特性的语言内建的trait。我们还将介绍`Self`类型、关联函数、关联类型。Rust从Haskell中提取了这三个特性,它们可以优雅地解决其他语言中需要通过变通的方法或者hack才能解决的问题。 +这一章首先展示trait怎么使用、怎么工作、怎么定义自己的trait。但trait的用途比我们目前提到的更多。我们将使用它们给现有类型添加扩展的方法,甚至像`str`和`bool`这种内建类型也可以。我们将会解释为什么给一个类型添加trait不会消耗多余的内存,以及如何在没有虚方法开销的情况下使用trait。我们将看到一些Rust提供的用于操作符重载和其他特性的语言内建的trait。我们还将介绍`Self`类型、关联函数、关联类型。Rust从Haskell中提取了这三个特性,它们可以优雅地解决其他语言中需要通过变通的方法或者hack才能解决的问题。 *泛型* 是Rust中另一种形式的多态。类似于C++的模板,一个泛型函数或类型可以用于多种不同的类型: ```Rust @@ -176,7 +176,7 @@ C++也有这种运行时的类型信息。它被称为 *虚表* 或者 *vtable* 类型`W`到底是什么取决于泛型函数如何被调用: ```Rust say_hello(&mut local_file)?; // 调用say_hello:: - say_hello(&mut bytes)?; // 调用say_hello> + say_hello(&mut bytes)?; // 调用say_hello::> ``` 当你把`&mut local_file`传递给泛型的`say_hello()`函数时,你实际是在调用`say_hello::()`。Rust会为这个函数生成机器码,机器码里还会调用`File::write_all()`和`File::flush()`。当你传递`&mut bytes`时,你实际是在调用`say_hello::>()`。Rust会为这个版本的函数生成单独的机器码,然后调用相应的`Vec`的方法。在这两种情况下,Rust都从参数的类型推导出类型`W`,这个过程被称为 *单态化(monomorphization)* ,编译器会自动进行处理。 @@ -334,7 +334,7 @@ trait对象就是解决方案: 相比与trait对象的行为,Rust直到运行时才能知道一个trait对象指向的值到底是什么类型。因此即使你传递了一个`Sink`,虚方法的调用开销和检查错误的开销仍然不可避免。 -泛型的第二个优势是有的trait不支持trait对象。trait只支持一部分特性,例如关联函数只能使用泛型,这样就完全排除了trait对象。当我们将到这些特性时会指出它们。 +泛型的第二个优势是有的trait不支持trait对象。trait只支持一部分特性,例如关联函数只能使用泛型,这样就完全排除了trait对象。当我们讲到这些特性时会指出它们。 泛型的第三个优势是可以很容易地一次给泛型类型参数添加多个trait约束,例如我们的`top_ten`函数就要求它的参数`T`要实现`Debug + Hash + Eq`。trait对象不能这么做:Rust不支持类似`&mut (dyn Debug + Hash + Eq)`这样的类型。(你可以用本章中稍后会讲到的”子trait”来实现类似的功能,但这样有点复杂。) @@ -655,7 +655,7 @@ trait对象实际上是为最简单的trait设计的,就是那种可以用Java ```Rust trait StringSet { - fn new() -> self + fn new() -> Self where Self: Sized; fn from_slice(strings: &[&str]) -> Self diff --git a/src/ch11.tex b/src/ch11.tex index 8f10055..1e59fd7 100644 --- a/src/ch11.tex +++ b/src/ch11.tex @@ -182,7 +182,7 @@ \subsection{泛型函数和类型参数} 类型\texttt{W}到底是什么取决于泛型函数如何被调用: \begin{minted}{Rust} say_hello(&mut local_file)?; // 调用say_hello:: - say_hello(&mut bytes)?; // 调用say_hello> + say_hello(&mut bytes)?; // 调用say_hello::> \end{minted} 当你把\texttt{\&mut local\_file}传递给泛型的\texttt{say\_hello()}函数时,你实际是在调用\\ @@ -347,7 +347,7 @@ \subsection{选择哪一种}\label{WhichToUse} 相比与trait对象的行为,Rust直到运行时才能知道一个trait对象指向的值到底是什么类型。因此即使你传递了一个\texttt{Sink},虚方法的调用开销和检查错误的开销仍然不可避免。 -泛型的第二个优势是有的trait不支持trait对象。trait只支持一部分特性,例如关联函数只能使用泛型,这样就完全排除了trait对象。当我们将到这些特性时会指出它们。 +泛型的第二个优势是有的trait不支持trait对象。trait只支持一部分特性,例如关联函数只能使用泛型,这样就完全排除了trait对象。当我们讲到这些特性时会指出它们。 泛型的第三个优势是可以很容易地一次给泛型类型参数添加多个trait约束,例如我们的\texttt{top\_ten}函数就要求它的参数\texttt{T}要实现\texttt{Debug + Hash + Eq}。trait对象不能这么做:Rust不支持类似\texttt{\&mut (dyn Debug + Hash + Eq)}这样的类型。(你可以用本章中稍后会讲到的\hyperref[subtrait]{子trait}来实现类似的功能,但这样有点复杂。) @@ -666,7 +666,7 @@ \subsection{类型关联函数} \begin{minted}{Rust} trait StringSet { - fn new() -> self + fn new() -> Self where Self: Sized; fn from_slice(strings: &[&str]) -> Self