uni*_*ion 4 generics traits rust serde
我正在尝试使用泛型从文件中反序列化结构,以便与 Swagger 生成的 API 一起使用。所以我把这个几乎可以工作,但我无法从“Owned”指针解压外部 Struct 对象,正如你在测试中看到的那样。
这可能是错误的策略,但问题是我有各种 yaml 文件,我想读入并反序列化暗示正确的结构来反序列化。我不想为每个 Struct 实现一个“readfile”函数,因为有很多。所以我试图让这个通用库工作,它应该反序列化为正确的结构,并与 Swagger API 一起使用。
这是非常接近的工作,但我似乎无法解开的Outer<ExternalStructA>逼到了ExternalStructA。
Owned(ExternalStructA { x: 1, y: 2 })
Owned(ExternalStructB { a: 1, b: 2 })
Run Code Online (Sandbox Code Playgroud)
lib.rs:
#[cfg(test)]
mod tests {
use crate::generics_yaml_deserializer::Outer;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Serialize, Deserialize)]
pub struct ExternalStructA {
x: u32,
y: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ExternalStructB {
a: u64,
b: u64,
}
#[test]
fn deserialize() {
let a = r#"---
ptr:
x: 1
y: 2
"#;
let b = r#"---
ptr:
a: 1
b: 2
"#;
let resulta: Outer<ExternalStructA> = serde_yaml::from_str(a).unwrap();
assert_eq!(1, resulta.ptr.x); // I can't seem to get into ptr ExternalStructA
let resultb: Outer<ExternalStructB> = serde_yaml::from_str(b).unwrap();
assert_eq!(1, resultb.ptr.a); // I can't seem to get into ptr ExternalStructB
}
}
mod generics_yaml_deserializer {
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::error::Error;
// empty holding struct which owns a owned ptr
#[derive(Deserialize, Debug)]
pub struct Outer<'a, T: 'a + ?Sized> {
#[serde(bound(deserialize = "Ptr<'a, T>: Deserialize<'de>"))]
pub ptr: Ptr<'a, T>,
}
#[derive(Debug)]
pub enum Ptr<'a, T: 'a + ?Sized> {
Ref(&'a T),
Owned(Box<T>),
}
impl<'de, 'a, T: 'a + ?Sized> Deserialize<'de> for Ptr<'a, T>
where
Box<T>: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Deserialize::deserialize(deserializer).map(Ptr::Owned)
}
}
}
Run Code Online (Sandbox Code Playgroud)
货物依赖:
serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0"
serde_yaml = "0.7.5"
serde_json = "1.0"
Run Code Online (Sandbox Code Playgroud)
更新:
我通过以下方式获得了 Struct 的部分成功:
let resulta: Outer<ExternalStructA> = serde_yaml::from_str(a).unwrap();
match resulta.ptr {
Ptr::Owned(e) => {assert_eq!(1, e.x);},
Ptr::Ref(e) => {println!("error")},
Ptr::Owned(_) => {println!("error")}
};
}
Run Code Online (Sandbox Code Playgroud)
但是当我尝试使用泛型类型将它实现为一个函数时,我得到了很多错误,主要是:
Owned(ExternalStructA { x: 1, y: 2 })
Owned(ExternalStructB { a: 1, b: 2 })
Run Code Online (Sandbox Code Playgroud)
添加到 mod generics_yaml_deserializer 的非工作代码
fn readfile<T>(filename: String) -> Result<Box<T>, Box<std::error::Error>> {
let f = std::fs::File::open(filename)?;
let config_data: Outer<T> = serde_yaml::from_reader(f)?;
Ok(Box::new(config_data))
}
fn readconfig<T>(filename: String) -> Result<Box<T>, &'static str> {
// read the config file
let config_data = readfile(filename);
match config_data {
Ok(e) => {
Ok(Box::new(e))
},
Err(_) => {
Err("nadda")
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 13
刚刚声明T是DeserializeOwned:
fn readfile<T: de::DeserializeOwned>(filename: String) -> Result<Box<T>, Box<std::error::Error>> {
let f = std::fs::File::open(filename)?;
let config_data: Outer<T> = serde_yaml::from_reader(f)?;
match config_data.ptr {
Ptr::Owned(data) => Ok(data),
_ => unimplemented!(),
}
}
Run Code Online (Sandbox Code Playgroud)
与 readconfig
当您使用像T这里这样的类型参数时:
fn readfile<T>(filename: String) -> Result<Box<T>, Box<std::error::Error>>;
Run Code Online (Sandbox Code Playgroud)
的具体类型T由调用者决定。编译器不只是查看所有可用的类型并猜测什么是有意义的。
首先,您需要告诉编译器T这里传递的任何内容实际上都可以工作。这意味着T在兼容的生命周期内限制为可反序列化的内容:
// filename should be &str here
fn readfile<'a, T: ?Sized>(filename: &str) -> Result<Box<Outer<'a, T>>, Box<std::error::Error>>
where
for<'de> T: Deserialize<'de> + 'a
{
let f = std::fs::File::open(filename)?;
let config_data: Outer<T> = serde_yaml::from_reader(f)?;
Ok(Box::new(config_data))
}
// filename should be &str here
fn readconfig<'a, T: ?Sized>(filename: &str) -> Result<Box<Outer<'a, T>>, &'static str>
where
for<'de> T: Deserialize<'de> + 'a
{
// read the config file
let config_data = readfile(filename);
match config_data {
Ok(e) => {
Ok(Box::new(*e)) // need to deref the Box before reboxing
},
Err(_) => {
Err("nadda")
}
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,当你调用它时,你需要告诉它一个具体的类型:
let result: Box<Outer<ExternalStructA>> = readconfig("config.yaml")?;
Run Code Online (Sandbox Code Playgroud)
如果输入无法解析为Box<Outer<ExternalStructA>>,这将失败,在这种情况下,您可以尝试将其解析为Box<Outer<ExternalStructB>>,也许使用Result::or_else.