6 ms·
> Rust is often different for no real reason. Casting is a good example of this; what was wrong with `(i8)thing`? What does `as` gain us? Couldn't we have used
by dbaupp 9y ago
> Rust is often different for no real reason. Casting is a good example of this; what was wrong with `(i8)thing`? What does `as` gain us? Couldn't we have used `as` in a more powerful way, like context managers in Python for example?
(type)expression is annoying to parse: in, say, '(i8)x' there's no way to tell that it's a cast expression until you get to the x. That is, (i8) is a valid expression (if there's a variable called i8), and is something like (x)(y) a cast or a function call?
In any case, destructors and move semantics gives most of the benefits of context managers.
> I'm not wild about `macro!`. Because macros are hygenic, I don't care if something is a macro or not and the `!` makes those calls stick out unnecessarily. I would have preferred that `!` indicated mutation in some way, like in Scheme for example. Or maybe get rid of the `mut` keyword and use `!` in variables. Really anything would be better.
It's a bit of a personal argument, but being explicitly marked highlights where weird things may happen, like side-effects that happen twice or returns out of the current function (a function call itself can't return: only `return`, `?` and macros that use them). But sure, it's something people might not like.
> Semicolons suppressing block expression values is one -- why would you ever want to use a block as an rvalue to only assign `unit` to the lvalue?
This is just consistency. Why have block-as-an-r-value as the only case when a semi-colon isn't allowed? Furthermore, there's places for blocks that are formally r-values, but perfectly legitimately have type (), like the arms of match blocks and bodies of closures. And, lastly, not having a special case like this makes writing macros easier.
> I'm a little on the fence with `match`. I think it strikes the right balance between Python's "use if/else for everything" and C's restrictive/dangerous switch, but most match blocks I see are messy. I think all in all I'm into it, and I like that it's got a history in other languages like OCaml, I just wish I could come up w/ a way to clean it up a little.
Match is necessary for working with `enum`s, and enums are great tool for avoiding allocations (e.g. Option<T> instead of a T* that's possibly null) and generally for guiding towards type safety. Having a single entity that does the complete deconstruction of an enum is important with move semantics, or else one would be forced to do a lot of extraneous nested "as_mut"/"is_none" etc. checking, especially for nested deconstructions.
> match guards make me crazy though; that's exactly what if statements are for. The whole reason I like match/switch more than if/else is that it's restricted to the thing you're matching/switching on. A match guard can run a conditional on anything. It's completely superfluous if your language has if/else.
It's very convenient for conditionalizing based on enums... and yes, it's unrestricted, but that's for consistency: why restrict it?
> You can probably guess I don't like match binding either. First I think that's what `let` is for, but I also think using `@` is both very non-intuitive and a big waste of an operator -- all to avoid a single `let` expression.
Do you don't like binding any variables in a match, or specifically doing it with @? I don't think you can use a let to emulate it, at least not without a lot of clunkiness. In any case, it's rarely used and rarely seen, and yes, probably not worth `@` (a keyword could be better).
> if/while let I really like, but its weird that they don't follow the same rules as regular let expressions, like can `Some(i)` be an lvalue normally (no, it can't). I would prefer that they did follow the same rules, or that they used different syntax than `let` because they are in fact different.
If they followed the same rules, they wouldn't be conditional and there would be no point. One can regard the 'if' and 'while' addition as exactly that difference in syntax: "if" means conditional, so an "if let" is a conditional let. I would think adding more keywords/syntax has a larger downside than the small expansion of let's behaviour, but it's hard to say without actually being able to compare it in practice. The "killer" argument for me is that other languages use the same syntax, so there's no particular reason for Rust to be different here, given it's mostly an aesthetics argument.
> `pub` doesn't really mean "public", it means "visibility" and pub without modifiers means "visibility(public)". I do sort of like `mod`, but I dislike having to nest things inside of it. I would prefer a file-based approach, where you declared the file's module at the top.
Rust does have a file-based approach.
"pub" does mean "public", just more restricted than globally. Which, to be fair, is usually what public means in english. Visibility/access control has been an endless argument in Rust.
> I would prefer `impl <trait> on <struct>` instead of `impl <trait> for <struct>` because `for` already means something else.
'for' means something else in a completely different context. I don't really see the benefit in distinguishing them, but sure, I guess you could rename a keyword.
> I would have liked to use traits as types in function signatures instead of the clunky generic syntax. Alternatively just require the `where` clause. Both options reduce the number of things you have to know.
You'll be excited for some of the "impl trait" stuff.
> I wish the "new type idiom" didn't reuse `struct`. Probably `type` goes there.
It's literally just an idiom built on top of a struct. There's nothing special about it. I take it you want the idiom to be baked into the language to be slightly nicer.
> I don't really like the ceremony around heap allocation. I know there are a lot of benefits to `Box`, but I'd prefer `` to Box<T> and `alloc` or even just `new` to `Box::new`. Or hey, if you're against operator reuse, let's use `@` now that we've tossed match binding. Mainly my complaint is this is unnecessarily different from other systems languages.*
Box is (only very slightly) special: it's more consistent to not preference it over other pointer types. In any case, Box literally used to be "~" and Rc "@". People complained endlessly about the impenetrable sigils.
Also, Box shouldn't be that common in most Rust code.
- camgunz 9y ago> (type)expression is annoying to parse Ehhh I think it's not that bad, no harder than arithmetic expression parsing certainly. It's just one more grammar rule. Plus Rust supports everything that's needed already because of operator overloading. > In any case, destructors and move semantics gives most of the benefits of context managers. They (well, destructors anyway) are less explicit though. When you use a context manager in Python you know it's cleaning things up. When you "use" a destructor in Rust you usually don't ever know you did. This can get you into trouble if you're relying on RAII to cleanup after you: you can get a handle to things and then enter a long loop or call chain. Sure you can avoid that by calling `drop`, but if context managers were the idiom this kind of thing would never be an issue. But I admit the difference is pretty small -- and in fact might be surprising to systems programmers so probably it's the right choice (even if destructors themselves are kind of mind boggling). > This is just consistency. Why have block-as-an-r-value as the only case when a semi-colon isn't allowed? Furthermore, there's places for blocks that are formally r-values, but perfectly legitimately have type (), like the arms of match blocks and bodies of closures. And, lastly, not having a special case like this makes writing macros easier. Well mostly I would deal with all this by removing "everything is an expression". Match blocks would just be regular blocks like `switch` in C or what have you, and so on. > Match is necessary for working with `enum`s, and enums are great tool for avoiding allocations (e.g. Option<T> instead of a T* that's possibly null) and generally for guiding towards type safety. Having a single entity that does the complete deconstruction of an enum is important with move semantics, or else one would be forced to do a lot of extraneous nested "as_mut"/"is_none" etc. checking, especially for nested deconstructions. Well switch/if/else have worked fine for a long time; and now C/C++ compilers warn you on missing branches when switching on enumerated values (Rust won't if you have a fallthrough case). But I prefer switch/match to if/else because if/else are too general for just switching on a variable (which is why I'm very dismayed at match guards -- they wholly abrogate the benefit of match over if/else), and I think switch's behavior is generally too restrictive (this is the only problem I have with switch provided you use proper blocks instead of the souped-up goto it really is). Match has so many features baked into it that every time I see one I have to stop and take a deep breath. It's very much geared towards writing and not reading, I feel. > [match guards are] very convenient for conditionalizing based on enums... and yes, it's unrestricted, but that's for consistency: why restrict it? I kind of went into this above, but in a language that doesn't have a good match/switch (like Python) you'll frequently run into code like this: if value == 1: # do a thing elif value == 2: # do a thing elif value in range(3, 20): # do a thing elif totally_unrelated_function_call() and other_thing == 98: # do a thing elif value >= 20: # do a thing But when you have switch, you can't run that 4th conditional and have it shortcircuit the 5th conditional. Switch is, in that way, very much specifically for breaking down enums, so when I see one I can restrict my thinking to that variable and that variable alone. Unless, of course, we're using match guards. Then I have to consider everything again, and I wonder why we're not just using if/else. Of course I understand that match is an expression so that's another "benefit", but if/else also have that behavior in Rust and I would get rid of that anyway. > Do you don't like binding any variables in a match, or specifically doing it with @? I don't think you can use a let to emulate it, at least not without a lot of clunkiness. I don't think this is too bad: fn main() { println!("Tell me type of person you are"); let my_age: age(); match my_age { 0 => println!("I'm not born yet I guess"), // Could `match` 1 ... 12 directly but then what age // would the child be? Instead, bind to `n` for the // sequence of 1 .. 12. Now the age can be reported. 1 ... 12 => println!("I'm a child of age {:?}", my_age), 13 ... 19 => println!("I'm a teen of age {:?}", my_age), // Nothing bound. Return the result. _ => println!("I'm an old person of age {:?}", my_age), } > Rust does have a file-based approach. Sure but I guess my argument is the file/folder hierarchy approach combined with mod is a little clunky. I think it's clearer to just declare a module at the top and let people use whatever folder structure they want. > Visibility/access control has been an endless argument in Rust. Hah, OK fair. I guess you can't please everyone ;) > I take it you want the idiom to be baked into the language to be slightly nicer. Yeah like `type` or some such. It's a little weird to kind of overload struct this way (and don't get me going on enum haha -- it's a variant!!!!!!!!!!! it's a union!!!!!!! it's anything other than an enum!!!!!). > People complained endlessly about the impenetrable sigils. Oh yeah, I was one of them. But my complaint was all the extra sigils. There was `~` for an owned pointer, `@` for GC pointers, and the `mut` suffix for mutable versions, and the worst sin of all was that `*` was strictly for unsafe pointers. So the one you're most likely to recognize is the one you'll basically never see. Booooo. But I guess mostly what it comes down to is that "everything is an expression" greatly weirds the language for me, match is a super feature, and there are weird tricks that don't really make sense like "Use _ as the default case in a match" and "if you're destructuring in a match and you don't care about some fields, just use `..`". At least in switch, the default case is called "default".