Stream: t-compiler

Topic: destructuring grammar conflicts


Jake Goulding (Oct 24 2018 at 21:01, on Zulip):

What piece of the grammar causes this exciting error message?

struct foo;
struct Thing { foo: String }

fn example(t: Thing) {
    let Thing { foo } = t;
}
error[E0308]: mismatched types
 --> src/lib.rs:5:17
  |
5 |     let Thing { foo } = t;
  |                 ^^^ expected struct `std::string::String`, found struct `foo`
  |
  = note: expected type `std::string::String`
             found type `foo`
davidtwco (Oct 24 2018 at 21:03, on Zulip):

That's a fun error.

Jake Goulding (Oct 24 2018 at 21:04, on Zulip):

Right? Like, I'd think it'd prefer an ident over a type, but I'm guessing its a pattern thing.

Matthew Jasper (Oct 24 2018 at 21:12, on Zulip):

This compiles...

struct foo;
struct Thing { foo: foo }

fn example(t: Thing) {
    let Thing { foo } = t;
}
Matthew Jasper (Oct 24 2018 at 21:13, on Zulip):

but this doesn't

struct foo;
struct Thing { foo: foo }

fn example(t: Thing) {
    let Thing { mut foo } = t;
}
nikomatsakis (Oct 24 2018 at 21:40, on Zulip):

when you name a constant (and struct foo; declares a constant)

nikomatsakis (Oct 24 2018 at 21:40, on Zulip):

you are not producing a binding

nikomatsakis (Oct 24 2018 at 21:40, on Zulip):

you are matching that value

nikomatsakis (Oct 24 2018 at 21:40, on Zulip):

let Thing { foo } is of course shorthand for let Thing { foo: foo }

nikomatsakis (Oct 24 2018 at 21:40, on Zulip):

so it says "match the value of the field foo with the constant foo"

nikomatsakis (Oct 24 2018 at 21:40, on Zulip):

mut foo doesn't make sense, this is not a binding

nikomatsakis (Oct 24 2018 at 21:40, on Zulip):

it's like saying match x { mut None => .. }

nikomatsakis (Oct 24 2018 at 21:41, on Zulip):

As an aside, in Ye Olde Rust of Yore, you had to write a . after constants

nikomatsakis (Oct 24 2018 at 21:41, on Zulip):

e.g., alt foo { Some(x) { value }, None. { other_value } } (in those days, we didn't write => either, and match was called alt)

nikomatsakis (Oct 24 2018 at 21:41, on Zulip):

also, we walked uphill both ways.

nikomatsakis (Oct 24 2018 at 21:42, on Zulip):

oh I and think that was before the capitalization conventions that made this largely a non-issue

nikomatsakis (Oct 24 2018 at 21:42, on Zulip):

alt foo { some(x) { value } none. { other_value } }?

nikomatsakis (Oct 24 2018 at 21:42, on Zulip):

something like that

nikomatsakis (Oct 24 2018 at 21:42, on Zulip):

I remember you would write option::t<t> instead of Option<T> :) (such ocaml)

nikomatsakis (Oct 24 2018 at 21:42, on Zulip):

/me stops rambling now

Jake Goulding (Oct 25 2018 at 14:15, on Zulip):

the capitalization conventions

I think that's a key thing here. That and the fact that it's a unit struct. If either of those were different, this likely wouldn't pop up in real code.

Jake Goulding (Oct 25 2018 at 14:15, on Zulip):

This is ultimately from Diesel, which generates a bunch of those lower-cased unit structs to represent column names.

nikomatsakis (Oct 25 2018 at 14:15, on Zulip):

I still sometimes wonder if we should have done Some(let x) instead of Some(x)

nikomatsakis (Oct 25 2018 at 14:15, on Zulip):

It seemed too verbose but Swift did it and .. it seems fine

Jake Goulding (Oct 25 2018 at 14:16, on Zulip):

And someone had a table with a column called title that they were trying to destructure.

nikomatsakis (Oct 25 2018 at 14:16, on Zulip):

I see

Jake Goulding (Oct 25 2018 at 14:16, on Zulip):

Some(let x) certainly has some consistency benefits

nikomatsakis (Oct 25 2018 at 14:16, on Zulip):

water under the bridge now, at least until Rust 2021 :P

oli (Oct 25 2018 at 14:17, on Zulip):

we could allow Some(let x) for explicitness and add an autofixable lint for it

Vadim Petrochenkov (Oct 25 2018 at 14:17, on Zulip):

This is ultimately from Diesel, which generates a bunch of those lower-cased unit structs to represent column names.

I suspect it's a hygiene issue first of all.

oli (Oct 25 2018 at 14:17, on Zulip):

no reason for breaking changes

nikomatsakis (Oct 25 2018 at 14:17, on Zulip):

true

Jake Goulding (Oct 25 2018 at 14:19, on Zulip):

allow Some(let x) for explicitness

Rather like dyn Foo in that case.

oli (Oct 25 2018 at 14:20, on Zulip):

jop

Jake Goulding (Oct 25 2018 at 14:21, on Zulip):

let MyStruct { let member, let mut other, .. } = s

kennytm (Oct 25 2018 at 15:04, on Zulip):

this may make let let r#let = "let"; a valid syntax :upside_down:

Vadim Petrochenkov (Oct 25 2018 at 15:13, on Zulip):

We already have x @ _ for unambiguously making identifier pattern a fresh binding.

Vadim Petrochenkov (Oct 25 2018 at 15:14, on Zulip):

But it still can't shadow unit structs for other reasons. Ha-ha.

Vadim Petrochenkov (Oct 25 2018 at 15:17, on Zulip):

(But that's fixable.)

nikomatsakis (Oct 25 2018 at 16:04, on Zulip):

We already have x @ _ for unambiguously making identifier pattern a fresh binding.

interesting, I hadn't thought of that :) kind of a hack though

Esteban Küber (Nov 02 2018 at 18:37, on Zulip):

I was going to suggest that dealing with this particular case wasn't required until I read this...
This indeed will be unlikely to occur on human written code, but it is very likely to happen in macros.

Should I create a ticket to handle this in the same way we handle the following?

error[E0530]: let bindings cannot shadow tuple structs
 --> src/lib.rs:5:17
  |
1 | struct foo();
  | ------------- a tuple struct `foo` is defined here
...
5 |     let Thing { foo } = t;
  |                 ^^^ cannot be named the same as a tuple struct
nikomatsakis (Nov 02 2018 at 20:34, on Zulip):

@Esteban Küber that looks nice

Esteban Küber (Nov 02 2018 at 22:13, on Zulip):

Filed https://github.com/rust-lang/rust/issues/55631

Last update: Nov 16 2019 at 01:55UTC