【问题标题】:Format specifier for truncating string-like types用于截断类似字符串的类型的格式说明符
【发布时间】:2021-04-14 23:21:23
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • &amp;branch.fommit_id.to_string()[0..7] 怎么样?
  • @Aplet123 你没有阅读问题
  • AFAIK,没有格式说明符主要只对std中的数字实现,没有人在自己的显示实现中这样做。我可以想出一些诡计,但我认为它不值得。
  • @Aplet123 我的类型没有to_string() 方法。这是一个git2::Oid。据我所知,将其强制转换为字符串的最简单方法是使用format!,因为它确实实现了Display
  • 任何拥有Display 的东西都可以通过一揽子实施免费获得ToString

标签: rust


【解决方案1】:

如 cmets 中所述,任何具有 Display 实例 automatically 的东西都会得到一个 ToString 实例,因此您可以调用 to_string 来获取一个字符串,然后截断任何实现 Display 的值。这可能是您正在寻找的实用、正确的答案。

但是为了完整起见,这里还有一个有趣的问题:我们可以在不生成中间字符串的情况下进行截断吗?碰巧的是,我们可以实现一个自定义格式化程序来做到这一点。大多数Display 实现或多或少都是对write! 宏的调用,这只是调用write_fmt 的一种奇特方式。令人困惑的是,这可能是来自 std::io::Writestd::fmt::Write 特征的 write_fmt,但我们现在将关注后者。我们可以实现我们自己的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)
    }
  }
}

然后我们可以对任何类型 T 进行精简包装,强制该类型使用我们定制的格式化程序。由于Display 上的fmt 只需要不可变引用,因此我们的包装器将只采用不可变引用,以最大限度地与调用站点兼容。

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

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

  println!("Full value '{}'\nTruncated value '{}'\n", test, TruncatedValue(7, &test));

Try it in the Rust Playground!

【讨论】:

    【解决方案2】:

    几天后我偶然发现了它。有一个格式说明符可以做到这一点。

    println!("Hello {:.5}", "world <-- only the the first 5 characters will be printed");
    

    编辑:正如Stargateur 在 cmets 中指出的那样,这在一般情况下不起作用。它恰好在我的工作,因为我使用的是git2::Oid,其Display 实现恰好在调用.fmt(f) 之前将自身转换为str (src)。

    【讨论】:

      猜你喜欢
      • 2017-11-23
      • 2012-11-06
      • 1970-01-01
      • 1970-01-01
      • 2020-05-23
      • 1970-01-01
      • 2023-03-10
      • 2020-10-07
      • 1970-01-01
      相关资源
      最近更新 更多