假设你买了一个网络摄像头,通过USB接口连接到你的电脑。现在,假设你又买了一个外接硬盘,也是通过USB接口连接到你的电脑上。这就是一个泛型在现实物理世界的应用,USB接口就是一个泛型接口。需要连接到电脑的设备,只需要遵循通用的USB规范就可以了。
在代码中,一个函数针对特定的类型执行特定的任务,一个泛型函数可以在一些类型上执行特定的任务。我们来看个例子:
fn add(x: i64, y: i64) -> i64 {
x + y
}
add函数只能对i64类型的值相加,我们把add函数改成泛型函数:
fn add<T>(x: T, y: T) -> T {
x + y
}
这段代码是无效的,编译器不知道怎么对泛型类型T进行相加,这就需要对泛型T做泛型约束:
use std::ops::Add;
fn add<T: Add<Output = T>>(x: T, y: T) -> T {
x + y
}
在这里,只要实现了Add Trait的类型,就都可以相加。
泛型(Generic)
泛型编程的目的是提高代码的可重用性和通过对function、struct、trait中的类型延迟定义来减少bug。在实践中,这意味着一个算法可以用于多种不同的类型,前提是它们满足约束条件。
在Rust中,function、trait和数据类型都是可以泛型的:
use std::fmt::Display;
fn generic_display<T: Display>(item: T) {
println!("{}", item);
}
struct Point<T> {
x: T,
y: T,
}
struct Point2<T>(T, T);
enum Option<T> {
Some(T),
None,
}
fn main() {
let a = "42";
let b = 42;
generic_display(a);
generic_display(b);
let (x, y) = (4, 2);
let point = Point {x, y};
// generic_display(point); // Point没有实现Disply
}
泛型在Rust中应用广泛,如果没有它们,就不可能有Vec、HashMap或BTreeSet这样的泛型集合:
use std::{vec, collections::HashMap};
#[derive(Debug)]
struct Contact {
name: String,
email: String,
}
fn main() {
let imported_contacts = vec![
Contact {
name: "John".to_string(),
email: "john@smith.com".to_string(),
},
Contact {
name: "steve".to_string(),
email: "steve@jobs.com".to_string(),
},
Contact {
name: "John".to_string(),
email: "john@smith.com".to_string(),
},
];
let unique_contacts: HashMap<String, Contact> = imported_contacts
.into_iter()
.map(|contact| (contact.email.clone(), contact))
.collect();
println!("{:?}", unique_contacts);
}
感谢泛型的强大,我们使用了标准库的HashMap,快速的去除了重复的数据。
特征(Trait)
Rust中的trait等同于其他语言的interface。
pub trait Dog {
fn bark(&self) -> String;
}
pub struct Labrador {}
impl Dog for Labrador {
fn bark(&self) -> String {
"wouf".to_string()
}
}
pub struct Husky {}
impl Dog for Husky {
fn bark(&self) -> String {
"Wuuuuuu".to_string()
}
}
fn main() {
let labrador = Labrador {};
println!("{}", labrador.bark());
let husky = Husky {};
println!("{}", husky.bark());
}
这里定义了一个狗的trait,所有实现了Dog trait的类型都被认为是一种狗。这里体现了程序员可以通过trait共享行为,即多个类型共享相同的行为。
默认实现
可以为trait提供默认的方法实现:
pub trait Hello {
fn hello(&self) -> String {
String::from("World")
}
}
pub struct Sylvain {}
impl Hello for Sylvain {
fn hello(&self) -> String {
String::from("Sylvain")
}
}
pub struct Anonymous {}
impl Hello for Anonymous {}
fn main() {
let sylvain = Sylvain{};
let anonymous = Anonymous{};
println!("Sylvain: {}", sylvain.hello());
println!("Anonymous: {}", anonymous.hello());
}
组合Trait
多个trait可以组合在一起形成更高级的约束:
pub trait Module {
fn name(&self) -> String;
fn description(&self) -> String;
}
pub trait SubdomainModule {
fn enumerate(&self, domain: &str) -> Result<Vec<String>, Error>;
}
fn enumerate_subdomains<M: Module + SubdomainModule>(module: M, target: &str) -> Vec<String> {
// ...
}
异步Trait
到目前为止,异步trait还没有得到Rust的原生支持,我们可以使用 async-trait 库来实现异步trait。
#[async_trait]
pub trait HttpModule: Module {
async fn scan(
&self,
http_client: &Client,
endpoint: &str,
) -> Result<Option<HttpFinding>, Error>;
}
泛型Trait
trait也支持泛型参数
use std::fmt::Display;
trait Printer<S: Display> {
fn print(&self, to_print: S) {
println!("{}", to_print);
}
}
struct ActualPrinter {}
impl<S:Display, T> Printer<S> for T {}
fn main() {
let s = "Hello";
let n = 42;
let ap = ActualPrinter {};
ap.print(s);
ap.print(n);
n.print(s);
s.print(n);
}
本文翻译自:
https://kerkour.com/rust-generics-traits