用于截断类字符串类型的格式说明符

Mic*_*rst 2 rust

是否有格式说明符会截断类似字符串的类型?我有一个不是字符串的类型,但确实实现了显示。不过,我想将其限制为 7 个字符长,而我知道如何做到这一点的唯一方法是先将其转换为字符串。

let sha1 = format!("{}", branch.commit_id);
let formatted = format!("{} ({})\n", branch.name, &sha1[0..7]));
Run Code Online (Sandbox Code Playgroud)

是否有一些格式说明符可以让我一步完成?就像是:

let formatted = format!("{} ({<something 7>})\n", branch.name, branch.commit_id);
Run Code Online (Sandbox Code Playgroud)

不幸的是,因为它不是字符串,所以我不能只做&branch.commit_id[0..7],这就是为什么我希望有一个格式说明符语法。

Sil*_*olo 5

正如评论中提到的,任何有Display实例的东西都会自动得到一个ToString实例,所以你可以调用to_string来获取一个字符串,然后截断任何实现Display. 这可能是您正在寻找的实用、正确的答案。

但是为了完整性,这里仍然有一个有趣的问题:我们可以在不生成中间字符串的情况下进行截断吗?碰巧的是,我们可以实现一个自定义格式化程序来做到这一点。大多数Display实现或多或少都是对write!宏的调用,这只是调用write_fmt. 令人困惑的是,这可能write_fmt来自std::io::Writestd::fmt::Write特质,但我们暂时将重点放在后者上。我们可以实现我们自己的std::fmt::Write截断到指定长度的实例。

pub struct TruncatedFormatter<'a, T> {
  pub remaining: usize,
  pub inner: &'a mut T,
}

impl<'a, T> fmt::Write for TruncatedFormatter<'a, T> where T : fmt::Write {
  fn write_str(&mut self, s: &str) -> fmt::Result {
    if self.remaining < s.len() {
      self.inner.write_str(&s[0..self.remaining])?;
      self.remaining = 0;
      Ok(())
    } else {
      self.remaining -= s.len();
      self.inner.write_str(s)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以对任何类型进行薄包装T,强制类型使用我们自定义的格式化程序。由于fmtonDisplay只需要一个不可变的引用,我们的包装器将只接受一个不可变的引用,以最大限度地与调用站点的兼容性。

pub struct TruncatedValue<'a, T>(pub usize, pub &'a T);

impl<'a, T> fmt::Display for TruncatedValue<'a, T> where T : fmt::Display {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    let TruncatedValue(remaining, value) = self;
    let mut wrapped_fmt = TruncatedFormatter { remaining: *remaining, inner: f };
    write!(wrapped_fmt, "{}", value)
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,当我们想要打印截断到特定长度的值时,我们可以在format!orprintln!调用期间将其包装起来。

  println!("Full value '{}'\nTruncated value '{}'\n", test, TruncatedValue(7, &test));
Run Code Online (Sandbox Code Playgroud)

在 Rust Playground 中试试吧!