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?