Rust: “decombine”特质?

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

Rust: "decombine" traits?

问题

假设我有一些特性:

  1. trait Device {}
  2. trait Named {}
  3. trait Report {}

我有一些结构体,其中一些实现了这些特性的某个子集,然后我有一些逻辑期望一个实现了所有这些特性的类型,例如,我想要一个包含具有名称并能够生成报告的设备集合。为了减少键盘输入,我创建了一个组合特性:

  1. pub trait ReportNamedDevice: Named + Device {}
  2. impl<T: Named + Device> ReportNamedDevice for T {}

(暂时省略Report,直到我能使现有的工作)

然后我在一个结构体中使用这些特性的集合:

  1. pub struct Room<'a> {
  2. pub name: String,
  3. pub devices: Vec<&'a dyn ReportNamedDevice>,
  4. }

现在,房间本身是Named的,我希望能够将其添加到House中。我希望所有包含Named事物的集合都包含具有唯一名称的事物,因此我编写了以下筛选函数:

  1. fn name_is_in_named_vector(name: &String, haystack: &Vec<&dyn Named>) -> bool {
  2. haystack
  3. .iter()
  4. .map(|x| x.name().clone())
  5. .collect::<Vec<String>>()
  6. .contains(name)
  7. }

但是当我尝试在我的Room实现中使用它时,我会得到一个“类型不匹配”的错误,因为编译器不将ReportNamedDevice视为Named

  1. impl<'a> Room<'a> {
  2. pub fn add_device(&mut self, device: &'a dyn ReportNamedDevice) {
  3. if !name_is_in_named_vector(device.name(), &self.devices) {
  4. self.devices.push(device)
  5. }
  6. }
  7. }

我知道我可以替换筛选函数中的haystack类型,但我希望能够重用它来筛选房间。

有没有办法使这个工作?

英文:

Suppose, I have a number of traits:

  1. trait Device {}
  2. trait Named {}
  3. trait Report {}

I have a number of structs, some of which implement some subset of these traits, and then I have some logic that expects a type that implements all of them, say, I want to have a collection of devices that have names and can produce reports. To reduce the typing (on the keyboard), I create a combined trait:

  1. pub trait ReportNamedDevice: Named + Device {}
  2. impl&lt;T: Named + Device&gt; ReportNamedDevice for T {}

(Report is omitted for now until I can make the existing ones work)

And then I use a collection of those in a struct:

  1. pub struct Room&lt;&#39;a&gt; {
  2. pub name: String,
  3. pub devices: Vec&lt;&amp;&#39;a dyn ReportNamedDevice&gt;,
  4. }

Now, the room itself is Named, and I want to be able to add it to a House. And I want all of my collections of Named things to contain things with unique names, so I write the following filter function:

  1. fn name_is_in_named_vector(name: &amp;String, haystack: &amp;Vec&lt;&amp;dyn Named&gt;) -&gt; bool {
  2. haystack
  3. .iter()
  4. .map(|x| x.name().clone())
  5. .collect::&lt;Vec&lt;String&gt;&gt;()
  6. .contains(name)
  7. }

But when I try to use it in my Room impl, I get a mismatched types error, because the compiler doesn't treat ReportNamedDevice as Named:

  1. impl&lt;&#39;a&gt; Room&lt;&#39;a&gt; {
  2. pub fn add_device(&amp;mut self, device: &amp;&#39;a dyn ReportNamedDevice) {
  3. if !name_is_in_named_vector(device.name(), &amp;self.devices) {
  4. self.devices.push(device)
  5. }
  6. }
  7. }

I know I could replace the haystack type in the filter function, but I would like to be able to reuse it for filtering rooms as well.

Is there a way I can make this work?

答案1

得分: 4

是的,有一种方法,只需使用泛型而不是特质对象,这可以工作,因为&dyn ReportNamedDevice虽然与&dyn Named不同,但确实实现了Named

  1. fn name_is_in_named_vector<T: ?Sized + Named>(name: &str, haystack: &[&T]) -> bool {
  2. haystack
  3. .iter()
  4. .find(|x| x.name() == name)
  5. .is_some()
  6. }

Playground

注意:我已经应用了isaactfa在评论中提到的关于find的提示以及为什么不建议接受引用&String、&Vec或&Box作为函数参数?的建议。

英文:

Yes there is a way, just use generics instead of trait objects, that works becaues while a &amp;dyn ReportNamedDevice isn't the same as a &amp;dyn Named it does implement Named:

  1. fn name_is_in_named_vector&lt;T: ?Sized + Named&gt;(name: &amp;str, haystack: &amp;[&amp;T]) -&gt; bool {
  2. haystack
  3. .iter()
  4. .find(|x| x.name() == name)
  5. .is_some()
  6. }

Playground

<sub><sub>Note: I've applied isaactfa's hint about find in the comments and the tips of Why is it discouraged to accept a reference &String, &Vec, or &Box as a function argument?</sub></sub>

huangapple
  • 本文由 发表于 2023年6月28日 23:51:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76574820.html
匿名

发表评论

匿名网友

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

确定