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

如何在Rust中实际使用动态大小的类型?,rust,Rust,理论上,动态大小的类型(DST)已经登陆,我们现在应该能够使用动态大小的类型实例。实际上,我既不能让它工作,也不能理解它周围的测试 一切似乎都围绕着大小?关键字。。。但你到底是如何使用它的呢 我可以把一些类型放在一起: // Note that this code example predates Rust 1.0 // and is no longer syntactically valid trait Foo for Sized? { fn foo(&self) ->

理论上,动态大小的类型(DST)已经登陆,我们现在应该能够使用动态大小的类型实例。实际上,我既不能让它工作,也不能理解它周围的测试

一切似乎都围绕着
大小?
关键字。。。但你到底是如何使用它的呢

我可以把一些类型放在一起:

// 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
}

我大致上理解,你不能有一个裸动态大小的类型;您只能通过指针与其中一个进行交互,但我不知道如何进行交互。

免责声明:这些只是我做的几个实验的结果,再加上我的测试

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

在DST之前 像
[i32]
这样的切片或像
IntoIterator
这样的裸特征不是有效的对象类型,因为它们没有已知的大小

结构可以如下所示:

// [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],
}
或者像这样:

// [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 您可以声明一个结构(或枚举或元组),如上面的
Foo
,其中包含一个未调整大小的类型。包含未调整大小的类型的类型也将不调整大小

虽然定义
Foo
很容易,但是创建
Foo
的实例仍然很难,而且可能会发生变化。由于从技术上讲,您无法根据定义创建未调整大小的类型,因此必须创建
Foo
的大小对应项。例如,
Foo{f:[1,2,3]}
,一个
Foo
,它有一个静态已知的大小,并对一些管道进行编码,以让编译器知道如何将其强制为其静态非大小的对应项
Foo
。从Rust 1.5开始,在安全和稳定的Rust中执行此操作的方法仍在研究中(有关更多信息,请参阅)

幸运的是,除非您正在创建一种新类型的智能指针(如
Rc
),否则您不太可能定义一个新的DST,这应该是非常罕见的

想象一下,
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();
}
我们想为实现
显示的所有包装的
T
定义一个覆盖impl

impl<'a, T> Print for FooSized<'a, T>
where
    T: 'a + fmt::Display,
{
    fn print(&self) {
        println!("{}", self.0)
    }
}
嗯。。。这很尴尬。。。幸运的是,我们有一种方法可以将结构泛化为直接使用
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)
    }
}

对于一个不太做作(但简单)的实际示例,您可以查看标准库中的trait

回到你的问题上来 大小为的
语法现在已过时。它过去指的是
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可用于未调整大小的类型,您可以使用
size
绑定:

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

目前,要创建一个
HasFoo
来存储一个已擦除的
Foo
类型,您需要首先创建一个具有固定具体类型的类型,然后强制将指向它的指针指向DST表单,即

let has_too: &HasFoo<Foo> = &HasFoo { f: Bar };
let has_too:&HasFoo=&HasFoo{f:Bar};
调用
具有\u foo.f.foo()

在将来,这些DST强制转换几乎肯定可以使用
as
,但目前需要通过显式类型提示进行强制。

要修改,这里有一种使用属性的不同方式来看待它

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();
}

struct Foo Print for Foo下面是一个基于的完整示例。重要的技巧是将要包含DST的类型设置为不需要调整泛型大小的泛型类型(通过
?调整大小
)。然后可以使用
Bar1
Bar2
构造一个具体值,然后立即将其转换

结构HasFoo(F); impl HasFoo{ fn使用信息技术(&self){ println!(“{}”,self.0.foo()) } } fn main(){ //同样可以使用“&HasFoo”或“Rc”等。 让ex1:Box=Box::new(HasFoo(Bar1)); 设ex2:Box=Box::new(HasFoo(Bar2)); 例1.使用_it(); ex2.使用_it(); } 特色食品{ fn foo(&self)->u32; } 结构Bar1; Bar1的impl Foo{ fn foo(和self)->u32{ 9 } } 结构Bar2; Bar2的impl Foo{ fn foo(和self)->u32{ 10 } }
您应该包括与您发布的任何错误消息相对应的代码。您确定DST真的已经登陆了吗?我想在这种情况下,应该会有一个适当的宣布。并且还没有关闭。@VladimirMatveev:IIRC,它的实现是不完整的,并且是在一个功能门后面。我在测试中没有看到功能门,DST似乎在工作,但在当前未完成的状态下还没有发挥应有的作用(如@VladimirMatveev提供的链接中所述)。请参阅useHi@Paolo的示例,感谢您的示例,有一点我无法跟上。
//未编译。“hello”是一个&'static str,所以self print是str/(没有大小)让h_s=FooSized(“hello”)原因
FooSized
定义为:structFooSized@RyanLe因为
T
没有大小(正如您所说,它是
str
)。在DST之前,您无法表示该绑定,因为
str
本身不能用作类型。即使在DST之后,您也不能直接使用
str
,它始终需要在指针后面,这样就不会发生变化。@PaoloFalabella为了避免混淆,我建议您在这里更正您的短语:“您表达这一点的方式是通过?size”边界“?”在某些方面与界限相反;它实际上说T可以是大小的也可以是非大小的,因此它拓宽了我们可以使用的可能类型,而不是像边界通常那样限制它们。。。它实际上说T可能实现也可能不实现
?size
trait,因此它拓宽了可能的类型w
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();
}