rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.8k stars 12.66k forks source link

Type inference insufficient around trait parameterized functions #3156

Closed msullivan closed 12 years ago

msullivan commented 12 years ago

This is related to #912.

The following code doesn't work:

fn main() {
    for iter::eachi(some({a: 0})) |i, a| { 
        #debug["%u %d", i, a.a];
    }
}

It fails with

nubs/closure-trait-infer.rs:3:27: 3:30 error: the type of this value must be known in this context
nubs/closure-trait-infer.rs:3         #debug["%u %d", i, a.a];

which is really unfortunate.

eachi is declared with the prototype fn eachi<A,IA:base_iter<A>>(vec: IA, blk: fn(uint, A) -> bool). When called in the above code, IA is instantiated with the type option<{a: int}>, and a note is made that option<{a: int}> must implement the trait base_iter<A>. However, without knowing the details of the implementation, there is no way to know that the only A such that option<{a: int}> implements base_iter<A> is {a: int}.

I can think of a couple of possible solutions, varying wildly in feasibility: 1) Do some sort of opportunistic impl search during typechecking, probably in some super ad-hoc place such as during function argument typechecking, after checking the non-closure arguments but before checking closure arguments. This is not particularly principled but might be pretty feasible.

2) Introduce proper higher kinded type variables and traits over them. Then traits wouldn't be parameterized over type variables but would instead be implemented for higher kinds. The signature of eachi would be fn eachi<A,IA:base_iter>(vec: IA<A>, blk: fn(uint, A) -> bool) and IA<A> would be unified with option<{a: int}> right off the bat.

3) Fix issue #912 so that the type isn't needed while typechecking the closure.

nikomatsakis commented 12 years ago

I agree this would be nice to fix. Considering your points in reverse order:

Moreover, even if we had higher-kinded types, I am not sure that iterable would be a suitable place to use it. Defining iterable that way would rule out a type like BitSet from implementing iterable<int> and no other type.

nikomatsakis commented 12 years ago

To clarify what I wrote about higher-kinded types and inference: what I meant is that somewhere (the impl, I suppose) we'd have to define how IA<A> is mapped to the concrete self type for this impl. I think implicit in your point was an assumption that, to implement an interface with one type parameter, you must supply a nominal type with one parameter. And maybe this is the right way to do it, but it's not the only way to do it.