-
Notifications
You must be signed in to change notification settings - Fork 259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support coercing structured Values into primitive types #379
Conversation
@@ -51,8 +51,8 @@ kv_unstable_sval = ["kv_unstable", "sval/fmt"] | |||
[dependencies] | |||
cfg-if = "0.1.2" | |||
serde = { version = "1.0", optional = true, default-features = false } | |||
sval = { version = "0.4.2", optional = true, default-features = false } | |||
sval = { version = "0.5", optional = true, default-features = false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This minor bump is fine because this is all unstable.
5eb722e
to
6743f4a
Compare
I've refactored the module layout of |
r? @yoshuawuyts |
I don't love the TryInto?This also got me thinking: how viable would it be to replace these inherent methods with I'm unsure how good of an idea this is, but this could be implemented with #![feature(try_trait)]
struct Foo { s: Option<String> }
impl std::convert::TryInto<String> for &Foo {
type Error = std::option::NoneError;
fn try_into(self) -> Result<String, Self::Error> {
let s = self.s.as_ref()?;
Ok(s.clone())
}
} Though admittedly I don't love this either, as usage could be a bit hard and it relies on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a note with some ideas that popped up while reading this; but that shouldn't be a blocker for merging this. This seems useful overall.
Thanks for the feedback @yoshuawuyts! Yeh, the impl<‘v> Value<‘v> {
fn cast<T>(&self) -> Option<T>
where
Self: Into<Option<T>>;
}
impl<‘v> From<Value<‘v>> for Option<&’v str>>;
...
let s: &str = record.key_values().get(key).unwrap().cast().unwrap(); What do you think? We should be careful about implementing foreign traits for foreign types, but it could be a workable approach that also gives us a bunch of useful |
I've refactored the impl<'v> Value<'v> {
pub fn cast<T>(self) -> Result<T, Self>
where
Self: Into<Option<T>>;
pub fn cast_ref<'a, T>(&'a self) -> Option<T>
where
&'a Self: Into<Option<T>>;
} which allows you to call: let s: &str = value.cast().unwrap(); or: let s: &str = value.cast_ref().unwrap(); The So why let v = Value::from(42i32);
let b: Box<dyn Any> = Box::new(42i32);
// This behaviour is the same
assert!(v.cast_ref::<i32>().is_some());
assert!(b.downcast_ref::<i32>().is_some());
// This is different
assert!(v.cast_ref::<u64>().is_some());
assert!(b.downcast_ref::<u64>().is_none()); I think this is much more useful for logging cases where you aren't necessarily in control of what concrete values you're given, but need a way to check if it looks something like what you're expecting (attempting to cast a timestamp to a So we can think of |
So I followed the So I've gone back to using inherent methods, with a let s = Value::from_debug(&42u64).to_u32().expect("invalid value"); which makes the casting API more reliable when you want to try get a specific kind of value out. I think we should definitely revisit the open trait approach in the future though, and it's something that could pair nicely with the discoverable |
59c1ab2
to
da1c868
Compare
Thanks for taking the time to review this and providing feedback @yoshuawuyts! |
revert to to_ inherent methods support downcasting erased values
da1c868
to
66af8f2
Compare
@KodrAus oh I love that API! I def think you made the right call here; glad it ended up the way it did! |
For #328
This PR adds methods to
kv::Value
so you can attempt to coerce it into primitive types:I went with the
to_
prefix instead of theas_
prefix thatserde_json::Value
has, because these aren't necessarily trivial conversions. The above example is, but something likeValue::from_fill(v)
may end up needing to call arbitrary user code to produce the value.Doing this means our
kv
support is enough that we could theoretically replace the existingRecord
type with something like this instead:We don't want to do that, but I think it's a good feature watermark to aim for so folks writing log frameworks can use the key values to store their own arbitrary data.