17次重大发布

0.18.1 2024年5月14日
0.18.0 2023年7月23日
0.17.0 2023年4月18日
0.10.0 2023年3月31日

#315 in 编码

Download history 8/week @ 2024-04-22 21/week @ 2024-04-29 29/week @ 2024-05-06 201/week @ 2024-05-13 50/week @ 2024-05-20 41/week @ 2024-05-27 3/week @ 2024-06-03 12/week @ 2024-06-10 5/week @ 2024-06-24 3/week @ 2024-07-01 87/week @ 2024-07-29 10/week @ 2024-08-05

97 每月下载量
用于 2 crates

自定义许可证

110KB
2.5K SLoC

值中有何?

Rust的核心优势在于其能够在编译时提供内存安全和性能保证,这是通过其所有权和借用系统实现的。但对于Rust编写的应用程序,开发人员处理运行时定义的动态数据类型的能力仍然是一项重要任务,这可以增加应用程序的通用性,同时提供处理广泛用例的能力。

在Rust中创建的各种解释型DSL语言使用了为特定语言创建的运行时动态数据类型系统。出色的crate Serde也实现了允许在运行时处理动态数据类型的trait Value。但没有任何一种解决方案是Rust开发者完美的,因为它们要么专注于特定的DSL,要么处理JSON值。

rust_dynamic是一个crate,专为Rust语言创建,实现了帮助Rust开发者解决在运行时定义的动态数据类型特定问题的原语。目前,rust_dynamic支持以下数据类型

  • 整数,内部表示为i64
  • 浮点数,内部表示为f64
  • 布尔值
  • 字符串,内部表示为String
  • 对,作为动态值的对
  • 列表,作为动态值的列表
  • 二进制,作为u8值的向量
  • 纳秒级时间戳
  • 包裹在任何动态类型对象中的对象
  • 用于i64和f64的复数
  • 映射
  • 关联
  • NODATA
  • 错误
  • 作为TIMESTAMP->F64样本向量的度量

动态值被包裹并存储在Value结构中,可以将其转换回原始Rust值。

use rust_dynamic::value::Value;

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

动态类型对象的属性是什么?

  • 值对象可以包裹任何受支持类型的值,并且是功能纯的。一旦你分配了值,你就不能更改它,只有一个例外。任何更改尝试都将导致生成一个新的Value对象。但你可以推送一个新的值到List类型的对象中,而无需重新创建一个新对象。
  • 所有动态类型对象都是可哈希的,因此您可以将它们用作HasMap对象的键。
  • 所有动态类型对象都包含存储数据类型的完整信息。
  • 所有动态类型对象存储一个唯一的对象标识符。
  • 所有动态类型对象都可以序列化为JSON和二进制编码。
  • 您可以遍历所有动态类型对象。
  • 您可以创建附加到包装数据的属性。
  • 支持基本数学运算:加、减、乘、除,适用于FLOAT和INTEGER类型。
  • 支持字符串连接和乘法。

如何创建动态类型值

rust_dynamic包支持许多函数原语,这些函数原语将接受原始值并返回包装的动态对象。

函数名 描述
Value::from_float() 从f64浮点数创建动态对象
Value::from_float32() 从f32浮点数创建动态对象
Value::from_integer() 从i64浮点数创建动态对象
Value::from_integer32() 从i32浮点数创建动态对象
Value::from_bool() 从布尔值创建动态对象
Value::from_string() 从Rust String创建动态对象
Value::from_str() 从Rust &str创建动态对象
Value::from_bin() 从Vec创建类型为BINARY的动态对象
Value::pair() 从值对创建类型为PAIR的动态对象
Value::list() 创建类型为LIST的空动态对象
Value::from_list() 从Vec创建类型为LIST的动态对象并初始化它
Value::from_dict() 从HashMap创建类型为MAP的动态对象并初始化它
Value::dict() 创建类型为MAP的空动态对象
Value::none() 创建包装None值的动态对象
Value::nodata() 创建不包含数据的动态对象
Value::now() 返回包含从UNIX_EPOCH以来的当前纳秒数的类型为TIME的动态对象
Value::exit() 返回类型为EXIT的动态对象
Value::metrics() 返回包含128个样本的类型为METRICS的动态对象
Value::metrics_n(n) 返回包含n个样本的类型为METRICS的动态对象
Value::lambda() 返回类型为LAMBDA的动态对象
Value::to_lambda() 从Vector创建类型为LAMBDA的动态对象
Value::result() 返回类型为RESULT的动态对象
Value::to_result() 从Vector创建类型为RESULT的动态对象
Value::from_metrics() 从Metrics Vec创建动态对象
Value::from_complex_int() 从Complex创建动态对象
Value::from_complex_float() 从Complex创建动态对象
Value::conv() 将一个类型对象转换为另一个类型

存在一个泛型函数Value::from(),它会自动转换适当的数据类型,并返回对象或错误消息。

如何将Rust值从动态类型值转换

rust_dynamic支持许多转换函数,这些函数将尝试从包含动态类型值的对象中提取包装值。

函数名 描述
Value::cast_float() 从FLOAT对象返回f64数字
Value::cast_integer() 从INTEGER对象返回i64数字
Value::cast_bool() 从BOOL对象返回布尔值
Value::cast_string() 从STRING对象返回String
Value::cast_bin() 从BINARY对象返回Vec
Value::cast_list() 从LIST/PAIR对象返回Vec
Value::cast_result() 从RESULT对象返回Vec
Value::cast_lambda() 从LAMBDA对象返回Vec
Value::cast_pair() 从PAIR对象返回Vec
Value::cast_pair_x() 从PAIR X对象返回Value
Value::cast_pair_y() 从PAIR Y对象返回Value
Value::cast_metrics() 从METRICS对象返回Vec
Value::cast_fifo() 从FIFO对象返回下一个Value
Value::cast_queue() 从QUEUE对象返回下一个Value
Value::cast_dict() 从MAP、INFO、CONFIG、ASSOCIATION对象返回HashMap
Value::cast_complex_int() 从CINTEGER对象返回Complex
Value::cast_complex_float() 从CFLOAT对象返回Complex
Value::export_float() 从Value对象返回Vec

示例

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 raw_value = value.cast_integer().unwrap()

如何设置Value的属性

Value属性是存储在Value对象中的值的Vector。您可以为Value对象分配任意数量的Value对象作为属性。属性是可序列化和可包装的。

函数名 描述
Value.attr(Vec) 将值向量设置为Value对象的属性
Value.attr_add(Value) 将Value添加到Value对象属性列表的末尾。返回Value对象的副本
Value.attr_merge(Vec) 合并Value对象的值向量与当前属性。返回Value对象的副本
Value.attr_len() 返回Value对象的属性数量

示例

// 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()]);

如何序列化和反序列化动态类型值

目前rust_dynamic支持两种序列化格式:JSON和Bincode。

函数名 描述
Value::to_json() 返回动态类型值的JSON表示
Value::to_binary() 返回动态类型值的Bincode表示
Value::from_json() 接受包含动态类型对象JSON表示的字符串,并返回重新创建的Value对象
Value::from_binary() 接受包含动态类型对象Bincode表示的Vec,并返回重新创建的Value对象
Value.wrap() 返回包含对象Bincode表示的ENVELOPE对象
Value.unwrap() 如果对象是ENVELOPE,则解包封装对象的二进制表示,重新创建它并返回

示例

// 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();

动态类型值与serde_json crate的集成

Crate rust_dynamic能够将Value对象导出为serde_json::value::Value

函数名 描述
Value.as_json_value() 函数将Value对象转换为serde_json::value::Value

示例

// 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();

与动态类型值的功能操作

虽然rust_dynamic crate并不力求提供完整的动态值功能接口,但已经实现了一些特定于函数式编程的功能。

函数名 描述
Value.bind() 接受一个接受Value作为参数的函数的引用,该函数用传递的当前对象和返回的新Value对象调用
Value::bind_values() 接受一个接受两个Value作为参数的函数的引用,以及两个值。返回新的Value对象,它是值的程序性绑定结果
Value.fmap() 对列表或值中的每个元素执行函数,并返回新的Value
Value.map_float() 对列表或值中的每个FLOAT元素执行函数,并返回新的Value
Value.push() 要么向列表添加新值,要么返回新Value
Value.maybe() 接受一个函数,如果返回true,Value.maybe()返回值,如果false返回Value::none()
Value::left_right() 接受一个函数,如果返回true,则返回两个Value的引用。Value::left_right()返回第一个值的副本,如果函数返回true,则返回第二个值,否则返回第一个值
Value::freduce() 接受两个参数 - 函数,它接受两个值并返回一个值,以及初始值,通过将函数应用于所有元素将动态值减少到单个值

映射示例

// 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);

绑定示例

// 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);

maybe()示例

// 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);

减少示例

// 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

如何将动态类型对象用作HashMap键

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<Value, String> = HashMap::new();

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

如何迭代动态类型对象

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();
}

如何映射动态类型对象

在这个示例中,我们将f64::sin函数应用于动态类型对象的每个可迭代的值

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

如何使用动态类型对象进行数学运算

目前,仅支持FLOAT和INTEGER对象的数学运算。

// 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;

如何连接字符串对象

目前,只有STRING对象支持该操作。

// 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;

如何将String转换为Float

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

rust_dynamic的功能特性

创建和使用Applicative

您可以通过包装一个函数来创建一个Applicative,并将带有包装值的Value对象应用到Applicative上。例如

// First, as usual, we are defining function, that will be wrapped in Applicative
fn comp_sin(value: Value) -> Value {
    match value.data {
        Val::F64(f_val) => {
            return Value::from_float(f64::sin(f_val));
        }
        _ => 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());

依赖项

~5.5–8MB
~140K SLoC