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

Rust 泛型的更多相关文章

  1. Atitit.rust语言特性 attilax 总结

    Atitit.rust语言特性 attilax 总结 1. 创建这个新语言的目的是为了解决一个顽疾:软件的演进速度大大低于硬件的演进,软件在语言级别上无法真正利用多核计算带来的性能提升.1 2. 不会 ...

  2. the rust book 的简单入门笔记

    rust learning day 1 (2021/05/27) 学了常量,变量,数据类型,控制流,所有权 char 的宽度是4字节,一个 unicode 的宽度 控制流条件都不要括号 rust 中的 ...

  3. Rust语言开发

    Rust开发 第一个程序 fn main() { println!("Hello, world!"); // 带!号的都是宏 并不是函数 } fn main() { let nam ...

  4. Rust生命周期bound用于泛型的引用

    在实际编程中,可能会出现泛型引用这种情况,我们会编写如下的代码: struct Inner<'a, T> { data: &'a T, } 会产生编译错误: error[E0309 ...

  5. Rust中的泛型

    go没有的,rust有呢~~ fn largest<T: PartialOrd + Copy>(list: &[T]) -> T { let mut largest = li ...

  6. Rust语言的多线程编程

    我写这篇短文的时候,正值Rust1.0发布不久,严格来说这是一门兼具C语言的执行效率和Java的开发效率的强大语言,它的所有权机制竟然让你无法写出线程不安全的代码,它是一门可以用来写操作系统的系统级语 ...

  7. Rust语言:安全地并发

    http://www.csdn.net/article/2014-02-26/2818556-Rust http://www.zhihu.com/question/20032903 Rust是近两年M ...

  8. D、GO、Rust 谁会在未来取代 C?为什么?——Go语言的定位非常好,Rust语言非常优秀,D语言也不错

    不要管我的地位和 D 语言创造者之一的身份.我会坦诚的回答这个问题.我熟悉 Go 和 Rust,并且知道 D 的缺点在哪里.我鼓励人们在 Rust 和 Go 社区相似身份的人,也可以提出他们诚恳的观点 ...

  9. Rust语言

    Rust语言 https://doc.rust-lang.org/stable/book/ http://www.phperz.com/article/15/0717/141560.html Rust ...

随机推荐

  1. 【洛谷】P1294 高手去散步

    题目背景 高手最近谈恋爱了.不过是单相思."即使是单相思,也是完整的爱情",高手从未放弃对它的追求.今天,这个阳光明媚的早晨,太阳从西边缓缓升起.于是它找到高手,希望在晨读开始之前 ...

  2. Dynamics CRM修改密码界面

    我们再实施Dynamics CRM的过程中经常会被客户吐槽没有一个修改密码的页面.市面上也有一些人通过自己操作AD库进行修改密码的解决方案不过多多少少会有一些BUG存在.其实ADFS提供了一个修改密码 ...

  3. element UI el-autocomplete 带输入建议的输入框

    项目需求:需要用户在输入框中输入公司 全名    但是为了避免用户输入不全   需要做一个带输入建议的输入框 参考:https://www.jianshu.com/p/de922caf337c

  4. 指方画圆之Java设计模式:适配器模式

    目录 应用场景 适配器模式 定义 意图 主要解决问题 何时使用 优缺点 指鹿为马VS指方为圆 指鹿为马 指方为圆 应用场景 使用者依赖的接口与提供者的接口不匹配时,就加一层适配,而不修改两端的代码 生 ...

  5. Data Mining UVA - 1591

      Dr. Tuple is working on the new data-mining application for Advanced Commercial Merchandise Inc. O ...

  6. Linux(CentOS7)安装与卸载MySQL8.0图文详解

    Mysql数据库的安装对于开发者来说,是我们必然会面对的问题,它的安装过程其实并不复杂,并且网络上的安装教程也非常多,但是对于新手来说,各种不同形式的安装教程,又给新手们带来了要选择哪种方式进行安装的 ...

  7. grafana接入zabbix数据源

    一.grafana介绍 grafana是开源免费的应用数据可视化仪表盘,由于zabbix本身对监控数据可视化并不侧重,所以大多使用第三方数据可视化工具来做大屏.下面向小伙伴们介绍grafana接入za ...

  8. CSS新特性contain,控制页面的重绘与重排

    在介绍新的 CSS 属性 contain 之前,读者首先需要了解什么是页面的重绘与重排. 之前已经描述过很多次了,还不太了解的可以先看看这个提高 CSS 动画性能的正确姿势. OK,下面进入本文正题, ...

  9. PHP大牛笔记收藏

    PHP大牛笔记收藏 Do not use PHP references 未完,待续

  10. hdu3724 字典树(商品条形码)

    题意:      给你一堆商品的名字,然后给你一些条形码,问你这些条形码转换成的字符串的 前缀在商品中出现的个数,条形码的每个字母是八个二进制数字,有两种数,大的是小的2倍,小的是0,大的是1,这里面 ...