类型限定

类型限定

现在我们回到类型 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 是一个好主意。

上一页