如何获取结构实现特征的方法?如何删除重复项?

时间:2019-07-09 16:03:59

标签: rust

我们使用了几种不同的API(在示例中,operator::Apisupervisor::Api)。这些API的有效载荷通常用不同的样式(ApiWrapper)进行包装,并且它们的使用方式也相似但略有不同。

我们的API客户端的方法是根据元数据(tool_btool_x等)生成的。

它们返回一个实现特征Reporter的结构,而不是返回有效载荷的有趣部分,该特征包装数据并提供其他方法。 实际上,为了能够存储有效负载的有趣部分(该部分将由tool方法返回),使用了PhantomData字段(最初我们添加了一个字段和Option<T>)。

为减少样板,我们创建了特征Reporter,该特征定义了应该由我们的API客户端实现的接口,但也实现了某些功能。不幸的是,即使采用这种方法,我们也有一些重复。看起来部分重复内容无法删除,但我们希望减少重复。我已经在代码中添加了一些对此的注释。

由于我们无法提供每个API Reporter的特定实现(Operator上的operator::ApiSupervisor上的supervisor::Api),因此我们决定使用{{ 1}}来指定将使用实现该特征的结构。

整个示例在https://github.com/foochi/duplicated-impl-traits

impl Reporter

Rust playground

如果可能的话,我们希望减少使用特征并为该特征的每个实现创建结构的重复,这似乎有点难看。

但是,我们最大的问题是,由于我们使用mod report { use crate::Result; use serde::de::DeserializeOwned; use std::io::{Error, ErrorKind}; #[derive(Debug)] pub struct Report { /// The entire report, as text pub text: String, } pub trait Reporter<T> where T: DeserializeOwned, { fn new(report: Report) -> Self; /// The report associated to the reporter fn report(&self) -> &Report; fn deserialize<U>(&self, text: &String) -> Result<U> where U: DeserializeOwned, { serde_json::from_str::<U>(text) .map_err(|_err| Error::new(ErrorKind::Other, "Failed when deserializing")) } /// The tool is inferred from the report fn tool(&self) -> Result<T>; } } mod supervisor { use crate::report::{Report, Reporter}; use crate::Result; use serde::de::DeserializeOwned; use std::marker::PhantomData; #[derive(Debug, Deserialize)] pub struct ToolX { pub tool_x: String, pub number: u8, } #[derive(Debug, Deserialize)] pub struct ToolY { pub tool_y: String, } #[derive(Debug, Deserialize)] pub struct ApiWrapper<T> { pub supervisor: T, } #[derive(Debug)] pub struct Supervisor<T> where T: DeserializeOwned, { report: Report, report_tool: PhantomData<T>, } impl<T> Reporter<T> for Supervisor<T> where T: DeserializeOwned, { fn new(report: Report) -> Supervisor<T> { Self { report, report_tool: PhantomData, } } // NOTE 1: This cannot be implemented inside the trait because it does not have any associated field fn report(&self) -> &Report { &self.report } fn tool(&self) -> Result<T> { let text = &self.report.text; let reporter = self.deserialize::<ApiWrapper<T>>(&text)?; let reporter = reporter.supervisor; match &reporter { ToolX => println!("INFO: a report of X has been received"), }; Ok(reporter) } } impl<T> Supervisor<T> where T: DeserializeOwned, { // NOTE 2: This method cannot be accessed pub fn review() { println!("REVIEWED"); } } pub struct Api {} impl Api { fn process<T>(&self, message: String) -> Result<Supervisor<T>> where T: DeserializeOwned, { let report = Report { text: message }; println!("Incoming report"); Ok(Supervisor::new(report)) } // This methods is generated, so it cannot use a specific implementation of Reporter pub fn tool_x(&self, message: String) -> Result<impl Reporter<ToolX>> { self.process::<ToolX>(message) } // This methods is generated, so it cannot use a specific implementation of Reporter pub fn tool_y(&self, message: String) -> Result<impl Reporter<ToolY>> { println!("WARNING: Y tool is deprecated!"); self.process::<ToolY>(message) } } } mod operator { use crate::report::{Report, Reporter}; use crate::Result; use serde::de::DeserializeOwned; use std::marker::PhantomData; #[derive(Debug, Deserialize)] pub struct ToolA { pub tool_a: String, } #[derive(Debug, Deserialize)] pub struct ToolB { pub tool_b: String, pub scale: u16, } #[derive(Debug, Deserialize)] pub struct ApiWrapper<T> { pub operator: T, } #[derive(Debug)] pub struct Operator<T> where T: DeserializeOwned, { report: Report, report_tool: PhantomData<T>, } impl<T> Reporter<T> for Operator<T> where T: DeserializeOwned, { fn new(report: Report) -> Operator<T> { Self { report, report_tool: PhantomData, } } // NOTE 1: This cannot be implemented inside the trait because it does not have any associated field fn report(&self) -> &Report { &self.report } fn tool(&self) -> Result<T> { let text = &self.report.text; let reporter = self.deserialize::<ApiWrapper<T>>(&text)?; Ok(reporter.operator) } } pub struct Api {} impl Api { fn process<T>(&self, message: String) -> Result<Operator<T>> where T: DeserializeOwned, { let report = Report { text: message }; Ok(Operator::new(report)) } // This methods is generated, so it cannot use a specific implementation of Reporter pub fn tool_a(&self, message: String) -> Result<impl Reporter<ToolA>> { println!("WARNING: A tool is unstable!"); self.process::<ToolA>(message) } // This methods is generated, so it cannot use a specific implementation of Reporter pub fn tool_b(&self, message: String) -> Result<impl Reporter<ToolB>> { self.process::<ToolB>(message) } } } #[macro_use] extern crate serde_derive; use report::Reporter; pub type Result<T> = ::std::result::Result<T, ::std::io::Error>; fn main() { // Operator let _message_a = r#" { "operator": { "tool_a": "a111a" } } "# .to_string(); let message_b = r#" { "operator": { "tool_b": "2b2bb", "scale": 400 } } "# .to_string(); let api_operator = operator::Api {}; let reporter_b = api_operator .tool_b(message_b) .expect("Error while processing report b"); let report_b = reporter_b.report(); let tool_b = reporter_b.tool().expect("Error while processing tool b"); println!("Operator report: {} => {}", report_b.text, tool_b.tool_b); // Supervisor let message_x = r#" { "supervisor": { "tool_x": "xXxxxX", "number": 5 } } "# .to_string(); let _message_y = r#" { "supervisor": { "tool_y": "yYYYy" } } "# .to_string(); let api_supervisor = supervisor::Api {}; let reporter_x = api_supervisor .tool_x(message_x) .expect("Error while processing report x"); // FIXME ideally this would work // reporter_x.preview(); let report_x = reporter_x.report(); let tool_x = reporter_x.tool().expect("Error while processing tool x"); println!("Supervisor report: {} => {}", report_x.text, tool_x.tool_x); } ,因此找不到具体实现(impl Reporter)的preview方法。 理想情况下,我们只能有1个结构(Supervisor)而不是当前的Reporter特性和2个结构(ReporterOperator),并实现这些方法对于使用泛型或类似方法的每个API来说都是唯一的,但我们还没有找到一种方法来做到这一点,而又不会丢失我们包装和存储 客户方法的返回类型的技术({{1} },Supervisor等。

0 个答案:

没有答案