为什么编译器声称泛型即使应有也不实现`Display`?

时间:2018-11-18 19:46:24

标签: rust formatting traits

我正在建立一个实现字符串连接的库;也就是说,打印由分隔符分隔的容器的所有元素。我的基本设计如下:

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上的边界。许多容器实现IntoIterator来引用其自身,以允许遍历对所包含项的引用(例如,Vec对其实现here)。

但是,当我实际尝试使用我的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类型是什么,但是根据我对the docs的阅读,它似乎是&T,在这种情况下意味着&&str

为什么编译器不认为&&str实现Display?我已经尝试过使用其他许多类型,例如usizeString,但它们都不起作用;它们都因相同的错误而失败。我知道这些引用类型不是直接 实现Display,但是应该通过deref强制自动选择实现,对吧?

1 个答案:

答案 0 :(得分:8)

似乎像编译器的限制。您现在可以通过用表示“终身显示”的私有帮助器特征编写隐含绑定来解决此问题。这使编译器可以看到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: ", ",
    });
}