如何随机选择格式字符串

mmi*_*ate 3 random string-formatting rust

有时,程序可以通过多种方式将包含动态值的消息短语发送给其用户.例如:

  • "剩下{}分钟."
  • "你需要在不到{}分钟内完成."

并非所有消息都包含仅作为前缀或后缀的值.在动态语言中,这似乎是字符串格式化的逻辑任务.

对于不希望重复性的媒体(例如Slack频道),有很多不同的措辞,使用以下内容产生每个最终String输出:

pub fn h(x: usize) -> String {
    rand::sample(rand::thread_rng(), vec![
        format!("{} minutes remain.", x),
        format!("Hurry up; only {} minutes left to finish.", x),
        format!("Haste advisable; time ends in {}.", x),
        /* (insert many more elements here) */
    ], 1).first().unwrap_or(format!("{}", x))
}
Run Code Online (Sandbox Code Playgroud)

将会:

  • 每次打字都是作者单调乏味的format!(/*...*/, x).
  • 浪费内存+时钟周期,因为在选择一个之前完全生成每个可能性,丢弃其他可能性.

有没有办法避免这些缺点?

如果不是格式字符串的编译时评估,则返回随机选择&'static str(从静态片)传入的函数format!将是首选解决方案.

Fra*_*gné 5

Rust支持在函数内定义函数.我们可以构建一个函数指针片段,rand::sample选择其中一个,然后调用所选函数.

extern crate rand;

use rand::Rng;

pub fn h(x: usize) -> String {
    fn f0(x: usize) -> String {
        format!("{} minutes remain.", x)
    }

    fn f1(x: usize) -> String {
        format!("Hurry up; only {} minutes left to finish.", x)
    }

    fn f2(x: usize) -> String {
        format!("Haste advisable; time ends in {}.", x)
    }

    let formats: &[fn(usize) -> String] = &[f0, f1, f2];
    (*rand::thread_rng().choose(formats).unwrap())(x)
}
Run Code Online (Sandbox Code Playgroud)

这解决了原始解决方案的"浪费"方面,但不是"乏味"方面.我们可以通过使用宏来减少重复次数.请注意,函数中定义的宏也是该函数的本地宏!这个宏利用Rust的卫生宏来定义多个名为的函数f,这样我们就不需要在使用宏时为每个函数提供一个名称.

extern crate rand;

use rand::Rng;

pub fn h(x: usize) -> String {
    macro_rules! messages {
        ($($fmtstr:tt,)*) => {
            &[$({
                fn f(x: usize) -> String {
                    format!($fmtstr, x)
                }
                f
            }),*]
        }
    }

    let formats: &[fn(usize) -> String] = messages!(
        "{} minutes remain.",
        "Hurry up; only {} minutes left to finish.",
        "Haste advisable; time ends in {}.",
    );
    (*rand::thread_rng().choose(formats).unwrap())(x)
}
Run Code Online (Sandbox Code Playgroud)