Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Generics 具有具有泛型方法的特征的动态分派_Generics_Rust - Fatal编程技术网

Generics 具有具有泛型方法的特征的动态分派

Generics 具有具有泛型方法的特征的动态分派,generics,rust,Generics,Rust,我有一个与所描述的用例相似的用例,但它有一点不同,我的解决方案不能用非泛型方法替换泛型方法。以下是我的代码: 使用serde::{de::反序列化拥有,序列化}; 使用serde_json; 特征序列化程序{ fn serialize_data&self,data:&V->Result,其中V:serialize; fn反序列化\u数据和self,seru数据:&str->选项,其中V:DeserializeOwned; } 结构JsonSerializer{ x:i32//我需要存储的某个成员

我有一个与所描述的用例相似的用例,但它有一点不同,我的解决方案不能用非泛型方法替换泛型方法。以下是我的代码:

使用serde::{de::反序列化拥有,序列化}; 使用serde_json; 特征序列化程序{ fn serialize_data&self,data:&V->Result,其中V:serialize; fn反序列化\u数据和self,seru数据:&str->选项,其中V:DeserializeOwned; } 结构JsonSerializer{ x:i32//我需要存储的某个成员 } impl JsonSerializer{ fn新->JsonSerializer{ JsonSerializer{x:1} } } JsonSerializer的impl序列化程序{ fn serialize_data&self,data:&V->Result其中V:serialize{ 将serde_json::与_stringdata匹配{ Okser_数据=>Okser_数据, Errerr=>Errerr.to_字符串 } } fn反序列化\u数据和self,seru数据:&str->选项,其中V:DeserializeOwned{ 匹配serde_json::from_strser_data.unwrap{ Okval=>Someval, 错误=>无 } } } //我可能想要更多的序列化程序对象,比如 //YamlSerizier、BincodeSerializer等等。。。 // ... 结构MyMainObject{ 序列化程序:框 } impl mymain对象{ fn新建->MyMain对象{ MyMainObject{serializer:Box::newJsonSerializer::new} } fn做点什么&自我{ println!{},self.serializer.serialize_数据&1; println!{},self.serializer.serialize_data&String::fromMY String; } } fn干线{ 让my_main_object=MyMainObject::new; 我的主要目标。做点什么; } 如前一个问题中所述,编译此代码时,我遇到一个错误,无法将trait`Serializer`转换为对象,因为它具有泛型方法:

   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
但在我的例子中,我希望这些方法保持通用性,以便可以序列化/反序列化任何类型的数据

因此,我的问题是如何保持动态分派模式并使其继续工作,这意味着我希望在MyMainObject中创建一个序列化器特征成员,我可以使用任何类型的序列化器对象Json、Yaml等进行初始化,然后在MyMainObject内调用Serializer.serialize_数据或Serializer.deserialize_数据

如果这是不可能的,你能建议的最佳替代方案是什么

编辑:

我需要一个能适用于不同类型序列化程序的解决方案,请列出这些序列化程序:


不能将非对象安全特性用于动态分派;对象安全规则专门针对阻止动态调度的内容

对于特定场景,有时会有变通办法。它们通常是复杂的。但对于serde来说,特别是板条箱,因为你不是第一个遇到这个问题的人。

注意 以下不是一个好的长期解决方案,它仅仅是一个变通方法。一种正确的方法是找出并实现一种方法,将bincode和serde_yaml与erased_serde协调起来。但如果你现在需要它工作,这里有

要点 基本上,您可以使用enum编写穷人的动态调度。它看起来或多或少像这样,我简化并省略了一些东西:

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),
        }
    }
}
这有严重的缺点,请参见下文,但它确实允许您将具有通用方法的东西打包到统一接口中

问题 这种方法的主要问题是很难向设置中添加新的序列化程序。对于Box,您所需要做的就是为某些东西导入序列化程序。在这里,您必须向枚举添加一个变量,并在所有相关方法中对其进行模式匹配。这在定义了序列化程序的板条箱中是不方便的,在其他板条箱中是不可能的。此外,向公共枚举中添加一个变体是一个突破性的变化,下游板条箱可能不太受欢迎。有一些方法可以在一定程度上改善这一点:

隐藏序列化程序 某些序列化程序公开是没有意义的。在它上面进行模式匹配的能力几乎没有什么好处,而且它是公共的,这限制了您可以对它做什么,而不会破坏下游的东西。通常的解决方案是将其放在不透明结构中,然后导出该结构,使枚举本身处于隐藏状态:

pub struct VisibleSerializer(SomeSerializer);
仍然使用该特性 不能在其他板条箱中使用额外的序列化程序扩展某个序列化程序。您可以继续在其上安装更多枚举层,这既不幸又丑陋,但是原始板条箱中的任何函数都不会接受这种构造。这是有帮助的:与其使serialize成为SomeSerializer的固有方法,不如为其实现Serializer,并使所有将使用SomeSerializer的函数成为泛型函数并接受T:Serializer。突然,所有下游板条箱都可以向设置中添加他们想要的序列化程序

特殊情况仅限特殊情况 用这种方式包装四个连载器中的三个以上有点可笑,更不用说尴尬了。但是,如果您想要使用的大多数序列化程序实际上都是擦除的,那么您可以使用一种全包枚举变量 在SomeSerializer中为它们设置t,并且仅为不兼容的变量设置单独的变量:

enum SomeSerializer {
    Whatever(Box<dyn erased_serde::Serializer>),
}

谢谢这个库也可以使用吗?@fx23我不知道,我自己也没用过。但是我看不出为什么它不应该。serde_json和serde_cbor包含一个实现serde::ser::Serializer特性的Serializer对象。被抹掉的塞德就靠这个了。但是bincode和serde_yaml没有这个对象,所以我不认为它们可以与erased_serdeI一起使用。我假设你是在erased_serde上打开bug的人。您可能对这些问题感兴趣:谢谢!显然这个库不能与bincode和serde_yaml一起使用。我知道这个设计在生锈的情况下不起作用。你能推荐一个尽可能接近我想要的替代设计吗?不久前在Rust的subreddit上公布的。我没有过多地研究它,但它可能是你想要的如果我理解正确,这个库可以实现trait对象的序列化/反序列化,但我要寻找的是一种在同一trait下实现不同序列化器的方法,或者至少是一种类似的设计哦,对了,应该仔细阅读这个问题,很抱歉嗯,总是有枚举方法,但这是大量的样板文件,扩展性不太好。请详细说明这个选项好吗?将所有序列化程序包装在一个枚举中,并使用枚举而不是长方体。主要的缺点是您正在失去trait对象提供的灵活性-每当您想要添加对新序列化程序的支持时,您需要向enum添加一个变体,并且如果您的库是一个变体,那么您的库用户就不能在没有类似解决方法的情况下插入自己的序列化程序。但它适用于一般事物。
enum SomeSerializer {
    Whatever(Box<dyn erased_serde::Serializer>),
}