3 个不稳定版本
0.2.0 | 2020 年 8 月 27 日 |
---|---|
0.1.1 | 2020 年 8 月 26 日 |
0.1.0 | 2020 年 8 月 26 日 |
在 开发工具 中排名 #2219
12KB
123 行
Constany 是一个 Rust 宏,允许任何函数返回常量结果。
Rust 中的常量函数是一组在编译时评估其结果的函数。它可以显著减少生成的二进制文件大小并提高性能。然而,由于技术和逻辑限制,某些表达式不能作为常量函数进行评估。例如
fn main() {
println!("{}", add_one_to_six());
}
const fn add_one_to_six() -> String {
let mut a = 1;
for b in 1..7 { // error[E0744]: `for` is not allowed in a `const fn`
a += b;
}
return a.to_string();
}
Constany 使用一种变通方法:通过使用 Constany,函数和主函数将被编译两次。函数的值将在第一次编译时评估,然后在第二次编译时将值封装为常量函数。
Cargo.toml:
[features]
stage_one = ["constany_stage_one"]
stage_two = ["constany_stage_two"]
[dependencies]
constany_stage_one = {version = "0.1", optional = true}
constany_stage_two = {version = "0.1", optional = true}
constany_blank = {version = "1"}
main.rs:
#[cfg(any(
not(any(feature = "stage_one", feature = "stage_two")),
all(feature = "stage_two", feature = "stage_one")
))]
use constany_blank as constany; // This line is for grammar checkers that enable all feature / disable all feature. If you do not have a checker, you can delete those lines safely.
#[cfg(all(feature = "stage_one", not(feature = "stage_two")))]
use constany_stage_one as constany;
#[cfg(all(feature = "stage_two", not(feature = "stage_one")))]
use constany_stage_two as constany;
#[constany::main_fn("function_evaled_at_compile_time")]
fn main() {
println!("Hello, world!");
function_evaled_at_compile_time();
}
#[constany::const_fn]
fn function_evaled_at_compile_time() -> i32 {
let mut a = 1;
let b = 5;
for _ in 0..b {
a += 1; // For loop is not allowed in `const fn`
}
a
}
当你需要构建函数时,这样做
$ cargo run --featues stage_one
$ cargo build --features stage_two // If you want to run the command instead, use `cargo run`
并且你的函数将被解释为常量函数。
多个常量函数
具有多个常量函数也适用,你只需确保你想设置为常量的每个函数都标记为 const_fn
,并且函数名称位于 main_fn
内
#[cfg(any(
not(any(feature = "stage_one", feature = "stage_two")),
all(feature = "stage_two", feature = "stage_one")
))]
use constany_blank as constany;
#[cfg(all(feature = "stage_one", not(feature = "stage_two")))]
use constany_stage_one as constany;
#[cfg(all(feature = "stage_two", not(feature = "stage_one")))]
use constany_stage_two as constany;
#[constany::main_fn("function_evaled_at_compile_time", "function_evaled_at_compile_time")]
fn main() {
println!("Hello, world!");
function_evaled_at_compile_time();
function_evaled_at_compile_time_2();
}
#[constany::const_fn]
fn function_evaled_at_compile_time() -> i32 {
let mut a = 1;
let b = 5;
for _ in 0..b {
a += 1;
}
a
}
#[constany::const_fn]
fn function_evaled_at_compile_time_2() -> i32 {
let mut a = 1;
let b = 100;
for _ in 0..b {
a += 1;
}
a
}
具有非原始结果的函数
返回非原始结果(可能是 struct
或 enum
)的函数既麻烦又容易出错。最优雅的方法是在第一阶段使用 lazy_static
并默认为避免编译器警告,然后在第二阶段使用常量值函数
#[cfg(feature = "stage_two")]
const ABC: String = constant_function().to_string();
#[cfg(not(feature = "stage_two"))]
lazy_static::lazy_static! {
const ref ABC: String = constant_function().to_string();
}
然而,这对于大多数非原始类型来说不起作用,因为它们的构造函数通常不是 static
。
有两种变通方法:一个是 debug + pub
解决方案,另一个是 memop
解决方案。
《debug + pub》解决方案首先使用debug
trait打印结构,然后使用pub
trait重建它。这种解决方案可以不使用unsafe
代码重建结构。然而,这要求结构实现Debug
。当前的实现还要求结构没有paths
,例如std::string::String
(如果标识符中有::
,则此解决方案可能无法正常工作)。
memop
解决方案直接转换内存。这种解决方案可以重建任何结构,但请注意,这种方法是unsafe
且非常危险的。生成的函数将是fn
而不是const_fn
,因为在const
中不允许内存分配,尽管内存本身在函数中是硬编码的。
依赖项
~1.5MB
~36K SLoC