您如何在Rust中实际使用动态大小的类型?

在理论上,动态尺寸类型(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 {

广泛地说,我不明白你的动态大小的类型;你只能通过一个指针与一个接口,但我不知道如何做到这一点.

编辑更新为Rust 1.5

免责声明:这些只是我做了几个实验的结果,结合reading Niko Matsakis’s blog.

DST是在编译时不一定知道的类型.

在DST之前

像[i32]或者像InIterator这样的裸露的特征,一个不合适的向量不是有效的对象类型,因为它们没有一个已知的大小.

在DST之前,结构体可能如下所示:

struct Foo { f: [i32; 2] } // [i32; 2] is a fixed-sized vector with 2 i32 elements
struct Foo2<'a> { f: &'a [i32] } // & 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 Foo { f: [i32] } // f is (statically) unsized, so Foo is unsized too

枚举和元组也是如此.

与DSTs

您可以声明一个像上面的Foo(/ enum / tuple),包含一个未定义的类型.如上所述,包含不合格类型的类型也将被不定.

注意,虽然定义Foo很容易,但创建Foo的实例仍然很难,可能会改变.由于你不能在技术上创造一个非常类型的定义,所以你要做的就是创建一个大小对应的Foo(例如Foo {f:[1,2,3]},这是一个Foo [ i32; 3],因此具有静态已知的尺寸)并且编码一些管道以使锈迹知道它如何将其强制到其静态不对应的对应物Foo [i32]中.在安全稳定的Rust中做到这一点的方法仍然在Rust 1.5(这里是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

大小限制

所以,既然你不太可能创建一个新的DST,什么是DST在您的日常生锈编码中有用?最经常地,您可以编写适用于大小类型的通用代码以及其现有的未对应的代码.最常见的是Vec / [] slice或String / str.
你表达的方式是通过“大小的”绑定(引号),因为?尺寸在某种方面与绑定相反;实际上它表示T可以是大小还是不定型,所以它扩大了我们可以使用的可能类型,而不是限制它们通常的方式).

有代表性的时间!假设我们有一个FooSized结构,它只包含一个引用和一个我们要为它实现的简单的Print特性.

struct FooSized<'a, T>(&'a T) where T:'a;

trait Print {
    fn print(&self);
}

现在我们要为实现Display的所有T的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(和一般的非类型):?大小

//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语法现在已经过时了.它曾经引用自己的类型(换句话说,它宣称Foo可以通过一个不合适的类型来实现),但现在是默认的.现在可以为不合格的类型实现任何特征,即现在可以拥有:

trait Foo {
  fn foo(&self) -> i32;
}

impl Foo for [i32] { //[i32] is unsized, but rustc does not complain for this impl
  fn foo(&self) -> i32 { 5 }
}

如果您不希望对特定类型实现特性,可以使用Sized bound:

trait Foo : Sized { // now the impl Foo for [i32] is illegal
  fn foo(&self) -> i32;
}
相关文章
相关标签/搜索