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

Dou*_*oug 14 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
}
Run Code Online (Sandbox Code Playgroud)

...但是如何创建一个HasFooDST 实例,或者具有?BarBar2

试图这样做似乎总是导致:

<anon>:28:17: 30:4 error: trying to initialise a dynamically sized struct
<anon>:28   let has_foo = &HasFoo {
Run Code Online (Sandbox Code Playgroud)

我广泛地理解你不能拥有一个动态大小的类型; 你只能通过一个指针与一个接口,但我无法弄清楚如何做到这一点.

Pao*_*lla 22

免责声明:这些只是我做过的一些实验的结果,再加上阅读Niko Matsakis的博客.

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

在DST之前

一个切片[i32]裸露的特质IntoIterator不是有效的对象类型,因为他们没有一个已知的大小.

结构可能如下所示:

// [i32; 2] is a fixed-sized vector with 2 i32 elements
struct Foo {
    f: [i32; 2],
}
Run Code Online (Sandbox Code Playgroud)

或者像这样:

// & 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],
}
Run Code Online (Sandbox Code Playgroud)

但不是这样的:

// f is (statically) unsized, so Foo is unsized too
struct Foo {
    f: [i32],
}
Run Code Online (Sandbox Code Playgroud)

对于枚举和元组也是如此.

使用DST

您可以声明上面的结构(或枚举或元组)Foo,包含未大小的类型.包含unsized类型的类型也将被取消.

在定义Foo很简单,创建一个实例Foo仍难并随时可能更改.由于您无法在技术上根据定义创建未大小的类型,因此您必须创建大小相同的类型Foo.例如, Foo { f: [1, 2, 3] }a Foo<[i32; 3]>,具有静态已知的大小和代码一些管道,让编译器知道如何将其强制转换为其静态未定义的对应物Foo<[i32]>.从Rust 1.5开始,在安全稳定的Rust中执行此操作的方法仍在继续(此处有关于DST强制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);
    // 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();
}
Run Code Online (Sandbox Code Playgroud)

操场的例子

?Sized

由于您不太可能创建新的DST,因此在日常Rust编码中有哪些DST有用?最常见的是,它们允许您编写通用代码,这些代码既适用于大小类型,也适用于现有的未定义类型.通常这些将是Vec/ []slices或String/ str.

你表达这种方式的方式是通过?Sized"约束".?Sized在某种程度上与一个界限相反; 它实际上说T可以是大小或非大小,因此它扩展了我们可以使用的可能类型,而不是像边界通常那样限制它们.

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

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

trait Print {
    fn print(&self);
}
Run Code Online (Sandbox Code Playgroud)

我们要定义的所有包裹毯子IMPL T的实现Display.

impl<'a, T> Print for FooSized<'a, T>
where
    T: 'a + fmt::Display,
{
    fn print(&self) {
        println!("{}", self.0)
    }
}
Run Code Online (Sandbox Code Playgroud)

让我们试着让它发挥作用:

// 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
Run Code Online (Sandbox Code Playgroud)

呃......这很尴尬......幸运的是,我们有一种方法可以将结构概括为直接使用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在这个工作:

let h = Foo("hello");
h.print();
Run Code Online (Sandbox Code Playgroud)

操场

对于一个设计较少(但很简单)的实际例子,您可以查看Borrow标准库中的特征.

回到你的问题

trait Foo for ?Sized {
    fn foo(&self) -> i32;
}
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不希望您的特征可以为unsized类型实现,您可以使用Sizedbound:

// now the impl Foo for [i32] is illegal
trait Foo: Sized {
    fn foo(&self) -> i32;
}
Run Code Online (Sandbox Code Playgroud)