当前位置: 首页 > news >正文

Rust泛型详解 - 实践

Rust泛型详解 - 实践

泛型可以看作构建函数和类型定义的模板。标准库中有很多常见类型例如Result<T, E>Option<T>Vec<T>等都是泛型实现的。主要用于代码的特化。Rust是静态类型语言,因此泛型的类型参数(T)需要在编译期解析为具体类型,这种特性就是参数化多态性。泛型的实现还采用单态化技术,会根据实际传入的类型参数,将泛型实例化为唯一的专用类型。
泛型的优点:

  • 代码复用:只需编写一套代码,可以用于不同的类型。
  • 重构:重构变得更简单,因为泛型只有一份源码,不需要维护多份类型不同但功能相同的代码
  • 扩展性:可扩展性强,未来即使出现新的数据类型,泛型代码依然适用。
  • 更不容易犯错:使用泛型减少了大量重复代码的实现,潜在的错误也变少。
  • 独特功能:Rust中的泛型机制带来了独特的能力->函数重载

泛型函数

泛型函数是使用类型参数的函数模板,用于创建具体函数,

  • 类型参数要在函数名后使用<>声明
  • 命名惯例使用大驼峰命名,第一个类型参数一般用T,接着是U、V等
fn function_name<T>(param: T)->T {let variable: T;}

示例

fn swap<T, U>(tuple: (T, U))->(U, T) {(tuple.1, tuple.0)}fn main() {let tuple = (1, "hello");let tuple1 = ("hello".to_string(), "world".to_string());let tuple2 = (3.14, 98);let t_swap = swap(tuple);let t1_swap = swap(tuple1);let t2_swap = swap(tuple2);println!("{:?}", t_swap);println!("{:?}", t1_swap);println!("{:?}", t2_swap);}

编译器自动推导出了具体的类型,编译器会根据函数定义,选择调用对应的函数版本。

编译器通常能从参数类型推导出对应的类型参数,但如果无法推导出具体类型,就需要显式指定类型

function_name::<type,...>(arg,...)

示例

fn do_something<T: Default>()->T {let value: T = T::default();value}fn main() {let ret = do_something::<i8>();println!("{}", ret);}

泛型约束

在编译期,编译器对这些参数类型的实际类型并不了解,因此,编译器需要对使用类型参数的代码添加合理的限制。
可以把类型参数看作一个装着某种工具的黑盒子,你不知道里面具体是什么,但是你想安全地执行一些操作,这个时候会看有关于盒子内容的提示会非常有用。
trait约束就是用来限制类型参数的行为,可以限定单个或多个trait,每个trait都会告诉编译器类型参数应该具备哪些能力。

使用(:)用于添加trait约束,如<T: Trait>

use std::fmt::Display;
fn do_something<T: Display>(t: T) {println!("{}",t);}fn main() {do_something(20);}

因为格式化字符串中的{}占位符需要实现Display trait,编译器不能假设类型参数T具有此特性。

可以给类型参数指定多个限制

<T: Trait1+Trait2+...>

示例

use std::{cmp::Ordering, fmt::Display};
fn largest<T: Ord+Display>(arg1: T, arg2: T) {match arg1.cmp(&arg2) {Ordering::Less => println!("{arg1} < {arg2}"),Ordering::Greater => println!("{arg1} > {arg2}"),Ordering::Equal => println!("{arg1} == {arg2}"),}}fn test() {largest(1, 2);largest("arg1", "arg2");largest('a', 'b');largest(100, 99);}fn main() {test();}

largest是一个泛型函数,用于比较并输出结果。
要支持比较,这个类型必须实现Ord trait; 使用占位符{},要求这个类型必须实现Display,这两个trait都被应用与T的约束。

where

where是约束的另一种表示方法,这种方法的表达力更强

use std::fmt::Debug;
fn do_something<T>(t: T)where T: Debug {println!("{:?}", t);}fn main() {let tuple = (1,2,3,5);do_something(tuple);}

泛型结构体

除了函数之外,结构体同样支持泛型,在定义结构体时,可以在结构体名称后面加上<T> 之后该参数可以被用于结构体定义的任何位置(字段和方法)

struct Wrapper<T> {internal: T}fn main() {let obj = Wrapper {internal: 24};/* 会被单态化成i32类型struct wrapper {internal: i32}*/}

由于单态化机制,编译器会用i32类型实例化Wrapper结构体

泛型结构体不仅可以使用类型参数定义字段,还可以将类型参数用于方法定义。

use std::fmt::Debug;#[derive(Debug)]
struct Wrapper<T> {internal: T}impl<T: Copy> Wrapper<T> {fn get(&self) -> T {self.internal}}fn main() {let obj = Wrapper { internal: 20 };let obj1 = Wrapper {internal: "hello".to_string() };let ret = obj.get();println!("{}",ret);}

只有字段类型实现了Copy的T,才能调用该impl块内的所有方法。由于set方法需要创建T类型的拷贝,所以需要T实现Copy trait。而String类型不属于实现了Copy trait的类型,因此实例化后不具备get方法。

除了从结构体那里继承的类型参数,方法也可以定义专属于自己的类型参数。

use std::fmt::{Debug, Display};#[derive(Debug)]
struct Wrapper<T> {internal: T}impl<T: Copy+Display> Wrapper<T> {fn display<U: Display>(&self, prefix: U, suffix: U) {println!("{prefix} {} {suffix}", self.internal);}}fn main() {let obj = Wrapper { internal: 20 };obj.display("(",")");}

关联函数

你可以将泛型与关联函数一起使用

use std::fmt::{Debug, Display};#[derive(Debug)]
struct Wrapper<T> {internal: T}impl<T: Display> Wrapper<T> {fn hello(name: T) {println!("hello {}",name)}}fn main() {Wrapper::hello("张三");}

由于hello方法传入了一个&str类型的参数,编译器可以推断出T的类型,因此就不需要手动指定。
而下面这种情况需要手动指定类型:

use std::fmt::{Debug, Display};#[derive(Debug)]
struct Wrapper<T> {internal: T}impl<T> Wrapper<T> {fn hello() {println!("helloworld");}}fn main() {Wrapper::<&str>::hello();}
http://www.hskmm.com/?act=detail&tid=23443

相关文章:

  • AT_abc205_e [ABC205E] White and Black Balls
  • Python 自动化导出PDF表格:List、Dictionary、Pandas DataFrame和数据库实例演示 - 指南
  • Redis 持久化机制 - 教程
  • 2025染井吉野樱公司 TOP 种植服务推荐排行榜,染井吉野樱花苗,五公分染井吉野樱,十公分染井吉野樱,染井吉野樱批发,染井吉野樱基地,染井吉野樱花树公司推荐
  • glazewm_windows平铺窗口管理器使用方法
  • 树莓派搭建NAS之三:使用OpenList挂载网盘
  • sg-ss 逆向分析
  • PySide6 之登录界面设计
  • Jupyter notebook 虚拟环境(服务)EnvironmentLocationNotFound: Not a conda environment
  • 【Groovy】类和对象
  • 【Groovy】Array、List、Set、Map简介
  • 20届-测试面经-华为OD - 指南
  • 做题记录4
  • 银河麒麟V10服务器桌面SP1、SP2、SP3国防版集采版国防集采版教育版
  • 完整教程:华为eNSP环境安装和命令使用教程
  • [IOI 1998 / USACO2.2] 派对灯 Party Lamps 题解 + bitset浅谈
  • 解题报告-小 A 的树
  • 【React 状态管理深度解析:Object.is()、Hook 机制与 Vue 对比实践指南】 - 教程
  • 2025 --【J+S 二十连测】-- 第一套 总结
  • LSTM
  • 调和级数
  • 【实验报告】华东理工大学随机信号处理实验报告 - 详解
  • 页面置换算法
  • 推进电子设计革新:仿真(Emulation)如何引领下一代验证方式
  • AT_abc309_g [ABC309G] Ban Permutation
  • 在Mac上运行Windows 365的完整指南
  • 摩刻S10 动感单车 速度传感器故障及更换!
  • 2025盐酸优质厂家权威推荐榜:高纯度盐酸的品质之选
  • 2025硫酸优质厂家权威推荐榜:高品质与强供应口碑之选
  • 2025冰乙酸供应厂家权威推荐榜:品质卓越与市场口碑双重保障