如何在不进行多次分配的情况下实现条件嵌套格式化?

phi*_*611 3 formatting rust

我有一个由多个条件组件组成的格式字符串,我正在寻找一种不需要为中间步骤多次分配字符串的解决方案。如果我使用 -macro 创建最终格式字符串的每个单独组件,format!那么它可以工作,但我需要为每个组件进行分配。

我尝试尝试仅使用宏来生成复杂的格式字符串及其参数。但是,这总是会导致“在此语句结束时释放临时值”错误。我尝试使用单一类型的缓冲区impl core::fmt::Write,但我也无法成功。

在高层次上,我想要这样的东西:

fn main() {
    let prefix_include_a = true;
    let prefix_include_b = true;
    // prefix itself is a formatted string and it is further formatted here
    println!("{prefix:<10}{message:>10}!",
        prefix = format_prefix(prefix_include_a, prefix_include_b),
        message = "message"
    );
}

// formats the prefix component of the final string.
// needs multiple String allocations as `format!` is used
fn format_prefix(inc_a: bool, inc_b: bool) -> String {
    format!("[{a:<5}{b:<5}]",
        a = if inc_a {
            format!("{:.1}", 1.234)
        } else {
            format!("")
        },
        b = if inc_b {
            format!("{:.2}", 1.234)
        } else {
            format!("")
        },
    )
}
Run Code Online (Sandbox Code Playgroud)

这是否可以在没有或只有一次分配的情况下实现?

Mas*_*inn 6

最简单的解决方案是直接write!访问底层流,例如

use std::io::{stdout, Write};

fn main() {
    let prefix_include_a = true;
    let prefix_include_b = true;
    
    let mut stdout = stdout();
    let _ = format_prefix(&mut stdout, prefix_include_a, prefix_include_b);
    let _ = write!(stdout, "{:>10}", "message");
}

// formats the prefix component of the final string.
// needs multiple String allocations as `format!` is used
fn format_prefix(mut s: impl Write, inc_a: bool, inc_b: bool) -> std::io::Result<()> {
    write!(s, "[")?;
    if inc_a {
        write!(s, "{:<5.1}", 1.234)?;
    } else {
        write!(s, "     ")?;
    }
    if inc_b {
        write!(s, "{:<5.2}", 1.234)?;
    } else {
        write!(s, "     ")?;
    }
    write!(s, "]")?;
    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是将其具体prefix化为类型并实现Display它。我认为(希望?)格式化程序是底层流的传递,尽管我从未真正看过:

use std::io::{stdout, Write};

fn main() {
    let prefix_include_a = true;
    let prefix_include_b = true;

    println!(
        "{prefix:<10}{message:>10}!",
        prefix = Prefix(prefix_include_a, prefix_include_b),
        message = "message"
    );
}

struct Prefix(bool, bool);
impl std::fmt::Display for Prefix {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "[")?;
        if self.0 {
            write!(f, "{:<5.1}", 1.234)?;
        } else {
            write!(f, "     ")?;
        }
        if self.1 {
            write!(f, "{:<5.2}", 1.234)?;
        } else {
            write!(f, "     ")?;
        }
        write!(f, "]")?;
        Ok(())
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:我没有处理这两个版本中的前缀填充,尽管我认为这没有多大意义:两个前缀值都填充为 5,因此前缀始终至少为 12 宽。填充到 10 没有任何意义。

但是,如果需要,前缀对象可以使用外部指定的填充来分配到内部填充。请参阅std::fmt::Formatter参考资料,您可以获得有关格式说明符的信息。

要清理条件,您可能可以使用format_args!,尽管我对此缺乏经验。