Skip to content
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

Support type transformations in FRU (functional record update) syntax #1975

Closed
abonander opened this issue Apr 17, 2017 · 16 comments
Closed
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@abonander
Copy link

abonander commented Apr 17, 2017

I remember discussing this a while ago. I thought I had an issue open somewhere for this but I guess not.

It's currently not allowed to use FRU (functional record update) syntax to return a transformed type, i.e.:

struct Foo<T> {
    val: T,
    tag: &'static str,
}

impl<T> Foo<T> {
    fn transform<U>(self, val: U) -> Foo<U> {
        Foo { val: val, .. self }
    }
}

will throw a "mismatched type" error with the span of self in the FRU literal because it is of type Foo<T> and not Foo<U>, even though the remaining fields of self are compatible.

According to RFC 736, FRU is supposed to be just syntactic sugar for a full struct literal, but a full struct literal works in this case because it doesn't place any restriction on the type of self.

Possible solutions:

  • Move fields nominally (foo: <tail expr>.foo, bar: <tail expr>.bar, etc.) and typecheck them individually, not typechecking the tail expression at all. This could allow FRU to work even between otherwise incompatible types which have compatible fields. I like this one more but it may be too implicit for some, and I gather that we don't want to add too much meaning to field names.

  • Only check equality of the outermost type constructor between the FRU literal and the tail expression, i.e. check that Foo<T> ~= Foo<U>; then, typecheck the moved fields individually. This would allow the above use-case but disallow using an entirely different type as the source or destination.

As far as I understand it, this should be backwards-compatible since it would strictly allow more code to compile.

Edit: clarify generic params

@mark-i-m
Copy link
Member

Sorry if this is a silly question, but what are the precise difference between Foo<T>, Foo<T_> and Foo<_>?

@KodrAus
Copy link
Contributor

KodrAus commented Apr 18, 2017

@mark-i-m, in this case T_ is just another type parameter. It's not the same as T, it could also be called K or NotT or anything else. The _ on its own means the type parameter is inferred, so we don't give it an explicit name in our code, but it's still there. I usually read Foo<_> as Foo of Whatever.

@mark-i-m
Copy link
Member

@KodrAus Got it 😄 I wasn't sure if T_ was some sort semi-inferred generic or something... Thanks!

@eddyb
Copy link
Member

eddyb commented Aug 5, 2017

Does anyone plan to write an RFC on this?

@abonander
Copy link
Author

abonander commented Aug 5, 2017

I can, but it'd be basically just the OP here.

@abonander
Copy link
Author

Also, not sure on which of the hypothesized solutions is better, that would have to be fleshed out before an RFC draft wouldn't it?

@eddyb
Copy link
Member

eddyb commented Aug 5, 2017

@abonander I'd go with the more conservative route, i.e. matching fields not by name but rather by the fact that they're defined in the same struct.

@burdges
Copy link

burdges commented Aug 5, 2017

We might want structural records back eventually, so avoid messing them up if possible.

@abonander
Copy link
Author

Based on the last comment on that thread and the fact that the compiler now does rearrange fields, structural records probably won't be making a return. If they do, it seems to be a completely orthogonal feature.

@burdges
Copy link

burdges commented Aug 5, 2017

I doubt the compiler rearranging fields poses any serious concern, but sure the syntax do not obviously conflict since Foo { .. } is quite different from { .. } or whatever.

@Centril Centril added the T-lang Relevant to the language team, which will review and decide on the RFC. label Dec 6, 2017
@jturner314
Copy link
Contributor

I put together a draft of an RFC for this. The trickiest part was thinking through the implications for type inference.

Does anyone have thoughts before I submit a PR? I haven't written an RFC before. In particular, I'm unsure if some of my terminology is correct/understandable, if the type inference sections make sense, and if I've provided sufficient detail. Also, am I missing any obvious edge cases?

@abonander
Copy link
Author

@jturner314 At a glance it looks okay to me, though I don't care so much about the details as long as something gets implemented. 😄

You can try posting it to internals.rust-lang.org for early comments but the RFC process itself is supposed to be the actual avenue for developing a proposal. Any issues should get shaken out at that point.

@Centril
Copy link
Contributor

Centril commented Aug 23, 2018

@jturner314 My initial reading is that this is a solidly written and well thought out RFC; Nice work, especially as your first RFC. I left some notes that are nits.

@eddyb
Copy link
Member

eddyb commented Aug 24, 2018

@jturner314 Looks great, please do open a PR!

cc @nikomatsakis

@jturner314
Copy link
Contributor

jturner314 commented Aug 24, 2018

I've submitted PR #2528. I made some changes from my initial version (primarily wording and more examples). Thanks for everyone's comments!

@Centril
Copy link
Contributor

Centril commented Oct 7, 2018

Closing in favor of #2528.

@Centril Centril closed this as completed Oct 7, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

7 participants