-
Notifications
You must be signed in to change notification settings - Fork 208
Description
Disclaimer:
I'm still getting acquainted with the codebase, and the issue history. This is not an issue per se, but is mainly intended as a question or discussion starter. Please let me know if there is a better place for that, and I'll happily migrate there.
I noticed a couple of issues that might share a similar underlying cause:
- Partial impl<'a> From<&'a Value> for ValueRef<'a> causes panics #533
- Implementing the list, struct, map types fully #394
While looking into this, I noticed that in the implementations of the Value and ValueRef enums there are similar todo! and unimplemented! macros. It also seems like the current dynamic type design might allow some unintended behavior.
For example, the following compiles:
#[cfg(test)]
mod test {
use crate::types::Value;
#[test]
fn should_this_compile() {
let list = Value::List(
vec![
false.into(),
1u8.into(),
2u32.into(),
3f64.into(),
]
);
// It compiles !
}
}This allows constructing a Value::List containing heterogeneous element types. I was wondering whether this is expected, or if this is something we’d prefer to prevent at the type level.
Has there been any prior exploration into introducing something like a ValueKind trait to model value categories more strictly?
Very roughly, I was thinking along the lines of:
use crate::types::Type;
pub trait ValueKind {
type Inner;
fn data_type() -> Type;
fn inner(&self) -> &Self::Inner;
fn inner_mut(&mut self) -> &mut Self::Inner;
fn into_inner(self) -> Self::Inner;
}With concrete wrappers such as:
pub struct Boolean(bool);
impl ValueKind for Boolean {
type Inner = bool;
fn data_type() -> Type {
Type::Boolean
}
fn inner(&self) -> &Self::Inner {
self.0;
}
fn inner_mut(&mut self) -> &mut Self::Inner {
&mut self.0
}
fn into_inner(self) -> Self::Inner {
self.0
}
}
pub struct List<T>(Vec<T>);
impl<T> ValueKind for List<T>
where
T: ValueKind,
{
type Inner = Vec<T>;
fn data_type() -> Type {
Type::List(Box::new(T::data_type()))
}
//...
}This would enforce homogeneous lists at the type level. The same would happen for the Map and Array types (Struct and Enum might be a bit harder to tackle). It would also allow data_type() to recursively construct the full Type
I’m very curious whether:
- This flexibility in Value::List is intentional
- There were prior discussions a similar implementation like this
I’d be happy to explore this further if there’s interest.