深入浅出 Rust 中 Sizedness 的奥秘(第四部分)
2023-10-22 20:52:42
Sizedness 在 Rust 中的含义(第四部分)
本文将探究 Rust 中 Sizedness 的进一步复杂性,重点关注对象安全和 trait 对象的 Sizedness 约束。
对象安全和 Sizedness
尽管一个 trait 被标记为对象安全,但仍然存在与 Sizedness 相关的特殊情况,这些情况限制了哪些类型可以转换为 trait 对象,以及可以通过一个 trait 对象表示多少种和哪种类型的 trait。
接收可变长度类型
考虑以下代码片段:
fn print_string(s: &String) {
println!("{}", s);
}
fn print_dyn_to_str(s: &dyn ToStr) {
println!("{}", s.to_str());
}
print_string
函数接受一个对 String
的引用,而 print_dyn_to_str
函数接受一个对实现 ToStr
trait 的类型的引用。根据我们的直觉,我们可能会期望能够传递一个 String
变量给 print_dyn_to_str
函数,因为它实现了 ToStr
trait。
然而,由于 String
不是一个 Sized 类型,因此无法将它转换为 trait 对象。这是因为 trait 对象的内存布局必须在编译时已知,而 String
的内存布局取决于其长度,这在编译时是未知的。
因此,以下代码将导致编译错误:
let s = String::from("Hello, world!");
print_dyn_to_str(&s); // 编译错误
限制 trait 对象的多态性
Sizedness 约束也限制了可以通过一个 trait 对象表示的 trait 的数量和类型。
trait 对象只能表示有限数量的 Sized trait。这是因为 trait 对象的内存布局必须包含其所有实现 trait 的字段,并且这些字段必须在编译时已知。
此外,trait 对象只能表示可共享的 trait。可共享 trait 是那些不包含任何可变状态的 trait。
实例
以下是一些 Sizedness 约束如何影响 trait 对象使用的实际示例:
Send
和Sync
trait 是可共享的 Sized trait。因此,trait 对象可以表示实现Send
和Sync
的类型。RefCell
和Mutex
trait 不是可共享的 trait,因为它们包含可变状态。因此,trait 对象不能表示实现这些 trait 的类型。Iterator
trait 是一个可共享的 trait,但它不是 Sized。因此,trait 对象可以表示实现Iterator
trait的 Sized 类型,但不能表示实现Iterator
trait的非 Sized 类型(例如String
)。
结论
Sizedness 是 Rust 类型系统的重要方面,它对 trait 对象的使用有重大影响。通过理解 Sizedness 约束,我们可以编写出更健壮、更高效的 Rust 代码。