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

3
我有一个格式化字符串,由多个条件组成。我正在寻找一种解决方案,不需要为中间步骤分配多个字符串。如果我使用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!("")
        },
    )
}

这是否可能在没有或仅有一个单一分配的情况下实现?
1个回答

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(())
}

一种替代方案是将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(())
    }
}

注意:我没有处理任何版本中前缀的填充,虽然我认为这没有太多意义:两个前缀值都填充到5,因此前缀始终至少为12。填充到10没有意义。
但是,如果需要,前缀对象可以使用外部指定的填充来分配给内部填充。有关格式说明符的信息,请参见std::fmt::Formatter
要清理条件语句,您可能可以使用format_args!,但我对此并没有太多经验。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接