理论上,动态大小类型(DST)已经着陆,我们现在应该能够使用动态大小的类型实例。实际上,我既不能使它工作,也不能理解围绕它的测试。
所有内容似乎都围绕着Sized?
关键字......但你究竟是如何使用它的呢?
我可以把一些类型放在一起:
// Note that this code example predates Rust 1.0
// and is no longer syntactically valid
trait Foo for Sized? {
fn foo(&self) -> u32;
}
struct Bar;
struct Bar2;
impl Foo for Bar { fn foo(&self) -> u32 { return 9u32; }}
impl Foo for Bar2 { fn foo(&self) -> u32 { return 10u32; }}
struct HasFoo<Sized? X> {
pub f:X
}
...但是如何创建一个HasFoo
的实例,即DST,可以有Bar
或Bar2
?
尝试这样做似乎总是导致:
<anon>:28:17: 30:4 error: trying to initialise a dynamically sized struct
<anon>:28 let has_foo = &HasFoo {
我广义地说,你不能拥有一个动态大小的裸体类型;你只能通过一个指针与一个接口,但我无法弄清楚如何做到这一点。
答案 0 :(得分:22)
免责声明:这些只是我做过的一些实验的结果,再加上reading Niko Matsakis's blog。
DST是在编译时不一定知道大小的类型。
像[i32]
这样的切片或像IntoIterator
这样的裸特征不是有效的对象类型,因为它们没有已知的大小。< / p>
结构可能如下所示:
// [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
,包含一个未大小的类型。包含unsized类型的类型也将被取消。
虽然定义Foo
很容易,但创建Foo
的实例仍然很难并且可能会发生变化。由于您无法在技术上按定义创建未定义类型,因此您必须创建Foo
的大小对应项。例如,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();
}
?Sized
绑定由于您不太可能创建新的DST,DST对您日常的Rust编码有用吗?最常见的是,它们允许您编写通用代码,这些代码既适用于大小类型,也适用于现有未大小的对应类型。最常见的是Vec
/ []
切片或String
/ str
。
你表达这种方式的方式是通过?Sized
&#34;绑定&#34;。 ?Sized
在某种程度上与约束相反;它实际上说T
可以是大小也可以是未大小的,因此它扩展了我们可以使用的可能类型,而不是像通常那样限制它们。
示例时间!让我们说我们有一个FooSized
结构,它只包含一个引用和一个我们想要为它实现的简单Print
特征。
struct FooSized<'a, T>(&'a T)
where
T: 'a;
trait Print {
fn print(&self);
}
我们希望为实施T
的所有已包装的Display
定义一个覆盖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
呃......这很尴尬......幸运的是,我们有一种方法可以将结构概括为直接使用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();
对于一个不那么做作(但很简单)的实际例子,你可以查看标准库中的Borrow
特征。
trait Foo for ?Sized {
fn foo(&self) -> i32;
}
for ?Sized
语法现已过时。它曾用于引用Self
的类型,声明`Foo可以通过unsized类型实现,但现在这是默认值。现在可以针对未大小的类型实现任何特征,即您现在可以:
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
}
}
如果您不希望您的特征可以实现未规范类型,则可以使用Sized
界限:
// now the impl Foo for [i32] is illegal
trait Foo: Sized {
fn foo(&self) -> i32;
}
答案 1 :(得分:2)
要修改the example that Paolo Falabella has given,这是使用属性查看它的另一种方法。
struct Foo<'a, T>
where
T: 'a + ?Sized,
{
printable_object: &'a T,
}
impl<'a, T> Print for Foo<'a, T>
where
T: 'a + ?Sized + fmt::Display,
{
fn print(&self) {
println!("{}", self.printable_object);
}
}
fn main() {
let h = Foo {
printable_object: "hello",
};
h.print();
}
答案 2 :(得分:0)
目前,要创建一个存储类型已删除HasFoo
的{{1}},您需要首先创建一个具有固定具体类型的类型,然后将指向它的指针强制转换为DST表单,即
Foo
调用let has_too: &HasFoo<Foo> = &HasFoo { f: Bar };
然后做你期望的事。
将来,has_foo.f.foo()
几乎可以肯定这些DST演员阵容,但目前需要通过明确的类型提示进行强制演绎。
答案 3 :(得分:0)
这是一个基于 huon's answer 的完整示例。重要的技巧是使您想要包含 DST 的类型成为泛型类型,不需要调整泛型的大小(通过 ?Sized
)。然后您可以使用 Bar1
或 Bar2
构造一个具体的值,然后立即将其转换。
struct HasFoo<F: ?Sized = dyn Foo>(F);
impl HasFoo<dyn Foo> {
fn use_it(&self) {
println!("{}", self.0.foo())
}
}
fn main() {
// Could likewise use `&HasFoo` or `Rc<HasFoo>`, etc.
let ex1: Box<HasFoo> = Box::new(HasFoo(Bar1));
let ex2: Box<HasFoo> = Box::new(HasFoo(Bar2));
ex1.use_it();
ex2.use_it();
}
trait Foo {
fn foo(&self) -> u32;
}
struct Bar1;
impl Foo for Bar1 {
fn foo(&self) -> u32 {
9
}
}
struct Bar2;
impl Foo for Bar2 {
fn foo(&self) -> u32 {
10
}
}