I am trying to use generics to deserialize structs from file for use with a Swagger generated API. So I have hacked together this which almost works, but I am unable to unpack t
just declare that T
is 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!(),
}
}
same with readconfig
When you use a type parameter like T
here:
fn readfile<T>(filename: String) -> Result<Box<T>, Box<std::error::Error>>;
The concrete type of T
is determined by the caller. The compiler doesn't just look at all available type and make a guess at what makes sense.
First of all, you need to tell the compiler that any T
passed here will actually work. That means constraining T
to be something that is deserializable, within compatible lifetimes:
// 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")
}
}
}
Next, when you call this, you need to tell it a concrete type:
let result: Box<Outer<ExternalStructA>> = readconfig("config.yaml")?;
This will fail if the input cannot be parsed into a Box<Outer<ExternalStructA>>
, in which case you can try parsing it to Box<Outer<ExternalStructB>>
, perhaps using Result::or_else
.