尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Comprehensive Rust 泛型数据详解:以 Logger 与 VerbosityFilter 为例掌握泛型结构体

Comprehensive Rust 泛型数据详解:以 Logger 与 VerbosityFilter 为例掌握泛型结构体 Comprehensive Rust 泛型数据详解以 Logger 与 VerbosityFilter 为例掌握泛型结构体【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust导读本指南基于 Google Android 团队使用的 Rust 课程comprehensive-rust泛型数据一节深入讲解如何在结构体中用泛型抽象字段的具体类型。你将学会如何定义struct FooT、如何为泛型结构体编写implT: Trait实现块、为什么 trait bound 应放在 impl 块而非类型定义上并通过课程配套的Logger/VerbosityFilter示例与 Genericmin练习获得可直接运行的实战代码。泛型数据抽象字段的具体类型泛型Generics是 Rust 中最核心的抽象工具之一。在泛型函数一节中我们已经看到函数可以这样抽象fn pickT(cond: bool, left: T, right: T) - T { if cond { left } else { right } }同样的能力也适用于数据。泛型数据类型的核心思想是在定义结构体时不指定某个字段的具体类型而是用一个类型参数如L、T占位在使用时再填入具体类型。这样一段代码就可以服务于任意满足约束的类型无需为每种类型重复编写结构体。核心示例用泛型包装日志记录器课程文档泛型数据一节直接沿用前一节的练习场景给出一个完整可运行的程序。它定义了一个日志 traitLogger、一个具体实现StderrLogger然后通过一个泛型结构体VerbosityFilterL包装任意 logger并为其增加只记录不超过指定 verbosity 级别日志的过滤能力pub trait Logger { /// Log a message at the given verbosity level. fn log(self, verbosity: u8, message: str); } struct StderrLogger; impl Logger for StderrLogger { fn log(self, verbosity: u8, message: str) { eprintln!(verbosity{verbosity}: {message}); } } /// Only log messages up to the given verbosity level. struct VerbosityFilterL { max_verbosity: u8, inner: L, } implL: Logger Logger for VerbosityFilterL { fn log(self, verbosity: u8, message: str) { if verbosity self.max_verbosity { self.inner.log(verbosity, message); } } } fn main() { let logger VerbosityFilter { max_verbosity: 3, inner: StderrLogger }; logger.log(5, FYI); logger.log(2, Uhoh); }运行结果中verbosity5的 FYI 消息因超过max_verbosity 3被过滤而verbosity2的 Uhoh 会被输出到标准错误。这个示例直观展示了泛型数据类型的三种关键语法定义带类型参数的结构体struct VerbosityFilterL { max_verbosity: u8, inner: L }其中L是类型参数inner字段的类型在定义时未知、使用时确定为泛型结构体实现 traitimplL: Logger Logger for VerbosityFilterL { .. }约束L必须实现了Logger从而保证能调用self.inner.log(...)构造时自动推断类型VerbosityFilter { max_verbosity: 3, inner: StderrLogger }编译器从inner: StderrLogger推断出L StderrLogger无需显式标注。深入理解为什么implL: Logger ... VerbosityFilterL中L出现两次这是课程文档折叠区中特意设置的课堂提问也是初学者最容易困惑的一点类型定义中的L与 impl 块中的L是两个相互独立的泛型参数声明。struct VerbosityFilterL声明的是这个结构体有一个类型参数LimplL: Logger声明的是下面这段实现适用于所有满足L: Logger的类型L。两者各自独立泛型化因此语法上都要写出来语义上implL: Logger Logger for VerbosityFilterL意味着该实现块对所有满足L: Logger的类型生效你完全可以只针对某一具体类型写实现块例如impl VerbosityFilterStderrLogger { .. }。此时VerbosityFilter依旧是泛型结构体你仍可以构造VerbosityFilterf64但该块中的方法仅对VerbosityFilterStderrLogger可用。这是为特定类型扩展专属行为如添加只对某实现有意义的方法的常用手法。最佳实践trait bound 放在 impl 块而非类型定义上文档明确指出Note that we dont put a trait bound on theVerbosityFiltertype itself. You can put bounds there as well, but generally in Rust we only put the trait bounds on the impl blocks.即不要在结构体定义处给类型参数加 bound而是把 bound 放在 impl 块上。原因在于若写成struct VerbosityFilterL: Logger { ... }该约束会传染给所有使用点——任何持有VerbosityFilterL的代码都必须满足L: Logger即使它根本不需要调用日志功能把 bound 放在implL: Logger上则类型定义保持完全自由只有需要调用log的实现块才受约束。这符合 Rust 社区尽量宽松定义类型、严格约束使用点的惯例也让错误信息更聚焦于真正需要约束的地方。由浅入深从泛型函数到泛型 trait泛型数据与泛型函数、泛型 trait共同构成 Rust 泛型三要素。其中泛型 trait 的典型代表是标准库的FromT它正是泛型数据思想在 trait 层的延伸pub trait FromT: Sized { fn from(value: T) - Self; }FromT的类型参数是输入关联类型如Item则是输出。一个类型可以为多个输入类型分别实现From例如课程中的Foo同时实现了Fromu32与Frombool但实现不需要覆盖所有可能的类型参数——Foo::from(hello)会编译失败因为没有Fromstr for Foo的实现。同时 Rust 要求任意类型组合(Trait, Self, T)至多只能有一个匹配实现不做最具体实现的启发式选择相关 specialization 支持仍在 RFC 1210 讨论中。实战验证泛型min练习与零成本抽象为巩固泛型数据与泛型约束的用法课程安排了 Genericmin练习其参考实现保存在 exercise.rs 中use std::cmp::Ordering; fn minT: Ord(l: T, r: T) - T { match l.cmp(r) { Ordering::Less | Ordering::Equal l, Ordering::Greater r, } } #[test] fn integers() { assert_eq!(min(0, 10), 0); assert_eq!(min(500, 123), 123); } #[test] fn chars() { assert_eq!(min(a, z), a); assert_eq!(min(7, 1), 1); } #[test] fn strings() { assert_eq!(min(hello, goodbye), goodbye); assert_eq!(min(bat, armadillo), armadillo); }该练习在 generics 包的 Cargo.toml 中被配置为 library crate[lib] path exercise.rs可直接通过cargo test运行验证。它印证了两个与泛型数据一脉相承的关键点约束带来能力minT: Ord通过Ord约束获得cmp方法正如VerbosityFilterL: Logger通过Logger约束获得log方法——类型参数本身没有能力能力来自约束零成本抽象泛型代码会在编译期基于调用点被**单态化monomorphization**为具体类型的非泛型代码。也就是说min(0, 10)会被编译成手写i32版本的min一样高效的代码没有任何运行时开销。这与 C 模板相似但 Rust 会在定义处即对泛型函数进行部分编译校验例如把pick的返回值改成left right即使调用处只用整数Rust 也会因T未必支持而直接判定非法C 则允许延迟到实例化时报错。总结泛型数据类型允许用类型参数抽象结构体字段实现一次定义、多类型复用implL: Logger Logger for VerbosityFilterL中类型定义与 impl 块的泛型参数相互独立且该写法表示实现对所有满足约束的类型生效实践中应把 trait bound 放在 impl 块而非结构体定义上保持类型定义最大自由泛型函数、泛型数据、泛型 trait 三者统一于同一套类型参数机制配合单态化实现零成本抽象。想继续深入可以按课程顺序学习 impl Trait 与 dyn Trait 的取舍、trait bounds 细节与泛型练习解答也可以通过课程根目录 SUMMARY.md 定位泛型章节的完整教学脉络。【免费下载链接】comprehensive-rustThis is the Rust course used by the Android team at Google. It provides you the material to quickly teach Rust.项目地址: https://gitcode.com/GitHub_Trending/co/comprehensive-rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表