类型限定
类型限定
现在我们回到类型 T,因为 Rust 代码通常使用 T。您将记住,Rust 中的某些类型是 Copy,某些是 Clone,某些是 Display,某些是 Debug,等等。使用 Debug,我们可以使用 {:?}
打印。因此,现在您可以看到如果要打印 T,我们将遇到问题:
fn print_number<T>(number: T) {
println!("Here is your number: {:?}", number); // ⚠️
}
fn main() {
print_number(5);
}
print_number 需要使用 Debug 来打印数字,但是 T 是带有 Debug 的类型吗?也许不会。也许没有知道的 #[derive(Debug)]
。编译器也不知道,因此会出现错误:
error[E0277]: `T` doesn't implement `std::fmt::Debug`
--> src\main.rs:29:43
|
29 | println!("Here is your number: {:?}", number);
| ^^^^^^ `T` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug`
T 没有实现 Debug。那么我们是否为 T 实现 Debug?不,因为我们不知道 T 是什么。但是我们可以告诉函数:“不用担心,因为此函数的任何 T 类型都将具有 Debug”。
use std::fmt::Debug; // Debug is located at std::fmt::Debug. So now we can just write 'Debug'.
fn print_number<T: Debug>(number: T) { // <T: Debug> is the important part
println!("Here is your number: {:?}", number);
}
fn main() {
print_number(5);
}
因此,现在编译器知道:“好吧,此类型 T 将具有 Debug”。现在代码可以正常工作了,因为 i32 具有 Debug。现在,我们可以给它提供许多类型:String,&str 等,因为它们都具有 Debug。现在我们可以创建一个结构,并使用 #[derive(Debug)]
对其进行调试,因此现在我们也可以打印它。我们的函数可以使用 i32,struct Animal 等:
use std::fmt::Debug;
#[derive(Debug)]
struct Animal {
name: String,
age: u8,
}
fn print_item<T: Debug>(item: T) {
println!("Here is your item: {:?}", item);
}
fn main() {
let charlie = Animal {
name: "Charlie".to_string(),
age: 1,
};
let number = 55;
print_item(charlie);
print_item(number);
}
Here is your item: Animal { name: "Charlie", age: 1 }
Here is your item: 55
有时我们在通用函数中需要多个类型。我们必须写出每个类型名称,然后考虑我们要如何使用它。在此示例中,我们需要两种类型。首先,我们要打印类型为 T 的语句。使用 {} 打印会更好,因此我们需要 Display 类型。接下来是类型 U,两个变量 num_1 和 num_2 具有类型 U(U 是某种数字)。我们想比较它们,因此我们需要 PartialOrd。该特征使我们可以使用 <,>,== 等。我们也希望打印它们,因此我们也需要 Display 类型。
use std::fmt::Display;
use std::cmp::PartialOrd;
fn compare_and_display<T: Display, U: Display + PartialOrd>(statement: T, num_1: U, num_2: U) {
println!("{}! Is {} greater than {}? {}", statement, num_1, num_2, num_1 > num_2);
}
fn main() {
compare_and_display("Listen up!", 9, 8);
}
- 首个类型为泛型 T,它必须能够可以用于 print 中被打印出来;
- 第二个类型为泛型 U,它必须能够可以用于 print 中被打印出来,并且它能够进行比较。
需要注意的是啊,类型 T 与 U 可以使用相同的类型:
use std::fmt::Display;
fn say_two<T: Display, U: Display>(statement_1: T, statement_2: U) { // Type T needs Display, type U needs Display
println!("I have two things to say: {} and {}", statement_1, statement_2);
}
fn main() {
say_two("Hello there!", String::from("I hate sand.")); // Type T is a &str, but type U is a String.
say_two(String::from("Where is Padme?"), String::from("Is she all right?")); // Both types are String.
}
I have two things to say: Hello there! and I hate sand.
I have two things to say: Where is Padme? and Is she all right?
where
为了使泛型函数更易于阅读,我们还可以在代码块之前的位置将其编写为:
use std::cmp::PartialOrd;
use std::fmt::Display;
fn compare_and_display<T, U>(statement: T, num_1: U, num_2: U)
where
T: Display,
U: Display + PartialOrd,
{
println!("{}! Is {} greater than {}? {}", statement, num_1, num_2, num_1 > num_2);
}
fn main() {
compare_and_display("Listen up!", 9, 8);
}
当您有许多泛型类型时,使用 where 是一个好主意。