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 是为了在必要时(通过指针间接访问)打破这一限制,以支持多态和切片操作。

相关推荐
Awh-4 分钟前
ARM 裸机开发 DAY10 学习笔记
arm开发·笔记·学习
我命由我123454 分钟前
动火作业极简理解
学习·职场和发展·产品运营·求职招聘·职场发展·产品经理·学习方法
He BianGu28 分钟前
【笔记】WPF中CachingHint的功能
笔记·wpf
andrsted1 小时前
练题簿助学教学:在线刷题、随堂考试,让一门课拥有自己的题库
学习·微信小程序·小程序·学习方法·刷题
问心无愧05131 小时前
[ACTF2020 新生赛]Exec
运维·服务器·笔记
apple_692658851 小时前
GitHub fqapp 番茄开源魔改版:小短剧+漫剧+听书聚合工具 安装指南使用
学习·开源
云半S一2 小时前
Agent 学习总结一
经验分享·笔记·学习·agent
跨知易知识产权2 小时前
美国发明专利和外观专利的保护期限分别是多久,外观专利是否需要缴纳维持费?
笔记
八月的雨季 最後的冰吻2 小时前
C++ 面试要点笔记(续)
c++·笔记·面试
chde2Wang2 小时前
Matplotlib数据可视化学习1
学习·matplotlib