rust语言学习笔记Trait(十二)Sized、?Sized (大小限制)

Sized 是 Rust 中一个极其特殊的标记 Trait(Marker Trait),它既没有方法,也极少需要手动实现,但却深刻地影响着 Rust 的类型系统、内存布局和 trait 对象的使用方式。

1、什么是 Sized?

Sized 的定义极其简单,位于 std::marker 模块中:

rust 复制代码
pub trait Sized { }

一个类型在‌编译期具有固定且已知的大小 ‌。Rust 中绝大多数类型都是 Sized 的,包括所有基本类型、结构体、枚举、数组、元组等。

如果一个类型的大小在编译期无法确定,它就被称为‌动态大小类型(DST, Dynamically Sized Type)。典型的 DST 包括:

  • 切片类型 [T]
  • 字符串切片 str(不是 &str)
  • trait 对象 dyn Trait
  • 包含一个 DST 作为最后一个字段的结构体
rust 复制代码
// 这些都是 DST,无法直接使用
let s: str = *"hello";            // 编译错误:size 未知
let arr: [i32] = [1, 2, 3];       // 编译错误:size 未知
let dyn_obj: dyn Display = ...;   // 编译错误:size 未知

由于 DST 的大小未知,它们无法被放在栈上,也不能作为函数参数按值传递。只能在‌指针后面 ‌使用它们,比如 &str、&[T]、Box<dyn Trait> 等。

2、隐式 Sized 绑定

Rust 的类型参数‌默认是 Sized 的 ‌。这意味着当你写一个泛型函数时,编译器实际上帮你在内部添加了 Sized 约束:

rust 复制代码
// 你写的代码
fn foo<T>(x: T) { ... }

// 编译器实际看到的
fn foo<T: Sized>(x: T) { ... }

这种隐式绑定覆盖了以下场景:

  • 泛型类型参数 T
  • 泛型结构体/枚举中的字段
  • impl Trait 的返回类型

3、 使用 ?Sized 解除限制

当你需要在泛型中接受 DST 时,需要使用特殊语法 ?Sized,意为 T 可以是 Sized 的,也可以不是。

rust 复制代码
// 解除 Sized 限制,T 可以是 DST
fn bar<T: ?Sized>(x: &T) {
    // x 是指针,所以即使 T 是 DST 也没问题
}

?Sized 只能在以下位置使用:

  • 泛型类型参数:T: ?Sized
  • 结构体泛型参数的最后一个字段:struct S<T: ?Sized> { tail: T }

‌不能‌在关联类型、trait 自身定义或函数返回值中使用。

rust 复制代码
use std::fmt::Debug;

fn print_test<T: Debug + ?Sized>(t: &T) {
    println!("{:?}", t);
}

fn main() {
    print_test("ssss");
    print_test(&[2, 5, 6]);
    print_test(&35);
}

4、标准库中的应用

类型/模块 应用方式 说明
Box<T> impl<T: ?Sized> Box<T> Box 可以指向堆上的 DST,如 Box<[i32]> 或 Box<dyn Error>。
Rc<T> / Arc<T> impl<T: ?Sized> Rc<T> 引用计数指针也可以指向 DST。
std::fmt::Debug trait Debug: ?Sized 允许对 &str、[i32] 等切片直接调用 .fmt()。
AsRef<T> impl<T: ?Sized, U: ?Sized> AsRef<U> for T 许多转换逻辑需要处理不定长类型。

5、Sized 和 ?Sized 的使用

  1. ‌默认不动 ‌:在绝大多数泛型编程中,保持默认的 Sized 约束。这保证了类型可以按值传递、存储在栈上,且性能最优。
  2. ‌需要灵活性时用 ?Sized :
    • 当你的函数接收的是‌引用 ‌(&T)或‌智能指针 ‌(Box<T>),且你希望它能通吃"具体类型"和"切片/Trait对象"时。
    • 典型签名:fn foo<T: ?Sized>(x: &T)。
  3. ‌设计 Trait 时注意 ‌:
    • 如果希望 Trait 能被用作 dyn Trait,不要给 Trait 本身加 Sized 约束。
    • 如果 Trait 中的某些方法依赖于静态大小(如返回 Self),给该方法加 where Self: Sized。

理解 Sized 和 ?Sized 的核心在于明白:‌**Rust 默认追求静态确定的内存布局,而 ?Sized 是为了在必要时(通过指针间接访问)打破这一限制,以支持多态和切片操作。

相关推荐
无心水2 小时前
24、系统架构师论文写作专题 核心考点与学习笔记
笔记·学习·系统架构·架构师·安全架构·软考架构师·软考系统架构设计师
蒸蒸yyyyzwd3 小时前
邓明 Agent 系统面试-意图识别学习笔记
笔记·学习
Vex2une4 小时前
Google Picasa 精神续作:Rust 重写的离线本地相册工具‌
rust·图片管理·picasa·马良·本地照片管理·重复图片检测·maliang
Zhao-Jichao6 小时前
AI Prompting for Everyone 课程笔记 - P21 总结 Conclusion
人工智能·笔记
欢欢李6 小时前
【LTE L3 笔记 05】LTE同步信号
笔记·无线通信·物理层·lte·空口协议
La Pulga8 小时前
【FreeRTOS源码学习笔记】空闲任务与阻塞延时
笔记·mcu·学习·嵌入式·freertos·实时操作系统
蒸蒸yyyyzwd8 小时前
秋招学习笔记 day106
笔记·学习
小李不想当小白9 小时前
WDG看门狗(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·嵌入式
欢欢李9 小时前
【LTE L3 笔记 04】LTE帧结构
笔记·无线通信·物理层·lte·空口协议
Zhao-Jichao12 小时前
AI Prompting for Everyone 课程笔记 - P20 实验与期末项目 Lab and Final Project
前端·人工智能·笔记