Random Musings: types in patterns


#1

The notion of types-in-patterns came up twice, afaict independently, within 8 hours of each other on Saturday:

(I don’t actually intend to propose anything right now; just thought it an interesting area to explore.)

For example, I rather like this:

match "4".parse() {
    Ok(x: u16) => ...,
    Err(e) => ...,
}

Compared to ::<u16> or let r: Result<u16, _> =, that feels more direct and less noisy/sigily.

Interestingly, one can already do a hacky version of that that if parsing a struct:

    match "asdf".parse() {
        Ok(x @ String {..}) => println!("yup: {}", x),
        Err(e) => eprintln!("Nope: {:?}", e),
    }

I could also imagine something like this being legal:

fn foo(Wrapping(x: u16)) -> ...

since it fully-specifies the type of the function argument.

Of course, @rkruppe rightly pointed out that this mixes poorly with struct patterns, since : there already has a different meaning.


#2

Off the top of my head, this seems like a great idea that is much less a hack than turbofish is :+1: .

Does the mixing with struct patterns cause ambiguity, or is it just a matter of unreadable code?

let Foo { x : u32 } = Foo { x : 1 };

This doesn’t seem so bad? To me, it is the right hand side which is weird and should have been Foo { x = 1 }. Or is there something else you had in mind being the problem?


#3

Would it be too crazy to change struct syntax to Foo { x = 1 } in the next epoch?


#4

That “works” today, but doesn’t do what you think it does:

    struct Foo { x: u32 }
    let y = Foo { x: 4 };
    let Foo { x: u32 } = y;
    println!("{}", u32); // 4

(Which, I suppose, is a good argument for changing to = for this…)


#5

I remember someone has proposed the struct initializer syntax as below, which is my favorite one:

// initializer
Foo { .x = 1 }
Foo { .0 = 1 }
// pattern
let Foo { var = .x } = Foo;

I can’t remember exactly why it was rejected though.


#6

There’s an old RFC issue about this:

It should be relatively easily implementable and I think we should implement it as an experiment under type_ascription feature (Idea: Change type ascription syntax from `:` to `type` keyword).

Also, there’s no ambiguity in struct patterns, only confusion.
field in Struct { field } is NOT a pattern, so type ascription is not applicable to it.
The syntax is either Struct { ident: PAT } (in this case there’s a pattern and this pattern can use type ascription) or Struct { ident } (in this case there’s no pattern syntactically, so there can be no type ascription).


#7

Does that really print 4? How? Did you mean:

println!("{}", x); // 4

If so, how is that not what is expected?


#8

Indeed. I was entirely taken aback by this. I wasn’t aware that you could do this. So… doesn’t types in pattern become ambiguous (for structs with named fields…) / constitute a breaking change?

let Foo { x : u32 } = y binds the field y.x to the variable u32 (not the type). It looks like it is type ascription, but it is not.


#9

OH! Yeah, that is a nasty surprise. I would not have expected that. Yikes!


#10

Yes, sadly

(By which I mean: I would be shocked if it weren’t. If it was considered too much of a breaking change in the leadup to 1.0…)


#11

Filed an issue for a clippy lint: https://github.com/rust-lang-nursery/rust-clippy/issues/2676