What in the Value ?

Rust's core strength is its ability to provide memory safety and performance guarantees at compile-time, which is achieved through its ownership and borrowing system. But ability for the developer to handle a dynamic data types defined at run-time for the applications written in Rust is still important endeavor and it is improving versatility of the application while providing an ability to handle a wide range of use cases.

Various interpreted DSL languages created in Rust uses run-time dynamic data type system created for the specific language. Excellent crate Serde also implements trait Value that's allow to handle dynamic datatypes in run-time. But none of those solutions is not perfect for a Rust developers as they ether focus on specific DSL or handling JSON values.

rustdynamic is a crate, created for Rust language, implementing primitives that will be helping to Rust developer with the specific issue of handling dynamic data types defined at run-time. Currently, rustdynamic supports following data types:

Dynamic values are wrapped and stored inside a Value structure and could be cast-able back into original Rust value.

```rust use rust_dynamic::value::Value;

let mut value = Value::from(42).unwrap(); println!("Type of the stored value is {}", &value.typename()); println!("Dynamic value of the value is {:?}", &value.castinteger());

```

What are the properties of dynamically-typed objects

How to create a dynamically-typed values

rust_dynamic crate supports a number of function-primitives that will take a raw value and return a wrapped Dynamic object.

| Function name | Description | |---|---| | Value::fromfloat() | Create dynamic object from f64 float number | | Value::fromfloat32() | Create dynamic object from f32 float number | | Value::frominteger() | Create dynamic object from i64 float number | | Value::frominteger32() | Create dynamic object from i32 float number | | Value::frombool() | Create dynamic object from boolean value | | Value::fromstring() | Create dynamic object from Rust String | | Value::fromstr() | Create dynamic object from Rust &str | | Value::frombin() | Create dynamic object of type BINARY from Vec | | Value::pair() | Create dynamic object of type PAIR from the pair of values | | Value::list() | Create empty dynamic object of type LIST | | Value::fromlist() | Create dynamic object of type LIST and initialize it from Vec | | Value::fromdict() | Create dynamic object of type MAP and initialize it from HashMap | | Value::dict() | Create dynamic empty object of type MAP | | Value::none() | Create dynamic object that wraps value of None | | Value::nodata() | Create dynamic object that contains no data | | Value::now() | Return dynamic object of type TIME containing current number of nanosecods from UNIXEPOCH | | Value::exit() | Return dynamic object of type EXIT | | Value::metrics() | Return dynamic object of type METRICS for 128 samples | | Value::metricsn(n) | Return dynamic object of type METRICS for n samples | | Value::frommetrics() | Return dynamic object created from Vec of Metrics | | Value::fromcomplexint() | Return dynamic object created from Complex | | Value::fromcomplex_float() | Return dynamic object created from Complex | | Value::conv() | Converting of the object of one type to another |

There are generic function Value::from() that will automatically cast proper data type and ether return object or error message.

How to cast Rust value from dynamically-typed values

rust_dynamic supports a number of casting functions that will try to extract wrapped value from the object that holds dynamically-typed value.

| Function name | Description | |---|---| | Value::castfloat() | Return f64 number from FLOAT object | | Value::castinteger() | Return i64 number from INTEGER object | | Value::castbool() | Return boolean from BOOL object | | Value::caststring() | Return String from STRING object | | Value::castbin() | Return Vec from BINARY object | | Value::castlist() | Return Vec from LIST object | | Value::castmetrics() | Return Vec from METRICS object | | Value::castdict() | Return HashMap from MAP,INFO,CONFIG,ASSOCIATION objects | | Value::castcomplexint() | Return Complex from CINTEGER object | | Value::castcomplexfloat() | Return Complex from CFLOAT object | | Value::export_float() | Return Vec from Value object |

Example:

```rust use rust_dynamic::value::Value;

// First we create a dynamically-typed value from a raw static value let mut value = Value::from(42).unwrap();

// Then we can cast raw value back from the dynamic object let rawvalue = value.castinteger().unwrap() ```

How to set attributes to the Value

Value attributes is a Vector of the values that stored in the Value object. You can assign any number of the Value objects stored as attributes of the Value object. Attributes are serailize-able and wrap-able.

| Function name | Description | |---|---| | Value.attr(Vec) | Set vector of values as an attributes of the Value object | | Value.attradd(Value) | Add a Value to the end of attributes list of the Value object. Returns duplicate of Value object | | Value.attrmerge(Vec) | Merge Vector of Values and current attributes of the Value object. Returns duplicate of Value object | | Value.attr_len() | Return number of attributes of the Value object |

Example:

rust // Create object let v = Value::from(42 as i64).unwrap() // Set the attributes of the object .attr(vec![Value::from(41.0 as f64).unwrap()]) // And merge some extra attributes. The first attribute now have a value of 42.0 .attr_merge(vec![Value::from(42.0 as f64).unwrap()]);

How to serialize and deserialize dynamically-typed values

There are two serialization formats that rust_dynamic presently supports: JSON and Bincode.

| Function name | Description | |---|---| | Value::tojson() | Return JSON representation of dynamically-typed value | | Value::tobinary() | Return Bincode representation of dynamically-typed value | | Value::fromjson() | Takes string containing JSON representation of the dynamically-typed object and return re-created Value object | | Value::frombinary() | Takes Vec containing Bincode representation of the dynamically-typed object and return re-created Value object | | Value.wrap() | Return a ENVELOPE object containing a Bincode representation of object | | Value.unwrap() | If object is ENVELOPE unpack binary representation of enclosed object, recreate it and return |

Example:

rust // This call will create a new dynamic value let mut data = Value::from(42 as i64).unwrap(); // This call will serialize object to Bincode format let bin_out = data.to_binary().unwrap(); // This will re-create an object from it's Bincode representation let mut data2 = Value::from_binary(bin_out).unwrap();

Integration of dynamically-typed values with serde_json crate.

Crate rustdynamic is capable to export Value object to serdejson::value::Value

| Function name | Description | |---|---| | Value.asjsonvalue() | Function converts Value object to serde_json::value::Value |

Example:

rust // This call will create a Value object of type PAIR and on-the-fly exports it // to the serde_json::value::Value object that can be processed as any other Value object created by serde_json crate. let v = Value::pair(Value::from_int(1), Value::from_int(2)).as_json_value();

Functional operations with dynamically-typed values

While rust_dynamic crate is not strive to provide a full-featured functional interface to the dynamic values, some functionality that specific to a Functional programming has been implemented.

| Function name | Description | |---|---| | Value.bind() | Takes a reference to a function that accepts Value as a parameter, that function is called with passed current object and new Value object returned | | Value::bindvalues() | Takes a reference to a function that accepts two Values as a parameter, and two values. New Value object returned that is result of programmatic binding of the values | | Value.fmap() | Execute function to each element of the LIST or to the value and return new Value | | Value.mapfloat() | Execute function to each FLOAT element of the LIST or to the value and return new Value | | Value.push() | Ether add a new value to the list, or return a new Value | | Value.maybe() | Takes a function which is if returns true, Value.maybe() returns value, if false Value::none() | | Value::leftright() | Takes a function which is if returns true, and a references on two Values. Value::leftright() returns clone of first value, if function return true, second othewise | | Value::freduce() | Takes two parameters - function which takes two values and returning a single value and initial value, reducing dynamic value to a single value by applying function to all elements |

Example of mapping:

rust // First, we define a function which will cast value of f64 and apply f64.sin() operation fn comp_sin(value: Value) -> Value { match value.data { Val::F64(f_val) => { return Value::from_float(f64::sin(f_val)); } _ => return value, } } // Then we create a single value object (map could be epplied to ether list or single value object) let mut v = Value::from(42.0).unwrap(); // Now v contains result of computation v = v.map_value(comp_sin);

Example of binding

rust // First, let's create a "binding function" which will takes two Value objects and return a new Value // In our example, simple sum() will be performed fn sum_of_values(v1: Value, v2: Value) -> Value { v1 + v2 } // Then let's create two values let v1 = Value::from(41.0 as f64).unwrap(); let v2 = Value::from(1.0 as f64).unwrap(); // Value referred as _s_ now contains value of 42.0 let s = Value::bind_values(sum_of_values, v1, v2);

Example of maybe()

rust // First, we create a function that will check if v is float and is 42 fn if_value_is_42(v: &Value) -> bool { if v.cast_float().unwrap() == 42.0 { return true; } false } // And because it is, v is object of Value(42.0) // Otherwise it would be Value::none() let v = Value::from(42.0 as f64).unwrap() .maybe(if_value_is_42);

Example of reducing

rust // First, we shall create a list of the values let mut v = Value::from_list( vec![Value::from_float(41.0 as f64), Value::from_float(1.0 as f64)]); // Then reduce this list applying function that sums "accumulator" and current value v = v.freduce( |x: Value,y: Value| -> Value { x+y }, Value::from_float(0.0 as f64)); // This function shall return a single FLOAT value wrapping number 42.0

How to use dynamically-typed objects as HashMap keys

```rust use std::collections::HashMap;

// This call will create a key object. Key object can be of any supported type let key = Value::from(42.0 as f64).unwrap();

// Then we are creating a HashMap let mut h: HashMap = HashMap::new();

// and store a key->value association h.insert(key, "value".to_string()); ```

How to iterate over dynamically-typed objects

rust let mut c = 0.0; // Let's create a object of LIST type and push two elements into list let v = Value::list() .push(Value::from(1.0 as f64).unwrap()) .push(Value::from(41.0 as f64).unwrap()); // We can iterate over dynamically-typed object for i in v { c += i.cast_float().unwrap(); }

How to map over dynamically-typed objects

In this example we are applying f64::sin function to all iterable values of the dynamically-typed object

rust let mut v = Value::from(42.0).unwrap(); v = v.map_float(f64::sin);

How to use a math operations with dynamically-typed objects

At this moment, only FLOAT and INTEGER objects supported math operations.

rust // Let's create x and y objects both of FLOAT type let mut x = Value::from(1.0 as f64).unwrap(); let y = Value::from(41.0 as f64).unwrap(); // And perform math operation as usual x = x + y;

How to concatenate string objects

At this moment, only STRING object supports that operation.

rust // Let's create x and y objects both of STRING type let mut x = Value::from("Hello").unwrap(); // Then perform operation as usual, x shall be a object of STRING type containing string "Hello world" let y = Value::from(" world").unwrap(); x = x + y;

How to convert from String to Float

rust // This call returns the object of type FLOAT let val = Value::from("42").unwrap().conv(FLOAT).unwrap();

Functional features of rust_dynamic

Create and use of Applicative

You can create an Applicative that is wrapping a function and apply a Value object with wrapped value to Applicative. For example:

```rust // First, as usual, we are defining function, that will be wrapped in Applicative fn compsin(value: Value) -> Value { match value.data { Val::F64(fval) => { return Value::fromfloat(f64::sin(fval)); } _ => return value, } }

// Create applicative and wrap a function let sin = Applicative::new(comp_sin); // Then apply a Value to a wrapped function let res = sin.apply(Value::from(42.0).unwrap()); ```