1个不稳定版本
0.2.0 | 2020年4月12日 |
---|
#46 在 #u32
10KB
226 行
Defunctionalize
Rust的进程宏解函数化。
注意:这还处于非常初级的阶段,我刚开始探索其机制。
解函数化是一种通过消除高阶函数并将其替换为第一阶函数的技术。
尽管这减少了灵活性,但也有某些好处。特别是,它允许序列化行为。
功能
- 将模块中所有公共函数解函数化到一个枚举中。
- 方法中的额外参数会被移动到枚举中。
- 可以对模块应用
derive
以推导出结果枚举。
用法
-
将
defunctionalize
属性应用于一个模块。这个属性接受一个函数签名,这是你打算解函数化的函数的签名。这个签名需要命名参数,并支持泛型。通常,生成的枚举类型的名称是从模块的名称计算得出的,但可以通过在签名中添加一个名称来使用该名称。请注意,在这种情况下,名称不会自动转换为驼峰式。
// Basic usage #[defunctionalize(fn(lhs: usize, rhs: usize) -> usize)] mod defunc_a {} // Generics are supported #[defunctionalize(fn<T: std::ops::Add>(lhs: T, rhs: T) -> T::Output)] mod defunc_b {} // The function name will override the module name #[defunctionalize(fn DefuncC(lhs: usize, rhs: usize) -> usize)] mod hello {} // The generated item will be `enum DefuncC { ... }` // Where clauses are supported too #[defunctionalize(fn<T>(lhs: T, rhs: T) -> T::Output where T: Add)] mod defunc_d {}
-
你也可以将
derive
属性应用于此模块。语法与通常一样,并且将派生特性应用于生成的枚举。对于枚举案例字段类型的限制将适用。 -
在此模块中定义
pub
函数。它们将被转换为枚举案例。非pub
函数可以定义为辅助函数,但不会被添加为枚举案例。这些函数必须至少具有
defunctionalize
属性中定义的签名,但也可能具有在列出的参数之前的额外参数。返回类型必须匹配。函数的名称被转换为驼峰式以成为枚举案例的名称。
#[defunctionalize(fn(lhs: usize, rhs: usize) -> usize)] mod defunc_a { // Helper won't get defunctionalized fn helper(s: String) -> usize { s.len() } // This signature matches pub fn add(lhs: usize, rhs: usize) -> usize { lhs + rhs } // Extra parameters at the front get converted to enum fields pub fn add_plus_n(n: usize, lhs: usize, rhs: usize) -> usize { lhs + rhs } // This is not supported: the signature is wrong pub fn bad(x: String, y: usize) -> &'static str { "what" } }
示例
最基本的使用方法如下
use defunctionalize::defunctionalize;
// Apply the defunctionalize attribute to a module. This attribute takes one parameter
// which is the signature of the generated `call` method.
//
// In this example, we are doing a mathematical operation, so our defunctionalized
// function takes two integers, and returns the result of the operation.
#[defunctionalize(fn(x: u32, y: u32) -> u32)]
pub mod operation {
// All public functions in this module are then defunctionalized. Private functions
// do not get defunctionalized, but they may be used internally.
pub fn add(x: u32, y: u32) -> u32 { x + y }
pub fn sub(x: u32, y: u32) -> u32 { x - y }
pub fn mult(x: u32, y: u32) -> u32 { x * y }
pub fn div(x: u32, y: u32) -> u32 { x / y }
pub fn rem(x: u32, y: u32) -> u32 { x % y }
}
// The above module will be compiled into an enum, with a case for each
// public function:
//
// pub enum Operation {
// Add,
// Sub,
// Mult,
// Div,
// Rem,
// }
//
// The name of the module is converted to CamelCase to become the name of the enum.
// Similarly, the name of the functions are converted to CamelCase to become the case
// names
// Values of the resulting enum can then be passed to functions. They contain a `call`
// method, which uses the signature provided in the defunctionalize attribute, which
// calls the corresponding method
fn perform_operation(operation: Operation) -> u32 {
operation.call(6, 7)
}
// And here we use the defunctionalized module to compute 6 * 7 = 42!
fn main() {
assert_eq!(42, perform_operation(Operation::Mult));
}
当然,这不是此技术功能的全部!在这里,我们将在此基础上扩展以展示两个更有用的功能
use defunctionalize::defunctionalize;
// First, notice that the function signature here has had one argument removed!
#[defunctionalize(fn(rhs: u32) -> u32)]
// Next, we put some derives *on the module*. These will get moved to the enum. This
// is most useful for serializing your defunctionalized functions.
#[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub mod operation {
// Back to the first point now, notice that these functions have not changed! Where
// do they get the extra parameter though?
pub fn add(x: u32, y: u32) -> u32 { x + y }
pub fn sub(x: u32, y: u32) -> u32 { x - y }
pub fn mult(x: u32, y: u32) -> u32 { x * y }
pub fn div(x: u32, y: u32) -> u32 { x / y }
pub fn rem(x: u32, y: u32) -> u32 { x % y }
}
// The extra parameter got moved to the enum! Now we can essentially serialize an
// entire function call.
//
// #[derive(Copy, Clone, Debug, serde::Serialize, serde::Deserialize)]
// pub enum Operation {
// Add(u32),
// Sub(u32),
// Mult(u32),
// Div(u32),
// Rem(u32),
// }
// Now we only need to provide one parameter to the `call` method:
fn perform_operation(operation: Operation) -> u32 {
operation.call(7)
}
// And here we use the defunctionalized module to compute 49 - 7 = 42!
fn main() {
assert_eq!(42, perform_operation(Operation::Sub(49)));
}
依赖关系
~2MB
~43K SLoC