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

RFC: Replace ref by * in patterns #742

Closed
wants to merge 3 commits into from

Conversation

krdln
Copy link
Contributor

@krdln krdln commented Jan 27, 2015

Change ref in patterns to *, to make pattern syntax better match expression syntax. Example: Some(ref x) becomes Some(*x). This change removes all the usage of ref keyword, so the RFC also discusses the fate of it.

Rendered


This is not a new proposal, because discussions (eg. here, here and there) about this change happen from time to time, but I think it deserves an RFC.

@glaebhoerl
Copy link
Contributor

+1 from my part, though it doesn't seem like people are very receptive towards syntax changes post-alpha.

@rpjohnst
Copy link

I like this because it makes more patterns work by destructuring, although that can't be done with mut and that leads to some visual confusion between *mut T raw pointers and pattern matching.

@gsingh93
Copy link

When I was first learning Rust, this is what I tried to do instead of using
ref. +1.

On Tue Jan 27 2015 at 10:13:30 AM Russell Johnston notifications@github.com
wrote:

I like this because it makes more patterns work by destructuring, although
that can't be done with mut and that leads to some visual confusion
between *mut T raw pointers and pattern matching.


Reply to this email directly or view it on GitHub
#742 (comment).

@nstoddard
Copy link

When I was learning Rust, I thought ref was some kind of weird synonym for &. This seems to clear up that potential confusion. +1.

@bfops
Copy link

bfops commented Jan 28, 2015

I wouldn't mind replacing ref with something a little more in-line with the rest of ref/deref syntax, but * is even less appealing to me. Some(*x) implies that some kind of dereference going on, but we're actually adding an extra layer of reference. Somewhat unfortunately, Some(&x) is already in use, and it does mean a dereference is going on. Otherwise that would be a nice syntax.


Note that most common usage of `mut` would be `mut *x` (corresponding to current `ref mut x`),
so the raw-pointer-resembling `* mut` wouldn't appear very often
(in rust source `ref mut` appears 139 times, compared to only 9 usages of `mut ref`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git grep 'mut ref' actually shows that although there are nine occurrences of the phrase mut ref in the Rust codebase, the first is just part of a list of keywords, the next four are examples in documentation (using &mut ref mut, which seems to be redundant), the next is a substring of mut reference in a comment, the next is in legitimate code, the next is a substring of mut ref_kind: in a function declarations’s argument list, and the final one is in a test. Of all of those, I think only one really counts, and only six are actual uses of mut ref at all.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Furthermore, none of them are equivalent to let mut x: &T = &val;: mut ref <ident> isn't a valid pattern. (They're all parsed as &mut (ref ...), i.e. pattern matching through a &mut.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn’t know that mut ref <ident> wasn’t even a valid pattern! The RFC should probably be updated to reflect that, then, given that it suggests *mut as a replacement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it wasn't only me who thought that mut ref is a valid pattern! I've updated the RFC.

@suhr
Copy link

suhr commented Jan 28, 2015

I think *(mut x) is much better than * mut x, because it cannot be misled with *mut.

@ftxqxd
Copy link
Contributor

ftxqxd commented Jan 28, 2015

I like the idea, but I’m worried that it will turn out to be confusing. Also, I wonder how this would interact with RFC #495, where subslice patterns require ref to get a &[T]; that RFC + this RFC would result in code looking like this:

match &mut [1, 2, 3] {
    &mut [a, mut *b..] => { ... },
    ...
}

where the * seems especially confusing.

@DanielKeep
Copy link

👎

This change just does my brain in. In pattern-matching, you're supposed to be destructuring things. So when I see &, I know I'm destructuring through a reference. So when I see *, I expect it to mean that I'm destructuring through an unsafe pointer, that is, removing a level of indirection.

Not only does this not have anything to do with unsafe pointers, it's also adding a level of indirection, which is the outright opposite of how all the other type constructors work in a pattern.

As Pedro would say: very no!

@ftxqxd
Copy link
Contributor

ftxqxd commented Jan 28, 2015

@DanielKeep * is only used for the type of unsafe pointers. The pattern syntax is much more closely related to the expression syntax, where there is no special construct for unsafe pointers. In expressions, * is used for dereferencing, so since patterns are backwards, it makes sense to be used for referencing there. You might as well say that * is a bad dereferencing operator in expressions because it looks like an unsafe pointer. The real syntax that’s wrong here is the * used for unsafe pointer types, but that’s just weirdness from C that we’re probably stuck with.

@suhr
Copy link

suhr commented Jan 28, 2015

Actually, unsafe pointer types are *const and *mut, not *.
But there's another issue: while ref mut x and mut ref x can be easily read as “reference to mutable x” and “mutable reference to x”, mut *x and *(mut x) are much less intuitive. So I say 👎 here.

@krdln
Copy link
Contributor Author

krdln commented Jan 28, 2015

@bfops Notice that when you write Some in a pattern, you don't really call a Some constructor. If you want patterns to mean actions which should be performed on matched value (& wrapping in a reference), then we could also use unwrap instead of Some. The main idea of this RFC is to make * work "backwards", since rest of patterns works "backwards". If you have an idea how to make everything in patterns work "forwards" (that's opposite way to make syntax consistent), I would be interested to hear!

@suhr You could read mut *x as "[matched value is] mutable dereference of x". Also, I've just learned that mut ref is not a valid syntax.

@P1start
Speaking of &mut [a, mut *b..] I prefer it to ref version. I guess it's the matter of taste. Maybe it would read better if it was written as mut (*b)... Seems that changing .. placement wasn't so good idea in the end (especially when we got rid of .. in array syntax).

And I agree that * for unsafe pointer types is confusing, especially because C's rule "declare as you would use it" (int *x; means "dereference of x would be an int") doesn't work in Rust at all. Maybe it's not to late to change it to ^?

@suhr
Copy link

suhr commented Jan 28, 2015

@krdln Well, with mut *x only it is indeed not that confusing.
@bfops Patten matching is not about doing something, but matching pattern with value. So just like Some(s) matches with Some("foo") when s = "foo", *x matches with expr (≡ *x matches with *&expr) when x = &expr. Pretty straightforward.

@nwin
Copy link

nwin commented Jan 28, 2015

-1

They way that ref works forward makes it easier to understand because in this case you’re not plainly destructuring the value but changing the type of one of it’s members (from value to ref-to-value).

Coincidentally it works the same way as mut. It “makes” x a reference such a mut “makes” x mutable.

If I'm wrong with this I would at least only rename ref to deref. I feel this introduces too much noise.

@krdln
Copy link
Contributor Author

krdln commented Jan 28, 2015

@nwin I tried to explain to myself that both mut and ref work forwards and changing ref would still leave some parts of patterns backwards and some forwards. But then I realized that you can think of mut as working as pattern too: let mut x = y tries to match y with some mutable x and it just has to perform implicit mutability change behind a scenes. That's in contrary to let ref x = y, which for me looks like it tries to match y with some kind of reference to x (and there's no reference in y). So with proposed change, one can think of all pattern matching syntax as working in the same direction and I think it's just more consistent and actually makes it easier to learn pattern matching, because there are no exceptions.

One other possibility that in my opinion would make ref less an exception could be change the ref x pattern to: x by ref. That makes it clear that you're not matching with some reference to x, but rather the x is taken by reference.

@rkjnsn
Copy link
Contributor

rkjnsn commented Jan 28, 2015

👍 This makes more sense to me, and seems more consistent.

@whatisaphone
Copy link

I'm for it. We're still alpha.

Consider the symmetry between the pattern and the assert in these matches:

let v = Some(2);
match v {
    Some(m) => assert!(v == Some(m)),
    _       => ()
}

let v = &2;
match v {     
    &m => assert!(v == &m)
}

The current syntax does not maintain the symmetry, and has a syntax error in the assert:

let v = "subliminal unease";
match v {
    ref m => assert!(v == ref m)
}

The proposed syntax would maintain the pattern nicely:

let v = "subliminal satisfaction";
match v {
    *m => assert!(v == *m)
}

@taralx
Copy link

taralx commented Jan 28, 2015

👍 This is an improvement on the status quo. IMO, we can try to fix "mut" in a different RFC, if people feel that it is still problematic after this change.

@nikomatsakis
Copy link
Contributor

As @krdln says, this is not a new proposal. Overall, though, I feel like this debate was settled long ago and I don't see any pressing reason to revisit it.

Update: Corrected name of RFC author.

@krdln
Copy link
Contributor Author

krdln commented Jan 29, 2015

@nikomatsakis I have quite opposite feeling – from what I've read in this discussion, I can't see an actual decision. Maybe I've missed something? Also, this RFC additionally addresses the *mut issue, which may not be obvious from the summary.

@nikomatsakis
Copy link
Contributor

@krdln this decision far predates the RFC tracker or other discussion. Here is the thread where discussion began. As you can see, it began with a proposal (by me, in fact) to use *T as the pattern syntax. Graydon has some (quite reasonable) objections here. I don't see a message with the resolution: presumably it could be found by digging in the minutes (or IRC logs). However, from memory, we eventually attempted to use &pat as the syntax for what is now ref pat, which involved changing &T to *T and *T to ^T, but found we hated it, went back and forth a bit, and settled on what we have now. I think the key point was that ref is really a "binding mode", whereas syntax and sigils are used to destruct values that have been constructed.

@nikomatsakis
Copy link
Contributor

In the triage meeting today we decided to close this RFC. A syntactic change of this magnitude seems out of scope at this time, and the current syntax was agreed upon long ago.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.