我正在构建一个实现字符串拼接的库,也就是将容器中所有元素用分隔符隔开打印出来。我的基本设计如下:
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: fmt::Display,
Sep: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.container.into_iter();
match iter.next() {
None => Ok(()),
Some(first) => {
first.fmt(f)?;
iter.try_for_each(move |element| {
self.sep.fmt(f)?;
element.fmt(f)
})
}
}
}
}
这个特性实现可以顺利编译。请注意&'a C: IntoIterator
的约束条件。许多容器为了允许对包含的项目的引用进行迭代(例如,Vec
在这里实现了它),会为自己的引用实现IntoIterator
。
然而,当我尝试使用我的Join
结构体时,我得到了一个无法满足的特性约束:
fn main() {
let data = vec!["Hello", "World"];
let join = Join {
container: data,
sep: ", ",
};
println!("{}", join);
}
这段代码会产生编译错误:
error[E0277]: `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` doesn't implement `std::fmt::Display`
--> src/main.rs:38:20
|
38 | println!("{}", join);
| ^^^^ `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` cannot be formatted with the default formatter
|
= help: the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
= note: required because of the requirements on the impl of `std::fmt::Display` for `Join<std::vec::Vec<&str>, &str>`
= note: required by `std::fmt::Display::fmt`
关键句似乎是这句话:
the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
很不幸,编译器实际上没有告诉我
Item
类型是什么,但根据我阅读文档的理解,它似乎是&T
,在这种情况下意味着&&str
。为什么编译器认为
&&str
没有实现Display
?我尝试了许多其他类型,比如usize
和String
,它们都无法工作;他们都以相同的错误失败。我知道这些引用类型不直接实现Display
,但实现应该通过deref coercion自动选择,对吗?
&&str
,您可以通过尝试编译let _: <&Vec<&str> as IntoIterator>::Item = ();
来轻松验证。而且&&str
绝对 实现了Display
,可以通过将<&&str as fmt::Display>::fmt(&&"hello", f)?;
添加到fmt()
的主体中进行验证。所以这应该绝对可行。我猜测<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item
由于某种原因没有正确规范化为&'a &str
。 - Sven MarnachIntoIterator
更改为IntoIterator<Item = T>
,其中T
是新的类型参数。但由于 HRTBs 的原因,在这种情况下无法使用此方法。 - Sven Marnach<T: Display> ... where &'a C: IntoIterator<Item=T>
。然而,我得到了一个不同的、有点更少用的关于未满足生命周期限制的错误信息。 - LucretielItem
的生命周期。在您的示例中,项目类型是&&str
,但是如果注释HRTB的生命周期,则必须将其设置为对于所有生命周期'a
都是&'a &str
,这当然是不可能的。这就是我在之前评论中所说的。 - Sven Marnach