I know the original post doesn’t even question the upper-case vs lower-case distinction in Rust, but since the point of lower-case snake_case
vs upper-case CamelCase
supposedly distinguishing between types and values came up, I’d like to quickly throw in my 2 cents, coming from Haskell.
Notable, regarding enum variants, which have been mentioned above, too. Not only enum variants, but also struct names can be used as
- constants, if it’s a unit-struct or a unit variant
- functions, if it’s a tuple-style struct of a tuple-style variant
and these things of course are not part of the “type world”, so the ‘types vs values’ ≙ ‘CamelCase vs snake_case’ claim is not really correct.
In these functions, they do differ from the typical casing style for either constants or functions. But arguably more importantly than their constant-like or function-like usage – and by the way, even in that usage, people may appreciate the additional hint that you have an enum-variant constructor of a struct constructor there, not an arbitrarily complex constant or an arbitrarily complex function – anways… other than this usage, these “constructors” can also be used in pattern matching. And in pattern matching in particular, the case distinctions help tremendously. Following the actual language specification, a pattern foo
could be
- matching against a constant “
foo
”, or
- matching against a unit-(struct/enum-variant) constructor “
foo
”, or
-
introducing a fresh variable
foo
depending on whether any constant or unit constructor named foo
is in scope or not. Naming conventions coming to the rescue, you should never actually have any constant or unit constructor named “foo
”, since that’s lower-case, so the potential for huge confusion is eliminated.
Now for the Haskell context, in Haskell a similar convention of lower-case functions / variable vs upper-case types and value constructors is present, but there it’s actually enforced; with the effect that the rules for interpreting patterns can be simplified: if it’s lower-case it introduces a variable, if it’s upper-case it’s referencing some existing constructor. (Constants, in particular constants in patterns, are not a thing in Haskell.)
FYI, the enforced bit is only whether the first character of the name is upper-case of lower-case, furthermore, the convention is to use ‘camelCase
’ for lowercase as well as ‘CamelCase
’ for upper-case identifiers; no ‘snake_case
’ used in Haskell conventionally.
On a related note, Haskell also uses an enforced case distinction for types: All types have to start upper-case, while generic type variables have to start lower-case. This has the nice benefit that you don’t have to explicitly introduce your generics explicitly in Haskell; a function like fn wrap_in_some<T>(x: T) -> Option<T> {…}
could be just written fn wrap_in_some(x: t) -> Option<t> {…}
if Rust were to follow that convention. (The explicit <T>
listing becomes optional/redundant.) Note that there’s an RFC to allow the same kind of thing in Rust at least / only for lifetime arguments; which is only possible because lifetimes arguments don’t look like anything else, so that an identifier like “'a
” can never be referencing something that’s already defined elsewhere, at least nothing already defined on the top level. (I haven’t checked the RFC to see how methods in impl
blocks are treated; both impl
blocks and functions containd in them can introduce lifetime argument.)