# Reviewing early- / late- bound PR changes ## Topic https://github.com/rust-lang/types-team/issues/62 ## Meeting conclusions ### How do we represent the type of a function? In this example... ```rust fn foo(x: &u32) { } let y = foo; ``` What is the type of `y`? It has to be a `FnDef` type, but the region for `x` should be higher-ranked, and right now our `Ty` structure can't represent that (you would need a `Binder<FnDef>`). Other examples to talk through: **Coercion to a higher-ranked fn pointer.** ```rust fn foo(x: &u32) { } let y = foo; let z: fn(&u32) = y; ``` ### Impl / trait mismatch This is nice, we like writing patterns like this ```rust trait Foo<T> { fn take(&self, x: T); } impl Foo<&u32> for () { fn take(&self, x: &u32) { ... } } // where elided lifetimes are trait Foo<T> { fn take<'a>(&'a self, x: T); } impl<'b> Foo<&'b u32> for () { fn take<'a, 'c>(&'a self, x: &'c u32) { ... } } ``` but how to manage in a principled way? [More examples](https://rust-lang.zulipchat.com/#narrow/stream/326132-t-types.2Fmeetings/topic/2023-03-20.20types-team.2362/near/343139824) **Impl detail.** One way to do it is by "desugaring" impl to something like this ```rust trait Foo<T> { fn take(&self, x: T); } impl<'a> Foo<&'a u32> for () { fn take(&self, x: &'a u32) { /* matches trait */ fn helper(&self, x: &u32) { /* exactly as user wrote it */ } helper(self, x) } } ``` i.e., the "public" signature is the same as trait. **Mapping.** Other idea would be to try and create a mapping between lifetimes, but that can be tricky. e.g. consider this example ```rust trait Foo { fn foo<'a, 'b>(x: &'a u32, y: &'b u32) { } } impl Foo for () { fn foo<'x>(x: &'x u32, y: &'x u32) { } } ``` which would require `'x` be mapped to `'a & 'b` ## Random notes ### nikomatsakis **Niko's ideal.** No difference between early- vs late-bound, just have generics. FnDef type: ```rust= fn foo<'a, T>(...) where T: Trait<'a> ``` the type of `foo` would be `for<'a, T> if (T: Trait<'a>) fn(...)`. Coercing from a type like the above to a fn pointer would entail some rules not unlike today's early- vs late- bound. Haven't though this all the way out. **Thoughts on the PR.** **Are there complications?** Questions to answer: * Today: * Does this have surprise interacts or admit new patterns of code? * Does this move us closer to the architecture we want? * Tomorrow: * What rules do we want so as to achieve "everything is just generics"? * In particular, how to manage "changing amount of late-bound lifetimes between trait/impl" ### lcnr longterm everything should be late-bound. reviewing that PR is confusing because it's from before we added `EarlyBinder`. "Notably, changing the amount of late-bound lifetimes between trait and impl is allowed" is really annoying. https://github.com/rust-lang/rust/pull/108782#issuecomment-1472523987 there's this comment on a related PR and... we have: - it is possible to have a different amount of late bound lifetimes between impl and trait method - longterm everythign should be late bound how would we actually unify this going forward?