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. C#中SQLite的使用及工具类

    目录 SQLite简介 存储类 亲和类型 引用System.Data.SQLite.dll 软件包分类 使用本机库预加载 常用部署包 工具类 参考资料 SQLite简介 SQLite是一款轻型的数据库 ...

  2. 2020 OO 第二单元总结

    只要跑得够快即使从头关到尾你也喜欢吗? 一.设计策略 1.1 总体策略概述 在多线程的协同和同步控制方面,我三次作业都是采用生产者/消费者模式(还憨憨地在内部分了customer.producer.t ...

  3. php添加excel更新数据表数据

    公司有个需求,是用excel更新数据的,把错误的行列放到一个数组返回出来,正常的数据则插入,且返回数量 1.先需要引入phpspreadsheet,这里使用composer 安装 composer r ...

  4. .Net程序内存泄漏解析

    一.概要 大概在今年三月份的时候突然被紧急调到另外一个项目组解决线上内存泄漏问题.经过两周的玩命奋战终于解决了这个问题这里把心路历程及思路分享给大家.希望可以帮助到各位或现在正遇到这样事情的小伙伴提供 ...

  5. Fiddler使用 断点 模拟返回 AutoResponder Mock 模拟数据 相关学习记录

    断点 测试中有时需要改变发出去的请求信息,需要用到打断点的方法.断点包含两种方式: before response:在request请求的时候,未到达服务器之前,一般用来修改请求参数 after re ...

  6. 5-tcp套接字服务端编程

    import socket 1.创建套接字 sockfd= socket.socket(socket_family = AF_INIT,socket_type=SOCK_STREAM,proto) 功 ...

  7. 4- MySQL创建表以及增删改查

    查看表结构 查看表的结构,使用命令:desc 表明: 创建表(命令) 格式:使用create table创建表,必须给出下列信息: 1.新表的名字. 2.表中列的名字和定义,用逗号隔开. 语法: cr ...

  8. hdu4277 DFS+SET

    题意:       给你一些木棍,问你可以组成多少个三角形.. 思路:      直接深搜,N很小深搜无压力,也可以直接算出来,但我不会算..  #include<stdio.h> #in ...

  9. 解决Android加固多进程ptrace反调试的思路整理

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/53613481 一.Android多进程反调试的原理代码 当ptrace附加目标进程 ...

  10. Windows核心编程笔记之内核对象

    0x01 子进程继承父进程内核对象句柄 父进程 #include <Windows.h> #include <iostream> #include <strsafe.h& ...