Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC: Coercible and HasPrefix for Zero Cost Coercions #91
Conversation
lilyball
reviewed
May 25, 2014
| vtable, and is considered a built-in "kind" alongside `Copy`, `Send`, etc. | ||
|
|
||
| Where single inheritance and subtyping conflate many different ideas, among them transparenta | ||
| ccess to superstruct fields, zero-cost conversion from sub- to supertypes, and these conversions |
This comment has been minimized.
This comment has been minimized.
lilyball
reviewed
May 25, 2014
|
|
||
| The trait is wired-in to the compiler, and user-defined impls of it are highly restricted as | ||
| described in the implementation section talking about roles. `coerce()` would coerce between | ||
| any two types where the target type "is a proper subtype of" the input type. Note that `coerce` |
This comment has been minimized.
This comment has been minimized.
lilyball
May 25, 2014
Contributor
Why does it need to be a proper subtype? This would prevent coercing between two types that are considered equivalent. For example, between a newtype and the underlying uint (which is to say, you could define the coercion in one direction, but not the other).
This comment has been minimized.
This comment has been minimized.
gereeter
May 25, 2014
Author
Good point - I'm not sure why I kept the word proper in there. I'll remove it.
This comment has been minimized.
This comment has been minimized.
glaebhoerl
May 26, 2014
Contributor
I think my original intent here with "proper subtype" was just to emphasize the distinction from merely "has prefix". For "strictly fewer inhabitants" I would have said "strict subtype".
lilyball
reviewed
May 25, 2014
| ```rust | ||
| impl Coercible<A> for B { } | ||
| impl Coercible<B> for A { } | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
lilyball
May 25, 2014
Contributor
This right here indicates that you are not actually enforcing the "is a proper subtype" rule, because B and A can't both be proper subtypes of each other.
This comment has been minimized.
This comment has been minimized.
lilyball
reviewed
May 25, 2014
| ```rust | ||
| impl<T, static N: uint> Coercible<[T, ..N]> for (T, T, .. times N) { } // fake syntax | ||
| impl<T, static N: uint> Coercible<(T, T, .. times N)> for [T, ..N] { } | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
lilyball
May 25, 2014
Contributor
Do we actually guarantee the layout of tuples? I didn't think we did. Or at the very least, I would expect RFC 18 to make the layout undefined.
This comment has been minimized.
This comment has been minimized.
gereeter
May 25, 2014
Author
I'm not exactly certain how this interacts with RFC 18. It probably is reasonable to assume that there isn't any difference between fixed size arrays and tuples (reordering does nothing when the types are the same). However, the tuple prefix rule might have to be dropped, and the first field rule might need an extra keyword. The only one of these I'm truly worried about is the first field rule - the tuple coercions don't seem that useful.
This comment has been minimized.
This comment has been minimized.
lilyball
May 25, 2014
Contributor
Reorder definitely does affect it. It may be the same memory layout, but if I coerce (a, b, c) to an array and get [c, b, a] then that's not very helpful.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
bstrie
May 26, 2014
Contributor
@huonw Which, frankly, seems like a backwards-compatibility hazard at this point. If we decide to make tuple layout unspecified for 1.0, we can always choose to re-specify it later. The opposite is impossible.
This comment has been minimized.
This comment has been minimized.
bstrie
May 26, 2014
Contributor
The question is, where would you stick the attribute if you wanted it to be specified? In that case, I'd be inclined to force users to just use structs instead of tuples. But this is stuff for a different RFC.
This comment has been minimized.
This comment has been minimized.
glaebhoerl
May 26, 2014
Contributor
I think the particular set of built-in coercion rules is not so important as just having the mechanism. The rules I originally included were a grab bag of whatever I could immediately think of, mainly to illustrate the possibilities. We could have only the most obviously useful and correct rules to begin with, and then add others later in a backwards compatible way, if we want to.
This comment has been minimized.
This comment has been minimized.
huonw
May 26, 2014
Member
@bstrie I don't disagree, I was just pointing out that the layout isn't (yet) undefined.
Maybe someone should submit a modification to that RFC making tuples also explicitly undefined.
This comment has been minimized.
This comment has been minimized.
|
Huge thanks for taking this up. I'll try to leave a few comments. |
glaebhoerl
reviewed
May 26, 2014
| ## Existing `transmute`s | ||
|
|
||
| There are various calls to `transmute` scattered throughout the Rust codebase, and a | ||
| number of them are perfectly safe. It would be beneficial to write these in a way that |
This comment has been minimized.
This comment has been minimized.
glaebhoerl
May 26, 2014
Contributor
I would hope that all of them are perfectly safe, because otherwise it's a bug! I think you just meant to say that the could compiler could verify their safety under this proposal. :)
This comment has been minimized.
This comment has been minimized.
gereeter
May 27, 2014
Author
They aren't all safe, and it is a bug: rust-lang/rust#13933. I think there are others, but those are the most blatant.
glaebhoerl
reviewed
May 26, 2014
| ``` | ||
|
|
||
| Note that nominal parameters are a strict subset of representational parameters, which are a strict | ||
| subset of covariant parameter, which, in turn, are a strict subset of phantom parameters. |
This comment has been minimized.
This comment has been minimized.
glaebhoerl
May 26, 2014
Contributor
Interesting, I've been trying to piece together in my head how these covariant, contravariant, (bivariant, invariant) roles relate to the nominal, representational, and phantom roles of GHC for some time now. (What they all have in common is that they are only consumed by the respective Coercibles.) I'll have to think about this.
glaebhoerl
reviewed
May 26, 2014
| And also as in GHC, to preserve abstraction boundaries, as a general principle, for those impls which involve conversions | ||
| between user-defined types, they would only "be in scope" when the means to do the conversion manually are in scope. This | ||
| means that you could only cast `&Struct` to `&FirstFieldOfStruct` if the first field of the struct is visible to you, you could | ||
| only cast `Foo` to `NewTypeOfFoo` if its constructor is visible, and other similar rules along these lines (described above). |
This comment has been minimized.
This comment has been minimized.
glaebhoerl
May 26, 2014
Contributor
FWIW, I was relating to GHC's implementation here, and I originally thought "here be dragons", that this whole "would only be in scope when ..." business was the least well thought out and well-specified part of the proposal. But since then, I've realized that Rust could do it much more simply and straightforwardly with something like the late RFC PR #10. And unlike GHC, we could actually implement the thing as just a bunch of (incoherent) wired-in impls. The mechanism would simply be that, like all other built-in traits, HasPrefix and Coercible would be automatically derived by the compiler for plain (public-definition) structs and enums (according to the rules above), and would need to be declared/derived by the programmer explicitly for abstract structs and abstract enums. All of the complexity in GHC's implementation is just to work around them having the wrong defaults.
glaebhoerl
reviewed
May 26, 2014
| - Somehow generalize the system so that users can put arbitrary bounds on `Coercible` instances. This sounds good, but could be | ||
| added on later (it is completely backwards compatible) and is hard to implement or prove correct. | ||
| - Use `as` instead of a standalone function. While this might work, it prevents inference of the final type and conflicts with | ||
| the fact that today's conversions using `as` are not necessarily free. |
This comment has been minimized.
This comment has been minimized.
glaebhoerl
May 26, 2014
Contributor
The inference part could be solved by allowing foo as _ - I think we already have type placeholders, so this would just fall out? The part about expected meaning is the real problem. If someone writes 6.0 as int, they probably don't intend a bitwise reinterpretation.
glaebhoerl
reviewed
May 26, 2014
| the fact that today's conversions using `as` are not necessarily free. | ||
| - Make everything use the loosest role possible by default. This would be fine from a memory safety standpoint, and is in fact | ||
| what GHC does. However, it can often break assumptions made by libraries, and Rust's philosophy of safety by default seems to | ||
| encourage using nominal by default. Additionally, this is more consistent with using opt-in kinds. |
This comment has been minimized.
This comment has been minimized.
glaebhoerl
May 26, 2014
Contributor
(See again RFC PR #10. I'm strongly in favor of properly enforcing abstraction boundaries by default, unlike GHC.)
glaebhoerl
referenced this pull request
Jun 2, 2014
Closed
Proposal to add methods to homogeneous tuples (`(T, T, ..., T)`) so that they can be used as slices (`&[T]`) #104
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
FWIW if I were writing this proposal today, I would go with this naming scheme:
In other words, rename the existing This is more consistent with Rust's existing terminology which uses "transmute" to mean the thing we are doing here, and "coerce" to mean something different (not-necessarily-free compiler-inserted casts). It also happens to directly mirror GHC's (quite logical) scheme of I still don't have a better idea for what to call |
This comment has been minimized.
This comment has been minimized.
|
Ergonomics in general is an area we want to focus on leading up to 1.0, and coercions probably have a role. We're not ready to make any decisions on the subject yet. Closing, but tagging with 'postponed' so it can be referenced later. |
brson
closed this
Jun 25, 2014
brson
added
the
postponed
label
Jun 25, 2014
This comment has been minimized.
This comment has been minimized.
This is much more about expressiveness, safety and performance than about ergonomics. The name "coercible" was misleading in this context, because Rust uses it to mean something different (see previous comment). I agree that implementing this is not urgent, just wanted to clarify. |
alexcrichton
referenced this pull request
Jun 27, 2014
Closed
Cannot transmute type parameters #15215
This comment has been minimized.
This comment has been minimized.
|
So... I thought I made a discovery, inspired by an email from Edward Kmett, and then I re-read the RFC, and realized that it already does things exactly the way I was going to suggest, following my "discovery". The discovery was that we can completely avoid the need to have explicit role/variance annotations on type parameters (e.g.
But, as I mentioned above, the RFC already specifies exactly this:
So I commend @gereeter's brilliance for realizing this before anyone else. I think I was mislead by the earlier statement that:
which I unthinkingly assumed implied explicit (Which is a very good thing!) |
rust-highfive
referenced this pull request
Sep 24, 2014
Open
RFC: Coercible and HasPrefix for Zero Cost Coercions #270
gereeter
referenced this pull request
Sep 20, 2015
Closed
Allow overlapping implementations for marker traits #1268
This comment has been minimized.
This comment has been minimized.
jsgf
commented
Jun 8, 2017
|
How does this RFC look through the eyes of 2017 Rust? |
gereeter commentedMay 25, 2014
This is largely based on (with lots of copied text) @glaebhoerl's proposal in rust-lang/rust#9912, but with a few changes:
rendered draft