如何在Rust中使用非常量初始值设定项初始化不可变全局值?

如何在Rust中使用非常量初始值设定项初始化不可变全局值?,rust,static,initialization,Rust,Static,Initialization,我试图得到一个在运行时只初始化一次的变量。 在C/C++中,static将是我要查找的关键字,而在Rust中,它必须由常量初始化 静态mut是不安全的,我可以理解为什么,但它不能从概念上捕获我想要的,我想要一个不可变的变量 以Tribonaci函数的这个简单示例为例: static sqrt_33_mul_3:f64=3.0*33.0f64.powf(0.5); 静态摩擦系数:f64=1.0 +(19.0-sqrt_33_mul_3).powf(1.0/3.0) +(19.0+sqrt_33_m

我试图得到一个在运行时只初始化一次的变量。 在C/C++
中,static
将是我要查找的关键字,而在Rust中,它必须由常量初始化

静态mut
是不安全的,我可以理解为什么,但它不能从概念上捕获我想要的,我想要一个不可变的变量

以Tribonaci函数的这个简单示例为例:

static sqrt_33_mul_3:f64=3.0*33.0f64.powf(0.5);
静态摩擦系数:f64=1.0
+(19.0-sqrt_33_mul_3).powf(1.0/3.0)
+(19.0+sqrt_33_mul_3).powf(1.0/3.0);
fn tribonacci(编号:f64)->f64{
返回(
(Tribonaci_常数/3.0).功率(n)
/ (
(4.0 / 3.0)
*Tribonaci_常数
- (1.0 / 9.0)
*Tribonaci_常数功率(2.0)-1.0
)
).round();
}
我希望函数外部的两个静态变量只初始化一次,并且powf不会在每次函数运行时被调用

我是一个难以置信的生锈新手,不知道对于普通的、有经验的用户来说什么是常识

这可能吗?如果可能的话,怎么做?

您可以使用:

使用lazy\u static::lazy\u static;//1.4.0
懒惰的人!{
静态参考cbrt_33_mul_3:f64=3.0*33.0f64.功率(0.5);
静态参考Tribonaci_常数:f64=1.0
+(19.0-*cbrt_33_mul_3).功率(1.0/3.0)
+(19.0+*cbrt_33_mul_3).powf(1.0/3.0);
}
fn tribonacci(编号:f64)->f64{
返回(
(*Tribonaci_常数/3.0)。功率(n)
/ (
(4.0 / 3.0)
**Tribonaci_常数
- (1.0 / 9.0)
*Tribonaci_常数功率(2.0)-1.0
)
).round();
}
fn main(){
println!(“你好,世界!”);
}

注意
ref
的用法以及
tribonaci_常量所需的解引用
如果
f64::powf
是一个常量函数,那么编译器应该将
3.0*33.0f64.powf(0.5)
之类的内容转换为一个固定值

虽然可以使用
lazy\u static
来解决这个问题,但使用lazy\u static会带来成本,因为它们的设计目的不仅仅是支持简单的浮点常量

通过使用标准对两个实现进行基准测试,您可以看到这一成本:

pub mod ls {
    use lazy_static::lazy_static; // 1.4.0

    lazy_static! {
        //TODO: Should this be a pow(1.0/3.0)?
        pub static ref cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
        
        pub static ref tribonacci_constant: f64 = 1.0
        + (19.0 - *cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + *cbrt_33_mul_3).powf(1.0 / 3.0);
    }

    pub fn tribonacci(n: f64) -> f64 {
        return (
            (*tribonacci_constant / 3.0).powf(n)
            / (
                (4.0 / 3.0)
                * *tribonacci_constant
                - (1.0 / 9.0)
                * tribonacci_constant.powf(2.0) - 1.0
            )
        ).round();
    }
}

pub mod hc {
    pub fn tribonacci(n: f64) -> f64 {
        let p = 1.839286755214161;
        let s = 0.3362281169949411;
        return (s * p.powf(n)).round();
    }
}

fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("trib 5.1 ls", |b| b.iter(|| ls::tribonacci(black_box(5.1))));
    c.bench_function("trib 5.1 hc", |b| b.iter(|| hc::tribonacci(black_box(5.1))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
成本很小,但如果在核心循环中,成本可能会很大。 在我的机器上,我得到(删除无关行后)

这大约是20%的差异

如果您不喜欢在代码中使用硬编码常量,您可以在构建时使用
build.rs
脚本生成这些常量

我的基准测试完整示例如下所示:

build.rs

use std::env;
use std::fs;
use std::path::Path;

fn main() {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("constants.rs");

    //TODO: Should this be a pow(1.0/3.0)?
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let p = tribonacci_constant / 3.0;
    let s = 1.0 / (
        (4.0 / 3.0)
        * tribonacci_constant
        - (1.0 / 9.0)
        * tribonacci_constant.powf(2.0) - 1.0
    );

    fs::write(
        &dest_path,
        format!("\
        pub mod tribonacci {{\n\
            pub const P: f64 = {:.32};\n\
            pub const S: f64 = {:.32};\n\
        }}\n", p, s)
    ).unwrap();
    println!("cargo:rerun-if-changed=build.rs");
}
pub mod constants {
    include!(concat!(env!("OUT_DIR"), "/constants.rs"));
}

pub mod ls {
    use lazy_static::lazy_static; // 1.4.0

    lazy_static! {
        //TODO: Should this be a pow(1.0/3.0)?
        pub static ref cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
        
        pub static ref tribonacci_constant: f64 = 1.0
        + (19.0 - *cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + *cbrt_33_mul_3).powf(1.0 / 3.0);
    }

    pub fn tribonacci(n: f64) -> f64 {
        return (
            (*tribonacci_constant / 3.0).powf(n)
            / (
                (4.0 / 3.0)
                * *tribonacci_constant
                - (1.0 / 9.0)
                * tribonacci_constant.powf(2.0) - 1.0
            )
        ).round();
    }

}

pub mod hc {
    pub fn tribonacci(n: f64) -> f64 {
        let p = super::constants::tribonacci::P;
        let s = super::constants::tribonacci::S;
        return (s * p.powf(n)).round();
    }
}
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rust_gen_const_vs_lazy_static::ls;
use rust_gen_const_vs_lazy_static::hc;

fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("trib 5.1 ls", |b| b.iter(|| ls::tribonacci(black_box(5.1))));
    c.bench_function("trib 5.1 hc", |b| b.iter(|| hc::tribonacci(black_box(5.1))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn tp(_item: TokenStream) -> TokenStream {
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let p = tribonacci_constant / 3.0;
    format!("{}f64",p).parse().unwrap()
}

#[proc_macro]
pub fn ts(_item: TokenStream) -> TokenStream {
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let s = 1.0 / (
        (4.0 / 3.0)
        * tribonacci_constant
        - (1.0 / 9.0)
        * tribonacci_constant.powf(2.0) - 1.0
    );
    format!("{}f64",s).parse().unwrap()
}
src/lib.rs

use std::env;
use std::fs;
use std::path::Path;

fn main() {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("constants.rs");

    //TODO: Should this be a pow(1.0/3.0)?
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let p = tribonacci_constant / 3.0;
    let s = 1.0 / (
        (4.0 / 3.0)
        * tribonacci_constant
        - (1.0 / 9.0)
        * tribonacci_constant.powf(2.0) - 1.0
    );

    fs::write(
        &dest_path,
        format!("\
        pub mod tribonacci {{\n\
            pub const P: f64 = {:.32};\n\
            pub const S: f64 = {:.32};\n\
        }}\n", p, s)
    ).unwrap();
    println!("cargo:rerun-if-changed=build.rs");
}
pub mod constants {
    include!(concat!(env!("OUT_DIR"), "/constants.rs"));
}

pub mod ls {
    use lazy_static::lazy_static; // 1.4.0

    lazy_static! {
        //TODO: Should this be a pow(1.0/3.0)?
        pub static ref cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
        
        pub static ref tribonacci_constant: f64 = 1.0
        + (19.0 - *cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + *cbrt_33_mul_3).powf(1.0 / 3.0);
    }

    pub fn tribonacci(n: f64) -> f64 {
        return (
            (*tribonacci_constant / 3.0).powf(n)
            / (
                (4.0 / 3.0)
                * *tribonacci_constant
                - (1.0 / 9.0)
                * tribonacci_constant.powf(2.0) - 1.0
            )
        ).round();
    }

}

pub mod hc {
    pub fn tribonacci(n: f64) -> f64 {
        let p = super::constants::tribonacci::P;
        let s = super::constants::tribonacci::S;
        return (s * p.powf(n)).round();
    }
}
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rust_gen_const_vs_lazy_static::ls;
use rust_gen_const_vs_lazy_static::hc;

fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("trib 5.1 ls", |b| b.iter(|| ls::tribonacci(black_box(5.1))));
    c.bench_function("trib 5.1 hc", |b| b.iter(|| hc::tribonacci(black_box(5.1))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn tp(_item: TokenStream) -> TokenStream {
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let p = tribonacci_constant / 3.0;
    format!("{}f64",p).parse().unwrap()
}

#[proc_macro]
pub fn ts(_item: TokenStream) -> TokenStream {
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let s = 1.0 / (
        (4.0 / 3.0)
        * tribonacci_constant
        - (1.0 / 9.0)
        * tribonacci_constant.powf(2.0) - 1.0
    );
    format!("{}f64",s).parse().unwrap()
}
长凳/my_benchmark.rs

use std::env;
use std::fs;
use std::path::Path;

fn main() {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("constants.rs");

    //TODO: Should this be a pow(1.0/3.0)?
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let p = tribonacci_constant / 3.0;
    let s = 1.0 / (
        (4.0 / 3.0)
        * tribonacci_constant
        - (1.0 / 9.0)
        * tribonacci_constant.powf(2.0) - 1.0
    );

    fs::write(
        &dest_path,
        format!("\
        pub mod tribonacci {{\n\
            pub const P: f64 = {:.32};\n\
            pub const S: f64 = {:.32};\n\
        }}\n", p, s)
    ).unwrap();
    println!("cargo:rerun-if-changed=build.rs");
}
pub mod constants {
    include!(concat!(env!("OUT_DIR"), "/constants.rs"));
}

pub mod ls {
    use lazy_static::lazy_static; // 1.4.0

    lazy_static! {
        //TODO: Should this be a pow(1.0/3.0)?
        pub static ref cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
        
        pub static ref tribonacci_constant: f64 = 1.0
        + (19.0 - *cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + *cbrt_33_mul_3).powf(1.0 / 3.0);
    }

    pub fn tribonacci(n: f64) -> f64 {
        return (
            (*tribonacci_constant / 3.0).powf(n)
            / (
                (4.0 / 3.0)
                * *tribonacci_constant
                - (1.0 / 9.0)
                * tribonacci_constant.powf(2.0) - 1.0
            )
        ).round();
    }

}

pub mod hc {
    pub fn tribonacci(n: f64) -> f64 {
        let p = super::constants::tribonacci::P;
        let s = super::constants::tribonacci::S;
        return (s * p.powf(n)).round();
    }
}
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rust_gen_const_vs_lazy_static::ls;
use rust_gen_const_vs_lazy_static::hc;

fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("trib 5.1 ls", |b| b.iter(|| ls::tribonacci(black_box(5.1))));
    c.bench_function("trib 5.1 hc", |b| b.iter(|| hc::tribonacci(black_box(5.1))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn tp(_item: TokenStream) -> TokenStream {
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let p = tribonacci_constant / 3.0;
    format!("{}f64",p).parse().unwrap()
}

#[proc_macro]
pub fn ts(_item: TokenStream) -> TokenStream {
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let s = 1.0 / (
        (4.0 / 3.0)
        * tribonacci_constant
        - (1.0 / 9.0)
        * tribonacci_constant.powf(2.0) - 1.0
    );
    format!("{}f64",s).parse().unwrap()
}
Cargo.toml

[package]
name = "rust_gen_const_vs_lazy_static"
version = "0.1.0"
edition = "2018"

[dependencies]
"lazy_static" = "1.4.0"

[dev-dependencies]
criterion = "0.3"

[[bench]]
name = "my_benchmark"
harness = false
[package]
name = "trib_macros"
version = "0.1.0"
edition = "2018"


[lib]
proc-macro = true
$OUTDIR/constants.rs(已生成)


正如Dilshod Tadjibaev所建议的,使用proc宏可以实现类似的结果,尽管在这种情况下需要做更多的工作。这提供了与构建时生成完全相同的速度

为了进行设置,我为宏创建了一个新的板条箱
trib_宏
,因为proc宏需要放在自己的板条箱中。这个新的板条箱只包含两个文件
Cargo.toml
src/lib.rs

Cargo.toml

[package]
name = "rust_gen_const_vs_lazy_static"
version = "0.1.0"
edition = "2018"

[dependencies]
"lazy_static" = "1.4.0"

[dev-dependencies]
criterion = "0.3"

[[bench]]
name = "my_benchmark"
harness = false
[package]
name = "trib_macros"
version = "0.1.0"
edition = "2018"


[lib]
proc-macro = true
src/lib.rs

use std::env;
use std::fs;
use std::path::Path;

fn main() {
    let out_dir = env::var_os("OUT_DIR").unwrap();
    let dest_path = Path::new(&out_dir).join("constants.rs");

    //TODO: Should this be a pow(1.0/3.0)?
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let p = tribonacci_constant / 3.0;
    let s = 1.0 / (
        (4.0 / 3.0)
        * tribonacci_constant
        - (1.0 / 9.0)
        * tribonacci_constant.powf(2.0) - 1.0
    );

    fs::write(
        &dest_path,
        format!("\
        pub mod tribonacci {{\n\
            pub const P: f64 = {:.32};\n\
            pub const S: f64 = {:.32};\n\
        }}\n", p, s)
    ).unwrap();
    println!("cargo:rerun-if-changed=build.rs");
}
pub mod constants {
    include!(concat!(env!("OUT_DIR"), "/constants.rs"));
}

pub mod ls {
    use lazy_static::lazy_static; // 1.4.0

    lazy_static! {
        //TODO: Should this be a pow(1.0/3.0)?
        pub static ref cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
        
        pub static ref tribonacci_constant: f64 = 1.0
        + (19.0 - *cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + *cbrt_33_mul_3).powf(1.0 / 3.0);
    }

    pub fn tribonacci(n: f64) -> f64 {
        return (
            (*tribonacci_constant / 3.0).powf(n)
            / (
                (4.0 / 3.0)
                * *tribonacci_constant
                - (1.0 / 9.0)
                * tribonacci_constant.powf(2.0) - 1.0
            )
        ).round();
    }

}

pub mod hc {
    pub fn tribonacci(n: f64) -> f64 {
        let p = super::constants::tribonacci::P;
        let s = super::constants::tribonacci::S;
        return (s * p.powf(n)).round();
    }
}
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rust_gen_const_vs_lazy_static::ls;
use rust_gen_const_vs_lazy_static::hc;

fn criterion_benchmark(c: &mut Criterion) {
    c.bench_function("trib 5.1 ls", |b| b.iter(|| ls::tribonacci(black_box(5.1))));
    c.bench_function("trib 5.1 hc", |b| b.iter(|| hc::tribonacci(black_box(5.1))));
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
extern crate proc_macro;
use proc_macro::TokenStream;

#[proc_macro]
pub fn tp(_item: TokenStream) -> TokenStream {
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let p = tribonacci_constant / 3.0;
    format!("{}f64",p).parse().unwrap()
}

#[proc_macro]
pub fn ts(_item: TokenStream) -> TokenStream {
    let cbrt_33_mul_3: f64 = 3.0 * 33.0f64.powf(0.5);
    
    let tribonacci_constant: f64 = 1.0 
        + (19.0 - cbrt_33_mul_3).powf(1.0 / 3.0)
        + (19.0 + cbrt_33_mul_3).powf(1.0 / 3.0);

    let s = 1.0 / (
        (4.0 / 3.0)
        * tribonacci_constant
        - (1.0 / 9.0)
        * tribonacci_constant.powf(2.0) - 1.0
    );
    format!("{}f64",s).parse().unwrap()
}
然后我们需要调整原始板条箱的
Cargo.toml
,以将其拉入

[dependencies]
...
trib_macros = { path = "path/to/trib_macros" }
最后,使用它是相对干净的:

pub mod mc {
    use trib_macros::{ts,tp};

    pub fn tribonacci(n: f64) -> f64 {
        return (ts!() * tp!().powf(n)).round();
    }
}
确实有一种更简洁的方法来输出浮点文字标记,但我找不到它



您可以在

TL中找到这些测试的完整存储库;DR,但您似乎在寻找问题在于
f64::powf
不是
const
函数。我已经找到了一些RFC,可以使大多数/所有浮点函数保持常量。大多数评论说这是“困难的”,但我无法找出确切的原因。(我怀疑这与更改某些硬件浮点模式有关,从而导致这些函数在运行时的行为发生潜在变化)。我生活在这样的希望中,这样的东西最终会变得生锈,这样你的代码就会“正常工作”。我很确定你可以在函数中插入变量定义。一般来说,函数中的静态变量只能初始化一次。@Adam不管它们在哪里或不在哪里,它们都需要由一个常量初始化。@MichaelAnderson我理解这一部分;这就是为什么我要来问。首先,你发现了我的小错误,我仔细检查了数学,这是sqrt,不是cbrt。修正了变量命名问题。实际上,我是在测试lazy_static之后回来报告的:在检查输出代码之后,所有数字都是在编译时生成的,代码只包含常量、对powf的调用和除法操作。因此,惰性静态和去引用实际上会导致更糟糕的代码@NetwaveI正在寻找一种实现性能的方法,不管编译器是否可以将它们优化为常量,而不牺牲代码可读性、开发人员的努力等等。我感到惊讶的是,没有一种简单、惯用的方法来做到这一点。我想我太习惯于在C++中使用命名空间的静态常量了。我认为要把它设置得更大(并且常数浮点函数会更好,并且解决所有这些问题)。但是,一旦这些部件就位,就很容易维护和缩放。@MichaelAnderson您也看过使用宏解决这个问题吗?我确实使用了proc宏,并添加了它作为进一步的示例。