Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to let Rust infer the correct type for an associated type?

Tags:

rust

I'm trying to create a trait which includes functions that return an iterator over some internal data, which can then be implemented by different structs. A minimal example is as follows:

trait WrapperTrait {
    type WrapperIterator: Iterator<Item=u32>;
    fn iter(&self) -> Self::WrapperIterator;
}
struct Wrapper {
    data: Vec<u32>
}
impl WrapperTrait for Wrapper {
    type WrapperIterator = ...;
    fn iter(&self) -> Self::WrapperIterator {
        return self.data.iter().map(|&x| x);
    }
}

However, the type for WrapperIterator is rather complex - from an error message (created via type mismatch) I find it to be:

std::iter::Map<std::slice::Iter<'_, u32>, [[email protected]:45:37: 45:43]>

Is there a way to let Rust infer the correct type for WrapperIterator?

like image 563
Henning Koehler Avatar asked Oct 18 '22 21:10

Henning Koehler


2 Answers

Unfortunately, this is not yet possible in stable rust, but there is a plan to introduce this feature. If you want to try the (currently unstable) feature, you can do so on rust nightly with #![feature(min_type_alias_impl_trait)]. You will have to add a type parameter to your trait :

#![feature(min_type_alias_impl_trait)]

trait WrapperTrait<'a> {
    type WrapperIterator: Iterator<Item = u32> + 'a;
    fn iter(&'a self) -> Self::WrapperIterator;
}

struct Wrapper {
    data: Vec<u32>,
}

impl<'a> WrapperTrait<'a> for Wrapper {
    type WrapperIterator = impl Iterator<Item = u32> + 'a;
    fn iter(&'a self) -> Self::WrapperIterator {
        self.data.iter().map(|&x| x)
    }
}

playground link: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=a9434bcd32ea9c910fd0f9e329d17bd5

To do it without adding a lifetime parameter to the trait, one needs yet another unstable rust feature: Generic Associated Types.

#![feature(min_type_alias_impl_trait)]
#![feature(generic_associated_types)]

trait WrapperTrait {
    type WrapperIterator<'a>: Iterator<Item = u32>;
    fn iter<'a>(&'a self) -> Self::WrapperIterator<'a>;
}

struct Wrapper {
    data: Vec<u32>,
}

impl WrapperTrait for Wrapper {
    type WrapperIterator<'a> = impl Iterator<Item = u32>;
    fn iter<'a>(&'a self) -> Self::WrapperIterator<'a>{
        self.data.iter().map(|&x| x)
    }
}

playground: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=049c875b27a0e9e472d854e6218df267

like image 92
lovasoa Avatar answered Oct 26 '22 22:10

lovasoa


Is there a way to let Rust derive the correct type for WrapperIterator?

No. In fact, this is directly the opposite of what Rust does. You annotate function argument and return types, then the compiler can infer the types inside the method based on that.

Inferring top-level types from the body of the method leads to terrible error messages that occur far away from the location that caused them. Haskell learned this the hard way, and now advocates that all top-level functions have explicit type signatures. Rust chose to never go down that path. See also this answer; while it talks about lifetimes, many of the same points hold.

Your unasked question (how do I return this iterator) already has many duplicates:

  • Returning a closure from a function
  • Correct way to return an Iterator?
  • Return a map iterator which is using a closure in Rust

Thank you for not asking yet another exact duplicate.

like image 28
Shepmaster Avatar answered Oct 27 '22 00:10

Shepmaster