Blanket impl of From for impls of some local trait

I'm struggling to figure out how to fit something into Rust's trait implementation rules.

For background, I'm working on a project where Unicode normalization, canonicalization, and a few other transforms are useful to do, and useful to annotate into the type system so that other parts of the program can reject strings which are not in the required form. It would be useful to provide an impl of From<String> for any type that carries a normalized string, but I can't figure out how to express this.

Here's my best effort:

trait Normalize {
    fn normalize(s: &str) -> String;

    fn new(s: String) -> Self;
}

impl<T> From<String> for T where T: Normalize {
    fn from(s: String) -> Self {
        let s = Self::normalize(&s);
        Self::new(s)
    }
}

// Usage example: asserting that a string is in Unicode Normalization Form C
// through the type system, converting it to that form as necessary.

struct Nfc(String);

impl Normalize for Nfc {
    fn new(s: String) -> Self { Self(s) }
    
    fn normalize(s: &str) -> String {
        // Real impl uses the unicode_normalization crate's `.nfc()` iterator
        // to generate a normalized string.
        s.chars()/* .nfc() */.collect()
    }
}

impl std::ops::Deref for Nfc {
    type Target = String;
    
    fn deref(&self) -> &String {
        let Self(s) = self;
        s
    }
}

fn main() {
    assert_eq!("\u{00C5}", Nfc::from("A\u{030a}".into()).as_str());
}

Rust rejects this program:

error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
 --> src/lib.rs:7:6
  |
7 | impl<T> From<String> for T where T: Normalize {
  |      ^ type parameter `T` must be used as the type parameter for some local type
  |
  = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local
  = note: only traits defined in the current crate can be implemented for a type parameter

For more information about this error, try `rustc --explain E0210`.
error: could not compile `playground` (lib) due to 1 previous error

As best as I understand, this error exists because some type implementing both From<String> and Normalize could exist, even though none actually do. Since Normalize is a local, non-public trait, I can control that, but I don't think the compiler cares.

Is there a way to provide this kind of blanket impl of a foreign trait? What am I missing?

Related IRLO topic:

2 Likes

Thanks. The main option proposed in that thread is a macro to generate per-type impls of From. That's definitely something I'm considering - I'd probably use a custom derive rather than a macro, but that's splitting hairs.

I'd be interested in other alternatives, but it sounds like the thing I'm trying to do is prohibited by the coherence rules outright. If so, it is what it is.

1 Like