3

I found myself writing different functions with an argument under the same bounds, like this:

pub fn foo<T>(mut self, path: T) -> Self where
    T: IntoIterator,
    T::Item: AsRef<str>,
{
    // ...
}

pub fn bar<T>(mut self, path: T) -> Self where
    T: IntoIterator,
    T::Item: AsRef<str>,
{
    // ...
}

Feeling this to be a bit of a chore, I tried to alias these bounds. But I didn’t find a way to. The closest I got, after checking a couple places[1],[2], is this:

trait Path {
    type I: IntoIterator<Item = Self::S>;
    type S: AsRef<str>;
}

impl<T, U> Path for T where
    T: IntoIterator<Item = U>,
    U: AsRef<str>,
{
    type I = T;
    type S = U;
}

Now, as an example, this compiles fine:

fn into_vec<T: Path>(it: T::I) -> Vec<String> {
    it.into_iter()
        .map::<String, _>(|x| x.as_ref().into())
        .collect()
}

But when I try to use it:

fn consume<T: Path>() {
    into_vec::<T>(&["one", "two"]);
}

I get the following error:

src/lib.rs:104:19: 104:34 error: mismatched types:
 expected `<T as Path>::I`,
    found `&[&str; 2]`
(expected associated type,
    found &-ptr) [E0308]
src/lib.rs:104     into_vec::<T>(&["one", "two"]);
                                 ^~~~~~~~~~~~~~~

So, no luck. How can I go forward?


1 https://github.com/rust-lang/rust/issues/8634
2 https://stackoverflow.com/a/30424219/3957040

Community
  • 1
  • 1
Pablo
  • 819
  • 8
  • 15
  • Can you show `consume` implemented with the separate traits? It's *very* suspicious to have a method with a generic type that is not used in the arguments *or* the result value. Like, I don't think it's possible. – Shepmaster Feb 10 '16 at 22:54
  • Do you mean something like `noalias_consume` in this playpen (http://is.gd/e9qIKV)? Indeed, it doesn’t work. – Pablo Feb 10 '16 at 23:19

1 Answers1

2

How can I go forward?

You can't directly. Let's look at your function:

fn consume<T: Path>() {
    into_vec::<T>(&["one", "two"]);
}

This says "for any type that implements Path, call into_vec with a slice of strings". However, there's no way that you can guarantee that whatever the T is, it will accept a slice of strings.

Looking in another direction, many types might accept a slice of strings, so it would make T ambiguous.

Looking in a third direction, there's no way for type inference to decide what T should be, as it's not used as an argument or return value.

You can get it to work by explicitly stating which T you want:

fn consume() {
    into_vec::<&[&'static str]>(&["one", "two"]);
}

To clarify, this has nothing to do with combining traits into another trait. This would be a problem with any trait.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366