什么是泛型编程?

  • 泛型编程是一种将类型作为参数的编程风格。用作参数的类型可以被称作"泛型(类型)"

    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

      type Point[T any] struct {
      X: T
      Y: T
      }
    • rust

      struct Point<T> {
      	x: T,
      	y: T,
      }
  • 泛型接口 - golang

    • golang
      type Iterator[T any] interface {
      	Next() (T,bool)
      }
    • rust
      pub trait Iterator<T> {
      	fn next(&mut self) -> Option<T>;
      }

泛型约束

泛型约束: 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 标准库中的 AddIterator
标准库里的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 的 vec

    trait 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 正式稳定
  • 泛型和异步编程