Why does the compiler claim that a generic doesn't implement `Display` even though it should?

  • A+
Category:Languages

I'm building a library that implements string joins; that is, printing all the elements of a container separated by a separator. My basic design looks like this:

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

This trait implementation compiles without complaint. Notice the bound on &'a C: IntoIterator. Many containers implement IntoIterator for a reference to themselves, to allow for iterating over references to the contained items (for instance, Vec implements it here).

However, when I actually try to use my Join struct, I get an unsatisfied trait bound:

fn main() {     let data = vec!["Hello", "World"];     let join = Join {         container: data,         sep: ", ",     };     println!("{}", join); } 

This code produces a compilation error:

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 key line seems to be this:

the trait `for<'a> std::fmt::Display` is not implemented for `<&'a std::vec::Vec<&str> as std::iter::IntoIterator>::Item` 

Unfortunately, the compiler doesn't actually tell me what the Item type is, but based on my reading of the docs, it appears to be &T, which in this case means &&str.

Why doesn't the compiler think that &&str implements Display? I've tried this with many other types, like usize and String, and none of them work; they all fail with the same error. I know that these reference type don't directly implement Display, but the implementation should be picked up automatically through deref coercion, right?

 


Seems like a compiler limitation. You can work around it for now by writing the impl bound in terms of a private helper trait that represents "display with lifetime". This enables the compiler to see that for<'a> private::Display<'a> implies 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: ", ",     }); } 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: