Luc*_*iel 10 formatting traits rust
我正在构建一个实现字符串连接的库; 也就是说,打印由分隔符分隔的容器的所有元素.我的基本设计如下:
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)
})
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这种特质实施无需投诉即可编译.注意绑定&'a C: IntoIterator.许多容器实现IntoIterator对自身的引用,以允许迭代对包含项的引用(例如,在此处Vec实现它).
但是,当我实际尝试使用我的Join结构时,我得到一个不满意的特征限制:
fn main() {
let data = vec!["Hello", "World"];
let join = Join {
container: data,
sep: ", ",
};
println!("{}", join);
}
Run Code Online (Sandbox Code Playgroud)
此代码生成编译错误:
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`
Run Code Online (Sandbox Code Playgroud)
关键线似乎是这样的:
the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item`
Run Code Online (Sandbox Code Playgroud)
不幸的是,编译器实际上并没有告诉我这Item是什么类型,但根据我对文档的阅读,它似乎是&T,在这种情况下意味着&&str.
为什么编译器不认为&&str实现Display?我已尝试过许多其他类型的东西,比如usize和String,并且它们都不起作用; 他们都失败了同样的错误.我知道这些引用类型没有直接实现Display,但实现应该通过deref强制自动获取,对吗?
看起来像编译器限制.您现在可以通过编写表示"使用生命周期显示"的私有帮助器特征的impl绑定来解决它.这使编译器能够看到这for<'a> private::Display<'a>意味着fmt::Display.
use std::fmt;
pub struct Join<Container, Sep> {
container: Container,
sep: Sep,
}
mod private {
use std::fmt;
pub trait Display<'a>: fmt::Display {}
impl<'a, T> Display<'a> for T where T: fmt::Display {}
}
impl<Container, Sep> fmt::Display for Join<Container, Sep>
where
for<'a> &'a Container: IntoIterator,
for<'a> <&'a Container as IntoIterator>::Item: private::Display<'a>,
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)
})
}
}
}
}
fn main() {
println!("{}", Join {
container: vec!["Hello", "World"],
sep: ", ",
});
}
Run Code Online (Sandbox Code Playgroud)