理论上,动态大小的类型(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,以获得BarBar2

try 这样做似乎总是会导致:

<anon>:28:17: 30:4 error: trying to initialise a dynamically sized struct
<anon>:28   let has_foo = &HasFoo {

我大致上理解,你不能有一个裸的动态大小的类型;你只能通过指针与一个接口,但我不知道怎么做.

推荐答案

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

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

在DST之前

slice like [i32]bare trait like IntoIterator不是有效的对象类型,因为它们没有已知的大小.

struct 可以如下所示:

// [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合作

您可以声明一个 struct (或枚举或元组),比如上面的Foo,其中包含一个未调整大小的类型.包含未调整大小的类型的类型也将不调整大小.

虽然定义Foo很容易,但Foo中的creating an instance仍然很难,而且可能会发生变化.因为从技术上讲,你不能根据定义创建一个无大小的类型,所以你必须创建Foosized对应项.例如,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();
}

playground example

?Sized bound

既然你不太可能使用新的DST,那么DST在你的日常编码中有什么用处呢?最常见的情况是,它们允许您编写通用代码,既适用于大小不同的类型,也适用于existing个大小不同的类型.通常是Vec/[]片或String/str片.

你表达这一点的方式是通过?Sized个"界限".?Sized在某些方面与界限相反;它实际上说T可以是大小的,也可以是非大小的,因此它拓宽了我们可以使用的可能类型,而不是像边界通常那样限制它们.

做作的例子时间!假设我们有一个FooSized struct ,它只包装了一个引用,还有一个简单的Print特性,我们想为它实现.

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

trait Print {
    fn print(&self);
}

我们想为实现Display的所有包装T定义一个毯子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

呃...这很尴尬...幸运的是,我们有一种方法可以将该 struct 推广到直接处理str个(通常是未大小化的类型):?Sized

//same as before, only added the ?大小绑定
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.

回到你的问题上来

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

for ?Sized语法现在已经过时了.它过go 指的是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可用于非大小的类型,可以使用Sized绑定:

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

Rust相关问答推荐

如何将元素添加到向量并返回对该元素的引用?

使用pyo3::Types::PyIterator的无限内存使用量

将大小为零的类型实例存储到空指针中

如何将`Join_all``Vec<;Result<;Vec<;Foo&>;,Anywhere::Error&>;`合并到`Result<;Vec<;Foo&>;,Anywhere::Error&>;`

同时从不同线程调用DLL的不同函数会出现分段错误或产生STATUS_STACK_BUFFER_OVERRUN

在使用#[NO_STD]时,如何在Rust中收到紧急消息?

如何删除Mac Tauri上的停靠图标?

当T不执行Copy时,如何返回Arc Mutex T后面的值?

交换引用时的生命周期

替换可变引用中的字符串会泄漏内存吗?

rust中的库插件管理器,现在是否可行?

将 &str 或 String 保存在变量中

我可以禁用发布模式的开发依赖功能吗?

我可以在 Rust 中 serde struct camel_case 和 deserde PascalCase

Rust 中 `Option` 的内存开销不是常量

实现不消费的迭代器

TcpStream::connect - 匹配武器具有不兼容的类型

有没有更好的方法来为拥有 DIsplay 事物集合的 struct 实现 Display?

在 Rust 中获得准确时间的正确方法?

有没有办法在 Rust 中对 BigInt 进行正确的位移?