Help, generic problem

I think the code is ok, but actually went wrong,
how can i handle such error;

my code :

struct Route<T> {
    path: String,
    generator: T,
}

impl<T> Route<T> {
    fn new(path: String) -> Self {
        let generator = path.split("/").skip(1).map(|x| format!("/{}", x));

        Route { path, generator }
                   // ^^^^^^^^^ expected type parameter `T`, found `Map<Skip<Split<'_, &str>>, ...>`
    }
}

There are two problems here:

  1. The caller (the place where you call new) gets to choose T, not the callee (new itself). Your implementation of new violates this contract, because it doesn't return Route<T> (T being defined by the caller), but Route<Map<_, _>>
  2. You are trying to create a self-referential struct, where the generator field borrows from the path field. Self-referential structs are considered an antipattern and are best to be avoided
6 Likes

@jofas posted the correct reasons for the problem. I don't know if this will help, but it is possible to write a function that returns the iterator:

fn generator(path: &str) -> impl Iterator<Item = String> {
    path.split("/").skip(1).map(|x| format!("/{}", x))
}

playground

You will need to ensure the source of the data (the path String) is in scope while you're using the iterator, as in the playground.

You cannot put the path String and the iterator in the same struct, for reason 2 that @jofas mentioned.

5 Likes

To extend on this:

What should Route<u8>::new do? Or what about Route<SomeTypeThatHasNoConstructors>::new? Generic arguments are arguments, decided by the caller of the function. They’re not the right tool for what you’re trying to do. You probably want to return a Route<impl Iterator<Item = String>> or make your generator field a Box<dyn Iterator>.

3 Likes