Add into_rc_any and push_value to Object

This commit introduces the `into_rc_any` and `push_value` methods to the
`Object` trait.

The `into_rc_any` method allows for efficient, zero-cost downcasting of
`Rc<Self>` to `Rc<dyn Any>`. This is a crucial step for enabling more
flexible dynamic dispatch and type manipulation within the VM.

The `push_value` method is implemented for series types (`ScalarSeries`,
`ValueSeries`, `RecordSeries`) to allow pushing values directly onto
them. This simplifies the `push` intrinsic function, removing the need
for manual downcasting within its implementation. The default
implementation for non-series objects panics, enforcing that only
mutable series support this operation.

Additionally, `ScalarValue` now includes a `from_value` method to
facilitate converting `Value` back into concrete scalar types. This
improves type safety and reduces boilerplate code when handling scalar
conversions.
This commit is contained in:
2026-03-22 19:04:32 +01:00
parent db443df932
commit f6c963cb41
5 changed files with 97 additions and 75 deletions
+60 -59
View File
@@ -77,22 +77,33 @@ impl<T> RingBuffer<T> {
/// (No pointers/heaps like String or Record).
pub trait ScalarValue: Copy + Debug + 'static {
fn to_value(self) -> Value;
/// Converts a dynamic `Value` back to this scalar type, returning `None` on mismatch.
fn from_value(v: Value) -> Option<Self>;
}
impl ScalarValue for f64 {
fn to_value(self) -> Value {
Value::Float(self)
}
fn from_value(v: Value) -> Option<Self> {
v.as_float()
}
}
impl ScalarValue for i64 {
fn to_value(self) -> Value {
Value::Int(self)
}
fn from_value(v: Value) -> Option<Self> {
v.as_int()
}
}
impl ScalarValue for bool {
fn to_value(self) -> Value {
Value::Bool(self)
}
fn from_value(v: Value) -> Option<Self> {
if let Value::Bool(b) = v { Some(b) } else { None }
}
}
/// A highly optimized, type-safe series for primitive values (Float, Int, Bool).
@@ -130,6 +141,14 @@ impl<T: ScalarValue> Object for ScalarSeries<T> {
fn as_any(&self) -> &dyn Any {
self
}
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn push_value(&self, value: Value) {
if let Some(v) = T::from_value(value) {
self.data.borrow_mut().push(v);
}
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
@@ -178,6 +197,12 @@ impl Object for ValueSeries {
fn as_any(&self) -> &dyn Any {
self
}
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn push_value(&self, value: Value) {
self.data.borrow_mut().push(value);
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
@@ -199,42 +224,15 @@ impl Series for ValueSeries {
// 3. RecordSeries (SoA - Struct of Arrays)
// ============================================================================
/// An interface allowing iteration over fields of a RecordSeries independently
/// of the exact type (Type Erasure for member arrays).
pub trait SeriesMember: Object + Series {
/// Pushes a dynamic Value into the series, performing the necessary downcast.
fn push_value(&self, value: Value);
}
/// Marker supertrait combining `Object` and `Series` for RecordSeries field buffers.
/// Ensures each field buffer can be stored as a typed series AND used as a dynamic object
/// (including `push_value` via the `Object` supertrait).
pub trait SeriesMember: Object + Series {}
impl SeriesMember for ScalarSeries<f64> {
fn push_value(&self, value: Value) {
if let Some(v) = value.as_float() {
self.data.borrow_mut().push(v);
}
}
}
impl SeriesMember for ScalarSeries<i64> {
fn push_value(&self, value: Value) {
if let Some(v) = value.as_int() {
self.data.borrow_mut().push(v);
}
}
}
impl SeriesMember for ScalarSeries<bool> {
fn push_value(&self, value: Value) {
if let Value::Bool(v) = value {
self.data.borrow_mut().push(v);
}
}
}
impl SeriesMember for ValueSeries {
fn push_value(&self, value: Value) {
self.data.borrow_mut().push(value);
}
}
impl SeriesMember for ScalarSeries<f64> {}
impl SeriesMember for ScalarSeries<i64> {}
impl SeriesMember for ScalarSeries<bool> {}
impl SeriesMember for ValueSeries {}
/// The "Struct of Arrays" implementation for Records (e.g., Ticks).
/// Instead of holding a large array of Records (AoS), this series
@@ -320,6 +318,20 @@ impl Object for RecordSeries {
fn as_any(&self) -> &dyn Any {
self
}
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn push_value(&self, value: Value) {
if let Value::Record(_, values) = value {
for (i, v) in values.iter().enumerate() {
if let Some(f) = self.fields.get(i) {
f.borrow().push_value(v.clone());
}
}
} else {
panic!("push to RecordSeries expects a record");
}
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
@@ -374,6 +386,9 @@ impl Object for SeriesView {
fn as_any(&self) -> &dyn Any {
self
}
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
@@ -420,6 +435,9 @@ impl<T: ScalarValue> Object for SharedSeries<T> {
fn as_any(&self) -> &dyn Any {
self
}
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
@@ -458,6 +476,9 @@ impl Object for SharedValueSeries {
fn as_any(&self) -> &dyn Any {
self
}
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
@@ -499,6 +520,9 @@ impl Object for SharedRecordSeries {
fn as_any(&self) -> &dyn Any {
self
}
fn into_rc_any(self: Rc<Self>) -> Rc<dyn Any> {
self
}
fn as_series(&self) -> Option<&dyn Series> {
Some(self)
}
@@ -598,30 +622,7 @@ pub fn register(env: &Environment) {
let (series_val, val) = (&args[0], &args[1]);
if let Value::Object(obj) = series_val {
let any = obj.as_any();
// Polymorphic push logic
if let Some(rs) = any.downcast_ref::<RecordSeries>() {
if let Value::Record(_, values) = val {
for (i, v) in values.iter().enumerate() {
if let Some(f) = rs.fields.get(i) {
f.borrow().push_value(v.clone());
}
}
} else {
panic!("push to RecordSeries expects a record");
}
} else if let Some(s) = any.downcast_ref::<ScalarSeries<f64>>() {
s.push_value(val.clone());
} else if let Some(s) = any.downcast_ref::<ScalarSeries<i64>>() {
s.push_value(val.clone());
} else if let Some(s) = any.downcast_ref::<ScalarSeries<bool>>() {
s.push_value(val.clone());
} else if let Some(s) = any.downcast_ref::<ValueSeries>() {
s.push_value(val.clone());
} else {
panic!("Object is not a mutable series");
}
obj.push_value(val.clone());
return Value::Void;
}
panic!("push expects a series object as the first argument")