rustc frustrations: trait coherence edition
Tags: rust, rant
Published: 2021-09-25 17:56:53

---
title: rustc frustrations: trait coherence edition
published: true
description: Once again, Rust's trait coherence rules have pissed me off.
tags: rust, rant
---

Problem: for some hobby-hacking OS dev I’m doing, I need (want) to implement drivers for a few different UART consoles. The first part of these drivers I build is for writing output from my little baby OS over the UART, which I can read from a real computer. This allows me to do some basic `printf` debugging for the rest of my system.

A naive good-enough implementation of writing a byte to a UART is: wait in a busy-loop until the UART is ready for a byte, then push the byte into it. To write a string, just do that for every byte in the string. To do this, you need to implement two operations which are specific to the UART chip you’re using:

- `can_write`: test if it’s ready to receive a byte
- `unchecked_write_byte`: push the byte into it

Then, you plug those two implementations into a generic `write_byte`, which in turn you plug into a generic `write_str`, and now you can write to your UART!

To that effect, the code I wrote was:

```rust
pub trait Console {
  /// write `byte` to `self` without first verifying that `self` is ready to recieve a
  /// byte.
  unsafe fn unchecked_write_byte(&mut self, byte: u8);
  fn can_write(&mut self) -> bool;
  fn blocking_write_byte(&mut self, byte: u8) {
    block_until(|| self.can_write(), 1);
    unsafe { self.unchecked_write_byte(byte); }
  }
}

impl<T> core::fmt::Write for T
where T: Console,
{
  fn write_str(&mut self, s: &str) -> fmt::Result {
    for b in s.bytes() {
      self.blocking_write_byte(b);
    }
    Ok(())
  }
}
```

Which, of course, doesn't compile. Can you spot the error? If you've written any serious amount of Rust, I'm sure you can, but for non-Rustaceans, the problem is:

```
error[E0119]: conflicting implementations of trait `core::fmt::Write` for type `&mut _`
  --> src/console.rs:18:1
   |
18 | / impl<T> core::fmt::Write for T
19 | | where T: Console,
20 | | {
21 | |     fn write_str(&mut self, s: &str) -> fmt::Result {
...  |
26 | |     }
27 | | }
   | |_^
   |
   = note: conflicting implementation in crate `core`:
           - impl<W> Write for &mut W
             where W: Write, W: ?Sized;
   = note: downstream crates may implement trait `console::Console` for type `&mut _`

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

The rule at play here is called "trait coherence," so if you want to read more, that's your search term. The short version is that the compiler requires that there must be at most one implementation of a trait for each type; not type can have two or more competing implementations of the same trait. This saves the compiler developers from having to answer some hard questions about which implementation they should choose when offered alternatives, and mostly seems like a reasonable requirement.

The problem, from where I'm standing, is that the rules which prevent you from defining multiple conflicting trait implementations are a bit overzealous. In my case, no type ever will have conflicting implementations of `Write`, since none of the handful of types for which I've implemented `Console` are mutable references and therefore none of them get the implementation from `Core` for `&mut W: Write + ?Sized`.

But, as the error message points out, "downstream crates may implement trait `console::Console` for type `&mut _`." I'm compiling a binary, not a library, so "downstream crates" is not a meaningful concept, but whatever. Let's try making `Console` `pub(crate)`, so that we know for sure that no "downstream crate" will ever implement `Console` for `&mut W: Write + ?Sized`.

Of couese, changing `pub trait Console` to `pub(crate) trait Console` doesn't change anything. Same error, same meaningless note about downstream crates.

I asked in the Rust Discord about what I should do, and the responses I got basically just made me more frustrated. To be clear, the people who responded were very helpful, and offered what I believe to be the best advice possible. It's just that rustc doesn't permit a good solution to my problem.

As is, I got two plausible suggestions, which look to me like these two implementations:

## Candidate 1: write an `impl Write` for each implementor of `Console`.

This would essentially look like:

```rust
pub trait Console {
  /// write `byte` to `self` without first verifying that `self` is ready to recieve a
  /// byte.
  unsafe fn unchecked_write_byte(&mut self, byte: u8);
  fn can_write(&mut self) -> bool;
  fn blocking_write_byte(&mut self, byte: u8) {
    block_until(|| self.can_write(), 1);
    unsafe { self.unchecked_write_byte(byte); }
  }
  fn write_str(&mut self, s: &str) -> fmt::Result {
    for b in s.bytes() {
      self.blocking_write_byte(b);
    }
    Ok(())
  }
}

impl Console for Pl011 {
  unsafe fn unchecked_write_byte(&mut self, byte: u8) {
    // this part is boring & irrelevant
  }
  fn can_write(&mut self) -> bool {
    // same with this
  }
}

impl fmt::Write for Pl011 {
  fn write_str(&mut self, s: &str) -> fmt::Result {
    Console::write_str(self, s)
  }
}

impl Console for Pc16550d {
  unsafe fn unchecked_write_byte(&mut self, byte: u8) {
    // this part is boring & irrelevant
  }
  fn can_write(&mut self) -> bool {
    // same with this
  }
}

impl fmt::Write for Pc16550d {
  fn write_str(&mut self, s: &str) -> fmt::Result {
    Console::write_str(self, s)
  }
}
```
  

Notice that the two `fmt::Write` implementations have the same text other than the name of the implementing type. Not my favorite...

## Candidate 2: use a generic wrapper struct to implement `Write`.

```rust
pub trait Console {
  /// write `byte` to `self` without first verifying that `self` is ready to recieve a
  /// byte.
  unsafe fn unchecked_write_byte(&mut self, byte: u8);
  fn can_write(&mut self) -> bool;
  fn blocking_write_byte(&mut self, byte: u8) {
    block_until(|| self.can_write(), 1);
    unsafe { self.unchecked_write_byte(byte); }
  }
}

pub struct ConsoleWriter<T>(T);

impl<T> fmt::Write for ConsoleWriter<T>
where T: Console,
{
  fn write_str(&mut self, s: &str) -> fmt::Result {
    let c = &mut self.0;
    for b in s.bytes() {
      c.blocking_write_byte(b);
    }
    Ok(())
  }
}
```

That part alone isn't so bad, but now I also have to wrap my `Console` instances in a `ConsoleWriter` struct, so

```rust
use spin::Mutex;
use crate::driver::uart::Pl011;

pub static CONSOLE: Mutex<Pl011> = Mutex::new(
  unsafe { Pl011::new(0x3F20_1000usize as *mut u8) }
);
```
    
becomes

```rust
use spin::Mutex;
use crate::driver::uart::Pl011;
use crate::console::ConsoleWriter;

pub static CONSOLE: Mutex<ConsoleWriter<Pl011>> = Mutex::new(
  ConsoleWriter(unsafe { Pl011::new(0x3F20_1000usize as *mut u8) })
);
```

Again, I don't love this.

## My solution: just don't implement `Write`.

After writing each of these solutions and considering their merits and flaws, I realized that, hey, who the hell even needs `core` traits? If interacting with the standard library (or the little part of it I can use on a bare-metal target) is going to make me miserable, I just won't do it.

Will this make my code more brittle? Yes. Will it be harder to read, since people joining the project will have to familiarize themselves with my own custom set of traits instead of the standard traits they already know? Probably. Will I lose the ability to interoperate with libraries that use `Write` to implement generic functionality, and end up reinventing the wheel instead? Almost certainly. Do I care? Not at all.