是否有格式说明符会截断类似字符串的类型?我有一个不是字符串的类型,但确实实现了显示。不过,我想将其限制为 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]
,这就是为什么我希望有一个格式说明符语法。
正如评论中提到的,任何有Display
实例的东西都会自动得到一个ToString
实例,所以你可以调用to_string
来获取一个字符串,然后截断任何实现Display
. 这可能是您正在寻找的实用、正确的答案。
但是为了完整性,这里仍然有一个有趣的问题:我们可以在不生成中间字符串的情况下进行截断吗?碰巧的是,我们可以实现一个自定义格式化程序来做到这一点。大多数Display
实现或多或少都是对write!
宏的调用,这只是调用write_fmt
. 令人困惑的是,这可能write_fmt
来自std::io::Write
或std::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
,强制类型使用我们自定义的格式化程序。由于fmt
onDisplay
只需要一个不可变的引用,我们的包装器将只接受一个不可变的引用,以最大限度地与调用站点的兼容性。
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)