https://github.com/rust-lang/rust/issues?q=label%3Arust-2-br...
• Struct literal syntax should have used C99 syntax. The language uses `name:type` everywhere except struct literals which use `name:value`, and this gets in the way of adding new syntax (type ascriptions).
• `Box` is semi-magical. Maybe it could have been a regular struct. Or maybe magical all the way to allow placement new and destructuring.
• Types are in borrowed, owned-fixed-size and owned-growable variants, but naming of them is a bit ad-hoc. There's str/String, but Path/PathBuf (instead of e.g. String/StringBuf or path/Path).
• Split between libcore and libstd is awkward to manage and not a good fit for WASM. It could have been one libstd with feature toggles (this might still happen).
• Some people think split between Eq and PartialEq is an overkill, and just makes floats annoying.
There are things that are still unsolved in Rust, like umovable types and self-referential structs. But it's hard to say they're a mistake — as far as we know, they're a necessary limitation to make other useful features work.
(I disagree personally on struct literal syntax but you're not alone, it's true.)
They then said that they'd probably do the same thing in 2021.
Now they're debating whether a 2021 edition is needed since there aren't any breaking changes with broad support except for the removal of deprecated syntax and APIs.
This is strong evidence that the answer to your question is "no".
There are also some thoughts about Rust-like languages with some differences, see https://boats.gitlab.io/blog/post/notes-on-a-smaller-rust/ as a prominent example.
This way slowly the ecosystem could move and stuff could be eventually removed.
> could be eventually removed.
It can not be used according to our stability policy. There's a lot of closed source Rust out there.
But this is just talk at the moment. Currently there is no mechanism to implement this. It would also be a challenge for documentation.
Why couldn't a new Rust edition create a new std2021 (for instance), and automagically use it when importing a std submodule in a project using the new edition (and in the prelude)? The old implementation would still be imported using std in crates using prior edition, and could still be imported in the new edition, but named std2018.
If all the implementation (except the now deleted stuff) is put in the std2021 module and the old std2018 () simply re-export it (and re-implement the old stuff), you wouldn't break anything would you?
I'm probably missing something, but so far I don't know what and I'd be really happy if somebody enlightened me.
There may be possible ways of doing this, but you're approaching this from the wrong angle. Right now, this is not possible, due to policy. This policy is informed by the technical restrictions right now. There may be possible ways in the future to handle this, but as of right now, there are not.
The reason this is true right now is that there is one copy of the standard library for every program. So it needs to support all editions, because code from multiple editions may call standard library functions.
If we had multiple copies of the standard library, you may end up with issues where two different programs can't interoperate because they'd be using different versions of the same crate, and given that one of the most important features of the standard library is interoperation, this is a huge drawback for the stdlib specifically.
(There may be other technical issues, I am not an expert here, but that's the biggest hurdle as far as I know.)
I still think you run into other issues, though.
That is, there's a path dependence, where the existence of try!() made the ? operator viable.
Yes, C++ creator Bjarne Stroustrup made a similar observation:
- For new features, people insist on LOUD explicit syntax.
- For established features, people want terse notation.
There seems to be an invisible "Overton Window" of evolving programming language features and syntax.
I feel like the reason pointers as a mechanism in native languages is such a barrier to cross for a newbie is because they are this magical star symbol * just floating around doing... something?
If people started out writing pointer<string> foo instead of string* foo and had to use explicit derefrencing via name rather than by magical glyphs onboarding would be so much easier.
I think it applies to almost all programming concepts too. Starting a language with a baseline grammar of just function invocation -f(x) = y as <noun>.<verb>(<noun>...) - and extrapolating from there and introducing terse grammar progressively as shortcuts would serve much better to get concepts in heads rather than arcane ritualistic glyphs. Introduce x.add(y) then say x + y is the shorthand.
Rust does a really great job "functionizing" almost everything in the language - it has the add trait after all https://doc.rust-lang.org/std/ops/trait.Add.html. You can actually write tons of Rust in just function call form and make it look like Lisp.
The futures design was built with epoll in mind, and now people are trying to wrap it round io_uring, they are feeling some pain. Would a different design have worked better, without massive drawbacks?
Could you provide an example? Because I don't really see it, except by requiring that non-const generics also be explicitly annotated?
const X: usize = 7;
let x: RectangularArray<i32, 2, 4>;
let y: RectangularArray<i32, X, {2 * 2}>;
Simple cases are fine, but i suspect a lot of real-world use of const generics are going to require blocks.AIUI, this is only because <> was chosen as the container for type arguments, and > is also a legal operator in expressions. So, for example, if Rust had used Scala-esque [] for type arguments, this would not be a problem.
[1] https://rust-lang.github.io/rfcs/2000-const-generics.html#ap...
The reason > needs braces is that without them, if you're parsing and so far you've seen these characters:
let y: RectangularArray<i32, X, 2>
You don't know if that final > is ending the argument list, or is a greater-than operator, with the rest of an expression coming after it.But if it was square brackets, then a closing square bracket on its own can only ever be the end of the list:
let y: RectangularArray[i32, X, 2]
Because if it was closing an array access, there would have to have been an opening square bracket: let y: RectangularArray[i32, X, a[1]https://keleshev.com/parsing-ambiguity-type-argument-v-less-...
foo<bar, qux>corge>
"is" valid syntax while foo[bar, qux]corge]
is not. The former requires some sort of disambiguation, while the latter is not. Put an other way, in terms of syntax `[]` always parses the same way (with `[` is infix and `]` terminates it), there can be ambiguity between indexing and generics but it doesn't really matter for the original parsing.For `<>` however, the parsing itself can be ambiguous as `>` could either be the terminator of an earlier `<` or it could be the infix `>` operator. That is where the issue lies, you can't know how to build the AST without either explicit disambiguation, or infinite lookaheads.
Now, we can't just do
drop<T>(T)
because of DSTs, so we we'll need a new type of consuming reference. And the dual to that, an initializing reference, would also solve the problem of creating DSTs with preallocated memory.Now both could use MaybeUninit, but it would be better to just have types that vary with the CFG, so one can insure that no matter how one get to point b, the memory is now initialized.
but Rust's drop isn't for that purpose. It's not for the actual cleanup of the struct and its children it is for additional cleanup before the children are deleted. So it has to be mutable. The compiler synthesizes the "delete children" code.
(I don't understand your point about drop<T>)
I think that purpose is the tail wagging the dog, an explanation of the current method rather than an actual requirement.
The simpler thing to do is just have drop on the aggregate calls drop on the fields, just as new on the aggregate can call new on the fields.
Ah I meant to write the function signature:
> fn drop<T>(T);
contrasted with the:
> fn drop<T>(&mut T);
that we have today
Furthermore, you still have to special-case Drop because now you have to support destructive destructuring for Drop types because it isn't allowed anywhere else.
And plus, if you forget to do this, the failure mode is a stack overflow.
The current design is absolutely based on practical requirements here, it is not a retroactive justification. This is by and large how destructors work, for good reason.
A Rust enum is an actual enumeration type, which C does not have. This is far more powerful.
For C compatibility you have lots of options like #[repr(C)] or #[repr(i32)] to be C compatible. So not sure what you are referring to?
#[repr(C)]
enum Foo {
A = 1,
B = 2,
C = 3,
UNKNOWN(i32),
}
which is not possible to define in current version of Rust.AKA Rust's enums are type-safe, not aliases for integers with some named constants.
Rather than Rust's, I'd say the mistake is C's enums. If you don't want enums, don't have them.
I guess that's one place where Go did something good: they didn't want to improve on C's enums with proper ADTs so they just stripped out the entire thing, an "enum" is an integer and a bunch of constants. Which you can also use to represent these non-enums in Rust though it doesn't have the iota / step convenience. A simple recursive macro might be able to handle it though.