Add the Display trait to a vector of strings in Rust

Rust type struct Display error code

The problem

Let's say we have a vector of strings, how can we print them easily?

let animals: Vec<String> = vec![String::from("snake"), String::from("camel"), String::from("crab")];

Trying to use the simplest way won't work:

println!("{}", animals);

We get the following error:

error[E0277]: `Vec<String>` doesn't implement `std::fmt::Display`
  --> src/main.rs:24:20
   |
24 |     println!("{}", animals);
   |                    ^^^^^^^ `Vec<String>` cannot be formatted with the default formatter
   |
   = help: the trait `std::fmt::Display` is not implemented for `Vec<String>`
   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)

further referring us to E0277

We can do what was suggested:

println!("{:?}", animals);

will yield

["snake", "camel", "crab"]

and

println!("{:#?}", animals);

will yield:

[
    "snake",
    "camel",
    "crab",
]

Those are actually not bad, but I wanted to show them in a different way.

Primarily as an experiment to see how to implement Display in general for a vector.

Implement Display for Vec

The implementation should look something like this:

impl fmt::Display for Vec<String> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", "Something comes here")
    }
}

However, this will give us a compiler error:

error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
  --> src/main.rs:23:1
   |
23 | impl fmt::Display for Vec<String> {
   | ^^^^^^^^^^^^^^^^^^^^^^-----------
   | |                     |
   | |                     `Vec` is not defined in the current crate
   | impl doesn't use only types from inside the current crate
   |
   = note: define and implement a trait or new type instead

Referencing us to E0117.

How can we implement the Display trait for a vector in Rust?

We cannot as we can only implement traits to our own data types.

What about creating an alias for this type? Would crating a type-alias work?

How to implement traits for type aliases in Rust?

type Words = Vec<String>;

impl fmt::Display for Words {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", "Something comes here")
    }
}
let animals: Words = vec![String::from("snake"), String::from("camel"), String::from("crab")];
println!("{:?}", animals);

We can define the type-alias and we can even use it in the declaration of variables, but we cannot implement the Display trait on a type-alias to a standard type. Type-aliases are just that. Aliases. They are not separate types.

Using a one-element tuple-based struct

A working solution is to create a one-element tuple-based struct. Because it is a totally new type we can implement the Display trait for that.

struct Words(Vec<String>);

impl fmt::Display for Words {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0.join(","))
    }
}

This is then how to use it:

let animals: Words = Words(vec![String::from("snake"), String::from("camel"), String::from("crab")]);
println!("{}", animals);

It will print:

snake,camel,crab

The drawback is that this means we need to reference to the 0th element in the tuple so we must write animals.0 in order to access the real vector:

println!("{}", animals.0[0]);
println!("{}", animals.0[1]);

to print:

snake
camel

I don't think I like this.

The full example

examples/display-vector-of-strings/src/main.rs

use std::fmt;



// Try to implement Display for the Vec<String>
// impl fmt::Display for Vec<String> {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "{}", "Something comes here")
//     }
// }

// Try to use a type-alias:
// type Words = Vec<String>;
// impl fmt::Display for Words {
//     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//         write!(f, "{}", "Something comes here")
//     }
// }


struct Words(Vec<String>);

impl fmt::Display for Words {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0.join(","))
    }
}


fn main() {
    // plain vector
    //let animals: Vec<String> = vec![String::from("snake"), String::from("camel"), String::from("crab")];
    //println!("{}", animals);
    //println!("{:?}", animals);
    //println!("{:#?}", animals);

    // type alias
    //let animals: Words = vec![String::from("snake"), String::from("camel"), String::from("crab")];
    //println!("{:?}", animals);

    // one-element struct
    let animals: Words = Words(vec![String::from("snake"), String::from("camel"), String::from("crab")]);
    println!("{}", animals);
    println!("{}", animals.0[0]);
    println!("{}", animals.0[1]);
}

Conclusion

We could not implement Display for a vector of strings in a nice way. If we frequently need to print out a whole vector of strings other than for debugging, then we might want to write a separate function or a macro for that.

Related Pages

Compiler Error codes in Rust
Compiler Error codes in Rust

Author

Gabor Szabo (szabgab)

Gabor Szabo, the author of the Rust Maven web site maintains several Open source projects in Rust and while he still feels he has tons of new things to learn about Rust he already offers training courses in Rust and still teaches Python, Perl, git, GitHub, GitLab, CI, and testing.

Gabor Szabo