2 个不稳定版本
0.2.0 | 2020 年 4 月 12 日 |
---|---|
0.1.0 | 2020 年 4 月 4 日 |
#5 in #first-order
3KB
Defunctionalize
Rust 的 proc-macro 去函数化。
注意:这还处于非常初级的阶段,我刚刚开始研究其机制。
去函数化是一种消除高阶函数,并用一阶函数代替的技术。
虽然这会降低一些灵活性,但也有一些好处。特别是,它允许序列化行为。
功能
- 将模块中的所有公共函数去函数化到一个枚举中。
- 方法中的额外参数将被移动到枚举中。
- 可以对模块应用
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)));
}
依赖项
~0–315KB