[E0530] "cannot shadow unit structs" - what in the earthly what?


#1

Today I ran into an error that I can hardly even believe that I have never encountered before. Apparently, incomplete patterns and complete patterns differ in how they interpret the pattern NameBelongingToAUnitStruct.

#[derive(Debug,Copy,Clone)]
struct MyUnit;

// '()' in an incomplete pattern destructures the unit
if let Some(()) = Some(()) {}
match () { () => {} };

// '()' in a complete pattern destructures the unit.
let ((), _) = ((), 0);
Some(()).map(|()| ());
    
// 'MyUnit' in an incomplete pattern destructures MyUnit
if let Some(MyUnit) = Some(MyUnit) {}
match MyUnit { MyUnit => () };

// 'MyUnit' in a complete pattern... actually names a binding?
let (MyUnit, _) = (MyUnit, 0); // [E0530]: let bindings cannot shadow unit structs
Some(MyUnit).map(|MyUnit| ()); // [E0530]: function parameters cannot shadow unit structs

(playground)
(E0530 in the error index)

The workarounds are to use a path like ::MyPath, the name: ty syntax, or to use _ and let type inference work.

let (::MyUnit, _) = (MyUnit, 0); // ok
Some(MyUnit).map(|_: MyUnit| ()); // ok
Some(MyUnit).map(|_| ()); // ok

I find this to be odd and inconsistent.


Note: Lest it be thought that MyUnit as a pattern is useless, my use case was to make an unusual usage of types self-documentiong:

fn_with_non_obvious_return_type()
    .map(|MeaningfullyNamedUnit| {
        // ...
    })

#2

makes sense to me that it is not possible to name a variable after a existing type


#3

I think this has to do with how a unit type is both a type and a value, that corner case doesn’t seem to be correctly handled when pattern matching. It might be worth a bug report to see if it can be fixed, or at least better documented.


#4

This is mostly an artificial error reported just to be conservative (also the error wording is bad, it should mention ambiguity, not binding).

The rules around “path pattern vs fresh binding” disambiguation can be simplified after some related issues are fixed, and this error can be removed as a part of that simplification, but this is a low priority work.


#5

Fixed in https://github.com/rust-lang/rust/pull/45050