简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者
新书发布:《Android系统多媒体进阶实战》🚀
优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀
优质专栏: 多媒体系统工程师系列【原创干货持续更新中……】🚀
优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
🍉🍉🍉文章目录🍉🍉🍉
🌻1.前言
本篇目的:Rust语言进阶之核心特性:Sized用法实例
🌻2. Rust核心特性Sized介绍
-
基本定义
Sized
是Rust中的一个核心特性,用于指示某个类型的大小在编译时是已知的。在Rust中,所有类型默认都是Sized
,这意味着它们的内存大小在编译期可以确定。然而,并非所有类型都符合这一要求,比如某些类型如动态大小类型(DST)在编译时无法确定其大小。Sized
trait是Rust类型系统的一部分,它帮助Rust进行内存管理和类型布局,确保类型的存储和访问具有确定的大小。 -
类型与内存布局的关系
Sized
trait在Rust中与内存布局紧密相关。每个实现了Sized
的类型都可以在栈上存储并且能够被确定大小。对于一个类型,它的大小一旦被确定,就可以根据该大小进行内存分配和栈分配。而没有实现Sized
trait的类型,通常是动态大小类型,如str
或[T]
,它们的大小是运行时确定的。因此,Sized
trait使得Rust能够在编译时为大多数类型分配适当的内存空间,并支持安全高效的内存管理。 -
与动态大小类型(DST)的区别
动态大小类型(DST)是Rust中一些无法在编译时确定大小的类型。典型的DST包括切片([T]
)和字符串(str
)。这些类型不能直接存储在栈上,因为它们的大小需要在运行时计算。与之对比,Sized
trait适用于那些大小是编译时已知的类型。通过Sized
,Rust区分了可以直接在栈上存储的类型与需要通过引用或指针在堆上存储的类型。Sized
的存在帮助Rust确保栈上的数据具有确定的大小,避免了不必要的动态内存分配。 -
泛型与
Sized
的结合
在Rust的泛型编程中,Sized
与泛型类型参数的约束密切相关。默认情况下,Rust要求所有的泛型类型参数都必须是Sized
,即在编译时能够确定大小。如果某个泛型类型需要处理动态大小类型(如切片或特征对象),可以使用?Sized
来表示该类型不一定实现Sized
,从而允许在函数或结构体中使用这些类型。Sized
的存在使得开发者能够控制泛型类型的大小约束,确保类型在内存布局和性能上的要求得到满足。 -
性能和内存安全
Sized
trait在Rust的内存管理中起着至关重要的作用。由于栈上存储的数据必须是Sized
的,Rust能够在编译时对内存分配进行精确计算,确保内存的有效利用。类型的大小在编译期就已知,这为编译器优化提供了充分的信息,进而提升了程序的运行效率。Sized
的引入使得Rust能够避免不必要的运行时开销,确保类型安全并减少潜在的内存错误,如野指针或缓冲区溢出。通过精确的内存控制,Rust保证了程序的高性能和内存安全。
🌻3. 代码实例
🐓3.1 默认对类型进行 Sized 约束
- 1.应用场景:在 Rust 中,所有的类型默认都被认为是 Sized。因此,Rust 编译器会假设你传入的泛型类型是有固定大小的,除非你显式地标明该类型是一个动态大小类型(例如切片或 trait 对象)。你无需手动指定 Sized,因为它是默认的约束
- 2.通用语法:
fn function<T>(value: T)
where
T: Sized,
{
// 函数体
}
- 3.用法实例
fn print_size<T>(value: T)
where
T: Sized,
{
println!("Size of T: {}", std::mem::size_of::<T>());
}
fn main() {
let x = 42;
print_size(x); // 输出 "Size of T: 4"
}
在这个例子中,print_size 函数的泛型 T 默认就有 Sized 约束。我们使用 std::mem::size_of::() 来打印类型 T 的大小。
由于 T 默认是 Sized,编译器会自动认为传入的类型 T 必须有已知的大小。
这意味着只有那些在编译时能确定大小的类型(如基本类型、结构体、枚举等)才可以传入这个函数。
🐓3.2 使用 ?Sized 允许接受动态大小类型
- 1.应用场景:如果你希望一个泛型类型能够接受动态大小类型(如切片 str 或 trait 对象),可以使用 ?Sized 来显式地表示该类型可以是 Sized 或不是 Sized。这样,函数就可以接受没有固定大小的类型。
- 2.通用语法:
fn function<T>(value: T)
where
T: ?Sized,
{
// 函数体
}
- 3.用法实例
fn print_dynamic_size<T>(value: T)
where
T: ?Sized,
{
println!("This is a dynamic-sized type.");
}
fn main() {
let s: &str = "Hello, world!";
print_dynamic_size(s); // 输出 "This is a dynamic-sized type."
}
这里的 ?Sized 允许传入动态大小类型(如 str、[T] 或 trait 对象)。
&str 是一个动态大小类型,print_dynamic_size 函数能够接受动态大小的类型。
使用 ?Sized 可以使得泛型接受那些具有不固定大小的类型。
🐓3.3 动态大小类型的指针(如 Box 和 &dyn Trait)
- 1.应用场景:Rust 的 trait 对象(如 Box 或 &dyn Trait)是动态大小类型。使用 Sized 时,通常不能直接存储 trait 对象的值,因为 trait 对象的大小在编译时是无法确定的。为了使用 trait 对象,我们通常将其存储在指针类型中,如 Box 或 &dyn Trait。
- 2.通用语法:
fn function<T>(value: T)
where
T: Sized,
{
// 函数体
}
fn function_with_trait_object(value: Box<dyn SomeTrait>) {
// 这里可以传入 trait 对象
}
- 3.用法实例
trait Speak {
fn speak(&self);
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) {
println!("Woof!");
}
}
impl Speak for Cat {
fn speak(&self) {
println!("Meow!");
}
}
fn call_speak(animal: Box<dyn Speak>) {
animal.speak();
}
fn main() {
let dog: Box<dyn Speak> = Box::new(Dog);
let cat: Box<dyn Speak> = Box::new(Cat);
call_speak(dog); // 输出 "Woof!"
call_speak(cat); // 输出 "Meow!"
}
在这个例子中,Box 是一个动态大小类型(因为 dyn Speak 是 trait 对象)。
Box 将 trait 对象存储在堆上,因此可以在运行时决定类型的具体实现。
如果没有 Sized,无法直接处理动态大小类型。通过 Box 或其他智能指针,我们能够处理这类类型。
🐓3.4 用法总结