如何将 trait 作为结构体的一个拥有的字段?

huangapple go评论70阅读模式
英文:

How do I have a trait as a owned field of a struct?

问题

抱歉,你的请求是要将英文文本翻译成代码部分,请看下面的翻译:

我对Rust还比较新,我试图实现一个结构体,该结构体可以拥有不同的实现了共同特性的结构体作为字段。

在传统的带有垃圾收集器的编程语言中,我会像这样实现它:

```rust
pub struct MemoryMapper {
    regions: Vec<Region>,
}

trait MemoryMappable: Sized {
    
}

pub struct Region {
    device: dyn MemoryMappable,
    start: u32,
    end: u32,
    remap: bool
}

但是在这里,我遇到了以下编译错误:

无法在编译时知道类型`(dyn MemoryMappable + 'static)`的大小
未实现`(dyn MemoryMappable + 'static)`的`Sized`特性
结构体中只能有最后一个字段具有动态大小类型
将字段的类型更改为具有静态已知大小

我尝试将Sized作为MemoryMappable的超特性,但仍然在以下代码中出现问题:

pub fn map(&mut self, device: dyn MemoryMappable, start: u32, end: u32, remap: bool) -> &Region {
    self.regions.insert(0, Region{device, start, end, remap});
    return self.regions.get(0).unwrap();
}
无法将`MemoryMappable`特性转换为对象
`MemoryMappable`无法转换为对象

因为根据Rust文档中的对象安全性

Sized不能是超特性。换句话说,它不能要求Self: Sized

我不知道该如何处理这个问题。

英文:

I'm fairly new to Rust, I was tying to implement a struct that could have different structs that implemented a common trait as a field

In traditional programming languages with garbage collector I would have implemented it like this:


pub struct MemoryMapper {
    regions: Vec&lt;Region&gt;,
}

trait MemoryMappable: Sized {
    
}

pub struct Region {
    device: dyn MemoryMappable,
    start: u32,
    end: u32,
    remap: bool
}

But here I have the following compilation error:

the size for values of type `(dyn MemoryMappable + &#39;static)` cannot be known at compilation time
the trait `Sized` is not implemented for `(dyn MemoryMappable + &#39;static)`
only the last field of a struct may have a dynamically sized type
change the field&#39;s type to have a statically known size

I have tried making Sized a supertrait of MemoryMappable, but it still gives me issues with the following code:

pub fn map(&amp;mut self, device: dyn MemoryMappable, start: u32, end: u32, remap: bool) -&gt; &amp;Region {
        self.regions.insert(0, Region{device, start, end, remap});
        return self.regions.get(0).unwrap();
}
the trait `MemoryMappable` cannot be made into an object
`MemoryMappable` cannot be made into an object

Because from Rust documentation about object safety

> Sized must not be a supertrait. In other words, it must not require Self: Sized.

I have no idea of how to go about this at this point

答案1

得分: 3

Sized 结构体的字段可以存在于堆栈上,因此在编译时必须具有已知大小。这就是 Sized 错误在说什么。

dyn Trait 是 "实现 Trait 的任何类型"。那它的大小是多少呢?嗯,这取决于底层类型是什么。

因此,即使 Trait 作为超trait 具有 Sized(这只是保证实现 Trait 的任何类型也实现 Sized,但你仍然不知道它是哪个),dyn Trait 从不实现 Sized

实际上,将 Sized 作为 Trait 的超trait 是用来选择不实现对象安全性的方式,这使得构造 dyn Trait 变得不可能。

相反,绕过这个问题的方式是使用类似指针的类型:

  • Box<dyn Trait> 创建一个拥有的指针
  • &dyn Trait 创建一个引用
  • Arc<dyn Trait> 创建一个引用计数的指针
  • 等等。

指针类型的选择取决于您的用例,但在大多数情况下,Box<dyn Trait> 是可以的,也是一个很好的起点。

英文:

Fields of Sized structs can exist on the stack, and so must have a known size at compile time. This is what the Sized error is saying.

dyn Trait is "any type that implements Trait. So what is its size? Well, it depends on what the underlying type is.

Therefore, dyn Trait never implements Sized, even if Trait has Sized as a supertrait (all this does is guarantee that any type that implements Trait also implements Sized, but you still don't know which one it is).

In fact, making Sized a supertrait of Trait is how you opt-out of object safety, which makes it impossible to construct a dyn Trait.

Instead, the way you get around this is by using a pointer-like type:

  • Box&lt;dyn Trait&gt; creates an owned pointer
  • &amp;dyn Trait creates a reference
  • Arc&lt;dyn Trait&gt; creates a reference counted pointer
  • etc.

The choice of pointer type depends on your use case, but in most scenarios, Box&lt;dyn Trait&gt; is fine, and is a good starting point.

huangapple
  • 本文由 发表于 2023年2月18日 17:54:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/75492513.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定