ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Comprehensive Rust 深入解析:Sized 与 ?Sized——Rust 静态尺寸与动态尺寸类型

Comprehensive Rust 深入解析:Sized 与 ?Sized——Rust 静态尺寸与动态尺寸类型 Comprehensive Rust 深入解析Sized 与 ?Sized——Rust 静态尺寸与动态尺寸类型【免费下载链接】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中的「多态复习」章节sized.md展开。在 Rust 的泛型与多态体系中Sized是一个看似隐形却无处不在的 trait它决定了你的类型参数默认能否容纳str、[T]、dyn Trait这类动态尺寸类型DST。读完本文你将理解Sized的自动实现机制、?Sized的宽松约束语法以及动态尺寸类型在胖指针fat pointer中的存储方式并掌握何时需要显式写出?Sized约束来编写真正通用的库代码。引言为什么大小已知如此重要在 Rust 中绝大多数类型在编译期就拥有确定的内存大小例如u32固定占 4 字节String固定占 3 个机器字指针 长度 容量。这种编译期已知尺寸的特性是 Rust 栈分配、数组布局和高效迭代的基础。但有一类类型在编译期无法确定自身大小str、切片[T]、trait 对象dyn Trait。它们被称为动态尺寸类型Dynamically Sized TypesDST。Rust 用内置 traitSized来区分这两种类型并通过?Sized语法让泛型参数可选尺寸。该主题在本课程的多态体系polymorphism.md中处于承上启下的位置上游是泛型与 trait 的基础复习refresher.md下游则直接支撑dyn Trait动态分发dyn-trait.md与Boxdyn Trait所有权 trait 对象trait-objects.md等高级用法。核心示例AlwaysSized 与 OptionallySized原文档给出了一个精炼到极致的可运行示例直接对比了强制有尺寸与允许无尺寸两种泛型声明方式use std::fmt::Debug; // 默认情况下T 隐式带有 Sized 约束T 必须是在编译期已知尺寸的类型。 pub struct AlwaysSizedT /* : Sized */(T); // 通过 ?Sized 解除默认的 Sized 约束T 可以是 DST如 str、[T]、dyn Trait。 pub struct OptionallySizedT: ?Sized(T); // dyn Debug 是一个动态尺寸类型因此只能被 OptionallySized 接受。 type Dyn1 OptionallySizeddyn Debug;逐行拆解声明含义AlwaysSizedT(T)T隐式拥有Sized约束等价于T: Sized。结构体内存布局要求T在编译期尺寸已知。OptionallySizedT: ?Sized(T)?Sized表示解除自动添加的Sized约束允许T为 DST。type Dyn1 OptionallySizeddyn Debugdyn Debug是 DST只能与?Sized泛型组合使用若试图将其放入AlwaysSized将无法通过编译。注意?Sized是 Rust 中唯一以?开头的约束语法它并非要求必须是无尺寸而是放宽默认限制使类型参数既可以是有尺寸的普通类型也可以是 DST。这一点与Sized的自动添加机制紧密相关下面展开说明。Sized 的自动实现与自动约束自动实现auto trait 式的行为Sized由编译器自动为所有编译期已知尺寸的类型实现用户无法手动为某个类型实现Sized。原文档明确指出绝大多数类型都实现了Sized即拥有编译期已知的尺寸而[T]、str、dyn Trait这类 DST没有实现Sized它们的尺寸被保存在指向该类型值的引用本身中而不是类型布局中。这一尺寸随引用走的机制正是胖指针的来源str、[T]、dyn Trait都是双指针宽的胖指针除数据地址外还额外携带一份元数据字符串长度、切片长度或 vtable 指针。自动添加的默认约束原文档强调Sized也会被自动添加到任何没有显式退出opt-out的类型参数上。 这意味着// 以下两种写法完全等价 pub struct AlwaysSizedT(T); pub struct AlwaysSizedT: Sized(T);同理泛型函数fn fT(x: T)中的T默认就是Sized。这一默认设计保证了栈上按值传参、数组与VecT的元素布局等基础操作总是安全可计算的——因为只有已知尺寸的类型才能被直接内嵌在栈帧或容器元素槽位中。?Sized是唯一例外当你在泛型参数后写上?Sized就是在告诉编译器不要自动施加Sized约束。这也是课程文档中OptionallySizedT: ?Sized的完整语义。一个典型的真实场景是标准库中的BoxT: ?Sized、RcT: ?Sized、ArcT: ?Sized以及引用T本身——它们都通过?Sized支持把 DST 装在指针后面。动态尺寸类型DST尺寸存在哪里原文档将 DST 归纳为三类代表[T]切片元素的长度在运行时才知道未定长数组类型本身没有固定大小strUTF-8 字符串的未定长形式String内部其实就是Vecu8加上str的语义dyn Traittrait 对象具体实现类型被类型擦除type-erased编译器在编译期不知道实际类型自然无法确定大小。对于 DST尺寸被存储为指向该类型值的引用的一部分。以dyn Trait为例本课程的 dyn-trait.md 给出了完整的底层刻画在运行时dyn Pet被表示为一个胖指针即一对指针一个指针指向实现Pet的具体对象另一个指针指向该类型 trait 实现的 vtable。调用dyn Pet上的talk方法时编译器从 vtable 中查找talk的函数指针并调用它同时把Dog或Cat的指针传进去——编译器无需知道Pet的具体类型即可完成这一切。为什么Vecdyn Pet不合法而VecBoxdyn Pet合法trait-objects.md 从内存布局角度解释了 DST 与容器元素的矛盾实现同一 trait 的不同类型如Dog与Cat尺寸不同Vec要求所有元素等宽等对齐因此Vecdyn Pet无法成立dyn Pet是告知编译器存在一个实现了Pet的动态尺寸类型的语法解决方案是把 DST 放进指针VecBoxdyn Pet中的每个元素都是固定尺寸的胖指针指向堆上不同大小的Dog/Cat数据及其 vtable。该章节还提供了一组std::mem::size_of对比命令可用于亲手验证胖指针的双倍宽度println!({} {}, std::mem::size_of::Dog(), std::mem::size_of::Cat()); println!({} {}, std::mem::size_of::Dog(), std::mem::size_of::Cat()); println!({}, std::mem::size_of::dyn Pet()); println!({}, std::mem::size_of::Boxdyn Pet());其中dyn Pet与Boxdyn Pet的尺寸等于两个普通指针的宽度之和而Dog/Cat只是单个普通指针。泛型参数为何默认 Sized与单态化的关系要理解Sized默认约束的必要性需要结合同章节的 monomorphization.md每个带泛型的函数或类型的实例都会在编译期被转换为该函数的一个唯一的、具体的版本。泛型在运行时并不存在只有具体类型。例如fn print_vecT: Debug(debug_vec: VecT)在同时传入Vecu32与Vecf32时会被单态化为两个独立的具体函数实例。这一过程的前提是编译器能在编译期为T确定具体类型及其尺寸。默认的Sized约束保证了这一点使VecT、[T; N]等容器内部可以按固定步长寻址。当T被放宽为?Sized例如VecT: ?Sized的泛型声明代码就无法再按值持有T只能通过T、BoxT、RcT等指针间接使用——因为指针的尺寸是固定的而 DST 的尺寸在编译期不可知。这正是课程强调的DST 的尺寸存储在指向它的引用中这一设计想要表达的核心。为什么这个知识点对日常 Rust 开发重要编写通用库代码任何想接受str、切片或 trait 对象的泛型 API都必须显式写出?Sized否则编译器会拒绝。标准库的BoxT: ?Sized、Cow_, str等类型就是范例。trait 对象的一切用法dyn Trait是 DST它必须藏在dyn Trait、Boxdyn Trait、Rcdyn Trait之后这是课程 dyn-trait.md 与 trait-objects.md 反复出现的模式其合法性根源正是?Sized约束。诊断编译错误当你看到 the size for values of typedyn Traitcannot be known at compilation time 之类的报错时意味着某处泛型约束过于严格缺少?Sized。原文档要点速查Sizedtrait 由编译期尺寸已知的类型自动实现且自动添加到所有未显式退出opt-out的泛型参数上大多数类型实现Sized编译期尺寸已知[T]、str、dyn Trait是动态尺寸类型其尺寸存储于指向该类型值的引用中泛型参数默认自动实现Sized除非用?Sized显式解除动机能够区分编译期尺寸已知与运行时尺寸已知的类型对编写支持 DST 的通用抽象至关重要参考 Rust Reference 动态尺寸类型章节。相关章节导航本知识点处于课程多态专题的复习环节建议按以下顺序联动阅读Polymorphism 总览Rust 多态机制泛型 trait dyn的入口Refresher 复习 与 Traits泛型与 trait 的基础Monomorphization泛型单态化与二进制体积、编译时间的权衡dyn Traittrait 对象与动态分发vtableOwned Trait ObjectsBoxdyn Trait的堆上布局与胖指针验证实验。【免费下载链接】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),仅供参考
返回列表