免责声明:这些只是我做的几个实验的结果,加上reading Niko Matsakis's blog个.
DST是编译时不一定知道大小的类型.
在DST之前
slice like [i32]
或bare trait like IntoIterator
不是有效的对象类型,因为它们没有已知的大小.
struct 可以如下所示:
// [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],
}
枚举和元组也是如此.
与DST合作
您可以声明一个 struct (或枚举或元组),比如上面的Foo
,其中包含一个未调整大小的类型.包含未调整大小的类型的类型也将不调整大小.
虽然定义Foo
很容易,但Foo
中的creating an instance仍然很难,而且可能会发生变化.因为从技术上讲,你不能根据定义创建一个无大小的类型,所以你必须创建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
bound
既然你不太可能使用新的DST,那么DST在你的日常编码中有什么用处呢?最常见的情况是,它们允许您编写通用代码,既适用于大小不同的类型,也适用于existing个大小不同的类型.通常是Vec
/[]
片或String
/str
片.
你表达这一点的方式是通过?Sized
个"界限".?Sized
在某些方面与界限相反;它实际上说T
可以是大小的,也可以是非大小的,因此它拓宽了我们可以使用的可能类型,而不是像边界通常那样限制它们.
做作的例子时间!假设我们有一个FooSized
struct ,它只包装了一个引用,还有一个简单的Print
特性,我们想为它实现.
struct FooSized<'a, T>(&'a T)
where
T: 'a;
trait Print {
fn print(&self);
}
我们想为实现Display
的所有包装T
定义一个毯子impl.
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
呃...这很尴尬...幸运的是,我们有一种方法可以将该 struct 推广到直接处理str
个(通常是未大小化的类型):?Sized
//same as before, only added the ?大小绑定
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.
回到你的问题上来
trait Foo for ?Sized {
fn foo(&self) -> i32;
}
for ?Sized
语法现在已经过时了.它过go 指的是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;
}