返回

深入浅出 Rust 中 Sizedness 的奥秘(第四部分)

见解分享

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 对象使用的实际示例:

  • SendSync trait 是可共享的 Sized trait。因此,trait 对象可以表示实现 SendSync 的类型。
  • RefCellMutex trait 不是可共享的 trait,因为它们包含可变状态。因此,trait 对象不能表示实现这些 trait 的类型。
  • Iterator trait 是一个可共享的 trait,但它不是 Sized。因此,trait 对象可以表示实现 Iterator trait的 Sized 类型,但不能表示实现 Iterator trait的非 Sized 类型(例如 String)。

结论

Sizedness 是 Rust 类型系统的重要方面,它对 trait 对象的使用有重大影响。通过理解 Sizedness 约束,我们可以编写出更健壮、更高效的 Rust 代码。