Rust从入门到实战系列九十九:使用 trait bounds 来修复 largest 函数

现在你知道了如何使用泛型参数 trait bound 来指定所需的行为。修复使用泛型类型参数的 largest 函数定义!回顾一下,最后尝试编译代码时出现的错误是:

Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0369]: binary operation `>` cannot be applied to type `T`
--> src/main.rs:5:17
|
5 | if item > largest {
| ---- ^ ------- T
| |
| T
|
help: consider restricting type parameter `T`
|
1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T {
| ++++++++++++++++++++++
For more information about this error, try `rustc --explain E0369`.
error: could not compile `chapter10` due to previous error

在 largest 函数体中我们想要使用大于运算符(>)比较两个 T 类型的值。这个运算符被定义为标准库中 trait std:: cmp::PartialOrd 的一个默认方法。所以需要在 T 的 trait bound 中指定 PartialOrd,这样largest 函数可以用于任何可以比较大小的类型的 slice。因为 PartialOrd 位于 prelude 中所以并不需要手动将其引入作用域。将 largest 的签名修改为如下:

# let mut largest = list[0];
#
# for &item in list {
# if item > largest {
# largest = item;
# }
# }
#
# largest
# }
#
# fn main() {
# let number_list = vec![34, 50, 25, 100, 65];
#
# let result = largest(&number_list);
# println!("The largest number is {}", result);
#
# let char_list = vec!['y', 'm', 'a', 'q'];
#
# let result = largest(&char_list);
# println!("The largest char is {}", result);
# }

但是如果编译代码的话,会出现一些不同的错误:

Compiling chapter10 v0.1.0 (file:///projects/chapter10)
error[E0508]: cannot move out of type `[T]`, a non-copy slice
--> src/main.rs:2:23
|
2 | let mut largest = list[0];
| ^^^^^^^
| |
| cannot move out of here
| move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait
| help: consider borrowing here: `&list[0]`
error[E0507]: cannot move out of a shared reference
--> src/main.rs:4:18
|
4 | for &item in list {
| ----- ^^^^
| ||
| |data moved here
| |move occurs because `item` has type `T`, which does not implement the `Copy` trait
| help: consider removing the `&`: `item`
Some errors have detailed explanations: E0507, E0508.
For more information about an error, try `rustc --explain E0507`.
error: could not compile `chapter10` due to 2 previous errors

错误的核心是 cannot move out of type [T], a non−copy slice,对于非泛型版本的 largest 函数,我们只尝试了寻找最大的 i32 和 char。正如第四章 ” 只在栈上的数据:拷贝” 部分讨论过的,像 i32 和 char这样的类型是已知大小的并可以储存在栈上,所以他们实现了 Copy trait。当我们将 largest 函数改成使用泛型后,现在 list 参数的类型就有可能是没有实现 Copy trait 的。这意味着我们可能不能将 list [0]的值移动到 largest 变量中,这导致了上面的错误。
为了只对实现了 Copy 的类型调用这些代码,可以在 T 的 trait bounds 中增加 Copy!
一个可以编译的泛型版本的 largest 函数的完整代码,只要传递给 largest 的 slice 值的类型实现了PartialOrd 和 Copy 这两个 trait,例如 i32 和 char:

let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![34, 50, 25, 100, 65];
let result = largest(&number_list);
println!("The largest number is {}", result);
let char_list = vec!['y', 'm', 'a', 'q'];
let result = largest(&char_list);
println!("The largest char is {}", result);
}

一个可以用于任何实现了 PartialOrd 和 Copy trait 的泛型的 largest 函数如果并不希望限制 largest 函数只能用于实现了 Copy trait 的类型,我们可以在 T 的 trait bounds 中指定 Clone 而不是 Copy。并克隆 slice 的每一个值使得 largest 函数拥有其所有权。使用 clone 函数意味着对于类似 String 这样拥有堆上数据的类型,会潜在的分配更多堆上空间,而堆分配在涉及大量数据时可能会相当缓慢。
另一种 largest 的实现方式是返回在 slice 中 T 值的引用。如果我们将函数返回值从 T 改为 &T 并改变函数体使其能够返回一个引用,我们将不需要任何 Clone 或 Copy 的 trait bounds 而且也不会有任何的堆分配。尝试自己实现这种替代解决方式吧!如果你无法摆脱与生命周期有关的错误,请继续阅读:接下来的 ” 生命周期与引用有效性” 部分会详细的说明,不过生命周期对于解决这些挑战来说并不是必须的。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值