I am implementing Rust's TryFrom
trait for a serde_json::value::Value
reference, and a function that generically converts a Vec<Value>
into a Vec<T>
, where T
implements TryFrom<&Value>
. Because I must specify a lifetime for the Value
reference in my function, I cannot return the result of T::try_from
(borrowed value is dropped).
I have tried an alternate approach that works; creating my own trait that is similar to TryFrom
but without a generic. This works, but I don't understand why I can't use TryFrom
and a generic, since the trait already exists.
My generic code which throws a compile-time error:
impl TryFrom<&Value> for Channel {
type Error = &'static str;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
let title = value.get("title").ok_or("couldn't find title property in json")?
.as_str().ok_or("title was not a string")?;
let name = value.get("user_name").ok_or("couldn't find user_name property in json")?
.as_str().ok_or("user_name was not a string")?;
Ok(Channel {
title: String::from(title),
user_name: String::from(name)
})
}
}
// I must add a lifetime below. Please ignore the self reference.
fn get_many_generic<'a, T: TryFrom<&'a Value>>(&self, url_str: &str) -> Result<Vec<T>, Box<dyn std::error::Error>> {
// perform_get returns a serde_json::Value
let mut value = &self.perform_get(url_str)?;
if let Value::Object(map) = value {
value = map.get("data").ok_or("found a map without a 'data' property when expecting an array")?;
}
if let Value::Array(vec) = value {
Ok(vec.iter()
.filter_map(|item|
match T::try_from(item) {
Ok(model) => Some(model),
Err(e) => {
println!("Could not deserialize value {}", item);
None
}
}
).collect())
}
else {
Err(Box::new(
Error::new(format!("Expected array from {}, but didn't receive one.", url_str))
))
}
}
My code which works:
pub trait TryFromValue where Self: std::marker::Sized {
fn try_from_value(value: &Value) -> Result<Self, Box<dyn Error>>;
}
impl TryFromValue for Channel {
fn try_from_value(value: &Value) -> Result<Channel, Box<dyn Error>> {
let title = value.get("title").ok_or("couldn't find title property in json")?
.as_str().ok_or("title was not a string")?;
let name = value.get("user_name").ok_or("couldn't find user_name property in json")?
.as_str().ok_or("user_name was not a string")?;
Ok(Channel {
title: String::from(title),
user_name: String::from(name)
})
}
}
fn get_many<T: TryFromValue>(&self, url_str: &str) -> Result<Vec<T>, Box<dyn std::error::Error>> {
// perform_get returns a serde_json::Value
let mut value = &self.perform_get(url_str)?;
if let Value::Object(map) = value {
value = map.get("data").ok_or("found a map without a 'data' property when expecting an array")?;
}
if let Value::Array(vec) = value {
Ok(vec.iter()
.filter_map(|item|
match T::try_from_value(item) {
Ok(model) => Some(model),
Err(e) => {
println!("Could not deserialize value {}. Error {}", item, e);
None
}
}
).collect())
}
else {
Err(Box::new(
Error::new(format!("Expected array from {}, but didn't receive one.", url_str))
))
}
}
Why does this approach work, but the first code example fails?