Toni Cárdenas
Toni Cárdenas

Reputation: 6848

Take any type that can yield a char iterator, plus &str

I want to write a function tokenize that takes chars from an iterator. Like this:

fn tokenize<F: IntoIterator<Item=char>>(file: F)

If I want to use a &str with this function, I can do it like this:

tokenize("foo".chars())

That works OK.

Now, I'd like to avoid that .chars() specifically for type &str, since I know that, if I'm given a &str, I can make it what I want by calling .chars().

I tried declaring a new trait, IntoChars, and implement it for &str:

trait IntoChars {
    type T: Iterator<Item=char>;
    fn into_chars(&self) -> Self::T;
}

impl<'a> IntoChars for &'a str {
    type T = Chars<'a>;
    fn into_chars(&self) -> Self::T {
        self.chars()
    }
}

If then I make my function like this:

tokenize<F: IntoChars>(file: F)

it works. Nice! But I want to implement IntoChars also for any type that can give me chars, ie. is IntoIterator<Item=char>.

impl<T: IntoIterator<Item=char>> IntoChars for T {
    type T = T::IntoIter;
    fn into_chars(self) -> Self::T {
        self.into_iter()
    }
}

That doesn't work. For two reasons:

  1. IntoIter::into_iter takes a self, but IntoChars::into_chars takes a &self. This is specifically because I don't want to consume &str when calling its into_chars method unnecessarily. Is there any way to achieve both?
  2. OK, let's say that I make IntoChars::into_chars take a self. This still doesn't work; I get: error: conflicting implementations for trait `IntoChars` [E0119]. But if I remove the impl<'a> IntoChars for &'a str, I get error: the trait `core::iter::Iterator` is not implemented for the type `&str`. Why is is conflicting then?

Any way I can achieve this?

Upvotes: 1

Views: 142

Answers (1)

Shepmaster
Shepmaster

Reputation: 432059

Let's look at your two implementations, which I've rewritten in the more verbose form:

impl<T> IntoChars for T
    where T: IntoIterator<Item = char>

impl<'a> IntoChars for &'a str

What should the compiler do when whoever owns either the trait IntoIterator or the type &str decides to implement IntoIterator for &str? All of the sudden, you'd have conflicting implementations for the same trait; There are two ways that IntoChars would be implemented for &str.

Other keywords for this problem are trait coherence and orphan rules.

To be able to compile this, you'd need impl specialization, which is an RFC that has not been accepted yet.

Upvotes: 2

Related Questions