免责声明:这些只是我进行的一些实验的结果,结合 阅读Niko Matsakis的博客。
DSTs是指大小在编译时不一定已知的类型。
DSTs出现之前
像 [i32]
这样的 切片 或者像 IntoIterator
这样的 裸trait 不是有效的对象类型,因为它们没有已知的大小。
一个结构体可能看起来像这样:
// [i32; 2] is a fixed-sized vector with 2 i32 elements
struct Foo {
f: [i32; 2],
}
或者像这样:
struct Foo2<'a> {
f: &'a [i32],
}
但不是这样:
struct Foo {
f: [i32],
}
这也适用于枚举和元组。
使用DSTs
您可以像上面的
Foo
一样声明一个包含未定类型的结构体(或枚举或元组)。 包含未定类型的类型也将是未定类型。
虽然定义
Foo
很容易,但是创建
Foo
的实例仍然很困难,并且可能会更改。 由于从定义上无法创建未定类型,因此必须创建
大小已知的Foo
的对应项。例如,
Foo { f: [1, 2, 3] }
,一个
Foo<[i32; 3]>
,它具有静态已知的大小和一些管道以让编译器知道如何将其强制转换为其静态未定类型的对应项
Foo<[i32]>
。 在安全和稳定的Rust中完成此操作的方法仍在研究中,截至Rust 1.5(此处是DST coercion的
RFC以获取更多信息)。
幸运的是,定义新的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);
data.foo();
}
playground示例
?Sized
限定
在你日常的Rust编程中,由于你不太可能创建新的DST,那么DST有什么用处呢?最常见的是,它们让你编写通用代码,既适用于大小类型又适用于它们的现有的非大小类型。最常见的情况是Vec
/[]
切片或String
/str
。
你可以通过?Sized
“限制”来表达这一点。在某种程度上,?Sized
是一个限制的相反;它实际上表示T
可以是具有大小或无大小的,因此它扩大了我们可以使用的可能类型,而不是像限制一样限制它们。
举个假例!假设我们有一个简单的FooSized
结构体,它只是包裹一个引用和一个简单的Print
trait,我们想要为它实现。
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)
}
}
让我们试着让它工作:
let h_s = FooSized("hello");
h_s.print();
let s = "hello";
let h_s = &s;
h_s.print();
嗯...这有点尴尬...幸运的是,我们有一种方法来将结构体泛化,以便直接与str
(以及无大小限制的类型)一起使用:?Sized
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 现在都可以针对无大小限制的类型进行实现,也就是说,现在可以有:
trait Foo {
fn foo(&self) -> i32;
}
impl Foo for [i32] {
fn foo(&self) -> i32 {
5
}
}
如果您不希望您的特质适用于未定大小的类型,您可以使用
Sized
约束:
trait Foo: Sized {
fn foo(&self) -> i32;
}