【问题标题】:How to create a static string at compile time如何在编译时创建静态字符串
【发布时间】:2015-10-05 19:08:36
【问题描述】:

我想创建一个由重复的字符序列组成的长 &'static str,例如abcabcabc...

Rust 中有没有办法通过表达式来做到这一点,例如Python 中的 long_str = 1000 * "abc" 之类的东西,还是我必须在 Python 中生成它并将其复制/粘贴到 Rust 代码中?

【问题讨论】:

    标签: rust


    【解决方案1】:

    你不能在稳定的 Rust 中做这样的事情。据我了解 Python,您的 1000 * "abc" 示例也不会在 Python 的“编译时”运行。

    包含一个文件

    如果它必须是静态的,您可以使用Cargo build script。这是一段 Rust 代码,可以在你的代码实际编译之前做很多事情。具体来说,您可以编写一个包含您的字符串的源文件,然后使用 include_str! 将其放入您的 crate:

    build.rs

    use std::{
        env, error::Error, fs::File, io::{BufWriter, Write}, path::Path,
    };
    
    fn main() -> Result<(), Box<Error>> {
        let out_dir = env::var("OUT_DIR")?;
        let dest_path = Path::new(&out_dir).join("long_string.txt");
        let mut f = BufWriter::new(File::create(&dest_path)?);
    
        let long_string = "abc".repeat(100);
        write!(f, "{}", long_string)?;
    
        Ok(())
    }
    

    lib.rs

    static LONG_STRING: &'static str = include_str!(concat!(env!("OUT_DIR"), "/long_string.txt"));
    

    延迟初始化

    您可以创建一个 once_celllazy_static 值,在运行时创建您的字符串,但只能创建一次。

    use once_cell::sync::Lazy; // 1.5.2
    
    static LONG_STR: Lazy<String> = Lazy::new(|| "abc".repeat(5000));
    

    另见:

    遥远的未来

    在某个时候,RFC 911 将完全实现。这一点,再加上一些额外的 RFC,每个都添加了新功能,将允许您编写如下内容:

    // Does not work yet!
    static LONG_STR: String = "abc".repeat(1000);
    

    【讨论】:

    • 现在有str::repeat(),所以你可以写"abc".repeat(100)用你的重复数据生成一个String。比iter::repeat() 版本干净一点:)
    【解决方案2】:

    有很多方法可以做到这一点。 如果您愿意,可以从文件中加载预先生成的字符串:

    const DATA: &'static str = include_str!("filename.txt");
    

    或者在编译过程中你可以使用concat!:

    const DATA: &'static str = concat!("abc", "abc");
    

    【讨论】:

    • 想为concat! 展示一个等同于1000 * "abc" 的示例吗? ^_^
    • 我正在尝试创建一个使用concat! 20 分钟的“字符串乘法”宏......遗憾的是,我分心了,我还没有成功:
    • 这是我的尝试:is.gd/fK8YIh 我用任何语言编写的第一个宏,所以不知道是否有好处。
    • @JohanLarsson 问题是,OP 想要一个在编译时创建的静态字符串。您的宏可以工作,但会在运行时执行所有操作并返回 String...
    • 好的,感谢您的评论,只是在黑暗中拍摄。
    【解决方案3】:

    不为这个答案感到自豪:D,但我想给出不同的观点。

    通过使用宏规则,您可以轻松地通过组合定义静态连接。在这种情况下,我定义 100 * str = 4 * 25 * str = 4 * 5 * 5 * str。你也可以做 100 * str = 10 * 10 * str,用更少​​的行(但更多的列:))

    macro_rules! rep {
        ($t:expr, 4) => { concat!($t, $t, $t, $t) };
        ($t:expr, 5) => { concat!($t, $t, $t, $t, $t) };
        ($t:expr, 25) => { rep!(rep!($t, 5), 5) };
        ($t:expr, 100) => { rep!(rep!($t, 25), 4) };
    }
    
    
    fn main() {
        assert_eq!(rep!("x", 100).len(), 100);
    }
    

    由于宏适用于语言元素,因此无法使用计数器并像这样简单地递归调用宏:

    macro_rules! does_not_work {
        ($t:expr, 1) => { $t };
        ($t:expr, $n:) => { concat!($t, does_not_work!($t, $n-1)) };
    }
    

    但是在这种简单的情况下,递归地编写宏应该可以解决问题。我没有尝试使用不同的 macro_rules 模式或其他类型的宏,但应该可以做一些更优雅的事情。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-21
      • 2015-02-13
      • 1970-01-01
      • 1970-01-01
      • 2015-06-16
      • 2019-09-22
      • 2015-02-12
      • 1970-01-01
      相关资源
      最近更新 更多