New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RFC for a more flexible, revised trait matching system #48

Merged
merged 12 commits into from Jun 10, 2014

Conversation

Projects
None yet
@nikomatsakis
Contributor

nikomatsakis commented Apr 16, 2014

Summary

Cleanup the trait, method, and operator semantics so that they are
well-defined and cover more use cases. A high-level summary of the
changes is as follows:

  1. Full support for proper generic traits ("multiparemeter type classes"),
    including a simplified version of functional dependencies that may
    evolve into associated types in the future.
  2. Generalize explicit self types beyond &self and &mut self etc,
    so that self-type declarations like self: Rc<Self> become possible.
  3. Expand coherence rules to operate recursively and distinguish
    orphans more carefully.
  4. Revise vtable resolution algorithm to be gradual.
  5. Revise method resolution algorithm in terms of vtable resolution.
additional crates are loaded.
- **Crate concatentation:** It should always be possible to take two
creates and combine them without causing compilation errors. This
property

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

Incomplete sentence. "This property..."?

@brendanzab

brendanzab Apr 16, 2014

Member

Incomplete sentence. "This property..."?

# Detailed design
In general, I won't give a complete algorithmic specification.
Instead, I refer readers to the [prototype implementation][prototype]. I would

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

Broken link.

@brendanzab

brendanzab Apr 16, 2014

Member

Broken link.

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 16, 2014

Contributor

Shouldn't be...? Look at the end of the file.

@nikomatsakis

nikomatsakis Apr 16, 2014

Contributor

Shouldn't be...? Look at the end of the file.

This comment has been minimized.

@zkamsler
@zkamsler

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Thu, Apr 17, 2014 at 07:39:18AM -0700, zkamsler wrote:

https://github.com/nikomatsakis/trait-resolution-prototype 404s for me. Perhaps it is a private repo?

Sorry, URL corrected. Should have been:

https://github.com/nikomatsakis/trait-matching-algorithm

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Thu, Apr 17, 2014 at 07:39:18AM -0700, zkamsler wrote:

https://github.com/nikomatsakis/trait-resolution-prototype 404s for me. Perhaps it is a private repo?

Sorry, URL corrected. Should have been:

https://github.com/nikomatsakis/trait-matching-algorithm

Show outdated Hide outdated active/0000-traits.md
inputs. Input type parameters must come *before* explicit type
parameters. \[[3](#3)]
## Method self-type syntax

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

I am a huge fan of this. Is it possible that you could introduce this higher up before introducing it in the detailed design?

@brendanzab

brendanzab Apr 16, 2014

Member

I am a huge fan of this. Is it possible that you could introduce this higher up before introducing it in the detailed design?

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 16, 2014

Contributor

It is mentioned in summary point 2.

@nikomatsakis

nikomatsakis Apr 16, 2014

Contributor

It is mentioned in summary point 2.

Show outdated Hide outdated active/0000-traits.md
the rules too, but that is work in progress and beyond the scope of
this RFC. Instead, I'll try to explain in "plain English".
*Note:* I have implemented a

This comment has been minimized.

@jdm

jdm Apr 16, 2014

Unfinished sentence.

@jdm

jdm Apr 16, 2014

Unfinished sentence.

Show outdated Hide outdated active/0000-traits.md
The core method search looks like this:
METHOD-SEARCH(R, m):
let TRAITS = the set of consisting of any in-scope trait T where:

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

Is this meant to be, let TRAITS = the set consisting of any in-scope trait T where:? (remove extra of)

@brendanzab

brendanzab Apr 16, 2014

Member

Is this meant to be, let TRAITS = the set consisting of any in-scope trait T where:? (remove extra of)

Show outdated Hide outdated active/0000-traits.md
well-defined and cover more use cases. A high-level summary of the
changes is as follows:
1. Full support for proper generic traits ("multiparemeter type classes"),

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member

parameter, not paremeter

@cmr

cmr Apr 16, 2014

Member

parameter, not paremeter

Show outdated Hide outdated active/0000-traits.md
Our current rules around operator overloading are too limiting.
Consider the case of defining a mathematical library for modeling
vectors. I might like to overload the `+` operator. Following mathematical
convention, I'd like to say that:

This comment has been minimized.

@bill-myers

bill-myers Apr 16, 2014

In mathematics "vector + scalar" has no meaning, unless the scalar is 0, the vector space is also an algebra or you explicitly specify a vector or finite basis, since there is otherwise no natural way to convert scalars into vectors.

@bill-myers

bill-myers Apr 16, 2014

In mathematics "vector + scalar" has no meaning, unless the scalar is 0, the vector space is also an algebra or you explicitly specify a vector or finite basis, since there is otherwise no natural way to convert scalars into vectors.

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 16, 2014

Contributor

afaik, It's pretty common to use "[x, y, z] + 3" as a shorthand for "[x, y, z] + [3, 3, 3]".

@nikomatsakis

nikomatsakis Apr 16, 2014

Contributor

afaik, It's pretty common to use "[x, y, z] + 3" as a shorthand for "[x, y, z] + [3, 3, 3]".

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

Maybe matrices would be a better example for this? There are many others I can think of, but that is the most prominent.

@brendanzab

brendanzab Apr 16, 2014

Member

Maybe matrices would be a better example for this? There are many others I can think of, but that is the most prominent.

Under this proposal we would keep these shorthands but also permit any
function in a trait to be used as a method, so long as the type of the
first parameter is either `Self` or something derefable `Self`:

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member

What happens when you try to pack a trait which has a fn foo(self: Gc<Self>) into an object, given that the Self ought to be erased?

@cmr

cmr Apr 16, 2014

Member

What happens when you try to pack a trait which has a fn foo(self: Gc<Self>) into an object, given that the Self ought to be erased?

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 01:10:23PM -0700, Corey Richardson wrote:

What happens when you try to pack a trait which has a fn foo(self: Gc<Self>) into an object, given that the Self ought to be erased?

Good point, I didn't address objects. The answer is basically that
Self can only appear in the type of the first argument (same as
today). I'll add some more precise text in a future revision.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 01:10:23PM -0700, Corey Richardson wrote:

What happens when you try to pack a trait which has a fn foo(self: Gc<Self>) into an object, given that the Self ought to be erased?

Good point, I didn't address objects. The answer is basically that
Self can only appear in the type of the first argument (same as
today). I'll add some more precise text in a future revision.

Show outdated Hide outdated active/0000-traits.md
trait Iterator<E> { ... }
It is common to want to write a function that iterates over

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

...?

@brendanzab
It would not be required that the first parameter be named `self`,
though it seems like it would be useful to permit it. It's also
possible we can simply make `self` not be a keyword (that would be my
personal preference, if we can achieve it).

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member

That appeals to me aesthetically, but I'm not sure it's a great idea.

@cmr

cmr Apr 16, 2014

Member

That appeals to me aesthetically, but I'm not sure it's a great idea.

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

Why are you not sure it's a great idea?

@brendanzab

brendanzab Apr 16, 2014

Member

Why are you not sure it's a great idea?

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member

It seems somewhat pointless to allow freedom here if we're going to say "but always just call it self, or you'll be eaten by a grue" like python does (except in the case of metaclasses, where they say "use cls, or the grue will be back")

@cmr

cmr Apr 16, 2014

Member

It seems somewhat pointless to allow freedom here if we're going to say "but always just call it self, or you'll be eaten by a grue" like python does (except in the case of metaclasses, where they say "use cls, or the grue will be back")

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

Oh, you mean not making it a keyword? I would prefer being able to write

fn mul(lhs: T, rhs: U) -> V { ... }

over

fn mul(self: T, other: U) -> V { ... }
@brendanzab

brendanzab Apr 16, 2014

Member

Oh, you mean not making it a keyword? I would prefer being able to write

fn mul(lhs: T, rhs: U) -> V { ... }

over

fn mul(self: T, other: U) -> V { ... }

This comment has been minimized.

@bill-myers

bill-myers Apr 16, 2014

Maybe we could generalize the current syntax, like this: fn foo(Gc<self>)?

Basically, if after splitting on commas, the first token sequence contains the "self" token, then it is intrepreted as "self: T", where T consists of the first token sequence with self replaced with Self.

@bill-myers

bill-myers Apr 16, 2014

Maybe we could generalize the current syntax, like this: fn foo(Gc<self>)?

Basically, if after splitting on commas, the first token sequence contains the "self" token, then it is intrepreted as "self: T", where T consists of the first token sequence with self replaced with Self.

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member

It would only be a "static method" if the first argument did not use the Self type in any way.

@cmr

cmr Apr 16, 2014

Member

It would only be a "static method" if the first argument did not use the Self type in any way.

This comment has been minimized.

@bstrie

bstrie Apr 16, 2014

Contributor

I'd considered that, but I think it could wind up being gross.

struct Foo;
trait Bar {
    fn a_normal_method(x: Self) {}
    fn a_static_method(x: Foo) {}
}
impl Bar for Foo {
    fn a_normal_method(x: Foo) {}
    fn a_static_method(x: Foo) {}
}

Not only does it necessarily restrict the possible types that can appear as the first parameter to static methods, but it also means that you may need to consult the trait definition itself to determine whether a method is static. Even in the common case I think it's simply harder to scan for a type containing Self than just having the self keyword.

@bstrie

bstrie Apr 16, 2014

Contributor

I'd considered that, but I think it could wind up being gross.

struct Foo;
trait Bar {
    fn a_normal_method(x: Self) {}
    fn a_static_method(x: Foo) {}
}
impl Bar for Foo {
    fn a_normal_method(x: Foo) {}
    fn a_static_method(x: Foo) {}
}

Not only does it necessarily restrict the possible types that can appear as the first parameter to static methods, but it also means that you may need to consult the trait definition itself to determine whether a method is static. Even in the common case I think it's simply harder to scan for a type containing Self than just having the self keyword.

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 02:56:40PM -0700, Ben Striegel wrote:

Getting rid of self would seem to mean reintroducing syntax for static methods.

No, it wouldn't. This proposal basically drops any remaining
distinction between "instance" and "static" methods. An instance
method is one that has a first argument that can be deref'd to the
Self type, essentially.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 02:56:40PM -0700, Ben Striegel wrote:

Getting rid of self would seem to mean reintroducing syntax for static methods.

No, it wouldn't. This proposal basically drops any remaining
distinction between "instance" and "static" methods. An instance
method is one that has a first argument that can be deref'd to the
Self type, essentially.

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 04:49:25PM -0700, Ben Striegel wrote:

Not only does it necessarily restrict the possible types that can appear as the first parameter to static methods, but it also means that you may need to consult the trait definition itself to determine whether a method is static. Even in the common case I think it's simply harder to scan for a type containing Self than just having the self keyword.

Don't forget about UFCS. There is no such thing as a static or
instance method going forward anyway, there are just associated fns on
traits. All associated fns can be directly invoked via a syntax like
Trait::method(...) (or, eventually, Self::method(...)). The
question is what subset can be used with method syntax.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 04:49:25PM -0700, Ben Striegel wrote:

Not only does it necessarily restrict the possible types that can appear as the first parameter to static methods, but it also means that you may need to consult the trait definition itself to determine whether a method is static. Even in the common case I think it's simply harder to scan for a type containing Self than just having the self keyword.

Don't forget about UFCS. There is no such thing as a static or
instance method going forward anyway, there are just associated fns on
traits. All associated fns can be directly invoked via a syntax like
Trait::method(...) (or, eventually, Self::method(...)). The
question is what subset can be used with method syntax.

This comment has been minimized.

@bstrie

bstrie Apr 17, 2014

Contributor

Yes, the fate of static methods under this proposal was something that I had been meaning to ask, but didn't see addressed anywhere in the document. It seems as though you know for certain the eventual shape of UFCS and how it will interact with the revised trait system, but I'm still fuzzy on the details. Is it safe to assume that the RFC in PR #4 is essentially correct?

@bstrie

bstrie Apr 17, 2014

Contributor

Yes, the fate of static methods under this proposal was something that I had been meaning to ask, but didn't see addressed anywhere in the document. It seems as though you know for certain the eventual shape of UFCS and how it will interact with the revised trait system, but I'm still fuzzy on the details. Is it safe to assume that the RFC in PR #4 is essentially correct?

Show outdated Hide outdated active/0000-traits.md
the following conditions:
1. The trait being implemented (if any) must be defined in the current crate.
1. At least one of the input type parameters (including but not

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member
@cmr
@bill-myers

This comment has been minimized.

Show comment
Hide comment
@bill-myers

bill-myers Apr 16, 2014

Input vs output trait parameters seems highly confusing: is it not allowed to have two impls that differ only on output types?

If so, then the proposed syntax is extremely unintuitive and IMHO unacceptable, since the normal way to express that is to have type trait members (aka "associated types"), which is what C++ and Scala use for instance.

Otherwise, what's the difference exactly?

bill-myers commented Apr 16, 2014

Input vs output trait parameters seems highly confusing: is it not allowed to have two impls that differ only on output types?

If so, then the proposed syntax is extremely unintuitive and IMHO unacceptable, since the normal way to express that is to have type trait members (aka "associated types"), which is what C++ and Scala use for instance.

Otherwise, what's the difference exactly?

Show outdated Hide outdated active/0000-traits.md
<a name=orphan> *Orphan check*: Every implementation must meet one of
the following conditions:
1. The trait being implemented (if any) must be defined in the current crate.

This comment has been minimized.

@edwardw

edwardw Apr 16, 2014

Only current crate? Does it mean a user type can't implement, say, Drop?

@edwardw

edwardw Apr 16, 2014

Only current crate? Does it mean a user type can't implement, say, Drop?

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member

No. That is satisfied by rule 2. The type the trait is being implemented on is an input type parameter, so if the type is defined within the current crate, the implementation will be allowed.

@cmr

cmr Apr 16, 2014

Member

No. That is satisfied by rule 2. The type the trait is being implemented on is an input type parameter, so if the type is defined within the current crate, the implementation will be allowed.

This comment has been minimized.

@brendanzab

brendanzab Apr 16, 2014

Member

Note condition number 2.

@brendanzab

brendanzab Apr 16, 2014

Member

Note condition number 2.

Show outdated Hide outdated active/0000-traits.md
is, by default, the only input type parameters. All explicit type
parameters on a trait default to *output* unless prefixed by the `in`
keyword. This means that, e.g., the trait `Add` should be defined as:

This comment has been minimized.

@nrc

nrc Apr 16, 2014

Member

As an alternative, could we use a tuple of types for Self? I.e., allow trait Add<LHS, RHS, SUM>: (LHS, RHS) (or something to bound Self to tuples) and impl Add<int, int, int> for (int, int), etc. I'm sure we can do better than this rather verbose first attempt.

I guess this is just an alternate syntax in the end. I would prefer to use an existing language feature than add a new keyword (well, use an existing keyword in a new way). Also I find the use of in here confusing since it is not directly related to the usual meaning of input/output parameters on regular (not type) parameters. E.g, I could have a function which maps T -> T where a type parameter is the type of both input and output parameters (assuming the second use of T is an out param, not a return type).

@nrc

nrc Apr 16, 2014

Member

As an alternative, could we use a tuple of types for Self? I.e., allow trait Add<LHS, RHS, SUM>: (LHS, RHS) (or something to bound Self to tuples) and impl Add<int, int, int> for (int, int), etc. I'm sure we can do better than this rather verbose first attempt.

I guess this is just an alternate syntax in the end. I would prefer to use an existing language feature than add a new keyword (well, use an existing keyword in a new way). Also I find the use of in here confusing since it is not directly related to the usual meaning of input/output parameters on regular (not type) parameters. E.g, I could have a function which maps T -> T where a type parameter is the type of both input and output parameters (assuming the second use of T is an out param, not a return type).

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 01:53:00PM -0700, Nick Cameron wrote:

As an alternative, could we use a tuple of types for Self? I.e.,
allow trait Add<LHS, RHS, SUM>: (LHS, RHS) (or something to bound
Self to tuples) and impl Add<int, int, int> for (int, int),
etc. I'm sure we can do better than this rather verbose first
attempt.

I thought of this and dismissed it for some reason. Maybe because I
thought input type parameters would come up more often. But I've not
found them in practice almost anywhere in the standard library besides
the operator traits. Given the coherence rules I was planning to use,
I think tuples would work, so that might be an appealing
simplification.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 01:53:00PM -0700, Nick Cameron wrote:

As an alternative, could we use a tuple of types for Self? I.e.,
allow trait Add<LHS, RHS, SUM>: (LHS, RHS) (or something to bound
Self to tuples) and impl Add<int, int, int> for (int, int),
etc. I'm sure we can do better than this rather verbose first
attempt.

I thought of this and dismissed it for some reason. Maybe because I
thought input type parameters would come up more often. But I've not
found them in practice almost anywhere in the standard library besides
the operator traits. Given the coherence rules I was planning to use,
I think tuples would work, so that might be an appealing
simplification.

Show outdated Hide outdated active/0000-traits.md
The implicit `Self` type parameter is *always* considered an input.
All explicit type parameters are considered outputs unless declared as
inputs. Input type parameters must come *before* explicit type
parameters. \[[3](#3)]

This comment has been minimized.

@alexcrichton

alexcrichton Apr 16, 2014

Member

How would this play in with unsized T or whatever keyword we're using for unsized types these days? Would an input type which is also unsized look like in unsized T?

(just desiring a few words about what's going to happen, no preference really)

@alexcrichton

alexcrichton Apr 16, 2014

Member

How would this play in with unsized T or whatever keyword we're using for unsized types these days? Would an input type which is also unsized look like in unsized T?

(just desiring a few words about what's going to happen, no preference really)

Show outdated Hide outdated active/0000-traits.md
the following conditions:
1. The trait being implemented (if any) must be defined in the current crate.
1. At least one of the input type parameters (including but not

This comment has been minimized.

@alexcrichton

alexcrichton Apr 16, 2014

Member

This number 2 looks like a 1

@alexcrichton

alexcrichton Apr 16, 2014

Member

This number 2 looks like a 1

This comment has been minimized.

@bstrie

bstrie Apr 16, 2014

Contributor

@alexcrichton Markdown's syntax for making numbered lists doesn't actually care which specific numbers that you use in the source.

  1. This is an 8 in the source
  2. This is a 1
@bstrie

bstrie Apr 16, 2014

Contributor

@alexcrichton Markdown's syntax for making numbered lists doesn't actually care which specific numbers that you use in the source.

  1. This is an 8 in the source
  2. This is a 1

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member

@bstrie but we humans reading plaintext do care.

@cmr

cmr Apr 16, 2014

Member

@bstrie but we humans reading plaintext do care.

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 02:05:59PM -0700, Alex Crichton wrote:

This number 2 looks like a 1

This was intentional. Markdown renumbers lists. I suggest you
all view the stylized view for goodness sakes! :)

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 02:05:59PM -0700, Alex Crichton wrote:

This number 2 looks like a 1

This was intentional. Markdown renumbers lists. I suggest you
all view the stylized view for goodness sakes! :)

Show outdated Hide outdated active/0000-traits.md
| (..., T, ...)
| X<..., T, ...> where X is not bivariant with respect to T
Here is a simple example that is OK:

This comment has been minimized.

@zkamsler

zkamsler Apr 16, 2014

this example appears to be absent.

@zkamsler

zkamsler Apr 16, 2014

this example appears to be absent.

@ben0x539

This comment has been minimized.

Show comment
Hide comment
@ben0x539

ben0x539 Apr 16, 2014

Wouldn't it be nicer to annotate the output types rather than the input types?

ben0x539 commented Apr 16, 2014

Wouldn't it be nicer to annotate the output types rather than the input types?

// Case 1.
if R <: E:
we're done.

This comment has been minimized.

@zkamsler

zkamsler Apr 16, 2014

Perhaps I am missing something, but, as written here, wouldn't case 1 spuriously accept a reference in a method that requires a value parameter (since R was derefed in METHOD-SEARCH)?

@zkamsler

zkamsler Apr 16, 2014

Perhaps I am missing something, but, as written here, wouldn't case 1 spuriously accept a reference in a method that requires a value parameter (since R was derefed in METHOD-SEARCH)?

This comment has been minimized.

@cmr

cmr Apr 16, 2014

Member

I don't believe it will. Note that the only subtyping is with regions, so R and E must be the same type, excepting their lifetimes.

@cmr

cmr Apr 16, 2014

Member

I don't believe it will. Note that the only subtyping is with regions, so R and E must be the same type, excepting their lifetimes.

This comment has been minimized.

@zkamsler

zkamsler Apr 16, 2014

I meant that, since R has been derefed by the time it gets to RECONCILE, R would be the same as E.

e.g.:

struct Foo { ... }
trait Bar {
  fn consume(self) { ... }
}
impl Bar for Foo { ... }

// should be an error, unless Foo is Copy
fn consume_foo(foo:&Foo) {
  foo.consume();
}

METHOD-SEARCH will deref &Foo to Foo (since that what Bar is defined on), and calls RECONCILE(Foo, Bar, m). E will be Foo. Foo <: Foo, so case 1 applies.

@zkamsler

zkamsler Apr 16, 2014

I meant that, since R has been derefed by the time it gets to RECONCILE, R would be the same as E.

e.g.:

struct Foo { ... }
trait Bar {
  fn consume(self) { ... }
}
impl Bar for Foo { ... }

// should be an error, unless Foo is Copy
fn consume_foo(foo:&Foo) {
  foo.consume();
}

METHOD-SEARCH will deref &Foo to Foo (since that what Bar is defined on), and calls RECONCILE(Foo, Bar, m). E will be Foo. Foo <: Foo, so case 1 applies.

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 03:00:29PM -0700, zkamsler wrote:

I meant that, since 'R' has been derefed by the time it gets to RECONCILE, R would be the same as E.

e.g.:

struct Foo { ... }
trait Bar {
  fn consume(self) { ... }
}
impl Bar for Foo { ... }

// should be an error, unless Foo is Copy
fn consume_foo(foo:&Foo) {
  foo.consume();
}

METHOD-SEARCH will deref &Foo to Foo (since that what Bar is defined on), and calls RECONCILE(Foo, Bar, m). E will be Foo. Foo <: Foo, so case 1 applies.

Yes. This is all correct. However, what you are missing is that just
because method-search succeeds doesn't mean that all other type checks
succeed. The borrow checker would then later flag this as an error as
a move out of a borrowed location.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 03:00:29PM -0700, zkamsler wrote:

I meant that, since 'R' has been derefed by the time it gets to RECONCILE, R would be the same as E.

e.g.:

struct Foo { ... }
trait Bar {
  fn consume(self) { ... }
}
impl Bar for Foo { ... }

// should be an error, unless Foo is Copy
fn consume_foo(foo:&Foo) {
  foo.consume();
}

METHOD-SEARCH will deref &Foo to Foo (since that what Bar is defined on), and calls RECONCILE(Foo, Bar, m). E will be Foo. Foo <: Foo, so case 1 applies.

Yes. This is all correct. However, what you are missing is that just
because method-search succeeds doesn't mean that all other type checks
succeed. The borrow checker would then later flag this as an error as
a move out of a borrowed location.

@cmr

This comment has been minimized.

Show comment
Hide comment
@cmr

cmr Apr 16, 2014

Member

@ben0x539 no, because being an input is going to be relatively rare. Marking the inputs reduces annotation burden.

Member

cmr commented Apr 16, 2014

@ben0x539 no, because being an input is going to be relatively rare. Marking the inputs reduces annotation burden.

Show outdated Hide outdated active/0000-traits.md
necessarily `Self`) must meet the following grammar, where `C`
is a struct or enum defined within the current crate:
T = C

This comment has been minimized.

@ben0x539

ben0x539 Apr 16, 2014

(This seems to need at least one more space to render in markdown as a code block nested inside the enumeration.)

@ben0x539

ben0x539 Apr 16, 2014

(This seems to need at least one more space to render in markdown as a code block nested inside the enumeration.)

@ben0x539

This comment has been minimized.

Show comment
Hide comment
@ben0x539

ben0x539 Apr 16, 2014

If I understand associated types in type families correctly, they basically wouldn't show up in the type parameters to the trait/type class, just among the methods. Maybe I'm getting ahead of ourselves here, but if we do get associated items I think it would also make our 'multi-param' traits declaration easier to visually parse to have the input types unannotated right there in the trait Whatever<Input> { bit and the output types as associated types among the methods.

Anyway, this all looks very exciting :)

ben0x539 commented Apr 16, 2014

If I understand associated types in type families correctly, they basically wouldn't show up in the type parameters to the trait/type class, just among the methods. Maybe I'm getting ahead of ourselves here, but if we do get associated items I think it would also make our 'multi-param' traits declaration easier to visually parse to have the input types unannotated right there in the trait Whatever<Input> { bit and the output types as associated types among the methods.

Anyway, this all looks very exciting :)

Show outdated Hide outdated active/0000-traits.md
implement `Add` for `int`. However, the type `RHS` is different in
each case, and `RHS` is an input type parameter.
Note that it is crucial for `RHS to be an input type parameter.

This comment has been minimized.

@bstrie

bstrie Apr 16, 2014

Contributor

Missing backtick

@bstrie

bstrie Apr 16, 2014

Contributor

Missing backtick

Show outdated Hide outdated active/0000-traits.md
is one that is used to decide what impl applies. An *output* type
parameter is one that is determined by the impl. In the case of the
`Add` trait, the *inputs* would be the implicit type parameter `Self`
as well as `RHS`. The *output* would be the type `SUM`.

This comment has been minimized.

@eddyb

eddyb Apr 16, 2014

Member

Wouldn't this be best implemented with associated types?

@eddyb

eddyb Apr 16, 2014

Member

Wouldn't this be best implemented with associated types?

This comment has been minimized.

@pnkfelix

pnkfelix Apr 17, 2014

Member

There is a section later in the document entitled: "How do functional dependencies and associated types relate?" which discusses cases where fundeps may be more appropriate than associated types. (I think the summary is "We will want associated types, but we think we want fundeps too, and this RFC is addressing fundeps.")

@pnkfelix

pnkfelix Apr 17, 2014

Member

There is a section later in the document entitled: "How do functional dependencies and associated types relate?" which discusses cases where fundeps may be more appropriate than associated types. (I think the summary is "We will want associated types, but we think we want fundeps too, and this RFC is addressing fundeps.")

Inherent methods can be "desugared" into traits by assuming a trait
per struct or enum. Each impl like `impl Foo` is effectively an
implementation of that trait, and all those traits are assumed to be
imported and in scope.

This comment has been minimized.

@bstrie

bstrie Apr 16, 2014

Contributor

I could use some clarification here. You can't assume a single trait per struct or enum if a given struct or enum can have multiple impl blocks. Would such desugaring involve concatenating all impl blocks for the same type in any given scope?

@bstrie

bstrie Apr 16, 2014

Contributor

I could use some clarification here. You can't assume a single trait per struct or enum if a given struct or enum can have multiple impl blocks. Would such desugaring involve concatenating all impl blocks for the same type in any given scope?

type `T` could implement `Foo` and then record for later that `T` must
implement `Base`. This will lead to weird errors.
### Overly conservative coherence

This comment has been minimized.

@bstrie

bstrie Apr 16, 2014

Contributor

Would this resolve rust-lang/rust#3429 ?

@bstrie

bstrie Apr 16, 2014

Contributor

Would this resolve rust-lang/rust#3429 ?

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 03:33:28PM -0700, Ben Striegel wrote:

Would this resolve rust-lang/rust#3429 ?

Probably not. That particular example involved two blanket impls, one
for all T:FooBase and one for all T:BarBase. Coherence cannot
currently know whether there exists a type Foo that implements both
FooBase and BarBase. We'd have to add some additional feature,
such as "negative trait bounds" (does not implement this trait) or
some way to declare traits as exclusive (which is basically the same
thing).

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 03:33:28PM -0700, Ben Striegel wrote:

Would this resolve rust-lang/rust#3429 ?

Probably not. That particular example involved two blanket impls, one
for all T:FooBase and one for all T:BarBase. Coherence cannot
currently know whether there exists a type Foo that implements both
FooBase and BarBase. We'd have to add some additional feature,
such as "negative trait bounds" (does not implement this trait) or
some way to declare traits as exclusive (which is basically the same
thing).

Show outdated Hide outdated active/0000-traits.md
**Note 4:** The prioritization of inherent methods could be
reconsidered after DST has been implemented. It is currently needed to
make impls like `impl Trait for @Trait` work.

This comment has been minimized.

@bstrie

bstrie Apr 16, 2014

Contributor

@Trait ?

@bstrie

bstrie Apr 16, 2014

Contributor

@Trait ?

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 04:37:13PM -0700, Ben Striegel wrote:

@Trait ?

I date myself.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 04:37:13PM -0700, Ben Striegel wrote:

@Trait ?

I date myself.

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 01:24:16PM -0700, bill-myers wrote:

Input vs output types seems highly confusing. Is it not allowed to have two impls that different only on output types?

That is correct.

If so, then the proposed syntax is extremely unintuitive, since the normal way to express that is to have type trait members (aka "associated types"), which is what C++ and Scala use for instance.

I address some of these questions below, though I think that text was
accidentally omitted from my first commit. TL;DR is that while I agree
associated types have advantages, we need time to bake the design and
work through its full implications, and I suspect that there may still
be some role to play for in/out parameters for ergonomic reasons.

It is inaccurate to say that Scala uses associated types for output
types. Scala does offer this feature, but in some cases it uses type
parameters as well. For example, consider the Scala Iterator
type. Scala's blend of inheritance and type classes makes direct
comparisons somewhat difficult in any case.

In any case, the goal with supporting the in/out distinction is to
give us a short-term path to annotate our libraries properly, and
allows us to say that associated items are not a 1.0 goal (we have
enough of those). I do hope we can get associated items before 1.0,
and while I am working on implementing this RFC I do plan to keep an
eye out to determine what it would take to add them.

Contributor

nikomatsakis commented Apr 17, 2014

On Wed, Apr 16, 2014 at 01:24:16PM -0700, bill-myers wrote:

Input vs output types seems highly confusing. Is it not allowed to have two impls that different only on output types?

That is correct.

If so, then the proposed syntax is extremely unintuitive, since the normal way to express that is to have type trait members (aka "associated types"), which is what C++ and Scala use for instance.

I address some of these questions below, though I think that text was
accidentally omitted from my first commit. TL;DR is that while I agree
associated types have advantages, we need time to bake the design and
work through its full implications, and I suspect that there may still
be some role to play for in/out parameters for ergonomic reasons.

It is inaccurate to say that Scala uses associated types for output
types. Scala does offer this feature, but in some cases it uses type
parameters as well. For example, consider the Scala Iterator
type. Scala's blend of inheritance and type classes makes direct
comparisons somewhat difficult in any case.

In any case, the goal with supporting the in/out distinction is to
give us a short-term path to annotate our libraries properly, and
allows us to say that associated items are not a 1.0 goal (we have
enough of those). I do hope we can get associated items before 1.0,
and while I am working on implementing this RFC I do plan to keep an
eye out to determine what it would take to add them.

@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Wed, Apr 16, 2014 at 02:38:21PM -0700, Benjamin Herr wrote:

Wouldn't it be nicer to annotate the output types rather than the input types?

No. We did a survey and almost all existing types ought to be output
types.

Contributor

nikomatsakis commented Apr 17, 2014

On Wed, Apr 16, 2014 at 02:38:21PM -0700, Benjamin Herr wrote:

Wouldn't it be nicer to annotate the output types rather than the input types?

No. We did a survey and almost all existing types ought to be output
types.

@dobkeratops

This comment has been minimized.

Show comment
Hide comment
@dobkeratops

dobkeratops Apr 17, 2014

"No. We did a survey and almost all existing types ought to be output types."

But is that just a consequence of the current lack of type deduction & multiparameters - in C++ is it more common that you specify input types, and results are infered via decltype/ auto etc.
If rust got that ability , would the situation change.

at the minute, for example, i gather selecting a matrix multiply vs matrix x vector can be 'hacked' by relying on using an output parameter; trait MatrixMul <LHS,RESULT>

but you wouldn't have bothered doing it that way if you had multi-param typeclasses, and deduction.
I think. its easier to think forwards than backwards. trait MatrixMul <LHS,RHS> { fn mul (LHS,RHS)->typeof(....) { ... }.
(Excuse the pseudocode , i dont know what you'll end up with )

thats me coming from C++, i suppose haskellers might have a different opinion.

dobkeratops commented Apr 17, 2014

"No. We did a survey and almost all existing types ought to be output types."

But is that just a consequence of the current lack of type deduction & multiparameters - in C++ is it more common that you specify input types, and results are infered via decltype/ auto etc.
If rust got that ability , would the situation change.

at the minute, for example, i gather selecting a matrix multiply vs matrix x vector can be 'hacked' by relying on using an output parameter; trait MatrixMul <LHS,RESULT>

but you wouldn't have bothered doing it that way if you had multi-param typeclasses, and deduction.
I think. its easier to think forwards than backwards. trait MatrixMul <LHS,RHS> { fn mul (LHS,RHS)->typeof(....) { ... }.
(Excuse the pseudocode , i dont know what you'll end up with )

thats me coming from C++, i suppose haskellers might have a different opinion.

An interesting thought would be to change this rule and say that we
always *autoderef first* and only resolve the method against the
innermost reference. Note that UFCS provides an explicit "opt-out" if
this is not what was desired. This should also have the (beneficial,

This comment has been minimized.

@eddyb

eddyb Apr 17, 2014

Member

Wouldn't that make Rc's .clone() method much harder to use (and this is just one example)?
I'm not advocating cloning often, but handing out Rc handles gets messy quick as it is right now (I may have some @T -> Rc<T> changes to exemplify soon).
With UFCS, it would be even worse, having to call Clone::clone(&handle) instead of handle.clone().

Then again, this might be a good time to consider a shorthand like .give() or .loan() for rc.clone(), though those aren't the best naming choices.

@eddyb

eddyb Apr 17, 2014

Member

Wouldn't that make Rc's .clone() method much harder to use (and this is just one example)?
I'm not advocating cloning often, but handing out Rc handles gets messy quick as it is right now (I may have some @T -> Rc<T> changes to exemplify soon).
With UFCS, it would be even worse, having to call Clone::clone(&handle) instead of handle.clone().

Then again, this might be a good time to consider a shorthand like .give() or .loan() for rc.clone(), though those aren't the best naming choices.

This comment has been minimized.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Thu, Apr 17, 2014 at 04:29:42AM -0700, Eduard Burtescu wrote:

Wouldn't that make Rc's .clone() method much harder to use (and this is just one example)?

Yes, you would have to write either Clone::clone(&rc) or
Rc::clone(&rc), I think, to clone the RC wrapper. Which is
unfortunate, since cloning the RC wrapper is a common operation. This
is the one interaction that's been stopping me from writing this
proposal out.

The fact is, though, that autoderef and traits like Clone and Eq,
which are implemented on almost every type, are a known hazard. They
frequently don't do what you want.

I haven't had time to think about it much yet, but I was thinking it'd
be nice to have a kind of compromise, that allows a smart pointer
(like Rc) to declare a fixed set of methods (and maybe fields) that
it exports and which explicitly override the referent. clone() for
Rc might be an example. This would probably be done by declaring a
version of clone using an interior method. But this whole issue is
separate from this proposal anyway.

@nikomatsakis

nikomatsakis Apr 17, 2014

Contributor

On Thu, Apr 17, 2014 at 04:29:42AM -0700, Eduard Burtescu wrote:

Wouldn't that make Rc's .clone() method much harder to use (and this is just one example)?

Yes, you would have to write either Clone::clone(&rc) or
Rc::clone(&rc), I think, to clone the RC wrapper. Which is
unfortunate, since cloning the RC wrapper is a common operation. This
is the one interaction that's been stopping me from writing this
proposal out.

The fact is, though, that autoderef and traits like Clone and Eq,
which are implemented on almost every type, are a known hazard. They
frequently don't do what you want.

I haven't had time to think about it much yet, but I was thinking it'd
be nice to have a kind of compromise, that allows a smart pointer
(like Rc) to declare a fixed set of methods (and maybe fields) that
it exports and which explicitly override the referent. clone() for
Rc might be an example. This would probably be done by declaring a
version of clone using an interior method. But this whole issue is
separate from this proposal anyway.

@bill-myers

This comment has been minimized.

Show comment
Hide comment
@bill-myers

bill-myers Apr 17, 2014

As far as I can tell, output type parameters in this RFC are completely equivalent to associated types, except for different syntax and the fact that associated types are identified by name and not parameter position.

That is:

trait Foo<in T, U>
{}

is equivalent to:

trait Foo<T>
{
    type U;
}

It's true that you'll need to invent some syntax to constrain associated types: obvious options are Foo<T>[U = Bar], Foo<T>{U = Bar} or even just Foo<T, U = Bar> (with the latter, I'd suggest putting associated types and generic parameters in the same namespace, so that future keyword-based generic parameters can be added compatibly).

This syntax would of course be usable with trait objects too.

Some syntax to refer to them both from types and values is also needed, such as X::U from C++, and either value.U or something like typeof(value)::U.

I think that the associated type syntax is far more intuitive, since it's obvious that you can only implement the trait once regardless of output types, while you need to read the language manual to figure out what "in" means and to realize that despite lack of type erasure, parameters are "out" by default ("in" may be confused for a variance annotation, for instance).

Also, it distinguishes between "input" and "output" parameters in trait uses in addition to declarations, making it easier to read code.

The special associated type constraint syntax would be unique to Rust, but also IMHO relatively intuitive and especially hard to misinterpret.

bill-myers commented Apr 17, 2014

As far as I can tell, output type parameters in this RFC are completely equivalent to associated types, except for different syntax and the fact that associated types are identified by name and not parameter position.

That is:

trait Foo<in T, U>
{}

is equivalent to:

trait Foo<T>
{
    type U;
}

It's true that you'll need to invent some syntax to constrain associated types: obvious options are Foo<T>[U = Bar], Foo<T>{U = Bar} or even just Foo<T, U = Bar> (with the latter, I'd suggest putting associated types and generic parameters in the same namespace, so that future keyword-based generic parameters can be added compatibly).

This syntax would of course be usable with trait objects too.

Some syntax to refer to them both from types and values is also needed, such as X::U from C++, and either value.U or something like typeof(value)::U.

I think that the associated type syntax is far more intuitive, since it's obvious that you can only implement the trait once regardless of output types, while you need to read the language manual to figure out what "in" means and to realize that despite lack of type erasure, parameters are "out" by default ("in" may be confused for a variance annotation, for instance).

Also, it distinguishes between "input" and "output" parameters in trait uses in addition to declarations, making it easier to read code.

The special associated type constraint syntax would be unique to Rust, but also IMHO relatively intuitive and especially hard to misinterpret.

@ghost

This comment has been minimized.

Show comment
Hide comment
@ghost

ghost Apr 18, 2014

I agree with bill-myers on output parameters being better served as associated types. A problem with both the current design and this proposal is that you end up having to repeat a lot of bounds like the ones for N in the following impl for S:

trait Foo<N: Unsigned + TotalOrd> {
    fn foo(&self) -> N;
}

struct S<F>;

impl<N: Unsigned + TotalOrd, F: Foo<N>> Iterator<F> for S<F> {
    ...
}

Whereas, if output parameters were to become associated types instead, that example would be simpler:

trait Foo {
    type N: Unsigned + TotalOrd;

    fn foo(&self) -> N;
}

struct S<F>;

impl<F: Foo> Iterator<F> for S<F> {
    ...
}

ghost commented Apr 18, 2014

I agree with bill-myers on output parameters being better served as associated types. A problem with both the current design and this proposal is that you end up having to repeat a lot of bounds like the ones for N in the following impl for S:

trait Foo<N: Unsigned + TotalOrd> {
    fn foo(&self) -> N;
}

struct S<F>;

impl<N: Unsigned + TotalOrd, F: Foo<N>> Iterator<F> for S<F> {
    ...
}

Whereas, if output parameters were to become associated types instead, that example would be simpler:

trait Foo {
    type N: Unsigned + TotalOrd;

    fn foo(&self) -> N;
}

struct S<F>;

impl<F: Foo> Iterator<F> for S<F> {
    ...
}
@dobkeratops

This comment has been minimized.

Show comment
Hide comment
@dobkeratops

dobkeratops Apr 18, 2014

Are you going to be able to work the same way as in C++ where you can just specify input types, and deduce the output types - or would that not play well with the 2-way inference.
(i'm pretty certain this idea of annotating 'in' is just due to things that are currently ommisions)

trait Dot<V:Vector> {
    type Scalar = type_of( a[0]*b[0] )  ../// if its 'deduced' like this, i can plug in fixed-point types and the shift- value is calculated..
    fn dot(a:&V, b:&V)->Scalar;
}
// ^^^ but will that be insufficient information to satisfy rust? 
// do you want to be able to infer backwards from code that expects a certain scalar type..
// are you still going to need to place type-param bounds on the outputs ?
// would you end up needing to specify both ?

trait Dot<V> {
    type Scalar: TotalOrd  = type_of( a[0]*b[0] ) ;
    fn dot(a:&V, b:&V)->Scalar;
}

dobkeratops commented Apr 18, 2014

Are you going to be able to work the same way as in C++ where you can just specify input types, and deduce the output types - or would that not play well with the 2-way inference.
(i'm pretty certain this idea of annotating 'in' is just due to things that are currently ommisions)

trait Dot<V:Vector> {
    type Scalar = type_of( a[0]*b[0] )  ../// if its 'deduced' like this, i can plug in fixed-point types and the shift- value is calculated..
    fn dot(a:&V, b:&V)->Scalar;
}
// ^^^ but will that be insufficient information to satisfy rust? 
// do you want to be able to infer backwards from code that expects a certain scalar type..
// are you still going to need to place type-param bounds on the outputs ?
// would you end up needing to specify both ?

trait Dot<V> {
    type Scalar: TotalOrd  = type_of( a[0]*b[0] ) ;
    fn dot(a:&V, b:&V)->Scalar;
}
@pepp-cz

This comment has been minimized.

Show comment
Hide comment
@pepp-cz

pepp-cz Apr 18, 2014

I find the input/output type parameters quite confusing and unnecessary. I am also in favour of associated types.

Edit:
If this proposal is implemented and later the associated types are also implemented, we probably end up using assoc. types instead of the output type parameters but all the remaining type parameters will have to use `in' keyword? I propose that if this RFC is going to happen then the output parameters are "tagged". No matter are much more common they are.

pepp-cz commented Apr 18, 2014

I find the input/output type parameters quite confusing and unnecessary. I am also in favour of associated types.

Edit:
If this proposal is implemented and later the associated types are also implemented, we probably end up using assoc. types instead of the output type parameters but all the remaining type parameters will have to use `in' keyword? I propose that if this RFC is going to happen then the output parameters are "tagged". No matter are much more common they are.

@dobkeratops

This comment has been minimized.

Show comment
Hide comment
@dobkeratops

dobkeratops Apr 18, 2014

this might be tangential but i'm asking after seeing the talk of generalizing the self parameter - and the ideas about assoc/static functions being changed -

Would it be possible for UFCS to go as far as eliminating the difference between a.foo(b) and foo(a,b) for all functions - a.foo would simply be sugar for putting a parameter at the front which is convient for chaining without nesting, and for IDE's giving suggestions - decouple that from anything to do with polymorphism or visibility, availalbe to all functions equally.

To provide a sane roadmap my suggestion would be that you still only use the first parameter for vtables ; 'Self' is just a shortcut for 'the current type in the impl block' and nothing special, the function goes in the vtable if the first parameter is of the self type.

Leave the door open for generalized multimethod dispatch in future, moving conceptually away from the concept of methods being something distinct from functions.. "methods are just functions that happen to have been pulled into a vtable"

dobkeratops commented Apr 18, 2014

this might be tangential but i'm asking after seeing the talk of generalizing the self parameter - and the ideas about assoc/static functions being changed -

Would it be possible for UFCS to go as far as eliminating the difference between a.foo(b) and foo(a,b) for all functions - a.foo would simply be sugar for putting a parameter at the front which is convient for chaining without nesting, and for IDE's giving suggestions - decouple that from anything to do with polymorphism or visibility, availalbe to all functions equally.

To provide a sane roadmap my suggestion would be that you still only use the first parameter for vtables ; 'Self' is just a shortcut for 'the current type in the impl block' and nothing special, the function goes in the vtable if the first parameter is of the self type.

Leave the door open for generalized multimethod dispatch in future, moving conceptually away from the concept of methods being something distinct from functions.. "methods are just functions that happen to have been pulled into a vtable"

Show outdated Hide outdated active/0000-traits.md
## Why functional dependencies and not traits implemented over tuples? <a name=tuples>
All things being equal, I might prefer to say that only the `Self`
type of a trait is

This comment has been minimized.

@glaebhoerl

glaebhoerl Jun 3, 2014

Contributor

(truncated sentence)

@glaebhoerl

glaebhoerl Jun 3, 2014

Contributor

(truncated sentence)

@alexcrichton

This comment has been minimized.

Show comment
Hide comment
@alexcrichton

alexcrichton Jun 10, 2014

Member

As discussed in today's meeting, we have decided to merge this. The controversial bits have been left out, and what was renaming was unanimously agreed upon.

Member

alexcrichton commented Jun 10, 2014

As discussed in today's meeting, we have decided to merge this. The controversial bits have been left out, and what was renaming was unanimously agreed upon.

@chris-morgan chris-morgan referenced this pull request Jun 11, 2014

Merged

RFC for index traits #111

@huonw huonw referenced this pull request Jun 12, 2014

Merged

Unboxed closures #114

@nikomatsakis nikomatsakis referenced this pull request Jun 12, 2014

Closed

Trait reform #5527

3 of 5 tasks complete
@zolkko

This comment has been minimized.

Show comment
Hide comment
@zolkko

zolkko Oct 28, 2014

"This same *" seems like a typo to me.

"This same *" seems like a typo to me.

@chriskrycho chriskrycho referenced this pull request Dec 30, 2016

Closed

Document all features in the reference #38643

0 of 17 tasks complete

@chriskrycho chriskrycho referenced this pull request Mar 11, 2017

Closed

Document all features #9

18 of 48 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment