我有一个与here描述的用例相似的用例,但是有一点不同,因为我的解决方案无法用非泛型方法代替泛型方法。这是我的代码(Rust Playground):
com.example.sourceapp
如上一个问题所述,在编译此代码时,我收到一条错误use serde::{de::DeserializeOwned, Serialize};
use serde_json;
trait Serializer {
fn serialize_data<V>(&self, data: &V) -> Result<String, String> where V: Serialize;
fn deserialize_data<V>(&self, ser_data: &str) -> Option<V> where V: DeserializeOwned;
}
struct JsonSerializer {
x: i32 // some member I need to store
}
impl JsonSerializer {
fn new() -> JsonSerializer {
JsonSerializer { x: 1 }
}
}
impl Serializer for JsonSerializer {
fn serialize_data<V>(&self, data: &V) -> Result<String, String> where V: Serialize {
match serde_json::to_string(data) {
Ok(ser_data) => Ok(ser_data),
Err(err) => Err(err.to_string())
}
}
fn deserialize_data<V>(&self, ser_data: &str) -> Option<V> where V: DeserializeOwned {
match serde_json::from_str(ser_data).unwrap() {
Ok(val) => Some(val),
Err(_) => None
}
}
}
// I may want to have more serializer objects like
// YamlSerizlier, BincodeSerializer and so on...
// ...
struct MyMainObject {
serializer: Box<Serializer>
}
impl MyMainObject {
fn new() -> MyMainObject {
MyMainObject { serializer: Box::new(JsonSerializer::new()) }
}
fn do_something(&self) {
println!("{}", self.serializer.serialize_data(&1));
println!("{}", self.serializer.serialize_data(&String::from("MY STRING")));
}
}
fn main() {
let my_main_object = MyMainObject::new();
my_main_object.do_something();
}
,因为它具有通用方法:
the trait `Serializer` cannot be made into an object
但是对于我来说,我希望这些方法保持通用,以便可以序列化/反序列化任何类型的数据。
所以我的问题是如何保持动态分配模式并使其仍然有效,这意味着我想要成为 Compiling playground v0.0.1 (/playground)
error[E0038]: the trait `Serializer` cannot be made into an object
--> src/main.rs:42:5
|
42 | serializer: Box<Serializer>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Serializer` cannot be made into an object
|
= note: method `serialize_data` has generic type parameters
= note: method `deserialize_data` has generic type parameters
中的Serializer
特质成员,我可以使用任何类型的序列化程序对象进行初始化(Json ,Yaml等),然后在MyMainObject
内部调用serializer.serialize_data()
或serializer.deserialize_data()
。
如果这不可能,那么您可以建议的最佳替代方法是什么?
编辑:
我需要一种适用于不同类型的序列化器的解决方案,列出以下解决方案:
答案 0 :(得分:2)
您不能在动态调度中使用非对象安全特征;对象安全规则专门针对阻止动态调度的事物。
某些情况下有时会有变通办法。它们通常很复杂。但是特别是对于serde
,有erased_serde
条板箱,因为您不是第一个遇到此问题的人。
答案 1 :(得分:1)
以下不是一个好的长期解决方案,它只是一种解决方法。一种正确的方法是找出并实现一种方法,以使bincode
和serde_yaml
与erased_serde
保持一致。但是,如果您需要它现在就可以工作,这里是
基本上,你可以使用枚举写一个穷人的动态调度。看起来或多或少是这样的(我已经简化并省略了一些东西):
struct JsonSerializer();
struct YamlSerializer();
trait Serializer {
fn serialize<V>(&self, thing: &V) -> ();
}
impl Serializer for JsonSerializer {
fn serialize<V>(&self, thing: &V) -> () {
println!("json");
}
}
impl Serializer for YamlSerializer {
fn serialize<V>(&self, thing: &V) -> () {
println!("yaml");
}
}
// That's what we'll be using instead of Box<dyn Serializer>
enum SomeSerializer {
Json(JsonSerializer),
Yaml(YamlSerializer),
}
impl SomeSerializer {
pub fn serialize<V>(&self, thing: &V) -> () {
match self {
SomeSerializer::Json(ser) => ser.serialize(thing),
SomeSerializer::Yaml(ser) => ser.serialize(thing),
}
}
}
这是您的使用方式(除非您可能希望在此处使用实际的构造函数):
pub fn main() {
let thing = 2;
let json = SomeSerializer::Json(JsonSerializer());
let yaml = SomeSerializer::Yaml(YamlSerializer());
json.serialize(&thing);
yaml.serialize(&yaml);
}
这有严重的缺点(请参阅下文),但是它确实允许您将具有通用方法的内容打包到一个统一的接口中。
这种方法的主要问题是很难在设置中添加新的序列化器。随着Box<dyn Serializer>
所有你需要做的是impl Serializer
的东西。在这里,您必须在所有相关方法中为枚举和模式匹配添加一个变体。这在SomeSerializer
被定义板条箱,和不可能在其他包装箱不方便。此外,将变体添加到公共枚举是一项重大变化,下游板条箱可能并不完全受欢迎。有一些方法可以在某种程度上改善这一点:
SomeSerializer
它并没有真正意义SomeSerializer
是公开的。在其上进行模式匹配的能力几乎没有好处,而且公开限制了您可以做的事情而不会破坏下游。通常的解决方案是将其放在不透明的结构中并导出该结构,而使枚举本身隐藏起来:
pub struct VisibleSerializer(SomeSerializer);
您不能扩展SomeSerializer
与其他包装箱额外串行器。您可以继续在其上安装更多的枚举层(这既不幸又丑陋),但是随后原始板条箱中的任何功能都无法接受这种构造。这可以得到帮助:与其使serialize
是SomeSerializer
的固有方法,不如为其实现Serializer
,并使所有将使用SomeSerializer
的函数都通用并接受{{ 1}}。突然,所有下游板条箱都可以将所需的序列化器添加到设置中。
以这种方式包装四个序列化器中的三个以上是荒谬的,更不用说笨拙了。但是,如果您要使用的大多数序列化程序实际上是T: Serializer
兼容的,则您可以在erased_serde
中为它们使用一种包罗万象的枚举变量,而仅对不兼容的变量有单独的变量一个:
SomeSerializer