泛型是指把类型抽象成一种“参数”, 数据和算法都针对这种抽象的类型参数来实现, 而不针对具体类型。 当我们需要真正使用的时候, 再具体化、 实例化类型参数。 使用泛型可以编写更为抽象的代码, 减少工作量。
Rust中的泛型属于静多态, 它是一种编译期多态。 在编译期,不管是泛型枚举,还是泛型函数和泛型结构体, 都会被单态化。单态化是编译器进行静态分发的一种策略。
使用不同类型参数将泛型类型单体化后, 获得的是完全不同的具体类型。 如Option<i32>和Option<i64>是完全不同的类型, 不可通用, 也不可相互转换。 当编译器生成代码的时候, 它会为每一个不同的泛型参数生成不同的代码。
泛型容器
最常见的泛型应用莫过于容器,Vec<T>、HashMap<K, V>都是泛型类型的应用。比如,Vec<T>在使用中可以指定T的类型为i32或String等——Vec<i32>或者Vec<String>。
fn main() {
let mut vec_int: Vec<i32> = vec![10, 20];
vec_int.push(30);
}
泛型枚举
泛型枚举是指枚举值类型是泛型类型。标准库提供的Option<T>就是一个应用广泛的泛型枚举。Option<T>表示可能有值,也可能无值这一抽象概念,Some(T)表示可能的值可以是任意类型T,None表示不存在。
enum Option<T> {
Some(T),
None,
}
fn output(age:i32) -> Option<i32>{
if age > 18{
return Some(age)
}else{
return None
}
}
fn main() {
let ret1 = output(19);
println!("{:?}",ret1);
let ret2 = output(13);
println!("{:?}",ret2);
}
泛型结构体
泛型类型的结构体是指结构体的字段类型是泛型类型,它可以拥有一个或多个泛型类型。定义泛型结构体时,可以在结构体名称后面带上<T>或<T, U>等,在定义字段的类型时使用T或U。我们在定义Num<T, U>时仅使用了两个个泛型,T和U可以相同也可以不同。如果是Nu<T>这个定义表明Nu<T>结构体对某个类型T是通用的。字段data与num都同时属于这个类型。假如我们像下边注释掉的代码一样使用不同的值类型来创建Nu<T>实例,那么代码是无法通过编译的。
#[derive(Debug)]
struct Num<T,U> {
data: Option<T>,
num: U
}
#[derive(Debug)]
struct Nu<T> {
data: Option<T>,
num: T
}
fn main() {
let ret = Num{data:Some(1), num:"hello"};
println!("{:?}",ret);
//let ret = Nu{data:Some(1), num:"hello"};
//println!("{:?}",ret);
}
泛型函数
函数的参数和返回值都可以是泛型类型,带有泛型类型的参数或返回值的函数叫作泛型函数。泛型函数不要求所有参数都是泛型,可以只是某个参数是泛型。在定义泛型函数时可以在函数名称后面带上<T>,在定义参数或返回值的类型时使用T。
编译之后的foo(5)和foo("hello".to_string()),分别生成对应的foo函数,fn foo(x:i32)->i32和fn foo(x: String)->String。
fn foo<T>(x: T) -> T {
return x;
}
fn main() {
println!("{}", foo(5));
println!("{}", foo("hello".to_string()));
}
//fn foo(x: i32) -> i32 {
// return x;
//}
//fn foo<T>(x: String) -> String {
// return x;
//}
泛型impl
impl的时候也可以使用泛型。 在impl<Trait>for<Type>{}这个语法结构中, 泛型类型既可以出现在<Trait>位置, 也可以出现在<Type>位置。与其他地方的泛型一样, impl块中的泛型也是先声明再使用。 在impl块中出现的泛型参数, 需要在impl关键字后面用尖括号声明。
#[derive(Debug)]
struct Container<T> {
item: T
}
impl<T> Container<T> {
fn new(item: T) -> Self {
Container { item }
}
}
fn main() {
let c = Container::new(9);
println!("{:?}",c);
}