8 ms·
Types going after identifiers avoids the need for the lexer hack, which causes all sorts of problems in C (such as "typename"). A colon nicely separates the two
by pcwalton 7y ago
Types going after identifiers avoids the need for the lexer hack, which causes all sorts of problems in C (such as "typename"). A colon nicely separates the two; I prefer something there as opposed to "x int" like in Go.
You have to nest square brackets to avoid ambiguity. Is &int[] an array of references or an reference to an array?
- petschge 7y agoI am sure there is good theoretical arguments. But they are hard on the humans. Ideally I would want something like constdata keymaps[1,2,3, u16] That is easy to read, gets rid of all the extra line noise and directly tells me everything I need to know about memory layout and performance. 1.) it is constant, known and compile time and can be put into a read-only segment (or possibly flash rom on an embedded system). 2.) it is named keymaps. The name is important and should come early 3.) it is an array. arrays and primitive datatype have many important differences and programming languages should not try to hide that. 4.) it has dimensions 1 by 2 by 3 (in that order). Listing the "3" first in Rust when the first dimension only has extend 1 might have good reasons but is damn hard to read if you have more than 2 dimensions. Especially if you end up with things like 3 by 3 by 4 by 3. Which of the inner two is larger? 5.) Having the type of the element last makes sense, because in terms of memory layout that just means that we have 2 consecutive bytes. I also makes it easier to which from "a 1 by 2 by 3 array of u16" to "a 1 by 2 array of (three vectors of u16)". Now you will probably give me reasons why I can't have that. But when I am coding I don't hard how hard it is on the compiler writers (as long as I can express things unambiguously), but want to have it as easy as possible so I have brain cycles to spare to think about data layout and algorithms.
- cdirkx 7y agoYou can have anything you want with macros :) I just wrote a macro `array` [1] that allows you to write #[no_mangle] static keymaps: array!{ u16[1, 2, 3] } = [ [ [1, 2, 3], [4, 5, 6], ], ]; or alternatively using a type alias type Keymaps = array!{ u16[1, 2, 3] }; #[no_mangle] static keymaps: Keymaps = ... On a more serious note, in general Rust favors explicit simple syntax: the only syntax related to arrays you need to learn is `[TYPE; LENGTH]` which is the way to write an array of type TYPE and length LENGTH, pretty straightforward. `[[[usize; 3]; 2]; 1]` is simply a composition of such arrays, as multidimensional arrays are just arrays of arrays. C has a few more variants: the implicit length of `keymaps[]`, the `[0] = ...` initializer , the alternative `keymaps[1,2,3]` syntax. This is nice syntactic sugar, but you don't technically need it. Although if you really don't like the raw Rust syntax, you can always use macros like shown or a library like multiarray [2]. In a way I would say this makes Rust easier to learn: there are only a few symbols and patterns you need to learn to recognize, and the rest is compositions. [1] WARNING: macro definitions are very symbol heavy, and thus even more unreadable. https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=6e122c09c7f8d4f38081f1671b72ab1b https://play.rust-lang.org/?version=nightly&mode=debug&editi... [2] https://docs.rs/multiarray/0.1.3/multiarray/ https://docs.rs/multiarray/0.1.3/multiarray/
- cdirkx 7y agoAlso, normally the array in Rust would also be a constant, with the keyword `const` instead of `static`: the reason it is static however, is so the C program can access it.
- petschge 7y agoI don't mind the const vs static. I mind just about everything else.
- pcwalton 7y agoThe consequences of the lexer hack are hard on humans (typename, most vexing parse, order of declarations being significant, weird function type syntax), not just compiler writers.
- Ar-Curunir 7y agoYou can already define a custom type which will allow you to have a nice syntax for multidimensional arrays: `Matrix<1,2,3>`. It solves your issue of nesting brackets, and you can impl arbitrary indexing for it.
- kazagistar 7y agoUnfortunately, rust does not have numeric types outside the special case baked in arrays, so it cannot do that yet afaik. There is a ticket for it, but it needs work.
- steveklabnik 7y agoYou can sorta do it kinda today: https://crates.io/crates/typenum https://crates.io/crates/typenum But it will be much nicer and better once const generics lands, it's true.
- Ar-Curunir 7y agoYou can do it in nightly already.
- Rusky 7y agoRust has an equivalent to `typename`, and it even requires it more often- the turbofish.
- pcwalton 7y agoThe turbofish rule is much easier to learn: just use ::<> when explicitly providing types for a call. The C++ concept of a "dependent qualified name" is a lot harder to explain. What's important isn't how often you need to help the compiler: it's how easy the rules are. The turbofish is unfortunate, but it's nowhere near as bad as typename.
- Rusky 7y agoBut C++ could take the same route of consistency regardless of the lexer hack. The turbofish is a strict superset of `typename`, and everywhere C++ lets you skip it it could simply require it instead.
- pcwalton 7y agoI don't think that's true. Consider a modified version of [1]: template<typename T> class X { void foo() { typename T::A* pa; } } The problem here is that C++ can't parse this without knowing whether T::A is a type or not. Otherwise it might be "T::A multiplied by pa". This is the lexer hack in action. Rust, by contrast, has no such limitation [2]: trait SomeTrait { type A; } struct X<T> { f: T, } impl<T> X<T> where T: SomeTrait { fn foo() { let pa: *mut T::A; } } This compiles and runs just fine with no need for a turbofish on T::A, because Rust has no lexer hack. [1]: https://en.cppreference.com/w/cpp/language/dependent_name https://en.cppreference.com/w/cpp/language/dependent_name [2]: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b62ed773e7243c5352e34dabb5077738 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- Rusky 7y agoThat's true but antiparallel to my point: C++ typename could have the same consistency as Rust's turbofish- its complicated rules are not necessitated by the lexer hack. (In a sense, the complicated rules are what enable the lexer hack.)
- discardable_dan 7y ago"The syntax is this way to make lexing it easier" is not a good argument for syntax. Ever. Lex it into tokens, parse it using semantic analysis, and be done. Plenty of compilers have been doing this for a long while now, and plenty of work has been done to make this a non-problem. Choosing syntax because it's slightly-easier to implement but slightly-harder to use is not a recipe for adoption.
- steveklabnik 7y agoWhat’s regular for computers is also more regular for humans. You’re absolutely right that taken to an extreme, doing things for computers isn’t great, but neither is making a super complex grammar
- discardable_dan 7y ago> What’s regular for computers is also more regular for humans. What does this even mean? Can you define "regular"? > neither is making a super complex grammar This isn't about grammatic complexity, it's about the location of a type relative to the associated identifier.
- steveklabnik 7y agoRegular has a technical meaning here, that is, Chomsky’s grammar hierarchy. It’s where the “regular” in “regular expression” comes from. That said, I’m using it in an imprecise way here to mean “simpler to process.” (This is because regular languages are simpler to process than say, context-sensitive languages.) Location of the type is about grammar complexity. Rust’s grammar plays into its type inference capabilities, and the pattern syntax. There’s an underlying uniformity with syntax elsewhere.
- deleted 7y ago[deleted]
- gbear605 7y agoThe example of typename shows that it’s a problem that can’t be overcome by the compiler, so it’s trading off one bad syntax for another, not trading off bad syntax for difficulty to implement.