How to Fix `T` doesn’t implement std:fmt:Display in Rust?

Developers who come from a JavaScript background, or any other high-level programming language, and are learning Rust might find this language to have a steep learning curve. Oftentimes, many new Rust developers will come across the error doesn't implement std:fmt:Display when attempting to print a value that is not a string, like the one we see in the following example.

fn main() {
    let mut array_cities: Vec<String> = Vec::new();
    array_cities.push("San Francisco".to_string());
    array_cities.push("Santo Domingo".to_string());

    print!("testing {}", array_cities); // It will error here
}

If you attempt to execute this piece of logic using cargo run it will fail during compilation and will throw an error message similar to the following.

error[E0277]: `Vec<String>` doesn't implement `std::fmt::Display`
  --> src\main.rs:12:26
   |
12 |     print!("testing {}", array_cities);
   |                          ^^^^^^^^^^^^ `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` (in Nightly builds, run with -Z macro-backtrace for more info)

This can be a little discouraging to most JavaScript developers who are used to logging just about anything using console.log, regardless of whether the data type of value to log in the terminal was a string, a number, an array, or any other data type, like you can see in the following example.

Using console.log with any data type in JavaScript

Data Type Must Implement Display trait (std::fmt::Display)

To fix the error doesn't implement std:fmt:Display in Rust, make sure the data type contains the implementation (impl) of fmt:Display. For example, if we attempt to log a String data type using either print! or println! macros, the code will successfully compile and log the value in the terminal.

fn main() {
    let my_string = "Hello Fellow Rustacean".to_string();
    print!("Logging my_string {}", my_string);
}

In this case, the variable my_string is logged not because it is a String, but because the String data type, which is a built-in struct, has an implementation of fmt:Display as you can see in the source code below.

impl fmt::Display for String {
    #[inline]
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&**self, f)
    }
}

If the String type wouldn’t have the implementation Display trait, it wouldn’t be possible to log its value using the print! macro.

Implementing Display Trait to Data Types

Remember when we attempted to log a Vec<T>, but we came across with the error Vec<string>doesn't implement std:fmt:Display? Here’s the snippet of code to refresh your memory.

fn main() {
    let mut array_cities: Vec<String> = Vec::new();
    array_cities.push("San Francisco".to_string());
    array_cities.push("Santo Domingo".to_string());

    print!("testing {}", array_cities); // It will error here
}

Now you know it is because the growable array (Vec<T>) hasn’t implemented the Display trait. If you are extra curious about this, you can always verify the set of implementations assigned to the Vec struct in the Rust source code.

In case you decide to check the source code, simply do a simple search (Ctrl+F) once you open the page and look for fmt::Display for Vec. You will find there are no results.

Cannot Implement Traits Outside of the Data Type Crate

The solution is to implement the Display Trait to Vec<T>. However, there is a small problem. We can only implement the Display Trait as long as we are in the same crate where the Vec<T> is defined.

This means we would have to implement this trait in Vec<T> crate. Technically, we could make those changes locally, but it means overriding the Rust source code, which is not recommended.

On top of that, you would have to manually update this code every time the rust library is installed in the directory, which is not an ideal solution when attempting to make automated deployments.

In other words, it is not possible to implement the Display trait in Vec<T> in our crate.

Solution: Generate a Wrapper Struct to Implement Trait

Luckily, there is a workaround for this: to generate a wrapper or custom struct which uses a data type defined outside the crate. Once the struct is created, implement the Display trait.

In the following example, we are going to define the struct WrapperVec to contain cities which is the Vec<String>.

struct WrapperVec {
    cities: Vec<String>
}

Once the WrapperVec is created, implement the Display trait to the new struct. For the sake of simplicity, we are only going to log the text Cities! rather the values that could be stored in WrapperVec.cities.

use std::fmt;

impl fmt::Display for WrapperVec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Cities!")
    }
}

Now, if we update our main function to use a WrapperVec struct and log (print!) its value, we no longer see the error doesn't implement std:fmt:Display.

use std::fmt;

fn main() {
    let mut array_cities: Vec<String> = Vec::new();
    array_cities.push("San Francisco".to_string());
    array_cities.push("Santo Domingo".to_string());

    let wrapper_vec = WrapperVec {
        cities: array_cities,
    };

    print!("testing {}", wrapper_vec);
}

struct WrapperVec {
    cities: Vec<String>
}

impl fmt::Display for WrapperVec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Cities!")
    }
}. 

Unfortunately, the fmt function doesn’t display the elements of the growable array.

At this point, we can update the logic of the fmt function to iterate through each of the elements of the Vec<String> array and list each city, or any value, defined in the program.

impl fmt::Display for WrapperVec {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "Cities found!")?;

        for city in self.cities.iter() {
            writeln!(f, "- {}", city)?;
        }

        write!(f, "Done!")
    }
}

If you updated the code using the previous example and execute it, you should see an output similar to the following screenshot.

Final output after implementing Display trait to wrapper struct

Conclusion

All in all, the reason behind getting the error doesn't implement std:fmt:Display is that the data type doesn’t have implemented the Display trait. To solve this you should either use a data type that does implement the Display trait or implement the trait to the data type that you are attempting to use in the code.

Did you learn something new?

I hope this article helped you understand a common error new Rust programmers tend to come across, and also learn how to fix it.

Share your thoughts by replying on Twitter of Become A Better Programmer or to my personal Twitter account.