🦀 Rust Trait 与泛型快问快答

测试你对 Rust Trait、泛型和生命周期的理解

10
题目数量
20
预计时间(分钟)
⭐⭐⭐⭐
难度等级
第1题 (判断题)
在Rust中,一个类型可以实现多个Trait。
正确
错误

答案解析:

答案:正确

Rust允许一个类型实现任意数量的Trait。这是Rust实现组合和行为复用的主要方式之一。

例如,一个类型可以同时实现 `Debug`、`Clone` 和自定义的 `Summary` Trait。

第2题 (选择题)
下面哪个关键字用于在函数参数中指定一个泛型类型必须实现某个Trait?
impl
where
trait
以上都对

答案解析:

答案:以上都对

在Rust中,有多种方式来约束泛型参数:

1. `impl Trait` 语法: `fn notify(item: &impl Summary)`

2. `where` 子句: `fn notify(item: &T) where T: Summary`

3. Trait Bound 语法: `fn notify(item: &T)`

`where`子句在有多个泛型参数和复杂约束时特别有用,可以使函数签名更清晰。

第3题 (选择题)
对于 `fn longest<'a>(x: &'a str, y: &'a str) -> &'a str`,生命周期参数 `'a` 的作用是什么?
确保x和y字符串的长度相同
确保返回的引用不会比x和y中生命周期更短的那个引用活得更长
确保x和y都是静态生命周期
这是一个编译时元数据,对运行时没有影响

答案解析:

答案:确保返回的引用不会比x和y中生命周期更短的那个引用活得更长

生命周期参数 `'a` 是泛型生命周期参数。它告诉编译器,返回的引用(`&'a str`)的生命周期与输入参数 `x` 和 `y` 中较短的那个生命周期相关联。

这可以防止悬垂引用,因为编译器会确保返回的引用在使用时,其所引用的数据(`x` 或 `y`)仍然是有效的。

第4题 (判断题)
Trait中可以包含具体的方法实现,也可以只包含方法签名。
正确
错误

答案解析:

答案:正确

Rust的Trait可以定义默认方法实现。如果一个类型实现了这个Trait,它可以选择使用默认实现,或者提供自己的实现来覆盖默认实现。

这为实现Trait的类型提供了便利,同时也保持了灵活性。

第5题 (选择题)
下面关于泛型和 `dyn Trait` (Trait Object) 的说法,哪个是错误的?
泛型在编译时进行静态分发,性能更高。
`dyn Trait` 在运行时进行动态分发,更灵活。
使用泛型会使最终的二进制文件更小,因为它只生成一份代码。
`dyn Trait` 允许你在一个集合(如`Vec`)中存储不同但实现了相同Trait的类型。

答案解析:

答案:使用泛型会使最终的二进制文件更小,因为它只生成一份代码。

这个说法是错误的。泛型通过“单态化”(Monomorphization)实现,编译器会为每个具体使用的类型生成一份独立的代码。这会导致二进制文件体积增大,但因为没有运行时查找,所以性能更高。

相比之下,`dyn Trait` 使用虚函数表(vtable)在运行时确定调用哪个方法,代码体积更小,但有轻微的运行时开销。

第6题 (简答题)
在Rust中,哪两个Trait通常用于实现可打印的格式化输出?请写出它们的名称。

答案解析:

答案:Debug, Display

`Debug` Trait用于开发者调试输出,通常使用 `#[derive(Debug)]` 自动实现,格式为 `{:?}`。

`Display` Trait用于面向用户的、更友好的格式化输出,需要手动实现,格式为 `{}`。

第7题 (选择题)
`impl Trait` 语法可以用在哪个位置?
仅用作函数参数类型
仅用作函数返回类型
既可以用作函数参数类型,也可以用作函数返回类型
只能用在 `let` 绑定中

答案解析:

答案:既可以用作函数参数类型,也可以用作函数返回类型

作为参数: `fn notify(item: impl Summary)`,这是泛型参数 `T: Summary` 的语法糖。

作为返回类型: `fn returns_summarizable() -> impl Summary`,这允许你返回一个实现了 `Summary` Trait的具体类型,而无需在函数签名中写出具体的返回类型。

第8题 (判断题)
如果一个函数返回一个闭包,你必须使用 `impl Fn(u32) -> u32` 或 `Box u32>` 作为返回类型,因为闭包的类型是匿名的。
正确
错误

答案解析:

答案:正确

每个闭包都有自己独特的、匿名的类型。因此,你无法在函数签名中直接写出这个类型。必须使用 `impl Trait` 或 `dyn Trait` 来抽象地表示返回的闭包。

`impl Fn(...)` 在编译时确定返回类型,而 `Box` 在堆上分配闭包,并在运行时确定类型。

第9题 (选择题)
下面哪个Trait Bound表示T必须实现`Clone`和`Debug`两个Trait?
T: Clone + Debug
T: Clone, Debug
T: Clone & Debug
T: Clone | Debug

答案解析:

答案:T: Clone + Debug

在Rust中,使用 `+` 号来指定一个泛型参数必须同时实现多个Trait。

例如 `fn process(item: T)` 要求传入的 `item` 必须是既可以克隆又可以调试打印的类型。

第10题 (简答题)
在Trait定义中,`Self` 关键字代表什么?

答案解析:

答案:实现该Trait的具体类型

`Self` (大写S) 是一个类型别名,它代表最终实现这个Trait的具体类型。

例如,在 `trait Clone` 中,`fn clone(&self) -> Self;` 意味着 `clone` 方法返回一个与调用者相同类型的新实例。

🎉
恭喜完成测验!
10/10
你对Rust Trait与泛型的掌握非常出色!