【问题标题】:Size of a box containing a struct with a trait parameter包含具有特征参数的结构的框的大小
【发布时间】:2018-08-05 04:04:02
【问题描述】:

我需要一个包含 trait 对象和更多自身的结构。令人失望的是以下代码无法编译:

trait Foo {}

struct Bar<T: Foo> {
    bars: Vec<Box<Bar<dyn Foo>>>,
    foo: T,
}

我设法通过将?Sized 添加到T 来强制编译,但我不明白为什么会这样。我认为这是因为所有 trait 对象都具有相同的大小,但 Bar 的大小取决于具体类型 T 的大小。如果是这样,Bar 和未调整大小的T 如何在内存中表示?具体来说是什么在堆上跟踪它的大小以及为什么不能在大小调整的情况下使用这种机制。

【问题讨论】:

  • 在我看来,您会想要Boxes 的Bars 而不是Bars 的Boxes。切换这些应该可以解决问题。
  • 我考虑过这种方法,但Box&lt;dyn Foo&gt; 没有实现Foo。实际上Foo 是一个更大的特征,并且为Box&lt;dyn Foo&gt; 实现一切将是一件苦差事。一种选择是将 _T_/::new 的边界切换为 AsRef&lt;dyn Foo&gt;,这可行,但不是很整洁。
  • 但是Box&lt;dyn Foo&gt; 没有实现Fooso implement it...
  • 实际上Foo 是一个相当大的特征,实现它不仅是一件苦差事,而且感觉像是编译器应该能够处理的事情。

标签: compiler-errors rust traits typechecking


【解决方案1】:

dyn Foo 类型在编译时没有已知的大小。因此,当您编写Bar&lt;dyn Foo&gt; 时,编译器将不允许这样做,因为(默认情况下)类型参数必须调整大小。编译器建议您通过允许 T 未调整大小来解决此问题,这是将 T 设为 dyn Foo 所必需的。

Bar 和未调整大小的T 如何在内存中表示?

一个结构最多允许有一个未调整大小的字段。然后它的数据首先在内存中布局,首先是大小字段,然后是未大小字段。这个限制意味着所有字段的相对内存地址可以在编译时知道。具有?Sized 类型参数的结构本身可以调整大小或不调整大小,具体取决于其参数的具体类型。当结构未调整大小时,它不能进入​​堆栈,因此您只能从指针后面使用它。

目前没有此类对象的文档。它不完全是一个特征对象,但它是一个指向可能没有调整大小的东西的指针。正如您的示例所示,这有效。但是我不能告诉你 vtable 指针存储在哪里,因为我不知道,也不知道如何找到。

具体是什么在堆上跟踪它的大小以及为什么不能在大小调整的情况下使用这种机制。

每个对象的大小实际上并没有改变——只是每个实例可能不同。机制可以在“Sized 案例”中使用,但您没有大小合适的案例!即使对于已调整大小的 Tbars 集合也将包含未调整大小的 Bar&lt;dyn Foo&gt; 框。这就是为什么您需要 T: ?Sized(而不是 T: !Sized)来说明这种类型适用于 T 的大小。

【讨论】:

  • 但是,这不太可能是您真正想要的,因为您在结构中有一个 T 字段,必须调整大小 这是我最初的想法,但让我感到惊讶这似乎工作正常吗?我假设实际上,如果 T 是一个未调整大小的类型,即使绑定了?Sized,编译器也会失败。示例playground.
  • 如果您尝试在此处实际使用未调整大小的类型,则会出现错误。
  • 这很好,因为我实际上不想传递任何未调整大小的类型。虽然我仍然不明白为什么要添加?Sized 才能使Box&lt;Bar&lt;dyn Foo&gt;&gt; 工作。
  • @mgoszcz2 那是因为dyn Foo的类型不是Sized
  • 这让我回到原来的问题。什么跟踪盒装Bar&lt;dyn Foo&gt; 的物理堆大小?只是所有的 trait 对象,只是 vtable,都具有相同的大小吗?
猜你喜欢
  • 2016-11-21
  • 1970-01-01
  • 2017-05-26
  • 2013-02-06
  • 2011-04-02
  • 2017-06-24
  • 1970-01-01
  • 2011-11-07
  • 2012-08-08
相关资源
最近更新 更多