---
title: "16.6 Display and ToString"
description: "Implementing the Display trait for human-facing {} output, how it differs from Debug, and the free ToString conversion that Display unlocks."
url: "https://learnrust.net/chapter-16/display-and-tostring/"
last_updated: "2026-06-13"
---

# 16.6 Display and ToString

Lesson [10.7](https://learnrust.net/chapter-10/deriving-debug/) left an IOU: `{}` (the `Display` formatter) is for human-facing output, and you'd have to implement it yourself because the compiler can't guess how your type should read. You now know what "implement a trait" means, so it's time to pay that IOU. `Display` is a trait, you implement it by hand, and doing so is the Rust equivalent of C++'s "overload `operator<<`," a lesson that gets one page here instead of a chapter.

## Why Display can't be derived

Recall the split from lesson [16.5](https://learnrust.net/chapter-16/deriving-traits/): `Debug` (`{:?}`) is derivable because "show the type name and fields" has an obvious form, but `Display` (`{}`) is not, because how a type should appear to a *human* is a design decision only you can make. Should a `Point` display as `(3, 4)`? `3, 4`? `x=3, y=4`? There's no right answer the compiler could pick, so it makes you choose by implementing `Display` yourself.

## Implementing Display

`Display` lives in `std::fmt`, and implementing it means writing one method, `fmt`:

```rust
use std::fmt;

struct Point {
    x: f64,
    y: f64,
}

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

fn main() {
    let p = Point { x: 3.0, y: 4.0 };
    println!("{}", p);
    let s = p.to_string();
    println!("as string: {s}");
}
```

```
(3, 4)
as string: (3, 4)
```

This is the same shape you glimpsed for error types in lesson [12.6](https://learnrust.net/chapter-12/custom-error-types/), now fully explained. The `fmt` method takes `&self` and a `Formatter` (`f`), and uses the `write!` macro, which works exactly like `println!` but writes into the formatter instead of to the screen. You choose the format string, here `"({}, {})"`, which is the design decision the compiler couldn't make. The return type `fmt::Result` reports whether writing succeeded; `write!` produces it, so ending the method with that `write!` is all you need.

Once `Point` implements `Display`, `println!("{}", p)` works, and so does `format!`, `print!`, and every other place `{}` appears. You taught the type how to present itself once, and the whole formatting system now knows.

## ToString comes free

Notice `p.to_string()` in the example. That method came from nowhere you wrote, and that's the bonus: **any type that implements `Display` automatically gets `to_string()`** for free, producing a `String` of its `Display` output. This is a *blanket implementation* in the standard library: it implements the `ToString` trait for every type that implements `Display`, so you implement one trait and get two capabilities. (Blanket implementations, "implement this trait for all types that implement that one," are a powerful pattern you'll meet again; here, just enjoy that `to_string()` is automatic.)

> **Key insight**
>
> `Display` is the one place you spell out how your type looks to a person, and you spell it out once. After that, every formatting context (`{}`, `format!`, `to_string()`, printing in an error message) uses it. Contrast `Debug`, which the compiler derives for the programmer's view. Most types you build for users to see will implement `Display`; most types you build for yourself will just derive `Debug`; many do both, exactly as recommended for error types in lesson [12.6](https://learnrust.net/chapter-12/custom-error-types/).

## The C++ comparison

Worth stating plainly, because it's the chapter's thesis in miniature. In C++, making a type printable with `<<` means overloading `operator<<`, which sits inside a large chapter on operator overloading with its own rules about friend functions, stream references, and return types. In Rust it's one trait with one method, and it's the same machinery as every other trait you've implemented. The next lesson generalizes the point to *all* operators: `+`, `*`, indexing, each is just a trait, and the lesson that handles them is one page where C++ needs sixteen.

> **Best practice**
>
> Implement `Display` for types that have a natural human-readable form and will be shown to users (errors, domain values like money or coordinates, anything that ends up in a message). Don't implement it for types that have no obvious single presentation, or that are purely internal, just derive `Debug` for those. And never put debugging detail in `Display` or polish in `Debug`: `{}` is the clean user-facing form, `{:?}` is the faithful developer dump, and keeping them in their lanes keeps both useful.

## Quiz time

**Question #1**

Why must you implement `Display` by hand when `Debug` can be derived?

<details class="solution">
<summary>Show solution</summary>

`Debug` has an obvious automatic form (show the type name and fields), so the compiler can derive it. `Display` is the human-facing presentation, and how a type should read to a person is a design decision the compiler can't make (is a point `(3, 4)` or `x=3, y=4`?). So you choose the format by implementing `Display` yourself.

</details>

**Question #2**

Implement `Display` for a `Temperature { celsius: f64 }` so it prints like `21.5°C`.

<details class="solution">
<summary>Show solution</summary>

```rust
use std::fmt;

struct Temperature {
    celsius: f64,
}

impl fmt::Display for Temperature {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}°C", self.celsius)
    }
}
```

`write!(f, "{}°C", self.celsius)` writes the chosen format into the formatter. Now `println!("{}", temp)` and `temp.to_string()` both produce `21.5°C`.

</details>

**Question #3**

You implemented `Display` for a type. What other method do you get automatically, and from where?

<details class="solution">
<summary>Show solution</summary>

You get `to_string()`, which returns a `String` of the `Display` output. It comes from a blanket implementation in the standard library that implements `ToString` for every type implementing `Display`. Implement one trait (`Display`), get the second (`ToString`) for free.

</details>

`Display` made `{}` work by implementing a trait. The next lesson reveals that `+`, `*`, `[]`, and the other operators are the *same* story: each is a trait you can implement for your own type, so a `Point + Point` becomes meaningful, in one lesson where C++ needs a whole chapter.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
