rusttraitstype-bounds

Why does an iterator of `ToString` items requires them to be `Display` also?


The following code:

enum MyEnum {
    A,
    B,
}

impl ToString for MyEnum {
    fn to_string(&self) -> String {
        match *self {
            Self::A => format!("A"),
            Self::B => format!("B"),
        }
    }
}

pub fn foo<I: ToString>(item: I) {
    println!("item: {}", item.to_string());
}

pub fn bar<I: ToString>(iter: impl Iterator<Item = I>) {
    iter.for_each(|item| println!("item: {}", item.to_string()))
}

fn main() {
    foo(MyEnum::A);
    bar([MyEnum::A, MyEnum::B].iter());
}

Produces the following compilation error:

error[E0277]: `MyEnum` doesn't implement `std::fmt::Display`
  --> src\bin\main6.rs:25:2
   |
25 |     bar([MyEnum::A, MyEnum::B].iter());
   |     ^^^ `MyEnum` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `MyEnum`
   = 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 `&MyEnum`
   = note: required because of the requirements on the impl of `ToString` for `&MyEnum`
note: required by a bound in `bar`
  --> src\bin\main6.rs:19:15
   |
19 | pub fn bar<I: ToString>(iter: impl Iterator<Item = I>) {
   |               ^^^^^^^^ required by this bound in `bar`

For more information about this error, try `rustc --explain E0277`.

bar accepts iterators whose items implement ToString. MyEnum implements ToString, so [MyEnum::A, MyEnum::B].iter() is in fact an iterator of items that implement ToString.

foo and bar are very similar, in both the generic type I is replaced with MyEnum, so why is the trait bound Display required in bar but not in foo?


Solution

  • [MyEnum::A, MyEnum::B].iter() creates an iterator whose item is &MyEnum. &MyEnum does not implement ToString, only MyEnum does. This works:

    enum MyEnum {
        A,
        B,
    }
    
    impl ToString for MyEnum {
        fn to_string(&self) -> String {
            match *self {
                Self::A => format!("A"),
                Self::B => format!("B"),
            }
        }
    }
    
    pub fn foo<I: ToString>(item: I) {
        println!("item: {}", item.to_string());
    }
    
    pub fn bar<I: ToString>(iter: impl Iterator<Item = I>) {
        iter.for_each(|item| println!("item: {}", item.to_string()))
    }
    
    fn main() {
        foo(MyEnum::A);
        bar([MyEnum::A, MyEnum::B].into_iter());
    }
    

    Playground

    And if you want to accept any &I such that I: Display:

    pub fn bar<'a, I: 'a>(iter: impl Iterator<Item = &'a I>) where I: ToString {
        iter.for_each(|item| println!("item: {}", item.to_string()))
    }