Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Rust中的泛型

Rust中的泛型

作者头像
端碗吹水
发布于 2022-06-01 01:09:03
发布于 2022-06-01 01:09:03
98200
代码可运行
举报
运行总次数:0
代码可运行

[TOC]

泛型程序设计是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。泛型编程的中心思想是从携带类型信息的具体的算法中抽象出来,得到一种可以与不同的数据类型表示相结合的算法,从而生成各种有用的软件。泛型编程是一种软件工程中的解耦方法,很多时候,我们的算法并不依赖某种特定的具体类型,通过这种方法,我们就可以将“类型”从算法和数据结构的具体示例中抽象出来。


泛型作为函数参数的类型

考虑以下问题:编写一个函数,这个函数接收两个数字,然后返回较大的那个数字。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fn largest(a: u32, b: u32) -> u32 {
    if a > b {
        a
    } else {
        b
    }
}

这个函数能工作,但它只能比较两个 u32 类型数字的大小。现在除了想比较两个 u32 外,还想比较两个 f32。有一种可以行的办法,我们可以定义多个 largest 函数,让它们分别叫做 largest_u32largest_f32… 这能正常工作,但不太美观。我们可以使用泛型语法对上述代码进行修改:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fn largest<T: std::cmp::PartialOrd>(a: T, b: T) -> T {
    if a > b {
        a
    } else {
        b
    }
}

fn main() {
    println!("{}", largest::<u32>(1, 2));
    println!("{}", largest::<f32>(1.0, 2.1));
}

其中,std::cmp::PartialOrd 被称作泛型绑定,在之后的课程中我们会对此进行解释。


结构体中的泛型

我们还可以使用泛型语法定义结构体,结构体中的字段可以使用泛型类型参数。下面的代码展示了使用 Point&lt;T&gt; 结构来保存任何类型的 x 和 y 坐标值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct Point<T> {
    x: T,
    y: T,
}

fn main() {
    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };
}

上述代码创建了一个 x 和 y 都是同一类型的 Point 结构体,但同时一个结构体中也可以包含多个不同的泛型参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct Point<T, U> {
    x: T,
    y: T,
    z: U,
}

fn main() {
    let integer = Point { x: 5, y: 10, z: 15.0 };
    let float = Point { x: 1.0, y: 4.0, z: 8 };
}

但是要注意,虽然一个结构体中可以包含任意多的泛型参数,但我仍然建议拆分结构体以使得一个结构体中只使用一个泛型参数。过多的泛型参数会使得阅读代码的人难以阅读。


结构体泛型的实现

我们可以在带泛型的结构体上实现方法,它的语法与普通结构体方法相差不大,只是要注意在它们的定义中加上泛型类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn x(&self) -> &T {
        &self.x
    }
}

fn main() {
    let p = Point { x: 5, y: 10 };

    println!("p.x = {}", p.x());
}

我们也可以在某种具体类型上实现某种方法,例如下面的方法将只在 Point&lt;f32&gt; 有效。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
impl Point<f32> {
    fn distance_from_origin(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
}

使用traits定义共同的行为

某一类数据可能含有一些共同的行为:例如它们能被显示在屏幕上,或者能相互之间比较大小。我们将这种共同的行为称作 Traits。我们使用标准库 std::fmt::Display 这个 traits 举例,这个 traits 实现了在 Formatter 中使用空白格式 {} 的功能。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pub trait Display {
    pub fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use std::fmt;

struct Point {
    x: i32,
    y: i32,
}

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

let origin = Point { x: 0, y: 0 };

assert_eq!(format!("The origin is: {}", origin), "The origin is: (0, 0)");

使用 Traits 作为参数类型

在知道如何定义和实现 Traits 后,我们就可以探索如何使用 Traits 来定义接受许多不同类型的函数。这一切都与 Java 中的接口概念类似,也就是所谓的鸭子类型。事实上它们的使用场景也基本上是类似的。

我们定义一个 display 函数,它接收一个实现了 Display Traits 的参数 item。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pub fn display(item: &impl std::fmt::Display) {
    println!("My display item is {}", item);
}

item 的参数类型是 impl std::fmt::Display 而不是某个具体的类型(例如 Point),这样,任何实现了 Display Traits 的数据类型都可以作为参数传入该函数。


自动派生

Rust 编译器可以自动为我们的结构体实现一些 Traits,这种自动化技术被称作派生。例如,在编写代码的过程中最常见的一个需求就是将结构体输出的屏幕上,除了使用上节课提到的手工实现的 Display,也可以采用自动派生技术让 Rust 编译器自动帮你添加代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#[derive(Debug)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p = Point { x: 1, y: 2 };
    println!("{:?}", p);
}

Debug Trait 允许将数据结构使用 {:?} 格式进行格式化。

自动派生有一个前提是,该结构体中全部字段都实现了指定的 Trait,例如,上面例子中的 i32 和 i64 就已经实现了 Debug Trait。

现在,我们来为 Point 实现另一个 Trait:PartialEq。该特征允许两个数据使用 == 进行比较。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#[derive(Debug, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let p1 = Point { x: 1, y: 2 };
    let p2 = Point { x: 1, y: 2 };
    println!("{}", p1 == p2);
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
24.Rust-泛型
泛型是运行时指定数据类型的一种机制。好处是通过高度的抽象,使用一套代码应用多种数据类型。比如我们的向量,可以使用数值类型,也可以使用字符串类型。泛型是可以保证数据安全和类型安全的,还同时减少代码量。
面向加薪学习
2022/06/30
4890
【Rust学习】24_泛型类型
我们将使用泛型来为函数签名、结构体等定义创建一个通用模板,这样它们就可以与多种不同的具体数据类型配合使用。
思索
2025/01/14
1090
【Rust学习】24_泛型类型
rust-泛型generics
rust 也有泛型,这种最早出现1970年代的Ada语言中,后来被许多基于对象和面向对象的语言所采用,包括BETA、 C++、java。 rust 也借鉴了这一特性。 这种特性让程序有更好的通用性。
潇洒
2023/10/23
1610
Rust之泛型特化
Rust不支持函数/结构体的特化,它支持的是针对 impl 块的特化。我们可以为一组类型,impl 一个 trait,同时为其中的一部分更特殊的类型,impl 同一个 trait。
杨永贞
2022/07/28
1.2K0
Rust之泛型特化
【Rust学习】08_使用结构体代码示例
为了了解我们何时可能想要使用结构体,让我们编写一个计算长方形面积的程序。我们将从使用单个变量开始,然后重构程序,直到我们改用结构体。
思索
2024/09/14
1340
【Rust学习】08_使用结构体代码示例
Rust入坑指南:海纳百川
在前面的文章中,我们其实已经提及了一些泛型类型。例如Option、Vec和Result。泛型可以在函数、数据结构、Enum和方法中进行定义。在Rust中,我们习惯使用T作为通用的类型名称,当然也可以是其他名称,只不过习惯上优先使用T(Type)来表示。它可以帮我们消除一些重复代码,例如实现逻辑相同但参数类型不同的两个函数,我们就可以通过泛型技术将其进行合并。下面我们分别演示泛型的几种定义。
Jackeyzhe
2020/03/10
5740
Rust泛型Generics
泛型(Generics)是一种程序设计风格,它允许程序员在强类型语言(例如rust,c#,c++)中编写代码时使用通用类型。以rust为例,如果你想实现一个通用的add函数,让其在u8, i32, u64等类型中通用。如果没有泛型,虽然它们的逻辑是一致的,但是你需要为不同类型编写不同的函数,而泛型帮助我们只需要编写一个函数,实现通用逻辑即可。例如:
zy010101
2023/03/01
7660
Rust修仙笔记结丹期
在方法后面使用通用类型T,比如fn foo<T: Display>(arg:T){}
Maic
2024/06/17
1090
Rust修仙笔记结丹期
【Rust每周一知】Rust 中 trait、关联类型与泛型配合的常见模式
Rust 中,trait,关联类型,泛型,这几个概念本身并不复杂。但是这些东西合在一起使用的时候,经常让初学者感觉天花乱坠,摸不着头脑。本文就用一些简单的例子,来梳理一下这些概念,以及它们之间的配合使用方式。
MikeLoveRust
2019/12/30
1.9K0
Rust特征(Trait)
特征(Trait) 特征(trait)是rust中的概念,类似于其他语言中的接口(interface)。在之前的代码中,我们也多次见过特征的使用,例如 #[derive(Debug)],它在我们定义的
zy010101
2023/03/09
6450
【Rust】005-Rust 结构体
在Rust中,元组结构体是一种特殊的结构体形式,它结合了元组和结构体的特性。元组结构体类似于普通的结构体,但它没有字段名称,只有字段类型。这种结构体更像是带标签的元组,通常用于需要对某些数据进行简单封装而不需要命名每个字段的场景。
訾博ZiBo
2025/01/06
1220
go 开发者的 rust 入门
即:在任意给定时间,要么 只能有一个可变引用,要么 只能有多个不可变引用。引用必须总是有效的。
王磊-字节跳动
2021/11/27
1.9K0
rust闭包(Closure)
闭包在现代化的编程语言中普遍存在。闭包是一种匿名函数,它可以赋值给变量也可以作为参数传递给其它函数,不同于函数的是,它允许捕获调用者作用域中的值。Rust 闭包在形式上借鉴了 Smalltalk 和 Ruby 语言,与函数最大的不同就是它的参数是通过 |parm1| 的形式进行声明,如果是多个参数就 |param1, param2,…|, 下面给出闭包的形式定义:
zy010101
2023/04/27
6910
rust闭包(Closure)
Rust 关联常量,泛型结构体,内部可变性
Rust 在其类型系统中的另一个特性也采用了类似于 C# 和 Java 的思想,有些值是与类型而不是该类型的特定实例关联起来的。在 Rust 中,这些叫作关联常量。
草帽lufei
2024/05/08
2190
Rust 关联常量,泛型结构体,内部可变性
Rust 中 Trait 的使用及实现分析
在 Rust 设计目标中,零成本抽象是非常重要的一条,它让 Rust 具备高级语言表达能力的同时,又不会带来性能损耗。零成本的基石是泛型与 trait,它们可以在编译期把高级语法编译成与高效的底层代码,从而实现运行时的高效。这篇文章就来介绍 trait,包括使用方式与三个常见问题的分析,在问题探究的过程中来阐述其实现原理。
MikeLoveRust
2021/05/11
2K0
【Rust学习】25_特征
特征(trait)定义了特定类型所具有的并且可以与其他类型共享的功能。我们可以使用特征以抽象的方式定义共享的行为。我们可以使用特征约束来指定泛型类型可以是任何具有特定行为的类型。
思索
2025/02/14
860
【Rust学习】25_特征
Rust语法入门
Rust 是一种系统级编程语言,它的设计目标是提供高性能、安全性和并发性。Rust 的主要优势包括:
码客说
2023/04/17
1.3K0
Rust学习笔记Day12 接口trait介绍及如何让trait支持泛型
通过昨天的学习,我们对Rust的类型系统有了新的认识。还学习了泛型数据结构和泛型函数来处理参数多态。接下来,我们将会学习特设多态和子类型多态。
用户1072003
2023/02/23
3970
Rust学习笔记Day12 接口trait介绍及如何让trait支持泛型
【翻译】Rust生命周期常见误区
我曾经有过的所有这些对生命周期的误解,现在有很多初学者也深陷于此。我用到的术语可能不是标准的,所以下面列了一个表格来解释它们的用意。
MikeLoveRust
2020/07/28
1.7K0
Rustilings 练习笔记
Rust有类型检查,执行运算或者赋值时候要遵循类型的规律,但是Rust可以重新定义同名变量,变量的类型可以发生改变
用户7267083
2023/03/20
1.5K0
相关推荐
24.Rust-泛型
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验