rust泛型过度使用思考

【Rust指南】泛型与特性 在上篇Rust 文章中涉及到了泛型的知识,那么今天就来详细介绍一下Rust 中的泛型与特性。泛型是一个编程语言不可或缺的机制,例如在C++ 语言中用模板来实现泛型。泛型机制是编程语言用于表达类型抽象的机制,一般用于功能确定、数据类型待定的类,如链表、映射表等。} i += 1;println!} //运行结果:max = 8这是一个简单的取最大值程序,可以用于处理i32数字类型的数据,但无法用于f64类型的数据。通过使用泛型} i += 1;实际上,并不是所有的数据类型都可以比大小。 阅读详情

写这篇文章的时候我只是用打印函数地址的方式来测试的,这个测试方法可能并不是很妥当,可能会引起编译器在某些情况下放弃优化(个人猜测),目前想到的方法:应该在release模式下大量impl一个实现了静态方法的trait看文件膨胀的速度,这个我目前还在想办法测试。所以目前来说下面的结论暂时还是存疑的,希望大家也能想办法测试或者能直接找到官方的实际做法的文章。

目前了解来看rust泛型的概念很好,比c++多了很多类型限定和编译时检查,出错提示相对c++的模板来说也特别特别的友好。但任何事物总是有两面性,过度的使用泛型会使得代码极度的膨胀,这个膨胀的速度在我看来比c++的模板快多了

1 trait里的默认函数和方法引起的代码膨胀

首先来看一段简单的代码:


trait Foo{
    fn foo(){
        println!("foo");
    }
}

struct Bar1;
struct Bar2;

impl Foo for Bar1{}
impl Foo for Bar2{}

fn main(){
    println!("{:p}",&Bar1::foo);
    println!("{:p}",&Bar2::foo);
}

看看这个运行结果:

0x7f8147412150
0x7f8147412178

结果可以看到:Bar1::foo和Bar2::foo是两个地址并不一样的函数,这样就意味着虽然这两个函数的二进制代码完全一样,但是rust还是给复制了两份。
从rust语言本身的特性来看这么做有他的理由,比如默认函数可以被覆盖等,但从结果上来看你就必须很小心了,如果你的trait Foo有很多默认函数,并且不巧你有很多类型都实现了Foo,那么代码的膨胀速度估计会出乎意料的

2 保存closure引起的代码膨胀

rust的closure与c++类似,都是生成一个匿名的结构体包装你用到的“环境”里所有的值,然后类似c++重载operator ()来实现调用,这也就意味着两个完全相同的closure,比如:|i|i+100 ,你写两次他就是两个不同的类型,这样你用泛型保存closure的时候就要注意了

下面看一个稍微复杂一点的例子:


trait Foo{
    fn foo(){
        println!("addr of Foo::foo:{:p}",&Self::foo);
    }

    fn print_foo_addr(&self);
}

//保存closure
struct Bar<F:Fn(i32)->i32>{
    func:F,
    i:i32,
}

impl <F> Bar<F> where F:Fn(i32)->i32{
    fn new(f:F)->Bar<F>{
        Bar{
            func:f,
            i:100
        }
    }

    fn print_new_addr(&self){
        println!("addr of Bar::new :{:p}",&Self::new);
        println!("addr of Bar::print :{:p}",&Self::print_new_addr);
    }   
}

impl<F>Foo for Bar<F> where F:Fn(i32)->i32 {
    fn print_foo_addr(&self){
        Self::foo();
        println!("addr of Foo::print:{:p}",&Self::print_foo_addr);
    }
}

fn main(){
    let b = Bar::new(|i|{
        i+100
    });

    let b2 = Bar::new(|i|{
        i+100
    });

    b.print_foo_addr();
    b.print_new_addr();
    println!("");
    b2.print_foo_addr();
    b2.print_new_addr();
}

Play地址:http://is.gd/CM8Vnj
无论是debug,release,nightly输出结果都差不多:

addr of Foo::foo:0x7ffbe66e5180
addr of Foo::print:0x7ffbe66e5190
addr of Bar::new :0x7ffbe66e51a0
addr of Bar::print :0x7ffbe66e51b0

addr of Foo::foo:0x7ffbe66e51c0
addr of Foo::print:0x7ffbe66e51d0
addr of Bar::new :0x7ffbe66e51e0
addr of Bar::print :0x7ffbe66e51f0

可以看到所有的方法的代码全部被复制了一遍

如果你只是用某个函数或者方法接收一个closure做回调那就好很多了,rust不是整个类型的全部函数方法都复制,rust只会把你接收closure的那个函数重新实现一下

附带一个列子强化下即使看起来一样的closure都是不一样的:

struct Foo<F:Fn(i32)->i32>{
    f:F,
}


impl<F:Fn(i32)->i32> Foo<F>{
    fn new(f:F)->Foo<F>{
        Foo{
            f:f,
        }
    }
}


fn main(){

    let b = Foo::new(|i|{i+100});
    (b.f)(100);
}

上面这个例子保存了一个closure,编译和运行都正常,play地址:http://is.gd/JVCd00

我们对其做一些修改,再添加一个成员:f2:F


struct Foo<F:Fn(i32)->i32>{
    f:F,
    f2:F,
}

impl<F:Fn(i32)->i32> Foo<F>{
    fn new(f:F,f2:F)->Foo<F>{
        Foo{
            f:f,
            f2:f2,
        }
    }
}

fn main(){

    let b = Foo::new(|i|{i+100},|i|{i+100});
    (b.f)(100);
    (b.f2)(100);
}

Play地址:http://is.gd/GSTQbq
这个列子是不能正确编译的的,看看编译结果:

<anon>:20:33: 20:43 error: mismatched types:
 expected `[closure <anon>:20:22: 20:32]`,
    found `[closure <anon>:20:33: 20:43]`
(expected closure,
    found a different closure) [E0308]
<anon>:20     let b = Foo::new(|i|{i+100},|i|{i+100});
                                          ^~~~~~~~~~
note: in expansion of closure expansion
<anon>:20:33: 20:43 note: expansion site
<anon>:20:33: 20:43 help: see the detailed explanation for E0308
<anon>:20:33: 20:43 note: no two closures, even if identical, have the same type
<anon>:20     let b = Foo::new(|i|{i+100},|i|{i+100});
                                          ^~~~~~~~~~
note: in expansion of closure expansion
<anon>:20:33: 20:43 note: expansion site
<anon>:20:33: 20:43 help: consider boxing your closure and/or using it as a trait object
<anon>:20     let b = Foo::new(|i|{i+100},|i|{i+100});
                                          ^~~~~~~~~~
note: in expansion of closure expansion
<anon>:20:33: 20:43 note: expansion site
error: aborting due to previous error
playpen: application terminated with error code 101
Compilation failed.

也就是说我们看起来两个完全一样的closure编译器认为是两种类型

3 lifetime参数并不会引起代码的复制

lifetime也放在了<>里,测试下看看:

struct Foo<'a>{
    i:&'a i32,
}

impl<'a> Foo<'a>{

    fn print_addr(&self){
        println!("i={},{:p}",self.i,&Self::print_addr);
    }
}

fn main(){
    let i=1i32;
    let f1 = Foo{i:&i};
    f1.print_addr();
    {
        {
            let k=2i32;
            let f3 = Foo{i:&k};
            f3.print_addr();
        }
        let j=3i32;
        let f2 = Foo{i:&j};
        f2.print_addr();

        {
            let f4 = Foo{i:&i};
            f4.print_addr();
        }
    }
}

Play地址:http://is.gd/lepdI2
结果可以看出lifetime并不会引起代码的复制

i=1,0x7f7ca62b1140
i=2,0x7f7ca62b1140
i=3,0x7f7ca62b1140
i=1,0x7f7ca62b1140
Rust编译时计算:const泛型实现矩阵运算优化.pdf 文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 Rust 以内存安全、零成本抽象和并发高效的特性,重塑编程体验。无需垃圾回收,却能通过所有权与借用检查机制杜绝空指针、数据竞争等隐患。从底层系统开发到 Web 服务构建,从物联网设备到高性能区块链,它凭借出色的性能和可靠性,成为开发者的全能利器。拥抱 Rust,解锁高效、安全编程新境界! 立即下载

相关推荐

用VINS-Fusion跑通EuRoC数据集:Ubuntu22.04+ROS2环境下的保姆级评测指南

本文详细介绍了在Ubuntu22.04和ROS2 Humble环境下使用VINS-Fusion处理EuRoC数据集的完整流程。从环境配置、数据集转换到参数调优和性能评估,提供了一套全面的多传感器融合分析方案,帮助开发者在无人机精准定位场景中实现高效部署与优化。

weixin_29212867的博客 517

第N次入门Rust - 9.泛型、trait和生命周期

这一篇介绍Rust的核心泛型、trait和生命周期~

你好! 1606

数学建模国赛B题保姆级攻略:从选题到代码实现的完整流程(2024最新版)

本文提供2024年数学建模国赛B题的保姆级实战攻略,涵盖从赛题解构、模型构建到算法实现与论文撰写的完整流程。文章详细解析了如何快速抓住问题核心、建立数学模型、进行算法求解与结果可视化,并分享了高效团队协作与专业论文写作的关键技巧,助力参赛者系统性地完成从选题到代码实现的全过程。

weixin_29089057的博客 138

c++ 与 rust 比较:所有权机制的区别

读完 rust 教程,用了一天时间写此总结,仔细回顾了变量所有权的机制,并与 c++ 的对应机制作了比较,以加深对 rust 所有权机制的理解。

程序猿视角 4778

BPMN2.0规范及流程引擎选型方案

本文主要简明扼要的介绍了BPMN2.0规范的内容,可让读者快速度的了解BPMN2.0所涵盖的主要内容和图形样式。也较为详细的阐述了如何绘制标准的流程图,以及在软件层面中如何实现业务流程功能,和技术选型。

比特桃 1万+

PyTorch转ONNX遇到算子不支持?3种实战解决方案+TensorRT适配技巧

本文针对PyTorch模型导出ONNX时常见的算子不支持问题,提供了从诊断到解决的完整实战指南。首先讲解如何精准定位错误根源,随后详细介绍了三种核心解决方案:注册缺失的符号映射、实现自定义算子,以及为TensorRT开发插件的终极落地方案。文章还分享了构建稳健部署流水线的工程化实践,帮助开发者系统性地解决模型部署中的算子兼容性挑战。

fern8的博客 842

rust 泛型、多态

无论是c++ STL还是rust std,里面所有的数据结构都是泛型的,c++和rust的结构体也类似,可以是泛型的也可以是非泛型的。所以,泛型函数内部对T的约束条件,都通过指明T所包含的trait的方式进行说明。Ord特征中并没有eq函数,但是Ord特征间接继承了PartialOrd,所以有Ord特征的类型肯定是可以使用==的。对于绝大部分trait,模板类型都是默认不包含该trait的,需要特征约束才能说明该类型具有该trait。作为泛型参数的常数类型,可以是所有整数类型、bool类型、char类型。

nameofcsdn的博客 2346

AWS S3文件上传性能优化:Java SDK多线程与分段上传实战指南

本文详细介绍了如何通过Java SDK优化AWS S3文件上传性能,重点讲解多线程并发上传与分段上传(Multipart Upload)技术的实战应用。通过动态调整分段策略、线程池配置及内存优化技巧,可显著提升大文件上传效率,实测显示组合方案比单线程上传提速近5倍。文章还涵盖加密上传、断点续传等高级场景解决方案,并提供了完整的性能对比测试数据。

d6e7f8的博客 1016

Rust与C++:模板与泛型编程对比

提纲:

2301_82243007的博客 447

脑电分析:为什么大脑信号会“变同步”?——事件相关同步化与去同步化 (ERS/ERD) 全景解析

脑电分析中的事件相关同步化(ERS)和去同步化(ERD)是研究大脑动态节律的重要方法,反映特定任务下脑电频段能量的变化。ERD表示神经元活动分散(功率下降),提示脑区活跃处理信息;ERS则反映神经元同步(功率增强),常与静息状态相关。本文详解了ERS/ERD的生理机制、计算流程(包括基线校正和功率谱分析),并提供了Python实现示例,展示如何通过Welch法和STFT进行时频分析,生成ERD/ERS热力图。该技术广泛应用于运动想象脑机接口、癫痫检测等领域,为理解大脑功能状态提供了量化工具。

qq_32516809的博客 1394

5大步骤!轻松玩转Dorisoy.Rigger:基于.NET Core 6、MAUI和Blazor的多数据库开发框架生成器

各位小伙伴,你们是否厌倦了每次项目启动都要从头配置数据库连接、编写重复的代码?今天,我们就来聊聊如何使用Dorisoy.Rigger这个强大的开发框架生成器,让你的.NET Core 6、MAUI和Blazor项目开发变得简单又高效!准备好迎接开发的新乐趣了吗?让我们一起开始吧!接下来,我们需要定义数据模型。假设我们有一个简单的用户表,包含ID、用户名和密码字段。在项目中创建一个新的类文件,命名为User.csset;set;set;这段代码定义了一个用户模型,其中包含了用户的ID、用户名和密码。

java专栏 1738

Rust Trait 约束深度解析:从泛型到型系统编程

看似 trait 约束在限制泛型的灵活性,实则它们为泛型代码提供了必要的"脚手架"。没有约束的泛型就像没有规则的游戏——初看自由无限,实际上充满了陷阱。而精心设计的 trait 约束不仅让代码更安全、更高效,还让 Rust 的类型系统成为表达复杂系统设计的强有力工具。当你开始思考"用什么 trait 来约束这个泛型参数"时,你实际上在进行系统级的接口设计,这正是 Rust 哲学的精髓所在。✨。

2501_94041496的博客 696

【YOLOv8全方位学习手册】第四章 数据增强:从扩充数据到塑造模型的认知边界

"""使用 Albumentations 做自定义增强,兼容 YOLO 格式"""])# 转成 numpy# boxes 必须是 [[x_min,y_min,x_max,y_max], ...]# 在数据加载时调用你可以把集成到 PyTorch 的 Dataset 类里,或使用 Ultralytics 自带的DataLoader。

VectorShift的博客 211

Rust 生命周期与泛型的组合使用:类型系统的双重抽象

摘要 本文深入探讨了 Rust 中生命周期与泛型的交互机制。生命周期通过时间参数保证引用安全,泛型通过类型参数实现多态性,二者的结合形成了强大的类型系统抽象能力。文章分析了生命周期参数优先于类型参数的约定及其技术原因,并通过五个典型案例展示了协同工作的实践:基础组合使用、带约束的泛型、多参数处理、泛型迭代器实现以及带生命周期的泛型 trait。这些案例揭示了如何在保持灵活性的同时确保类型安全,为设计优雅 API 提供了深刻见解。理解这种双重抽象机制是编写高质量 Rust 代码的关键。

超哥的博客 505

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Rust中的where子句:优雅处理复杂泛型约束

where子句不仅仅是一个语法糖,它体现了Rust在类型系统设计上的深思熟虑。通过合理使用where子句,我们能够构建出既强大又易读的泛型代码,这正是Rust"零成本抽象"哲学的完美体现。掌握where子句的精髓,就是掌握了编写高质量Rust代码的关键技能。

weixin_63031932的博客 677

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Rust 进阶之路:别再写重复代码了!深度解析泛型与 Trait 的实战艺术

不要过度约束:如果只需要读取数据,用&T就够了,没必要要求T: Clone。不要忽视生命周期:泛型中引入引用时,必须明确生命周期标记'a,否则编译器会教你做人。尽早测试多类型:泛型代码由于是延迟生成的,有时候在i32下正常,换成自定义结构体就可能因为没实现某个 Trait 而报错。掌握泛型和 Trait,是你从 Rust 开发者向 Rust 架构师跨越的关键一步。它们让你编写的代码不仅是“能运行”,更是“易扩展、高性能且类型安全”。

weixin_67351993的博客 462

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

CLAUDE.md:你的AI编程搭档是否在“过度思考“?

你是否曾让AI助手编写代码,结果却得到一份冗长复杂的解决方案,而实际上你只需要几行简单的代码?或者AI在修改文件时"顺手"重构了相邻的代码,让你不得不花时间审查那些本不需要的改动?这正是许多开发者在使用大型语言模型编程时面临的真实困境——AI助手过于"热心",却不够"聪明"。 今天,我们介绍一个简单却强大的解决方案:一个名为`CLAUDE.md`的单一文件。这个项目源自Andrej Karpat

gitblog_00038的博客 882

Rust高阶trait边界(HRTB)中的生命周期深度解析

生命周期边界是Rust类型系统中连接安全性与表达力的关键桥梁。通过精确的边界约束,开发者能够在编译时构建安全的内存访问模式,同时保持代码的抽象能力和灵活性。在实践中,需要深入理解边界约束的数学本质、类型系统中的具体表现,以及如何平衡安全性与灵活性。随着Rust编译器的持续优化和新特性(如泛型关联类型、const泛型)的引入,生命周期边界将在更复杂的类型系统中发挥核心作用。

2501_94029210的博客 1498

你为什么总是入门 Rust 失败

如果你正在学习 Rust,或者曾经尝试学过 Rust,大概率会有这种感受,明明跟着教程敲完了所有代码,感觉语法都懂了,可一旦脱离教程自己写,就完全无从下手。折腾个两三天,信心被一点一点磨没,最后只能放弃,默默给 Rust 贴上太难学的标签。

github_28443763的博客 1301

Rust 代码审查清单:从编译期到运行时的专业实践指南

一份专业的 Rust 代码审查不仅要确保代码能编译和运行,更要追求所有权设计的合理性、生命周期的清晰性、错误处理的结构化、泛型约束的精准性、并发安全的完整性和性能的可接受性。这需要审查者不仅理解 Rust 的规则,更要理解这些规则背后的哲学——在零成本抽象的原则下,实现内存安全和线程安全。只有这样,才能将 Rust 的潜力从语言层面充分释放到实战应用中。

2401_88071536的博客 801

Rust API 设计中的零成本抽象原则:从理论到实战

摘要:Rust的零成本抽象原则强调高级API不应产生运行时开销,本文探讨了其实践应用。关键点包括:1)理解零成本本质是"不比低级代码更昂贵";2)合理使用泛型与单态化优化;3)利用常量泛型实现编译时计算;4)谨慎使用内联属性;5)通过类型系统确保内存安全。文章指出,优秀的API设计需平衡性能、安全性和易用性,建议开发者从编译器优化角度审视抽象设计。掌握零成本抽象是成为高级Rust开发者的关键能力。

My2007_的博客 431

Rust中`‘static`生命周期的特殊含义深度解析

'static生命周期在Rust中扮演着独特的双重角色:既是内存安全的基石,又是高级抽象的关键工具。通过全局内存管理、类型约束、并发安全等多维度应用,'static确保了Rust系统编程的可靠性和高效性。在实践中,开发者需要深入理解'static的内存布局特性、类型约束能力和并发安全边界。在享受其带来的全局数据管理能力的同时,也要警惕过度使用导致的灵活性丧失和潜在内存泄漏风险。随着Rust语言的持续发展,'static。

2501_94029210的博客 343

Rust impl 块的组织方式:从混乱走向优雅 [特殊字符]

在 Rust 中,impl 块是实现类型行为的核心机制。?这不仅仅是代码风格问题,更是架构设计的体现。传统做法是将所有方法堆砌在一个巨大的 impl 块中,这会导致文件臃肿、查找困难、职责混乱。但 Rust 提供了多种组织方式,每种都有其适用场景。

2502_94042132的博客 326

Rust代码审查清单:从安全性到工程实践的深度思考

本文探讨了Rust代码审查的关键要点,包括:所有权系统的合理使用、避免过度克隆和复杂生命周期;错误处理的工程化实践,推荐使用自定义错误类型;并发安全审查重点,如死锁预防和异步代码安全性;性能与可读性平衡的考量;以及文档和测试的专业标准。这些审查要点帮助团队构建更健壮、可维护的Rust系统。

weixin_64512910的博客 236

Rust trait定义与实现:类型系统的契约机制

摘要:Rust的trait机制是类型系统的核心,它通过契约式设计将静态安全与运行时多态有机结合。trait支持默认实现、关联类型和泛型约束,并通过孤儿规则确保实现唯一性。高级应用包括动态分发的trait对象、where子句约束和标记trait。工程实践中需要权衡trait粒度与性能考量,单态化可能带来二进制膨胀。trait不仅是接口抽象,更是编译器的类型指令系统,能帮助开发者构建更安全、高效的软件架构。

Superman_chaoge的博客 1615

Rust Workspace 多项目管理:从实践到深度思考

Workspace 不仅是代码组织工具,更是一种架构思维的体现。它通过编译时约束引导我们构建模块化、可维护的系统。在实践中,理解其底层机制——从 Cargo.lock 的共享到增量编译的优化——能够帮助我们做出更明智的设计决策。对于大型 Rust 项目而言,合理运用 workspace 是从"能跑"到"跑得好"的关键一步。

weixin_63031932的博客 1380
上一篇: rust的一些细节
下一篇: 在rust里使用c样式的函数指针
varding
博客等级 码龄17年 115粉丝 116原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值