DST(dynamic size type)-中译“动态大小类型"。本文简要讨论动态大小类型的一些问题。
一、前言
rust作为一门静态类型语言,和大部分其它静态类型语言(C,C++,C#,JAVA)一样,希望在编译的时候知道每个实例/类型的大小。
作为静态类型语言,优点是毋庸置疑的的:
1.类型错误(如字符串与整数运算)在编译阶段即可被捕获,减少运行时崩溃风险
2.编译器可基于类型信息优化内存分配与代码执行效率
但无论哪一种静态类型语言,都有同样的问题:实际业务场景中,必然有动态大小的类型,那么应该如何处理了?
每个静态类型语言都有它的处理机制,但由于rust的设计哲学和目标,所以它的处理方式是非常特别的!
无论如何,rust必须能够处理动态大小类型,否则这个语言无法用(或者变为极其难用的玩具)。
二、RUST动态类型
如前,rust也有动态类型,例如常见的str。
只是可惜的是,我们常用的其实是&str,注意不是str。如果直接let a:str="abc"是报告编译错误的:
doesn't have a size known at compile-time现在聊聊动态类型的几个问题.2.1、如何处理动态大小类型
rust使用了新类型设计模式(个人更愿意看作是封装模式)来解决这个问题。
具体而言就是用智能指针来解决这个问题。
如我们所知,智能指针的典型结构:一个指向数据的指针、少量的其它元数据、额外实现的一些特质(例如Deref,Drop).
因此,编译器会把智能指针视为一个固定大小。 是的,String也可以看作式某种智能指针。
我们来看经典的Box盒子指针的定义:- pub struct Box<
- T: ?Sized,
- #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
- >(Unique<T>, A);
- #[lang = "ptr_unique"]
- pub struct Unique<T: ?Sized> {
- pointer: NonNull<T>,
- // NOTE: this marker has no consequences for variance, but is necessary
- // for dropck to understand that we logically own a `T`.
- //
- // For details, see:
- // https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
- _marker: PhantomData<T>,
- }
- pub struct NonNull<T: ?Sized> {
- pointer: *const T,
- }
- pub struct PhantomData<T: ?Sized>;
- #[unstable(feature = "allocator_api", issue = "32838")]
- pub unsafe trait Allocator {
- //此处略
- }
复制代码 注意,T是?Sized,意思T可以是固定大小或者动态大小。其次根据文档说明 ?T(T是特质)目前只能有?Sized,换言之,不会有?Drop,?Deref之类的申明。
在Box的底层通过 *const T(不可变原始指针)来指向实际的数据,整体上可以看作是固定大小的。
2.2、Sized特质
Sized特质,故名思意就是大小的意思,在rust中表示一个特质,表示被它绑定(限定)的类型是固定大小。rust编译器会为每一个添加了Sized特质的类型实现具体内容。
?Size则表示类型是可以固定也可以不是固定大小。其次根据文档说明 ?T(T是特质)目前只能有?Sized,换言之,不会有?Drop,?Deref之类的申明。
看看Box的定义就是知道类型是?Sized,而实践也告诉我们,可以在Box中存放标量类型和堆栈类型。
2.3、动态分发(dyn)
动态分发(dynamic dispatch),意思就是在运行的时候才确定特质对应的实际类型。
在编码的时候,如果使用指针存储一个特质,那么必须添加一个dyn,这样rustc通过编译,并在运行的时候,会把特质替换为实际的类型实例。
三、示例
[code]trait Animal { fn eat(&self);}struct Tiger { name: String, age: u8,}struct Pig { name: String, age: u8,}impl Animal for Tiger { fn eat(&self) { println!("{}岁{} 正在吃野猪", self.age, self.name); }}impl Animal for Pig { fn eat(&self) { println!("{}岁{} 正在吃竹笋和地瓜", self.age, self.name); }}impl Tiger { fn clone(&self) -> Tiger { Tiger { name: self.name.clone(), age: self.age, } }}fn feed_animal_dyn(animals: Vec) { for animal in animals { animal.eat(); }}/** * Sized特质测试,告知编译器这个参数可以是固定大小也可以是动态大小,而非固定大小 */fn feed_animal(animal: T)where T: Animal,{ animal.eat();}fn main() { //这样定义会报告编译错误:doesn't have a size known at compile-time //let me:str="lzf"; dst_test(); dyn_test(); sized_test();}fn sized_test() { let tiger = Tiger { name: "大王
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |