英文:
Rust: "decombine" traits?
问题
假设我有一些特性:
trait Device {}
trait Named {}
trait Report {}
我有一些结构体,其中一些实现了这些特性的某个子集,然后我有一些逻辑期望一个实现了所有这些特性的类型,例如,我想要一个包含具有名称并能够生成报告的设备集合。为了减少键盘输入,我创建了一个组合特性:
pub trait ReportNamedDevice: Named + Device {}
impl<T: Named + Device> ReportNamedDevice for T {}
(暂时省略Report
,直到我能使现有的工作)
然后我在一个结构体中使用这些特性的集合:
pub struct Room<'a> {
pub name: String,
pub devices: Vec<&'a dyn ReportNamedDevice>,
}
现在,房间本身是Named
的,我希望能够将其添加到House
中。我希望所有包含Named
事物的集合都包含具有唯一名称的事物,因此我编写了以下筛选函数:
fn name_is_in_named_vector(name: &String, haystack: &Vec<&dyn Named>) -> bool {
haystack
.iter()
.map(|x| x.name().clone())
.collect::<Vec<String>>()
.contains(name)
}
但是当我尝试在我的Room
实现中使用它时,我会得到一个“类型不匹配”的错误,因为编译器不将ReportNamedDevice
视为Named
:
impl<'a> Room<'a> {
pub fn add_device(&mut self, device: &'a dyn ReportNamedDevice) {
if !name_is_in_named_vector(device.name(), &self.devices) {
self.devices.push(device)
}
}
}
我知道我可以替换筛选函数中的haystack
类型,但我希望能够重用它来筛选房间。
有没有办法使这个工作?
英文:
Suppose, I have a number of traits:
trait Device {}
trait Named {}
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:
pub trait ReportNamedDevice: Named + Device {}
impl<T: Named + Device> 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:
pub struct Room<'a> {
pub name: String,
pub devices: Vec<&'a dyn ReportNamedDevice>,
}
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:
fn name_is_in_named_vector(name: &String, haystack: &Vec<&dyn Named>) -> bool {
haystack
.iter()
.map(|x| x.name().clone())
.collect::<Vec<String>>()
.contains(name)
}
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
:
impl<'a> Room<'a> {
pub fn add_device(&mut self, device: &'a dyn ReportNamedDevice) {
if !name_is_in_named_vector(device.name(), &self.devices) {
self.devices.push(device)
}
}
}
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
:
fn name_is_in_named_vector<T: ?Sized + Named>(name: &str, haystack: &[&T]) -> bool {
haystack
.iter()
.find(|x| x.name() == name)
.is_some()
}
注意:我已经应用了isaactfa在评论中提到的关于find
的提示以及为什么不建议接受引用&String、&Vec或&Box作为函数参数?的建议。
英文:
Yes there is a way, just use generics instead of trait objects, that works becaues while a &dyn ReportNamedDevice
isn't the same as a &dyn Named
it does implement Named
:
fn name_is_in_named_vector<T: ?Sized + Named>(name: &str, haystack: &[&T]) -> bool {
haystack
.iter()
.find(|x| x.name() == name)
.is_some()
}
<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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论