Rust 泛型

泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。在Rust中编译时编译器会将泛型代码具化为不同具体类型的多份代码,所以泛型代码只是提供了一种通用代码的抽象,并不能减少编译后真正生成的代码量。

泛型创建

泛型函数

fn give_me<T>(value:T){
let _=value;
}

泛型函数的创建需要在方法名和圆括号间放置泛型参数,之后就可以将该泛型参数视为类型使用。

使用

fn main() {
let a="generics";
let b=1024;
give_me(a);
give_me(b);
}

泛型类型

泛型类型的创建需要在类型名和{}间放置泛型参数,之后就可以将该泛型参数视为类型使用。

结构体:

struct GenericStruct<T>(T);
struct Container<T>{
item:T,
}

使用:

let i=GenericStruct(1);
let s=GenericStruct(String::from("1111"));
let ic=Container{item:1};
let is=Container{item:String::from("is")};

枚举:

enum Transmission <T>{
Singal(T),
NoSingal,
}

使用:

let ei=Transmission::Singal(1);
let es=Transmission::Singal(String::from("222"));
let eni=Transmission::<i32>::NoSingal;
let ens:Transmission<String>=Transmission::NoSingal;

注意:使用泛型ZST枚举实例要使用::<>(turbofish)来指明泛型参数是哪种类型。

泛型实现:

泛型实现要在impl后声明泛型参数,然后就可以把泛型参数当作具体类型使用。

拿上一部分的Container为例,为其实现方法:

impl<T> Container<T> {
fn new(item:T)->Self{
Container{item}
}
}

使用:

let container_i32=Container::new(1);
let container_str=Container::new(String::from("111"));

还可以为具体类型参数提供实现:

impl Container<i32>{
// fn new(item:i32)->Self{
// Container{item}
// }
fn toString(&self)->String{
self.item.to_string()
}
}

这种具体类型参数的实现只对此类型可用,并且不能和泛型实现函数名冲突。

特征区间(trait bounds)

上述所有示例程序,我们都只是简单的提供了一个占位符来表示类型,编译器并不知道具体的类型信息,所以我们除了用它们来表示类型外,并不能对这些占位符或者它们表示的变量调用方法,好在Rust提供了trait bounds来告知编译器相关的类型信息来完成这项工作。

我们可以在泛型声明处使用“:”来对类型占位符进行一些约束来告知编译器相关的类型信息。一些例子如下:

在方法上使用trait bounds:
trait Person{
fn sleep(){
println!("person need sleep");
}
fn get_name(&self)->&str;
}
struct Student{name:String}
impl Person for Student {
fn get_name(&self)->&str{
&self.name
}
} fn who<T:Person>(anyone:&T){
println!("I'm {}",anyone.get_name());
}
fn doing_at_night<T:Person>(anyone:&T){
T::sleep();
}

使用:

 let stu=Student{name:"YSS".to_string()};
Student::who(&stu);
Student::doing_at_night(&stu);

需要注意的是,trait bounds提供了对泛型参数的约束导致对传入的泛型类型有了一定的要求,只有符合泛型约束的类型才能正确使用泛型代码。

trait bounds除了可以像上述那样指定以外,还可以把泛型约束单独提出来放置到返回值之后{}之前的where语句中(多个泛型参数","分割),这在约束较长或者泛型参数较多的情况下能够提高代码可读性。

上述例子可改写如下:

fn who<T>(anyone: &T)
where
T: Person,
{
println!("I'm {}", anyone.get_name());
}
fn doing_at_night<T>(anyone: &T)
where
T: Person,
{
T::sleep();
}
类型上的trait bounds:
struct Foo<T:Display>{
bar:T
}

struct Foo<T> where T:Display{
bar:T
}

这样约束后,实例化结构体Foo时,bar只能是实现了Display trait的实例。

泛型实现上的trait bounds:
trait Eatable{
fn eat(&self);
}
#[derive(Debug)]
struct Food<T>(T);
impl<T:Debug> Eatable for Food<T>{
fn eat(&self) {
println!("Eating{:?}",self);
}
}

impl<T> Eatable for Food<T>where T:Debug{
fn eat(&self) {
println!("Eating{:?}",self);
}
}

特征区间组合

我们还可以使用“+”为泛型指定多个特征区间,例如:

trait Eat{
fn eat(&self){
println!("eat");
}
}
trait Code{
fn code(&self){
println!("code");
}
}
trait Sleep{
fn sleep(&self){
println!("sleep");
}
}
fn coder_life <T:Eat+Code+Sleep> (coder:T){
coder.eat();
coder.code();
coder.sleep();
}

impl特征区间

对于涉及到堆分配或无法用具体类型表示约束的类型(例如闭包),我们可以使用impl来指定特征区间:

fn lazy_adder(a:u32,b:u32)->impl Fn()->u32{
move||a+b
}

如果用普通trait bounds的话,编译器会报如下错误:

fn lazy_adder<T:Fn()->u32>(a:u32,b:u32)->T{
move||a+b
}
error[E0308]: mismatched types
--> src/main.rs:106:5
|
105 | fn lazy_adder<T:Fn()->u32>(a:u32,b:u32)->T{
| - this type parameter - expected `T` because of return type
106 | move||a+b
| ^^^^^^^^^ expected type parameter `T`, found closure
|
= note: expected type parameter `T`
found closure `[closure@src/main.rs:106:5: 106:14]`
= help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `T`

因为闭包是堆分配的,如果返回类型不做特殊处理的话,是在栈上的变量,不会像智能指针那样指向堆分配内容,所以我们只能用impl或者用智能指针包裹,像下面这样:

fn lazy_adder(a:u32,b:u32)->Box<Fn()->u32>{
Box::from(move||a+b)
}

对返回闭包感兴趣的老哥可以看看这个帖子:

https://medium.com/journey-to-rust/closures-part-2-29536393dd92

最新文章

  1. BAT实现服务器文件同步
  2. 1.&lt;%@Page%&gt;中的Codebehind、AutoEventWireup、Inherits有何作用?
  3. appium 常用API
  4. C#字符串和数据之间的转换
  5. css 打字动画
  6. try-catch和throw,throws的区别和联系
  7. 【BZOJ 2818】Gcd - 筛法求素数&amp;phi()
  8. web的各种前端打印方法之CSS控制网页打印样式
  9. 三星Galaxy Note 10.1 N8010 最后的救赎 Andorid 5.0.2 ROM
  10. Hadoop基础教程之高级编程
  11. Spring AOP实现方式四之注入式AspectJ切面【附源码】
  12. ld: 18 duplicate symbols for architecture i386 .linker command failed with exit code 1 (use -v to see invocation)_
  13. 鼠标滚轮(mousewheel)和DOMMouseScroll事件
  14. 雪碧图(sprite)
  15. mac 下mysql
  16. Ubuntu下使用终端ssh访问设置了密钥的云服务器
  17. WPF PrismDialog PopupWindowAction使用MetroWindow
  18. 【HANA系列】SAP HANA XS的JavaScript安全事项
  19. 9-centos定时任务-不起作用- 没指明路径!!!
  20. 创建自己的Code Snippet(代码模板)

热门文章

  1. [源码解析] 并行分布式任务队列 Celery 之 消费动态流程
  2. BUAA_2021_SE_READING_#2
  3. [Python]import使用的疑难杂症与包管理
  4. 【笔记】《算法竞赛入门》习题7-3 UVa211_多米诺效应
  5. (十七)VMware Harbor 垃圾清理
  6. Ribbon、Feign和OpenFeign的区别
  7. 从苏宁电器到卡巴斯基第24篇:难忘的三年硕士时光 II
  8. hdu2830 可交换行的最大子矩阵
  9. dalvik浅析二:jni、so
  10. Linux-鸟菜-2-主机规划与磁盘分区