免责声明:这些只是我做的一些实验的结果,结合reading Niko Matsakis's blog。
DST 是在编译时不一定知道大小的类型。
夏令时之前
像[i32] 这样的slice 或像IntoIterator 这样的bare trait 不是有效的对象类型,因为它们没有已知的大小。
结构可能如下所示:
// [i32; 2] is a fixed-sized vector with 2 i32 elements
struct Foo {
f: [i32; 2],
}
或者像这样:
// & is basically a pointer.
// The compiler always knows the size of a
// pointer on a specific architecture, so whatever
// size the [i32] has, its address (the pointer) is
// a statically-sized type too
struct Foo2<'a> {
f: &'a [i32],
}
但不是这样:
// f is (statically) unsized, so Foo is unsized too
struct Foo {
f: [i32],
}
枚举和元组也是如此。
使用夏令时
你可以像上面的Foo 那样声明一个结构(或枚举或元组),其中包含一个未调整大小的类型。包含未调整大小类型的类型也将未调整大小。
虽然定义Foo 很容易,但创建Foo 的实例 仍然很困难,并且可能会发生变化。由于从技术上讲,您无法根据定义创建未调整大小的类型,因此您必须创建 Foo 的 sized 对应项。例如,Foo { f: [1, 2, 3] },一个Foo<[i32; 3]>,它有一个静态已知的大小和一些代码,让编译器知道它如何将它强制转换为它的静态无大小对应Foo<[i32]>。从 Rust 1.5 开始,在安全和稳定的 Rust 中执行此操作的方法仍在研究中(这里是 RFC for DST coercions 了解更多信息)。
幸运的是,您不太可能定义新的 DST,除非您正在创建一种新类型的智能指针(如 Rc),这种情况应该很少见。
想象一下Rc 的定义就像我们上面的Foo。由于它具有从大小到非大小的强制转换的所有管道,因此可以使用它来执行此操作:
use std::rc::Rc;
trait Foo {
fn foo(&self) {
println!("foo")
}
}
struct Bar;
impl Foo for Bar {}
fn main() {
let data: Rc<Foo> = Rc::new(Bar);
// we're creating a statically typed version of Bar
// and coercing it (the :Rc<Foo> on the left-end side)
// to as unsized bare trait counterpart.
// Rc<Foo> is a trait object, so it has no statically
// known size
data.foo();
}
playground example
?Sized绑定
由于您不太可能创建新的 DST,那么 DST 在您的日常 Rust 编码中有什么用处?最常见的是,它们允许您编写通用代码,这些代码既适用于已调整大小的类型,也适用于其现有 未调整大小的对应物。大多数情况下,这些将是Vec/[] 切片或String/str。
您表达这一点的方式是通过?Sized“绑定”。 ?Sized 在某些方面与界限相反;它实际上是说T 可以调整大小或不调整大小,因此它扩大了我们可以使用的可能类型,而不是像通常的边界那样限制它们。
人为的示例时间!假设我们有一个 FooSized 结构,它只包装了一个引用和一个简单的 Print 特征,我们想为它实现。
struct FooSized<'a, T>(&'a T)
where
T: 'a;
trait Print {
fn print(&self);
}
我们想为所有实现Display 的包装T 定义一个全面的实现。
impl<'a, T> Print for FooSized<'a, T>
where
T: 'a + fmt::Display,
{
fn print(&self) {
println!("{}", self.0)
}
}
让我们试着让它工作:
// Does not compile. "hello" is a &'static str, so self print is str
// (which is not sized)
let h_s = FooSized("hello");
h_s.print();
// to make it work we need a &&str or a &String
let s = "hello"; // &'static str
let h_s = &s; // & &str
h_s.print(); // now self is a &str
嗯...这很尴尬...幸运的是,我们有一种方法可以将结构概括为直接使用str(以及一般未调整大小的类型):?Sized
//same as before, only added the ?Sized bound
struct Foo<'a, T: ?Sized>(&'a T)
where
T: 'a;
impl<'a, T: ?Sized> Print for Foo<'a, T>
where
T: 'a + fmt::Display,
{
fn print(&self) {
println!("{}", self.0)
}
}
现在可以了:
let h = Foo("hello");
h.print();
playground
对于一个不那么做作(但简单)的实际示例,您可以查看标准库中的 Borrow 特征。
回到你的问题
trait Foo for ?Sized {
fn foo(&self) -> i32;
}
for ?Sized 语法现已过时。它曾经引用Self 的类型,声明`Foo 可以由一个未定义大小的类型实现,但现在这是默认值。现在可以为未调整大小的类型实现任何特征,即您现在可以拥有:
trait Foo {
fn foo(&self) -> i32;
}
//[i32] is unsized, but the compiler does not complain for this impl
impl Foo for [i32] {
fn foo(&self) -> i32 {
5
}
}
如果您不希望您的 trait 可用于未调整大小的类型,您可以使用 Sized 绑定:
// now the impl Foo for [i32] is illegal
trait Foo: Sized {
fn foo(&self) -> i32;
}