diff --git a/projects/dst/src/main.rs b/projects/dst/src/main.rs index 19bcaab..f3e5c85 100644 --- a/projects/dst/src/main.rs +++ b/projects/dst/src/main.rs @@ -1,4 +1,6 @@ fn main() { - let s1: str = "致以问候!"; - let s2: str = "最近过得怎么样?"; + let s1: &str = "致以问候!"; + let s2: &str = "最近过得怎么样?"; + + println! ("{}, {}", s1.len(), s2.len()); } diff --git a/projects/never_type/src/main.rs b/projects/never_type/src/main.rs index 2071f77..a1af0fa 100644 --- a/projects/never_type/src/main.rs +++ b/projects/never_type/src/main.rs @@ -1,7 +1,10 @@ fn main() { print! ("永永 "); - loop { + let res = loop { print! ("远远 "); - } + break + }; + + println! ("{res:?}"); } diff --git a/projects/type_aliases/src/main.rs b/projects/type_aliases/src/main.rs index 0b5bb45..bb684bc 100644 --- a/projects/type_aliases/src/main.rs +++ b/projects/type_aliases/src/main.rs @@ -5,16 +5,4 @@ fn main() { let y: Kilometers = 5; assert_eq! (x, y); - - type Thunk = Box; - - let f: Thunk = Box::new(|| println! ("嗨")); - - fn takes_long_type(f: Thunk) { - // --跳过代码-- - } - - fn returns_long_type() -> Thunk { - // --跳过代码-- - } } diff --git a/src/advanced_features/adv_types.md b/src/advanced_features/adv_types.md index e4e8577..7d991ec 100644 --- a/src/advanced_features/adv_types.md +++ b/src/advanced_features/adv_types.md @@ -1,39 +1,33 @@ # 高级类型 -**Advanced Types** +Rust 类型系统有着一些我们到目前为止提到过,但尚未讨论的特性。我们将 + +- 首先概述新型模式,并讨论他们作为类型为何有用; +- 然后,我们将继续讨论类型别名,一项与新型模式类似的特性,但语义略有不同; +- 我们还将讨论 `!` 类型,以及动态长度的类型。 -Rust 的类型系统有着一些到目前为止咱们曾提到过但尚未讨论过的特性。咱们将以一般意义上检视新型模式作为类型为何有用,而讨论新型模式开始。随后咱们将移步到类型别名,一项与新型模式类似,不过有着些许不同语义的特性。咱们还将讨论 `!` 类型与动态大小的类型。 +## 类型安全与新型模式下的抽象 -## 为类型安全与抽象而运用新型模式 +这一小节假设咱们已经阅读了前面小节 [通过新型模式实现外部特质](./adv_traits.md#通过新型模式实现外部特质) 。新型模式对于我们迄今为止讨论过的任务也很有用,包括静态地强制值永不混淆,以及标明值的单位。在 [清单 20-16](./adv_traits.md#listing_20-16) 中,咱们已经看到使用新型表示单位的示例:回顾一下,`Millimeters` 与 `Meters` 两个结构体都将 `u32` 值封装在新型中。若我们编写了一个带有 `Millimeters` 类型参数的函数,我们将无法编译一个意外尝试以类型 `Meters` 或普通 `u32` 值调用该函数的程序。 -**Using the Newtype Pattern for Type Safety and Abstraction** +我们还可以使用新型模式来抽象出类型的一些实现细节:新类型可以暴露一个不同于私有的内层类型 API 的公开 API。 + +新型模式还可以隐藏内部实现。例如,我们可提供 `People` 类型来封装 `HashMap`,该 `HashMap` 存储与姓名关联的人员 ID。使用 `People` 的代码只会与我们提供的公开 API 交互,比如添加姓名字符串到 `People` 集合的方法;该代码无需知道我们在内部分配 `i32` 的 ID 给姓名。新型模式属于一种实现封装以隐藏实现细节的轻量级方法,我们在第 18 章中 [隐藏实现细节的封装](../oop/characteristics_oop.md#隐藏实现细节的封装) 小节中曾对此讨论过。 -> **注意**:此小节假定你已读过早先的 [“使用新型模式来再外层类型上实现外层的特质”](#使用新型模式在外层类型上实现外层的特质") 小节。 +## 类型同义词和类型别名 -对于那些超出到目前为止咱们曾讨论过的任务,包括静态强制要求值绝不会混淆,以及表明某个值的单位等等,新型模式同样是有用的。在清单 19-15 中,咱们就曾看到一个使用新型,表明单位的一个示例:回顾到 `Millimeters` 与 `Meters` 两个结构体,都曾将 `u32` 值封装在新型中。而若咱们编写了带有一个类型 `Millimeters` 参数的函数,那么咱们就无法编译某个偶然尝试以类型 `Meters` 或普通 `u32` 的值,调用那个函数的程序。 - - -咱们还可以使用新型模式,来抽象出某个类型的一些实现细节:新的类型可暴露处不同意私有内部类型 API 的一个公开 API。 - -新类型还可以隐藏内部实现。比如,咱们可提供一个 `People` 类型,来封装一个存储着某人与其名字关联的 ID 的 `HashMap`。使用 `People` 的代码,只需与咱们提供的公开 API,比如某个将名字字符串添加到 `People` 集合的方法交互;那些代码将不需要知悉咱们在内部分配了`i32` 的 ID 给那些名字。新型模式是达成,咱们曾在第 17 章讨论过的 [“隐藏实现细节的封装”](Ch17_Object_Oriented_Programming_Features_of_Rust.md#隐藏了实现细节的封装) 的一种轻量方式。 - - -## 使用类型别名创建类型同义词 - -**Creating Type Synonyms with Type Aliases** - - -Rust 提供给到既有类型另一个名字的声明 *类型别名,type alias* 的能力。为此,咱们要使用 `type` 关键字。比如,咱们可以像下面这样,创建到 `i32` 的别名 `Kilometers`: +Rust 提供了声明 *类型别名* 的能力,以便给予现有类型另一个名字。为此,我们使用 `type` 关键字。例如,我们可以像下面这样创建 `i32` 的别名 `Kilometers`: ```rust type Kilometers = i32; ``` -现在,别名 `Kilometers` 便是 `i32` 的同义词了;与在清单 19-15 中咱们曾创建的 `Millimeters` 与 `Meters` 两个类型不同,`Kilometers` 不是个单独的、新类型。有着类型 `Kilometers` 的那些值,将与类型 `i32` 的那些值做同样对待: +现在,别名 `Kilometers` 是 `i32` 的 *同义词*;与我们在 [清单 20-16](./adv_traits.md#listing_20-16) 中创建的 `Millimeters` 与 `Meters` 两个类型不同,`Kilometers` 并不是个单独的新类型。类型为 `Kilometers` 的值将被视为与类型 `i32` 的值相同: +文件名:`projects/type_aliases/src/main.rs` ```rust type Kilometers = i32; @@ -44,18 +38,17 @@ type Kilometers = i32; assert_eq! (x, y); ``` -由于 `Kilometers` 与 `i32` 为同样类型,因此咱们可将这两种类型的值相加,且咱们可将 `Kilometers` 值传递给取 `i32` 参数的那些函数。但是,在使用这种方法时,咱们不会获得咱们早先所讨论的新型模式中的类型检查的那些益处。换句话说,当咱们在一些地方混淆了 `Kilometers` 与 `i32` 时,编译器将不会给到咱们一个报错。 - -类型同义词的一种主要用例,是为减少重复。比如,咱们可能有下面这样一个冗长的类型: +由于 `Kilometers` 与 `i32` 属于同一类型,我们可以将这两种类型的值相加,并且可以传递 `Kilometers` 的值给取 `i32` 参数的函数。但是,使用这种方法,我们无法获得前面讨论过的新型模式中的类型检查优势。换句话说,当我们在某处混用了 `Kilometers` 与 `i32` 的值时,编译器也不会给予我们报错。 +类型同义词的主要用例是减少重复。例如,我们可能遇到如下这种冗长的类型: ```rust Box ``` +在函数签名中,以及作为代码各处的类型注解,写下这种冗长的类型,可能会很烦人且容易出错。设想一下,当某个项目全是像下面清单 20-25 中的代码。 -在函数签名中,以及在全部代码中作为类型注解编写这种冗长类型,就会令人疲倦而容易出错。设想有个全部是下面清单 19-24 中代码的项目: - + ```rust let f: Box = Box::new(|| println! ("嗨")); @@ -68,11 +61,11 @@ Box } ``` -*清单 19-24:在多处使用长类型* +**清单 20-25**:在多处使用冗长类型 +类型别名通过减少重复,使这段代码更易于管理。在下面清单 20-26 中,我们针对该冗长类型引入了一个名为 `Thunk` 的别名,并可以用这个更短的别名 `Thunk`替换该类型的所有使用。 -类型别名通过降低重复,而令到这样的代码更为可管理。在下面清单 19-25 中,咱们为那个冗长类型,引入了一个名为 `Thunk` 的别名,从而便可以使用这个更简短的别名 `Thunk`,替换全部的该种类型。 - + ```rust type Thunk = Box; @@ -87,11 +80,11 @@ Box } ``` -*清单 19-25:引入类型别名 `Thunk` 来减少重复* +**清单 20-26**:引入类型别名 `Thunk`,以减少重复 -这样的代码,阅读和编写起来要容易得多!给类型别名选择有意义的名字,也可以有助于表达咱们的意图( *形实替换,thunk* 是个表示会在稍后被计算执行,因此对于会被存储的闭包,其是个恰当的名字)。 +这段代码现在更容易月的和编写!为类型别名选择一个有意义的名字,也可以帮助传达咱们的意图( *thunk* 是指将在稍后执行的代码,因此对于一个被存储的闭包来说,这是个恰当的名字)。 -类型别名,还普遍用于 `Result` 下的消除重复。设想标准库中的 `std::io` 模组。I/O 操作经常会返回一个 `Result`,以处理操作失效时的情况。这个库有个表示了所有可能 I/O 错误的 `std::io::Error` 结构。`std::io` 中的许多函数,都会在那个 `E` 为 `std::io::Error` 下,返回 `Result`,比如 `Write` 特质中的这些函数: +类型别名也经常与 `Result` 一起使用,以减少重复。设想标准库中的 `std::io` 模组。I/O 操作通常返回 `Result`,来处理操作失败的情况。这个库有个 `std::io::Error` 结构体,表示所有可能的 I/O 错误的。`std::io` 中的许多函数都会返回 `Result`,其中 `E` 就是 `std::io::Error`,比如 `Write` 特质中的这些函数: ```rust use std::fmt; @@ -106,33 +99,30 @@ pub trait Write { } ``` -其中的 `Result<..., Error>` 就被重复了很多。由此,`std::io` 便有了下面这样的类型别名声明: +其中 `Result<..., Error>` 多次重复。因此,`std::io` 便包含了以下的类型别名声明: ```rust type Result = std::result::Result; ``` -由于这种声明是在 `std::io` 模组中,因此咱们就可以使用完全合格的别名 `std::io::Result`;那即是,带有 `E` 被填充为 `std::io::Error` 的 `Result`。那个 `Write` 特质的函数签名,最终看起来就像下面这样了: +由于这一声明位于 `std::io` 模组中,我们可以使用完全限定的别名 `std::io::Result`;也就是说,这是个 `Result`,其中 `E` 被填入为 `std::io::Error`。`Write` 特质的函数签名最终看起来像下面这样: ```rust pub trait Write { fn write(&mut self, buf: &[u8]) -> Result; fn flush(&mut self) -> Result<()>; - fn write_all(&mut self, buf: &[u8]) -> Result<(); + fn write_all(&mut self, buf: &[u8]) -> Result<()>; fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()>; } ``` -类型别名以这两种方式发挥作用:其令到代码更易于编写 *并* 在整个 `std::io` 层面给到咱们一个一致的接口。由于其为一个别名,因此他仅是另一个 `Result`,这意味着咱们可以与其一道使用那些全部工作于 `Result` 上的方法,以及诸如 `?` 运算符那样的特殊语法。 +类型别名有两种作用:他使代码更容易编写,*并* 给予我们对整个 `std::io` 的一致接口。由于他是个别名,因此他只是另一个 `Result`,这意味着我们可以时与哦嗯任何适用于 `Result` 的方法,以及诸如 `?` 运算符这样的特殊语法。 -## 永不返回的永不类型 +## 永不返回的 `never` 类型 -**The Never Type that Never Returns** - - -Rust 有着一种因其没有值,而因此在类型理论术语中,叫做 *空类型,empty type* 的名为 `!` 的类型。因为在某个函数绝不会返回值时,这个类型立于返回值类型处,所以咱们称其为 *永不类型,never type*。下面是个示例: +Rust 有一种名为 `!` 的特殊类型,在类型论术语种称为 *空类型*,因为他没有值。我们更倾向于称其为 *`never` 类型*,因为当函数永远不返回时,他会代替返回类型。下面是个示例: ```rust fn bar() -> ! { @@ -140,11 +130,11 @@ fn bar() -> ! { } ``` -此代码读作 “函数 `bar` 返回永不。” 返回永不的函数被称为 *发散函数,diverging functions*。咱们无法创建出类型 `!` 的值,因此 `bar` 就永不会有可能返回值。 +这段代码读作 “函数 `bar` 返回 `never`。” 返回 `never` 函数被称为 *发散函数*。我们无法创建 `!` 类型的值,因此 `bar` 永远不可能返回。 +但是,一种咱们永远无法针对其创建值的类型有什么用处呢?回顾 [清单 2-5](../Ch02_Programming_a_Guessing_Game.md#listing_2-5) 中的代码,那时猜数游戏的一部分;我们已在下面清单 20-27 中重现了其中一部分。 -然而一种咱们永不能创建出值的类型,到底有什么用处呢?回顾到清单 2-5 中,作为那个猜数游戏一部分的代码;咱们已在在下面清单 19-26 中,重现了他的一点点: - + ```rust let guess: u32 = match guess.trim().parse() { Ok(num) => num, @@ -152,10 +142,9 @@ fn bar() -> ! { }; ``` -*清单 19-26:有着一个以 `continue` 结束支臂的 `match` 表达式* - -那个时候,咱们跳过了此代码的一些细节。而在第 6 章中的 [“`match` 控制流运算符”](Ch06_Enums_and_Pattern_Matching.md#match-控制流结构) 小节,咱们曾讨论了 `match` 支臂必须全部返回同一类型。那么,比如说,下面的代码就不会工作: +**清单 20-27**:带有一个以 `continue` 结束的支臂的 `match` 表达式 +当时,我们跳过了这段代码种的一些细节。在第 6 章中 [`match` 控制流结构](../enums_and_pattern_matching/match_control_flow.md) 小节中,我们讨论了 `match` 支臂必须返回相同的类型。因此,例如,以下代码不会正常运行: ```rust let guess = match guess.trim().parse() { @@ -164,27 +153,30 @@ fn bar() -> ! { } ``` -此代码中的 `guess` 类型,将必须为整数与字符串,而 Rust 要求 `guess` 只有一种类型。那么 `continue` 到底返回的是什么呢?到底是怎样咱们才在清单 19-26 中,曾被允许从一个支臂返回一个 `u32`,并有着以 `continue` 结束另一个支臂的呢? +这段代码中的 `guess` 类型将必定是整数和字符串,而 Rust 要求 `guess` 只能有一种类型。那么,`continue` 返回什么呢?在清单 20-27 中,我们为何被允许在一个支臂中返回 `i32`,并让另一个支臂以 `continue` 结束呢? -描述这种行为的正式方式,即类型 `!` 的表达式,可被强制转换为任何别的类型。由于 `continue` 不会返回值,因此咱们就被允许以 `continue` 结束这个 `match` 支臂;相反,这个 `match` 支臂将控制移回到该循环的顶部,因此在 `Err` 情形下,咱们就绝不会赋给 `guess` 一个值。 +正如您可能已经猜到的,`continue` 有个 `!` 的值。也就是说,当 Rust 计算 `guess` 的类型时,他会同时查看两个匹配支臂,前者的值为 `u32`,后者的值为 `!` 值。由于 `!` 永远不会有值,Rust 因此确定 `guess` 的类型是 `u32`。 -在 `panic!` 宏下,这个永不类型也是有用的。回顾到咱们在 `Option` 值上调用 `unwrap` 函数来生成一个值,或在此定义下中止运行: +描述这种行为的正式说法是,类型为 `!` 的表达式可以被强制转换为任何其他类型。我们允许用以 `continue` 结束这个匹配支臂,因为 `continue` 不会返回值;相反,他会迁移控制权到该循环的顶部,因此在 `Err` 情形下下,我们永远不会指派值给 `guess`。 +`never` 类型对 `panic! 宏` 也很有用。回顾我们对 `Option` 值调用 `unwrap` 函数,以通过以下定义生成一个值或终止运行: ```rust impl Option { pub fn unwrap(self) -> { match self { Some(val) => val, - None => panic! ("在 `None` value 上调用了 `Option::unwrap()`"), + None => panic! ("called `Option::unwrap()` on a `None` value"), } } } ``` -此代码中,与清单 19-26 中那个 `match` 同样的事情发生了:Rust 会发现那个 `val` 有着类型 `T`,且 `panic!` 有着类型 `!`,因此整个 `match` 表达式的结果便是 `T`。此代码之所以有效,是由于 `panic!` 不会产生值;他会终止这个程序。在 `None` 情形下,咱们不会从 `unwrap` 返回值,所以此代码是有效的。 +在这段代码中,发生了与清单 20-27 中 `match` 表达式中相同的事情:Rust 发现 `val` 具有类型 `T`,而 `panic!` 有着类型 `!`,因此整个 `match` 表达式的结果是 `T`。这段代码之所以能正常运行,是因为 `panic!` 不会产生值;他会终止程序。在 `None` 情形下,我们不会从 `unwrap` 返回值,因此这段代码是有效的。 -最后一个有着类型 `!` 的表达式,则是一个 `loop`: +最后一个有着 `!` 类型的表达式是 `loop` 表达式: + +文件名:`projects/never_type/src/main.rs` ```rust print! ("永永 "); @@ -194,30 +186,43 @@ impl Option { } ``` -这里,那个循环永不会结束,因此 `!` 便是该表达式的值。但是,若咱们包含了一个 `break`,由于这个循环会在其到达 `break` 时终止,因此这就不再成立了。 +在这里,循环永远不会结束,因此 `!` 就是该表达式的值。然后,若我们包含一个 `break`,情况就不同了,因为循环在遇到 `break` 时就会终止。 +> **译注**:包含一个 `break` 时,该表达式的值为单元值 `()`。相关代码和打印输出如下。 +> +> 文件名:`projects/never_type/src/main.rs` +> +> ```rust +> print! ("永永 "); +> +> let res = loop { +> print! ("远远 "); +> break +> }; +> +> println! ("{res:?}"); +> ``` +> +> 打印输出:`永永 远远 ()` -## 动态大小的类型与 `Sized` 特质 +## 动态大小类型与 `Sized` 特质 -**Dynamically Sized Types and the `Sized` Trait** +Rust 需要知道其类型的某些详细信息,比如为特定类型的值分配多少空间。这使得其类型系统的一个方面乍看之下有些令人困惑:*动态大小类型,dynamically sized types* 的概念。这些类型有时又被称为 DSTs 或 *未知大小类型,unsized types*,他们允许我们编写出,使用只有在运行时才知道其长度的值的代码。 - -Rust 需要知道其类型的确切情况,比如给某种特定类型值分配多少的内存空间。在一开始这就给其类型系统的一个角落留下了一点混乱:那便是 *动态大小类型,dynamically sized types* 这个概念。此概念有时被称为 DSTs 或 *未知大小类型,unsized types*,这些类型让咱们编写出,使用了仅在运行时才知道其大小值的代码来。 - -下面来深入到名为 `str`,贯穿这本书咱们一直都在使用一个的动态大小类型细节。那正是 `str`,而非 `&str`,确实是个 DST。在运行时之前,咱们是无法掌握字符串有多长,就是说咱们无法创建出一个类型 `str` 的变量,也无法取类型 `str` 的参数。设想下面的这段无法工作的代码: +我们来深入探讨一下名为 `str` 的动态长度类型,我们在这整本书一直在使用他。没错,不是 `&str`,而是 `str` 本身就属于 DST。在许多情况下,比如存储用户输入的文本时,我们无法在运行时之前知道字符串的长度。这意味着我们既不能创建 `str` 类型的变量,也不能取 `str` 类型的参数。请考虑以下代码,他无法正常运行: ```rust let s1: str = "致以问候!"; let s2: str = "最近过得怎么样?"; ``` -Rust 需要清楚,要给特定类型的任何值分配多少内存,且某种类型的所有值,都必须使用同样数量的内存。若 Rust 运行咱们编写此代码,那么这两个 `str` 值就将需要占据同样数量的内存空间。但他们有着不同长度:`s1` 需要 15 字节的存储,而 `s2` 需要 `24` 字节。这就是为何创建保存动态大小类型值的变量不可行的原因。 +Rust 需要知道为特定类型的任何值分配多少内存,且一种类型的所有值都必须使用同样数量的内存。若 Rust 允许我们编写这段代码,那么这两个 `str` 值将需要占用相同数量的空间。但他们有着不同长度:`s1` 需要 15 字节的存储,而 `s2` 需要 `24` 字节。这就是为什么无法创建保存动态长度类型值的变量的原因。 -那么咱们要怎么做呢?在这种情况下,咱们就已经知道答案了:咱们要令到 `s1` 与 `s2` 的类型为 `&str` 而非 `str`。从第 4 章的 [“字符串切片”](Ch04_Understanding_Ownership.md#字符串切片) 小节,回顾到切片数据结构,只会存储其开始位置和切片的长度。因此尽管 `&T` 是存储了 `T` 所处内存地址的单个值,而一个 `&str` 则是 *两个* 值:`str` 的地址与其长度。如此,咱们就知道某个 `&str` 在编译时的大小了:其为 `uszie` 长度的两倍。那便是,咱们总是清楚 `&str` 的大小,而不管他所指向的字符串有多长。一般来说,这就是 Rust 中动态大小类型被运用的方式:他们有着存储了动态信息大小的额外的一点元数据。动态大小类型的黄金法则,就是咱们必须始终把那些动态大小类型的值,放置某种指针之后。 +那么,我们该怎么办?在这种情况下,咱们已经知道答案了:我们构造 `s1` 和 `s2` 的类型为 `&str`,而不是 `str`。回顾第 4 章中 [字符串切片](../ownership/the_slice_type.md#字符串切片) 小节,切片数据结构仅存储切片的起始位置和长度。因此,尽管 `&T` 属于存储 `T` 所在内存地址的单个值,但字符串切片则是 *两个* 值:`str` 的地址与其长度。正因如此,我们可以在编译时知道字符串切片值的长度: 他是 `uszie` 长度的两倍。也就是说,我们始终知道字符串切片的长度,无论他引用的字符串有多长。一般来说,Rust 中动态长度类型的使用方式是这样的:他们有着额外的元数据,存储动态信息的长度。动态长度类型的黄金法则是,我们必须始终将动态长度类型的值放在某种指针之后。 -咱们可将 `str` 与所有类别的指针结合:比如,`Box` 或 `Rc`。事实上,之前咱们就已经见到过这样的,只不过是在一种不同的动态大小类型下:那便是特质。每个特质都是咱们可以通过使用特质名字而加以引用的动态大小类型。在第 17 章中的 [“使用允许不同类型值的特质对象”](Ch17_Object_Oriented_Programming_Features_of_Rust.md#使用允许不同类型值的特质对象) 小节,咱们曾提到为了将特质用作特质对象,咱们就必须将其放在指针之后,比如 `&dyn Trait` 或 `Box` (`Rc` 也应生效)。 +我们可以将 `str` 与各种类别的指针结合:例如 `Box` 或 `Rc`。事实上,咱们之前已经见过这种情况,只不过以一种不同的动态长度类型:那便是特质。每个特质都属于动态长度类型,我们可以使用特质名字来引用他。在第 18 章中 [使用特质来抽象共用行为](../oop/trait_objects.md) 小节中,我们提到要将特质作为特质对象使用,就必须将其置于指针之后,比如 `&dyn Trait` 或 `Box` (`Rc` 也可以)。 -为处理 DSTs 相关问题,Rust 提供了 `Sized` 特质,来判断在编译时某个类型的大小是否已知。在运行时大小已知的全部物件,都已自动实现了这个特质。此外,Rust 会隐式地将 `Sized` 上的边界,添加到每个泛型函数。那就是说,像下面的一个泛型函数: +为了使用 DST,Rust 提供了 `Sized` 特质,来确定某种类型的长度是否在编译时已知。对于所有在编译时大小已知的类型,都会自动实现该特质。此外,Rust 会隐式地将一个 `Sized` 的边界添加到每个泛型函数。也就是说,像下面这样的一个泛型函数: ```rust fn generic(t: T) { @@ -225,8 +230,7 @@ fn generic(t: T) { } ``` -实际上会被如咱们像下面写的这样被对待: - +实际上会被视为我们写了这样一段代码: ```rust fn generic(t: T) { @@ -234,8 +238,7 @@ fn generic(t: T) { } ``` -默认情况下,泛型函数只将在那些编译时有着已知大小的类型上工作。但是,咱们可以使用下面的特殊语法来解除这种限制: - +默认情况下,泛型函数仅适用于编译时已知大小的类型。但是,咱们可以使用以下特殊语法来放宽这一限制: ```rust fn generic(t: &T) { @@ -243,11 +246,11 @@ fn generic(t: &T) { } ``` -在 `?Sized` 上的特质边界,表示 “`T` 可能是也可能不是 `Sized` 的”,而这样的注解就会重写泛型在编译时务必要有已知大小的默认限制。有着这种意义的 `?Trait` 语法,只对 `Sized` 可用,对其他任何特质都是不可用的。 +`?Sized` 的特质边界,意味着 “`T` 可能属于 `Sized` 类型,也可能不属于”,这种写法覆盖了 “泛型类型必须在编译时具有已知大小” 的默认规则。具有这种含义的 `?Trait` 语法,仅适用于 `Sized`,不适用于其他任何特质。 -还要注意咱们已将那个参数 `t` 的类型,从 `T` 更换为了 `&T`。由于这个类型可能不是 `Sized`,因此咱们就需要在某种指针之后使用他。在这种情况下,咱们选择了一个引用。 +另请注意,我们将参数 `t` 的类型从 `T` 改为 `&T`。由于该类型可能不是 `Sized`,我们需要在某种指针之后使用他。在这种情况下,我们选择了引用。 -接下来,咱们将谈谈函数与闭包! +接下来,咱们将讨论函数和闭包! (End)