以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正
泛型的作用主要体现在提高代码的复用能力。在官方文档中,泛型被定义为具体类型或其他属性的抽象代替
,在实际使用时可以将泛型理解为一种模板
或占位符
,编译器会在编译时 执行单态化(monomorphization)
操作,即将泛型类型替换为具体的类型
在结构体中使用泛型
在结构体中使用泛型一般存在两种情况,其一为定义结构体时,其二为在方法定义时
在定义中使用泛型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| struct Point<T> { x: T, y: T, }
fn main() { let rec = Point { x: 12.5, y: 9.2 };
let squ = Point { x: 9, y: 9 };
println!("x: {}, y: {}", rec.x, rec.y); println!("x: {}, y: {}", squ.x, squ.y); }
|
在方法中使用泛型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| struct Point<T, U> { x: T, y: U, }
impl<T, U> Point<T, U> { fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> { Point { x: self.x, y: other.y, } } }
impl Point<i32, i32> { fn mix_i32(self, other: Point<i32, i32>) -> Point<i32, i32> { Point { x: self.x, y: other.y, } } }
fn main() { let p1 = Point { x: 5, y: 10.4 }; let p2 = Point { x: "Hello", y: 'c'};
let p3 = p1.mixup(p2); println!("p3.x = {}, p3.y = {}", p3.x, p3.y); }
|
同时,由上面的例子可注意到在结构体中的泛型类型参数可以
和方法的泛型类型参数不同
在枚举中使用泛型
在枚举中使用泛型和结构体类似,一般用在使枚举的变体持有泛型数据类型。比如Option枚举和Result枚举
1 2 3 4 5 6 7 8 9 10 11
| enum Option<T> { Some(T), None, }
enum Result<T, E> { Ok(T), Err(E), }
|
在函数中使用泛型
以下是一个最简单的在函数中使用泛型的例子,但在实际项目中,我们通常需要对泛型进行限制以保证函数得到的参数能满足后续相关的功能要求 ,这时需要使用Trait对泛型参数进行约束
1 2 3 4 5 6 7 8 9 10
| fn main() { let a = 1; let b = 2.0; println!("{}", back(a)); println!("{}", back(b)); }
fn back<T>(item: T) -> T { item }
|
泛型的Trait约束
在Trait部分我们说,可以通过Trait对传入或返回参数的类型进行约束。在使用泛型类型参数时可以配合Trait使用,以实现对传入参数的类型进行约束
简单情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| use std::fmt::Debug;
trait Shape { fn perimeter(&self) -> f64; fn area(&self) -> f64; }
#[derive(Debug)] struct Rectangle { width: f64, height: f64, }
impl Shape for Rectangle { fn perimeter(&self) -> f64 { 2.0 * (self.width + self.height) } fn area(&self) -> f64 { self.width * self.height } }
fn main() { let rec = Rectangle { width: 12.0, height: 20.0, };
trait_param(&rec); }
fn trait_param<T: Shape + Debug>(item: &T) { println!("{:?}", item); println!("{}", item.area()); }
|
使用where
关键字
在上面的例子中,我们实现了对函数传入参数进行约束。但在实际项目中,泛型参数或Trait约束较多会造成代码不易阅读,我们可以使用where
关键字来使代码更加直观。上面例子其实可以看做使用where
关键字的语法糖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| use std::fmt::Debug;
trait Shape { fn perimeter(&self) -> f64; fn area(&self) -> f64; }
struct Rectangle { width: f64, height: f64, }
impl Shape for Rectangle { fn perimeter(&self) -> f64 { 2.0 * (self.width + self.height) } fn area(&self) -> f64 { self.width * self.height } }
#[derive(Debug)] struct Circle { radius: f64, }
impl Shape for Circle { fn perimeter(&self) -> f64 { 2.0 * std::f64::consts::PI * self.radius } fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius } }
fn main() { let rec = Rectangle { width: 12.0, height: 20.0, };
let cir = Circle { radius: 11.0 };
trait_param(&rec, &cir); }
fn trait_param<T, U>(rec: &T, cir: &U)
where T: Shape, U: Shape + Debug, { println!("{}", rec.area()); println!("{:?}", cir); }
|