Rust and serde deserializing using generics

前端 未结 2 1208
面向向阳花
面向向阳花 2021-01-21 07:47

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

相关标签:
2条回答
  • 2021-01-21 08:07

    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

    0 讨论(0)
  • 2021-01-21 08:21

    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.

    0 讨论(0)
提交回复
热议问题