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: Futureproof box and & patterns #462

Closed
wants to merge 1 commit into from
Closed

RFC: Futureproof box and & patterns #462

wants to merge 1 commit into from

Conversation

ghost
Copy link

@ghost ghost commented Nov 11, 2014

Futureproof box patterns by renaming them to deref.
In an effort to consolidate box and & patterns, change the latter to use the deref syntax as well, in recognition of them being semantically equivalent to box patterns.
Make the newly introduced deref keyword a non-strict keyword.

Rendered view

@ghost ghost changed the title Futureproof box and & patterns RFC: Futureproof box and & patterns Nov 11, 2014
@ftxqxd
Copy link
Contributor

ftxqxd commented Nov 11, 2014

I’m not so sure that this is a good idea. Patterns intentionally look very similar to their expression counterparts; e.g., the expression &Foo(box 1, "bar") matches the pattern &Foo(box 1, "bar") as well. Combining & and box into a single deref pattern loses this symmetry: deref Foo(deref 1, "bar") doesn’t look similar at all to its expression counterpart. It looks strange, too: deref 1 seems like it’s dereferencing 1, which is nonsensical.

I think the best way of avoiding the problem with box altogether is an idea that I saw on discuss, but apparently was discussed on an RFC: use box foo to represent a pointer (with the type inferred, instead of defaulting to Box), and in(place) foo for placement-new-like functionality. Having in(place) in patterns doesn’t make much sense, so would not be allowed, but since box would always represent a pointer, it could be used in patterns as well.

@ghost
Copy link
Author

ghost commented Nov 11, 2014

@P1start The main point this RFC is making is that types that will in the future support the box allocator syntax are not necessarily the same set of types that implement the Deref trait, which is what will be of interest in patterns. So while there definitely exists a symmetry between some of the types of patterns and their equivalent expressions, for box patterns matching types like Box, Rc, Gc the symmetry is only superficial. For example, http://doc.rust-lang.org/std/cell/struct.Ref.html can be dereferenced but is not at all allocated with box therefore the RFC argues it'd be misleading to see a box pattern being used to destructure it.

@Gankra
Copy link
Contributor

Gankra commented Nov 11, 2014

I'm not sure if this is the exact right solution, but I would like to see & go away in patterns. It causes a lot of confusion, especially for new comers (very frequent question on irc). The symmetry with "how you make it" isn't exactly there, since &foo is also how you match on an &mut Foo. Putting &mut does something completely wrong. That is:

    let f = &mut Some(1u);
    match f {
            &mut Some(x) => {}
            _ => {}
    }

spits out

<anon>:4:18: 4:22 error: expected identifier, found enum pattern
<anon>:4             &mut Some(x) => {}
                          ^~~~

The correct way is:

    let f = &mut Some(1u);
    match f {
            & Some(x) => {}
            _ => {}
    }

Which is different than construction. Having box for this task is just kind of random, as jakub notes.

Just some notes of things that were discussed on IRC:

ref is currently "backwards" in that everything else undoes itself, and ref adds. Technically, perfect symmetry would swap the roles of deref and ref. That is, deref is how you would get a reference, and ref is how you would take one away. But that's super confusing, and would break every existing pattern in a crazy way (ref's meaning would be flipped).

* was also proposed as an alternative token for matches. It could replace deref, or ref depending on whether you want "true" symmetry or the current symmetry. Having & and * would be kind of nice in that it would avoid adding any keywords just for matches.

Using & where box is used now would match some peoples' suggestion of making & a "borrow" operator, and not just "address of".

@reem
Copy link

reem commented Nov 11, 2014

I'm a fan of some version of this proposal because of the possible integration with smart pointers - matching against stuff contained inside of Arc or Rc is a huge pain right now, and a generalized "deref" pattern would certainly help there.

Also, I agree with @gankro that in a perfect world deref and ref would be swapped, but I don't think it's very confusing, you would either match ref x, as in something behind a reference, or deref y as in something that dereferences to y.

@blaenk
Copy link
Contributor

blaenk commented Nov 11, 2014

I agree with some form of this, but I don't really like deref itself (bikeshedding). It never really sat well with me that box was more or less special-cased for pattern matching.

I'm not sure if this is the exact right solution, but I would like to see & go away in patterns. It causes a lot of confusion, especially for new comers (very frequent question on irc).

ref is currently "backwards" in that everything else undoes itself, and ref adds.

I strongly, completely agree with @gankro

* was also proposed as an alternative token for matches. It could replace deref, or ref depending on whether you want "true" symmetry or the current symmetry. Having & and * would be kind of nice in that it would avoid adding any keywords just for matches.

That is a great idea. I for one would love to have true symmetry. It has always seemed like a wart to me how confusing matches could be due to not being truly symmetrical.

@ghost
Copy link
Author

ghost commented Nov 12, 2014

  • was also proposed as an alternative token for matches. It could replace deref, or ref depending on whether you want "true" symmetry or the current symmetry. Having & and * would be kind of nice in that it would avoid adding any keywords just for matches.

Just to give everyone an idea, the following:

    match ty::get(function_type).sty {
        ty::ty_closure(box ty::ClosureTy {
            store: ref store,
            ..
        }) => {}
        _ => {}
    }

would, with & *, read:

    match ty::get(function_type).sty {
        ty::ty_closure(&ty::ClosureTy {
            store: *store,
            ..
        }) => {}
        _ => {}
    }

In general, I really appreciate this reusing existing sigils and not introducing new keywords, as well as the symmetry with expressions. I am, however, dubious about it being easy to grok. As you've mentioned, @gankro, & in patterns is already a common source of confusion and misunderstanding, I'm afraid * would only make it worse. However, maybe it's just a matter of communicating the idea better in the guide and other sources of reference.

cc @steveklabnik Any thoughts?

@glaebhoerl
Copy link
Contributor

I agree that patterns are initially confusing, because everything is backwards, but it gets much easier once you realize that "oh, everything is backwards". I suspect that having some parts be backwards and others "forwards" would just end up making things more confusing in the long run.

The bigger problem with using * in place of ref, I think (otherwise I would have suggested it long ago), is that specifying that you want a mutable reference becomes really awkward:

            store: *mut store,

(Or how else?)

This is made even more confusing by the fact that we also have an unrelated type called *mut, but it's at least somewhat confusing even on its own merits, because there is no analogue for it in expressions.

That said, the fact that in expressions you would specify mutability for & and elide it for *, while in patterns you would elide it for & and specify it for *, might still be a symmetry we could work with, and be less bad than the alternatives. But only if the alternatives are pretty bad.

@ghost
Copy link
Author

ghost commented Nov 12, 2014

Perhaps some change is necessary but I'm not convinced this would be a significant improvement. I like box and don't find & or ref in patterns confusing. The deref syntax is way too long for how frequently it occurs and I'd hate having that everywhere. I see the point about box being a special case in patterns but aren't Box and friends important enough that maybe it's justified? I may be misunderstanding this last point, but if part of the problem is that some things in the future will support box allocator syntax but not implement Deref and that would cause confusion wrt patterns, then maybe box shouldn't be use for those in the first place.

@Manishearth
Copy link
Member

Restrict the RFC to only propose the first change, namely renaming box patterns to deref patterns whilst leaving & patterns intact. While this is still an improvement and addresses the main issue in question, it leaves the language with two constructs that are semantically equivalent.

Actually this seems to make sense to me. & indicates &-pointers, and can be used to create a reference, as well as match a reference. Similarly, deref can be used to match all types of pointers.

The fact that they have some overlap isn't really a bad thing. There should be more than one way of doing something. I'm no so fond of removing the & shorthand just because it's slightly redundant in a particular context.


I'm also a bit wary about ref vs deref. ref is for binding a variable by reference, deref is for destructuring pointers. They're not exactly opposites, but it looks like they are. Perhaps ptr? (match x {Some(ptr y) => ...}).

I'm also okayish with box as-is

@Ericson2314
Copy link
Contributor

@glaebhoerl: That said, the fact that in expressions you would specify mutability for & and elide it for *, while in patterns you would elide it for & and specify it for *, might still be a symmetry we could work with, and be less bad than the alternatives. But only if the alternatives are pretty bad.

* and & with this seems like the last bad option to me too.

@nikomatsakis
Copy link
Contributor

I was just discussing this with @jakub- on IRC. TL;DR is:

  1. I prefer to keep & and &mut
  2. I prefer to feature-gate box patterns
  3. I'm not sure if I prefer renaming box or not

More details:

  1. We ought to feature gate box patterns in any case until they are fully implemented. I am concerned they may allow some code for Box<T> that they would not be able to allow when implemented in a fully general way.
  2. I think & and &mut T are important enough types in Rust that's it's ok to have specialized patterns just for them. They also happen to be the only built-in types that can be (safely) dereferenced. So I'm opposed to trying to unify &, &mut and box.
  3. As far as renaming box to deref, it seems very long.
  4. As far as renaming box to *, it is perhaps ok, but it means that *T patterns don't match against unsafe pointers, as one might expect, but rather do overloaded deref. I'm not sure if that's good or bad. (I guess they could also match against an unsafe pointer as a kind of separate thing.)
  5. One thing that had bugged me is that we were planning to write box(Rc) 5 to make an Rc<int> but box x to destructure one, which was a pretty imprecise match. However, I am now favoring the idea that box 5 should infer the sort of box and hence you would write box 5 to produce an Rc<int> and box x to destructure one, which seems good.
  6. However, I know that box could be used for anything that implements Deref, and hence could be used for things that are/were not produced with box. We could change that too but tying it to deref seems to make sense.

UPDATE: Tweaked order of text very slightly, putting TL;DR on the top where it belongs.

@Manishearth
Copy link
Member

@nikomatsakis What about renaming the destructuring box to ptr? * is already a rather confusing character in programming and hard to give different meaning than &. This is one of the most common confusions of a C++ newbie, and even more so a Rust newbie (eg our reference type is &T as opposed to C's T*). Another keyword might be quite useful, and ptr isn't too long.

Agree with everything else there.

@pnkfelix
Copy link
Member

cc me

@ghost
Copy link
Author

ghost commented Nov 14, 2014

@nikomatsakis A note on your 4th point: it is actually ref that has been proposed to be renamed to * and box to &. This is to make patterns symmetrical across the board.

Also, as I think emerged from some of the discussion on IRC, &mut means something different in patterns right now. There's an outstanding RFC to address this: #179

I am certainly in favour of feature-gating box patterns. There's always the risk with feature gates that even a feature-gated feature becomes so wide-spread after 1.0 that it'll be considered de facto part of the core language but in the case of box patterns it is very very unlikely.

However, I know that box could be used for anything that implements Deref, and hence could be used for things that are/were not produced with box. We could change that too but tying it to deref seems to make sense.

I am rather skeptical about this when picturing real code that'd use the generalised box patterns in the future:

Example

use std::cell::RefCell;
fn main() {
    let cell = RefCell::new(42u);
    if let box 42u = cell.borrow() {
    }
}

In this particular case, if let &42u = cell.borrow() reads much better and indeed deref would seem way too long.

I think feature-gating box patterns may be a sufficient step to take on this before 1.0 assuming we're happy with ref.

@rkjnsn
Copy link
Contributor

rkjnsn commented Dec 12, 2014

would, with & *, read:

match ty::get(function_type).sty {
    ty::ty_closure(&ty::ClosureTy {
        store: *store,
        ..
    }) => {}
    _ => {}
}

I'm not sure this is the best solution, but I definitely like it better than the status quo.

@glaebhoerl
Copy link
Contributor

Cross-linking: there's also some discussion going on on discourse here.

@nikomatsakis
Copy link
Contributor

We discussed this RFC at the triage meeting today and decided to close it. We do not plan major breaking changes to the syntax of & and &mut patterns, and we have already accepted https://github.com/rust-lang/rfcs/blob/master/text/0469-feature-gate-box-patterns.md which feature-gates box patterns.

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.