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.