ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

读懂 Rust 命名规范:comprehensive-rust 课程中的 `from` 前缀——构造式类型转换的信号

读懂 Rust 命名规范:comprehensive-rust 课程中的 `from` 前缀——构造式类型转换的信号 读懂 Rust 命名规范comprehensive-rust 课程中的from前缀——构造式类型转换的信号【免费下载链接】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本文基于 comprehensive-rust 课程Google Android 团队使用的 Rust 教学课程中 idiomatic 模块的命名规范章节详解from这一函数名前缀的语义、适用场景与三条使用规则并结合仓库内From/Intotrait 的配套章节与配套练习帮助你判断何时该用from、何时该用new、to、into或try_从而设计出可读、可预测的 Rust API。为什么from重要把名字当领域特定语言来看仓库中 命名规范章节 的开篇即点明其价值One core component of readability and predictability is the way function names are composed. A formal and consistently-applied naming convention lets developers treat names like a domain-specific language and quickly understand the functionality and use cases of a method.也就是说一套形式化且被一致执行的命名约定能让开发者把方法名当作一门领域特定语言DSL来阅读——光看名字就能推断出函数的功能与用途。Rust 社区很早就形成了这套约定标准库中已经基本保持一致。在 可预测 API 章节 中课程把这一点提升到了 API 设计的层面一个可预测的 API是指用户可以基于名字、类型、签名等表面细节对该 API 的某一部分做出合理假设。from就是这套名字语言中的一个关键词。其定义页位于 naming-conventions/from.md。from的核心语义构造函数 强烈的类型转换暗示课程对from的定义只有一句话但信息量很足A constructor function, strongly implying type conversion.一个构造函数强烈暗示类型转换。课程给出的标准库风格示例是impl Duration { fn from_days(days: u64) - Duration; } impl i32 { fn from_ascii(src: [u8]) - Resulti32, ParseIntError; } impl u32 { fn from_le_bytes(bytes: [u8; 4]) - u32; }这三个例子恰好覆盖了from前缀的三类典型用途方法输入语义解读Duration::from_days天数的u64单位转换以天为单位的数值构造出Duration调用者提供的是另一种计量下的原始值i32::from_ascii一段[u8]格式转换从 ASCII 字节序列解析出整数可失败因此返回Resultu32::from_le_bytes小端字节数组[u8; 4]字节序转换从小端little-endian字节序列构造u32与之相对的还有from_be_bytes、from_ne_bytes注意这些例子的共同点调用者提供的是一个更原始、更底层的表示天数、字节、字节序函数负责把它转换成目标类型。这正是from想传达的信号——输入不是目标类型的自然构造材料而是另一种类型/表示。from的三条使用规则from.md中的讲师备注details 部分给出了三条明确的规则逐条理解它是构造式、From-trait 风格函数的前缀。from出现在与从别的值造出一个Self对应的关联函数上——无论是字面意义的Fromtrait 方法还是形如from_days、from_ascii的构造式关联函数。可以接受多个参数但通常暗示调用者承担的工作比一般构造函数多。普通的new通常参数少、语义是给我一个默认或最小化的实例。而from_xxx往往要求调用者提供一段完整的原始数据整个字节切片、整个字节数组并可能还要处理失败如from_ascii返回Result。名字本身就在提醒调用者这不是零成本、无脑的构造。对大多数构造式函数new仍是首选from的专属含义是从一种数据类型转换到另一种。仓库中配套的 new.md 解释了这一点Rust 没有new关键字new只是约定俗成的默认构造函数名字如VecT::new()、BoxT::new(T)。如果你的函数只是给几个普通参数、造一个实例就该叫new或new_xxx只有当参数在语义上是另一种类型的数据时才用from。一句话总结判断标准参数是不是另一种数据类型的值是用from只是普通构造参数用new。from与From/Intotrait约定之下的标准库实证from前缀的权威来源是标准库的Fromtrait。仓库中两个章节对此有专门讲解common-traits/from-into.mdidiomatic 模块内std-traits/from-and-into.mdstd-traits 模块内from-and-into.md给出了标准库中from命名的真实用法fn main() { let s String::from(hello); let addr std::net::Ipv4Addr::from([127, 0, 0, 1]); let one i16::from(true); let bigger i32::from(123_i16); println!({s}, {addr}, {one}, {bigger}); }这四行分别展示了从str构造String、从[u8; 4]构造Ipv4Addr、从bool构造i16、从i16无损加宽为i32——全部是从另一种类型转换而来的场景与from.md中三条规则完全吻合。common-traits/from-into.md则补充了 trait 层面的工程惯例pub struct Wrapper(String); impl Fromstr for Wrapper { fn from(value: str) - Self { Wrapper(value.to_owned()) } } impl Fromi32 for Wrapper { fn from(value: i32) - Self { Wrapper(value.to_string()) } } // Into 作为 trait bound 更自然。 fn into_stringS: IntoString(s: S) {} fn string_fromT(t: T) where String: FromT {} fn main() { // Wrapper 可以从 str 和 i32 构造。 let a Wrapper::from(Hello, obvious!); let b Wrapper::from(-123); // 实现了 From就自动获得 Into 实现。 let c: Wrapper Hello, implementation!.into(); }由此得到三条与from命名直接相关的工程惯例优先为你的类型实现FromT而不是手写IntoT——标准库会自动为每个From实现补上对应的Into实现。From提供的是构造式函数Wrapper::from(...)Into提供的是已有值上的方法value.into()——这正是from.md中from是构造式前缀的 trait 层面体现。函数参数侧优先写S: IntoString这样的 trait bound比String: FromT意图更清晰。另外需要注意边界From/Into对应无损且不可失败的转换如果转换可能失败应使用TryFrom/TryInto参见 common-traits/try-from-into.md 中DivisibleByTwo::try_from的示例而函数名上则体现为try_前缀。i32::from_ascii虽然叫from_但它返回Resulti32, ParseIntError——这说明from_前缀并不保证 infallible失败时通过Result返回值显式表达即可。如何区分from与其他转换前缀naming-conventions目录下还有一组姊妹页面与from构成完整的对照。快速梳理如下前缀含义仓库中的示例出处new默认构造函数无特殊语法含义VecT::new()、BoxT::new(T)new.mdfrom构造式函数强烈暗示类型转换Duration::from_days、i32::from_asciifrom.mdto借引用造一个 owned 值暗示转换str::to_owned、str::to_uppercase、u32::to_beto.mdinto消耗self返回另一类型的 owned 值IntoIterator::into_iter、Boxstr::into_stringinto.mdtry_可失败、返回Result的方法Receiver::try_recv、TryFrom::try_fromtry.md其中与from最容易混淆的两组对比值得展开fromvsto谁持有数据谁是来源to.md 指出to前缀方法取一个借用值通常selfCopy类型可直接按值取self创建一个新的 owned 值如str::to_owned(self) - String。它是从当前对象拷/转出而from是从别的类型造出当前对象。注意to.md特别提醒的例外如果方法只是取self返回同类型的 owned 值应该实现 [Clone] 或 [ToOwned] trait而不是发明一个to_xxx方法。fromvsinto方向相反的一对。from.md 讲从别的类型进来into.md 讲把self消耗掉、变成别的类型出去。into.md特别强调一点容易踩坑的事实Not reinterpret cast! The data can be rearranged, reallocated, changed in any way, including losing information.不是位模式重解释数据可以被重排、重新分配、以任意方式改变甚至丢失信息。from侧同理i32::from_ascii是解析不是把字节按位重解释成整数。两个方向的名字都在提示读者这里发生了真正的数据转换成本不为零。动手应用从练习看from的命名决策课程配套的 练习 直接训练了看签名想名字的能力。其中与from直接相关的题目是// What should we name methods with these types? fn ____(String) - Self;练习给出的参考答案是fn from_string(String) - Self。注意这里的选择逻辑String是一种独立的数据类型用它来构造Self语义上是从一个 String 类型转换/构造而来因此按from.md规则 3from_string比new更能准确传达参数是另一种类型这一信息。同一练习中其他几题fn inner(self) - OptionInnerType、fn with_string(self, String) - Self等则分别演示了get/inner与with_前缀的取舍可结合 exercise.md 的完整答案一起阅读。小结给 API 设计者的from决策清单综合 from.md 的定义、三条规则以及仓库中From/Into配套章节的实证可以提炼出如下决策路径我要写一个关联函数返回值是Self参数在语义上是另一种数据类型的值其他单位、字节序列、字节序、别的类型→ 用from_xxx前缀而不是new如果这个转换是无损且不可失败的优先实现FromTtrait名字自然就是from并免费获得Into如果转换可能失败用TryFromtrait方法名try_from或返回Result的from_xxx关联函数如果来源不是另一个类型而是当前对象的借用那不是from的领域改用to_xxx非消耗或into_xxx消耗self。按这套约定命名你的 API 就和标准库一样让使用者看名字即知语义这正是 comprehensive-rust 课程 predictable-api 章节所追求的可预测性。【免费下载链接】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),仅供参考
返回列表