夷老永宜 發表於 2025-4-8 19:20:00

rust学习二十.12、RUST动态大小类型DST以及Sized特质

<p>DST(dynamic size type)-中译“动态大小类型"。本文简要讨论动态大小类型的一些问题。</p>
<h1>一、前言</h1>
<p>rust作为一门静态类型语言,和大部分其它静态类型语言(C,C++,C#,JAVA)一样,希望在编译的时候知道每个实例/类型的大小。</p>
<p>作为静态类型语言,优点是毋庸置疑的的:</p>
<p><strong>1.<span style="color: rgba(51, 51, 51, 1); font-family: &quot;PingFang SC&quot;, Arial, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; text-align: left; text-indent: 0; text-transform: none; word-spacing: 0; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(255, 255, 255, 1); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none">类型错误(如字符串与整数运算)在编译阶段即可被捕获,减少运行时崩溃风险</span></strong></p>
<p><strong>2.<span style="color: rgba(51, 51, 51, 1); font-family: &quot;PingFang SC&quot;, Arial, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; letter-spacing: normal; text-align: left; text-indent: 0; text-transform: none; word-spacing: 0; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(255, 255, 255, 1); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none">编译器可基于类型信息优化内存分配与代码执行效率</span></strong></p>
<p>但无论哪一种静态类型语言,都有同样的问题:实际业务场景中,必然有动态大小的类型,那么应该如何处理了?</p>
<p>每个静态类型语言都有它的处理机制,但由于rust的设计哲学和目标,所以它的处理方式是非常特别的!</p>
<p>无论如何,rust必须能够处理动态大小类型,否则这个语言无法用(或者变为极其难用的玩具)。</p>
<h1>二、RUST动态类型</h1>
<p>如前,rust也有动态类型,例如常见的str。</p>
<p>只是可惜的是,我们常用的其实是&amp;str,注意不是str。如果直接let a:str="abc"是报告编译错误的:</p>
<div style="color: rgba(0, 0, 0, 1); background-color: rgba(255, 255, 255, 1); font-family: Consolas, &quot;Courier New&quot;, monospace; font-weight: normal; font-size: 14px; line-height: 19px; white-space: pre">
<div><span style="color: rgba(0, 128, 0, 1)">doesn't have a size known at compile-time</span></div>
<div>现在聊聊动态类型的几个问题.</div>
<h2>2.1、如何处理动态大小类型</h2>
<p>rust使用了新类型设计模式(个人更愿意看作是封装模式)来解决这个问题。</p>
<p>具体而言就是用智能指针来解决这个问题。</p>
<p>如我们所知,智能指针的典型结构:一个指向数据的指针、少量的其它元数据、额外实现的一些特质(例如Deref,Drop).</p>
<p>因此,编译器会把智能指针视为一个固定大小。 是的,String也可以看作式某种智能指针。</p>
<p>我们来看经典的Box盒子指针的定义:</p>
<pre class="language-rust highlighter-hljs"><code>pub struct Box&lt;
    T: ?Sized,
    # A: Allocator = Global,
&gt;(Unique&lt;T&gt;, A);
#
pub struct Unique&lt;T: ?Sized&gt; {
    pointer: NonNull&lt;T&gt;,
    // 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&lt;T&gt;,
}
pub struct NonNull&lt;T: ?Sized&gt; {
    pointer: *const T,
}
pub struct PhantomData&lt;T: ?Sized&gt;;
#
pub unsafe trait Allocator {
    //此处略
}</code></pre>
<p>注意,T是?Sized,意思T可以是固定大小或者动态大小。其次根据文档说明 ?T(T是特质)目前只能有?Sized,换言之,不会有?Drop,?Deref之类的申明。</p>
<p>在Box的底层通过 *const T(不可变原始指针)来指向实际的数据,整体上可以看作是固定大小的。</p>
<p>&nbsp;</p>
<h2>2.2、Sized特质<br><br></h2>
<p>Sized特质,故名思意就是大小的意思,在rust中表示一个特质,表示被它绑定(限定)的类型是固定大小。rust编译器会为每一个添加了Sized特质的类型实现具体内容。</p>
<p>?Size则表示类型是可以固定也可以不是固定大小。其次根据文档说明 ?T(T是特质)目前只能有?Sized,换言之,不会有?Drop,?Deref之类的申明。</p>
<p>看看Box的定义就是知道类型是?Sized,而实践也告诉我们,可以在Box中存放标量类型和堆栈类型。</p>
<h2>2.3、动态分发(dyn)</h2>
<p>动态分发(dynamic dispatch),意思就是在运行的时候才确定特质对应的实际类型。</p>
<p>在编码的时候,如果使用指针存储一个特质,那么必须添加一个dyn,这样rustc通过编译,并在运行的时候,会把特质替换为实际的类型实例。</p>
</div>
<h1>三、示例</h1>
<pre class="language-rust highlighter-hljs"><code>trait Animal {
    fn eat(&amp;self);
}

struct Tiger {
    name: String,
    age: u8,
}
struct Pig {
    name: String,
    age: u8,
}

impl Animal for Tiger {
    fn eat(&amp;self) {
      println!("{}岁{} 正在吃野猪", self.age, self.name);
    }
}

impl Animal for Pig {
    fn eat(&amp;self) {
      println!("{}岁{} 正在吃竹笋和地瓜", self.age, self.name);
    }
}

impl Tiger {
    fn clone(&amp;self) -&gt; Tiger {
      Tiger {
            name: self.name.clone(),
            age: self.age,
      }
    }
}

fn feed_animal_dyn(animals: Vec&lt;Box&lt;dyn Animal&gt;&gt;) {
    for animal in animals {
      animal.eat();
    }
}
/**
* Sized特质测试,告知编译器这个参数可以是固定大小也可以是动态大小,而非固定大小
*/
fn feed_animal&lt;T: Sized&gt;(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: "大王🐯".to_string(),
      age: 3,
    };
    let 武松的老虎 = tiger.clone();
    feed_animal(武松的老虎);
}

/**
* 动态大小类型测试,使用&amp;T和Box&lt;T&gt;来封装动态大小类型(DST)
*/
fn dst_test() {
    //Box指针封装动态大小类型(DST).编译器认为Box指针式固定大小,典型的障眼法
    let code: Box&lt;str&gt; = Box::from("Hello, world!");
    println!("{}", code);
    let name:Box&lt;&amp;str&gt; = Box::from("狄仁杰");
    println!("{}", name);

}
/**
* 动态分发测试,使用dyn告诉编译器这是一个动态分发,而非静态分发,是特质而不是其它的类型(stuct,enum等)
*/
fn dyn_test() {
    let tiger = Tiger {
      name: "松崽🐅".to_string(),
      age: 10,
    };

    let pig = Pig {
      name: "小胖🐖".to_string(),
      age: 5,
    };
    // 必须使用as 关键字,将Tiger和Pig转换为特质对象(trait object)
    // 必须使用dyn关键字,告诉编译器这是一个动态分发(dynamic dispatch),即在运行的时候才使用具体的类型
    let animals = vec![
      Box::new(tiger) as Box&lt;dyn Animal&gt;,
      Box::new(pig) as Box&lt;dyn Animal&gt;,
    ];
    feed_animal_dyn(animals);
}</code></pre>
<p>本例基本模仿了书本上的例子,演示了三种情况:</p>
<p>1.不能直接定义一个动态大小类型,否则编译器报错</p>
<p>2.如何用智能指针处理动态大小类型,以便可以通过编译</p>
<p>3.Sized特质的使用,以及如何使用Box存储特质。使用了dyn(动态分发)</p>
<p><strong>动态分发(dyn)</strong></p>
<p>函数fn feed_animal_dyn(animals: Vec&lt;Box&lt;dyn Animal&gt;&gt;)只接收Animal特质的Box指针向量,无法定义为:</p>
<p>fn feed_animal_dyn(animals: Vec&lt;Box&lt;Animal&gt;&gt;),这是因为特质本身是无意义的,必须和特定类型关联,要关联必然涉及到动态分发。</p>
<p>&nbsp;</p>
<p>演示结果:</p>
<p><img src="https://img2024.cnblogs.com/blog/1177268/202504/1177268-20250408191110728-206928788.png"></p>
<p>&nbsp;</p>
<h1>四、小结</h1>
<p>1.绝大部分静态类型语言都支持固定大小类型和动态大小类型,包括rust</p>
<p>2.rust使用指针来解决动态大小类型的编译问题和运行问题</p>
<p>3.动态分发机制可以解决指针中定义特质的问题</p>

</div>
<div id="MySignature" role="contentinfo">
    <p>本文来自博客园,作者:正在战斗中,转载请注明原文链接:https://www.cnblogs.com/lzfhope/p/18815198</p><br><br>
来源:https://www.cnblogs.com/lzfhope/p/18815198
頁: [1]
查看完整版本: rust学习二十.12、RUST动态大小类型DST以及Sized特质