Rust 泛型与特性 | 菜鸟教程


本站和网页 https://www.runoob.com/rust/rust-generics.html 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

Rust 泛型与特性 | 菜鸟教程
菜鸟教程 -- 学的不仅是技术,更是梦想!
首页
HTML
CSS
JavaScript
Vue
Bootstrap
NodeJS
Python3
Python2
Java
C++
C#
Go
SQL
Linux
jQuery
本地书签
首页
HTML
CSS
JS
本地书签
Search
Python3 教程
Python2 教程
Vue3 教程
vue2 教程
Bootstrap3 教程
Bootstrap4 教程
Bootstrap5 教程
Bootstrap2 教程
Rust 教程
Rust 教程
Rust 环境搭建
Cargo 教程
Rust 输出到命令行
Rust 基础语法
Rust 数据类型
Rust 注释
Rust 函数
Rust 条件语句
Rust 循环
Rust 所有权
Rust Slice(切片)类型
Rust 结构体
Rust 枚举类
Rust 组织管理
Rust 错误处理
Rust 泛型与特性
Rust 生命周期
Rust 文件与 IO
Rust 集合与字符串
Rust 面向对象
Rust 并发编程
Rust 宏
Rust 错误处理
Rust 生命周期
Rust 泛型与特性
泛型是一个编程语言不可或缺的机制。C++ 语言中用"模板"来实现泛型,而 C 语言中没有泛型的机制,这也导致 C 语言难以构建类型复杂的工程。
泛型机制是编程语言用于表达类型抽象的机制,一般用于功能确定、数据类型待定的类,如链表、映射表等。
在函数中定义泛型
这是一个对整型数字选择排序的方法:
实例
fn max(array: &[i32]) -> i32 {
    let mut max_index = 0;
    let mut i = 1;
    while i < array.len&#40;&#41; &#123;
        if array&#91;i&#93; > array&#91;max_index&#93; &#123;
            max_index = i;
        &#125;
        i += 1;
    &#125;
    array&#91;max_index&#93;
&#125;
fn main&#40;&#41; &#123;
    let a = &#91;2, 4, 6, 3, 1&#93;;
    println!&#40;"max = {}", max&#40;&a&#41;&#41;;
&#125;
运行结果:
max = 6
这是一个简单的取最大值程序,可以用于处理 i32 数字类型的数据,但无法用于 f64 类型的数据。通过使用泛型我们可以使这个函数可以利用到各个类型中去。但实际上并不是所有的数据类型都可以比大小,所以接下来一段代码并不是用来运行的,而是用来描述一下函数泛型的语法格式:
实例
fn max<T>&#40;array: &&#91;T&#93;&#41; -> T &#123;
    let mut max_index = 0;
    let mut i = 1;
    while i < array.len&#40;&#41; &#123;
        if array&#91;i&#93; > array&#91;max_index&#93; &#123;
            max_index = i;
        &#125;
        i += 1;
    &#125;
    array&#91;max_index&#93;
&#125;
结构体与枚举类中的泛型
在之前我们学习的 Option 和 Result 枚举类就是泛型的。Rust 中的结构体和枚举类都可以实现泛型机制。
struct Point<T> {
x: T,
y: T
这是一个点坐标结构体,T 表示描述点坐标的数字类型。我们可以这样使用:
let p1 = Point {x: 1, y: 2};
let p2 = Point {x: 1.0, y: 2.0};
使用时并没有声明类型,这里使用的是自动类型机制,但不允许出现类型不匹配的情况如下:
let p = Point {x: 1, y: 2.0};
x 与 1 绑定时就已经将 T 设定为 i32,所以不允许再出现 f64 的类型。如果我们想让 x 与 y 用不同的数据类型表示,可以使用两个泛型标识符:
struct Point<T1, T2> {
x: T1,
y: T2
在枚举类中表示泛型的方法诸如 Option 和 Result:
enum Option<T> {
Some(T),
None,
enum Result<T, E> {
Ok(T),
Err(E),
结构体与枚举类都可以定义方法,那么方法也应该实现泛型的机制,否则泛型的类将无法被有效的方法操作。实例
struct Point<T> &#123;
x: T,
y: T,
&#125;
impl<T> Point<T> &#123;
fn x&#40;&self&#41; -> &T &#123;
&self.x
&#125;
&#125;
fn main&#40;&#41; &#123;
let p = Point &#123; x: 1, y: 2 &#125;;
println!&#40;"p.x = {}", p.x&#40;&#41;&#41;;
&#125;
运行结果:
p.x = 1
注意,impl 关键字的后方必须有 <T>,因为它后面的 T 是以之为榜样的。但我们也可以为其中的一种泛型添加方法:
impl Point<f64> {
fn x(&self) -> f64 {
self.x
impl 块本身的泛型并没有阻碍其内部方法具有泛型的能力:
impl<T, U> Point<T, U> {
fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
Point {
x: self.x,
y: other.y,
方法 mixup 将一个 Point<T, U> 点的 x 与 Point<V, W> 点的 y 融合成一个类型为 Point<T, W> 的新点。
特性
特性(trait)概念接近于 Java 中的接口(Interface),但两者不完全相同。特性与接口相同的地方在于它们都是一种行为规范,可以用于标识哪些类有哪些方法。
特性在 Rust 中用 trait 表示:
trait Descriptive {
fn describe(&self) -> String;
Descriptive 规定了实现者必需有 describe(&self) -> String 方法。我们用它实现一个结构体:
实例
struct Person &#123;
name: String,
age: u8
&#125;
impl Descriptive for Person &#123;
fn describe&#40;&self&#41; -> String &#123;
format!&#40;"{} {}", self.name, self.age&#41;
&#125;
&#125;
格式是:
impl <特性名> for <所实现的类型名>
Rust 同一个类可以实现多个特性,每个 impl 块只能实现一个。
默认特性
这是特性与接口的不同点:接口只能规范方法而不能定义方法,但特性可以定义方法作为默认方法,因为是"默认",所以对象既可以重新定义方法,也可以不重新定义方法使用默认的方法:
实例
trait Descriptive &#123;
fn describe&#40;&self&#41; -> String &#123;
String::from&#40;"[Object]"&#41;
&#125;
&#125;
struct Person &#123;
name: String,
age: u8
&#125;
impl Descriptive for Person &#123;
fn describe&#40;&self&#41; -> String &#123;
format!&#40;"{} {}", self.name, self.age&#41;
&#125;
&#125;
fn main&#40;&#41; &#123;
let cali = Person &#123;
name: String::from&#40;"Cali"&#41;,
age: 24
&#125;;
println!&#40;"{}", cali.describe&#40;&#41;&#41;;
&#125;
运行结果:
Cali 24
如果我们将 impl Descriptive for Person 块中的内容去掉,那么运行结果就是:
[Object]
特性做参数
很多情况下我们需要传递一个函数做参数,例如回调函数、设置按钮事件等。在 Java 中函数必须以接口实现的类实例来传递,在 Rust 中可以通过传递特性参数来实现:
fn output(object: impl Descriptive) {
println!("{}", object.describe());
任何实现了 Descriptive 特性的对象都可以作为这个函数的参数,这个函数没必要了解传入对象有没有其他属性或方法,只需要了解它一定有 Descriptive 特性规范的方法就可以了。当然,此函数内也无法使用其他的属性与方法。
特性参数还可以用这种等效语法实现:
fn output<T: Descriptive>(object: T) {
println!("{}", object.describe());
这是一种风格类似泛型的语法糖,这种语法糖在有多个参数类型均是特性的情况下十分实用:
fn output_two<T: Descriptive>(arg1: T, arg2: T) {
println!("{}", arg1.describe());
println!("{}", arg2.describe());
特性作类型表示时如果涉及多个特性,可以用 + 符号表示,例如:
fn notify(item: impl Summary + Display)
fn notify<T: Summary + Display>(item: T)
注意:仅用于表示类型的时候,并不意味着可以在 impl 块中使用。
复杂的实现关系可以使用 where 关键字简化,例如:
fn some_function<T: Display + Clone, U: Clone + Debug>(t: T, u: U)
可以简化成:
fn some_function<T, U>(t: T, u: U) -> i32
where T: Display + Clone,
U: Clone + Debug
在了解这个语法之后,泛型章节中的"取最大值"案例就可以真正实现了:
实例
trait Comparable &#123;
fn compare&#40;&self, object: &Self&#41; -> i8;
&#125;
fn max<T: Comparable>&#40;array: &&#91;T&#93;&#41; -> &T &#123;
let mut max_index = 0;
let mut i = 1;
while i < array.len&#40;&#41; &#123;
if array&#91;i&#93;.compare&#40;&array&#91;max_index&#93;&#41; > 0 &#123;
max_index = i;
&#125;
i += 1;
&#125;
&array&#91;max_index&#93;
&#125;
impl Comparable for f64 &#123;
fn compare&#40;&self, object: &f64&#41; -> i8 &#123;
if &self > &object &#123; 1 &#125;
else if &self == &object &#123; 0 &#125;
else &#123; -1 &#125;
&#125;
&#125;
fn main&#40;&#41; &#123;
let arr = &#91;1.0, 3.0, 5.0, 4.0, 2.0&#93;;
println!&#40;"maximum of arr is {}", max&#40;&arr&#41;&#41;;
&#125;
运行结果:
maximum of arr is 5
Tip: 由于需要声明 compare 函数的第二参数必须与实现该特性的类型相同,所以 Self (注意大小写)关键字就代表了当前类型(不是实例)本身。
特性做返回值
特性做返回值格式如下:
实例
fn person&#40;&#41; -> impl Descriptive &#123;
Person &#123;
name: String::from&#40;"Cali"&#41;,
age: 24
&#125;
&#125;
但是有一点,特性做返回值只接受实现了该特性的对象做返回值且在同一个函数中所有可能的返回值类型必须完全一样。比如结构体 A 与结构体 B 都实现了特性 Trait,下面这个函数就是错误的:
实例
fn some_function&#40;bool bl&#41; -> impl Descriptive &#123;
if bl &#123;
return A &#123;&#125;;
&#125; else &#123;
return B &#123;&#125;;
&#125;
&#125;
有条件实现方法
impl 功能十分强大,我们可以用它实现类的方法。但对于泛型类来说,有时我们需要区分一下它所属的泛型已经实现的方法来决定它接下来该实现的方法:
struct A<T> {}
impl<T: B + C> A<T> {
fn d(&self) {}
这段代码声明了 A<T> 类型必须在 T 已经实现 B 和 C 特性的前提下才能有效实现此 impl 块。
Rust 错误处理
Rust 生命周期
点我分享笔记
取消
分享笔记
昵称昵称 (必填)
邮箱邮箱 (必填)
引用地址引用地址
分类导航
HTML / CSSHTML 教程HTML5 教程CSS 教程CSS3 教程Bootstrap3 教程Bootstrap4 教程Bootstrap5 教程Font Awesome 教程Foundation 教程 JavaScriptJavaScript 教程HTML DOM 教程jQuery 教程AngularJS 教程AngularJS2 教程Vue.js 教程Vue3 教程React 教程TypeScript 教程jQuery UI 教程jQuery EasyUI 教程Node.js 教程AJAX 教程JSON 教程Echarts 教程Chart.js 教程Highcharts 教程Google 地图 教程 服务端Python 教程Python2.x 教程Linux 教程Docker 教程Ruby 教程Java 教程C 教程C++ 教程Perl 教程Servlet 教程JSP 教程Lua 教程Rust 教程Scala 教程Go 教程PHP 教程数据结构与算法Django 教程FastAPI 教程Zookeeper 教程设计模式正则表达式Maven 教程Verilog 教程ASP 教程AppML 教程VBScript 教程 数据库SQL 教程MySQL 教程PostgreSQL 教程SQLite 教程MongoDB 教程Redis 教程Memcached 教程 数据分析Python 教程NumPy 教程Pandas 教程Matplotlib 教程Scipy 教程R 教程Julia 教程 移动端Android 教程Swift 教程jQuery Mobile 教程ionic 教程Kotlin 教程 XML 教程XML 教程DTD 教程XML DOM 教程XSLT 教程XPath 教程XQuery 教程XLink 教程XPointer 教程XML Schema 教程XSL-FO 教程SVG 教程 ASP.NETASP.NET 教程C# 教程Web Pages 教程Razor 教程MVC 教程Web Forms 教程 Web ServiceWeb Service 教程WSDL 教程SOAP 教程RSS 教程RDF 教程 开发工具Eclipse 教程Git 教程Svn 教程Markdown 教程 网站建设HTTP 教程网站建设指南浏览器信息网站主机教程TCP/IP 教程W3C 教程网站品质
Advertisement
反馈/建议
在线实例
&middot;HTML 实例
&middot;CSS 实例
&middot;JavaScript 实例
&middot;Ajax 实例
&middot;jQuery 实例
&middot;XML 实例
&middot;Java 实例
字符集&工具
&middot; HTML 字符集设置
&middot; HTML ASCII 字符集
&middot; JS 混淆/加密
&middot; PNG/JPEG 图片压缩
&middot; HTML 拾色器
&middot; JSON 格式化工具
&middot; 随机数生成器
最新更新
&middot;
13.5k star, 免...
&middot;
Rust 宏
&middot;
Seaborn 教程
&middot;
Pandas 相关性分析
&middot;
31.2k star, 免...
&middot;
Dev Home &#8212...
&middot;
免费开源的 AI ...
站点信息
&middot;
意见反馈
&middot;
免责声明
&middot;
关于我们
&middot;
文章归档
关注微信
Copyright 2013-2024 菜鸟教程
runoob.com All Rights Reserved. 备案号:闽ICP备15012807号-1
微信关注