类型限定

类型限定

现在我们回到类型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)] 对其进行调试,因此现在我们也可以打印它。我们的函数可以使用i32struct 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_1num_2具有类型UU是某种数字。我们想比较它们,因此我们需要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中被打印出来,并且它能够进行比较。

需要注意的是啊,类型TU可以使用相同的类型:

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是一个好主意。

上一页