什么是泛型编程?
-
泛型编程是一种将类型作为参数的编程风格。用作参数的类型可以被称作"泛型(类型)"
Generic programming is a style of computer programming in which algorithms are written in terms of data types to-be-specified-later that are then instantiated when needed for specific types provided as parameters
-
fn add_i8(a: i8, b: i8)
: i8 是类型但不是参数,所以add_i8
不是泛型 -
fn add<T>(a:T, b:T)
: T 是一个类型,并且由于在定义add
方法时未知,所以T
是一个参数 类型T
在此处是参数,所以add
是泛型函数 泛型会应用在何种对象上?最基本的场景包括函数
,结构体
和接口
-
泛型函数
-
golang
func add[T](a,b T) T { return a+b }
-
rust
fn add<T>(a: T, b:T) T{ a + b }
-
-
泛型结构体
-
泛型接口 - golang
- golang
type Iterator[T any] interface { Next() (T,bool) }
- rust
pub trait Iterator<T> { fn next(&mut self) -> Option<T>; }
- golang
泛型约束
泛型约束: Constraint/Trait Bound
-
What? 泛型约束是一种定义泛型参数范围的方法。
-
Why?在一些情况下,我们希望作为参数的类型满足一些基本的要求。一个完全没有任何约束的类型可能没有意义。
-
Syntax: 以最简单的 A+B 泛型函数说明一下声明泛型约束的语法
-
golang
type AddableNumber interface{ int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | string } type AddableString interface { string } func Add[T AddableNumber | AddableString ] (a,b T){ return a + b } // correct var a = int(3) var b = int(4) Add(a,b) // error during compile, float is not inclueded in the AddableNumber constraint var a = float(8) var b = float(9) Add(a,b)
-
rust
fn add<Rhs,T: std::ops::Add<Rhs>>(a:T b:Rhs){ a+b } pub trait IsOdd{ fn is_odd()->bool; } fn add_odd<Rhs,T>(a:T b:Rhs) where T: std::ops::Add<Rhs> Rhs: IsOdd { if b.is_odd(){ a+b } a }
-
常见的需要泛型约束的场景
-
代数运算
trait Point<T: std::ops::Add+std::ops::Mul>{ fn dim() -> i32 fn get_dim(dim_idx: i32) -> T } // 计算任意维度两个点的距离 fn calculate_distance<K>(p1: Point<K>, p2: Point<K>) where K: std::ops::Add+std::ops::Mul { for i in 0..p1.dim(){ p1.get_dim(i)**2 + p2.get_dim(i)**2 } ... }
-
容器类数据结构
pub trait FancyCache\<T\> where T: std::hash::Hash { Put(item: T) }
泛型和关联类型的区别
Generic Type Vs Associated Type
关联类型是一个和泛型很像的概念:关联类型也是一种将类型作为参数的编程方法
关联类型仅能应用在Trait
对象上。区别于泛型是调用时传递具体类型参数,关联类型仅能在实现Trait 时传递类型参数
trait AddAssociated<Rhs> {
type Output;
fn add_assoc(&self, b: Rhs) -\> Self::Output;
}
关联类型典型的两个例子:Rust 标准库中的 Add
和 Iterator
标准库里的Add
对于任何 Add<T>
,输出类型Output
都和T
无关,而和实现Add<T>
Trait 的类型相关,所以使用关联类型。 Rhs
的类型是在调用时确定的,因此使用泛型参数
//https://doc.rust-lang.org/src/core/ops/arith.rs.html#76
pub trait Add\<Rhs = Self\> {
type Output;
// Required method
fn add(self, rhs: Rhs) -\> Self::Output;
}
对于Iterator
Trait 来讲,输出的Item
类型仅和实现 Iterator 的类型关联,和调用next
方法的类型无关,所以Item
使用关联类型而非泛型
//https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#75
pub trait Iterator {
type Item;
// Required method
fn next(&mut self) -\> Option\<Self::Item\>;
}
关联泛型 GAT
GAT(Generic Associate Type) 在 Rust 1.65 正式稳定 Generic associated types to be stable in Rust 1.65 | Rust Blog (rust-lang.org) 一句话解释什么是 GAT: GAT 就是使用泛型类型作为关联类型
// Item 是关联类型
trait Producer {
type Item;
fn produce(&self) -\> Self::Item;
}
// Item\<T\> 是关联类型,同时Item\<T\>是一个有一个参数T的泛型,所以Procuder Trait 使用了 GAT
// 这段代码在 Rust 1.65 支持GAT以前默认不可编译
trait Producer {
type Item\<T\>;
fn produce\<T\>(&self, value: T) -\> Self::Item\<T\>;
}
GAT 的应用场景(TBD)
Mappable
使用 GAT 实现 Mappable 类型 (或是函数式编程中的 Functor(函子))
-
rust 的 container 类型不支持直接 map 操作,只能通过迭代器进行
map
let u = vec![1, 2, 3]; let v: Vec\<_\> = u.iter().map(|e| e.to_string()).collect();
-
GAT 允许我们定义
Functor
(或Mappable
)类型,为 vec 实现Functor
Trait 后,我们获得了一个可以直接 map 的 vectrait Functor { type Unwrapped; type Wrapped\<B\>: Functor; fn fmap<F, B>(self, f: F) -> Self::Wrapped<B> where F: FnMut(Self::Unwrapped) -> B; } impl<A> Functor for Vec<A> { type Unwrapped = A; type Wrapped<B> = Vec<B>; fn fmap<F, B>(self, mut f: F) -> Self::Wrapped<B> where F: FnMut(Self::Unwrapped) -> B { let mut retval: Self::Wrapped<B> = vec![]; for el in self { retval.push(f(el)) } retval } } fn main() { let v1: Vec<u8> = vec![1u8, 2, 3]; // the declared type is not necessary let v2: Vec<String> = v1.fmap(|n| n.to_string()); println!("{:?}", v2) }
Lending Iterator
一个无法编译的版本
主要问题是,我们无法表达 where 'a:'t
struct WindowsMut<'t, T> {
slice: &'t mut [T],
start: usize,
window_size: usize,
}
impl<'t, T> Iterator for WindowsMut<'t, T> {
type Item = &'t mut [T];
fn next<'a>(&'a mut self) -> Option<Self::Item> {
let retval = self.slice[self.start..].get_mut(..self.window_size)?;
self.start += 1;
Some(retval)
}
}
-
使用 GAT 的版本
impl<'t, T> LendingIterator for WindowsMut<'t, T> { type Item<'a> where Self: 'a = &'a mut [T]; fn next<'a>(&'a mut self) -> Option<Self::Item<'a>> { let retval = self.slice[self.start..].get_mut(..self.window_size)?; self.start += 1; Some(retval) } }
Pointer Family
- rust code block below:
trait PointerFamily {
type Pointer<T>: Deref<Target = T>;
fn new<T>(value: T) -> Self::Pointer<T>;
}
struct ArcFamily;
struct RcFamily;
impl PointerFamily for ArcFamily {
type Pointer<T> = Arc<T>;
...
}
impl PointerFamily for RcFamily {
type Pointer<T> = Rc<T>;
...
}
struct MyStruct<P: PointerFamily> {
pointer: P::Pointer<String>,
}
关联泛型和高阶类型
GAT 是 HKT(Higher Kind Type) 的一个子集
常数泛型 Const Generics(TBD)
- 常数泛型在 Rust 1.51 正式稳定
- 泛型和异步编程