How do you actually use dynamically sized types in Rust?
从理论上讲,动态大小类型(DST)已经着陆,我们现在应该能够使用动态大小的类型实例。 实际上,我既无法使其正常运行,也无法理解其周围的测试。
一切似乎都围绕着
我可以将一些类型放在一起:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // 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 } |
...但是如何创建
尝试这样做似乎总是会导致:
1 2 | :28:17: 30:4 error: trying to initialise a dynamically sized struct :28 let has_foo = &HasFoo { |
从广义上讲,我不能理解动态大小的类型。 您只能通过一个指针与一个接口交互,但是我不知道如何做到这一点。
免责声明:这些只是我做过的一些实验的结果,结合阅读Niko Matsakis的博客可以得出。
DST是在编译时不一定知道大小的类型。
夏令时之前
像
一个结构可能看起来像这样:
1 2 3 4 | // [i32; 2] is a fixed-sized vector with 2 i32 elements struct Foo { f: [i32; 2], } |
或像这样:
1 2 3 4 5 6 7 8 | // & 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], } |
但不是这样的:
1 2 3 4 | // f is (statically) unsized, so Foo is unsized too struct Foo { f: [i32], } |
枚举和元组也是如此。
使用DST
您可以像上面的
虽然定义
幸运的是,除非创建了新型的智能指针(如
想象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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(); } |
游乐场的例子
由于您不太可能创建新的DST,因此在您的日常Rust编码中,DST有什么用?大多数情况下,它们使您可以编写适用于大小类型及其现有未大小对应对象的通用代码。最常见的是
表达方式是通过
人为的例子时间!假设我们有一个
1 2 3 4 5 6 7 | struct FooSized<'a, T>(&'a T) where T: 'a; trait Print { fn print(&self); } |
我们要为实现
1 2 3 4 5 6 7 8 | impl<'a, T> Print for FooSized<'a, T> where T: 'a + fmt::Display, { fn print(&self) { println!("{}", self.0) } } |
让我们尝试使其工作:
1 2 3 4 5 6 7 8 9 | // 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 |
嗯...这很尴尬...幸运的是,我们有一种方法可以推广该结构以直接与
1 2 3 4 5 6 7 8 9 10 11 12 13 | //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) } } |
现在这有效:
1 2 | let h = Foo("hello"); h.print(); |
操场
对于不太人为(但简单)的实际示例,您可以查看标准库中的
回到您的问题
1 2 3 | trait Foo for ?Sized { fn foo(&self) -> i32; } |
1 2 3 4 5 6 7 8 9 10 | 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 } } |
如果您不希望自己的特征可用于非大小类型,则可以使用
1 2 3 4 | // now the impl Foo for [i32] is illegal trait Foo: Sized { fn foo(&self) -> i32; } |
为了修改Paolo Falabella给出的示例,这里是使用属性的另一种查看方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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(); } |
目前,要创建一个存储有类型擦除的
1 | let has_too: &HasFoo<Foo> = &HasFoo { f: Bar }; |
调用
将来,使用