Почему компилятор утверждает, что универсальный не реализует `Display`, даже если он должен? - PullRequest
0 голосов
/ 18 ноября 2018

Я создаю библиотеку, которая реализует объединения строк; то есть печать всех элементов контейнера, разделенных разделителем. Мой базовый дизайн выглядит так:

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 реализует его здесь ).

Однако, когда я на самом деле пытаюсь использовать мою 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, но реализация должна быть автоматически подхвачена путем принудительного приведения, верно?

1 Ответ

0 голосов
/ 19 ноября 2018

Похоже на ограничение компилятора. Вы можете обойти это сейчас, написав 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: ", ",
    });
}
...