6 ms·
> I don't intuitively know what "transmute" is That's fair, but... it's easily resolved by using Rust/reading the documentation. Humans don't intuitively know
by dbaupp 9y ago
> I don't intuitively know what "transmute" is
That's fair, but... it's easily resolved by using Rust/reading the documentation. Humans don't intuitively know what, say, 'float' in C means, either, but they learn it quickly.
Additionally, the non-Rust meaning of "transmute" is pretty close to how it's used in Rust: "To change, transform or convert one thing to another, or from one state or form to another".
There's definitely a lot of good in making things obvious to beginners to the language, but there's also always a general collection of "jargon"/symbols one ends up having to learn. Someone's who's a beginner in Rust but familiar with C may spend more time working out what the transmute line means than the C, but someone familiar with Rust wouldn't (and certainly not if they're not familiar with C: they'd have to work out the src/dst order for memcpy).
> I think Rust has achieved a great thing, but I wish it were like, 50x simpler
As others have pointed out elsewhere many times: what would you remove? The core of what people complain about in Rust (lifetimes etc.), is also core to achieving it's goals. All of those core features are pretty orthogonal and fairly minimal.
- camgunz 9y agoSo I looked up transmute, and it turns out this is how it's called: let x: f32 = 0; let y: i32 = std::mem::transmute::<f32, i32>(x) I'm fine with this, actually. With the type information it's clear. I do think it's a little long-winded, but I don't see a way to shorten it without giving up namespacing or safety. > As others have pointed out elsewhere many times: what would you remove? The core of what people complain about in Rust (lifetimes etc.), is also core to achieving it's goals. All of those core features are pretty orthogonal and fairly minimal. I'm pretty OK with lifetimes. I think probably they could be a little more explicit and have better syntax, but I think they're complicated by nature and -- as you point out -- core to achieving memory/data race safety in Rust. I'm happy, however, to create a big gripe list here for you, haha :) Mostly my criticisms are: - some concepts are unclear because they use overloaded keywords or imprecise language - some features are confusing due to inconsistent structure/syntax - some (many) features aren't worth their complexity Innnnnnn order of the Rust book: 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? 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. I don't find "everything is an expression" to be that valuable, and it leads to a lot of weirdness. 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? Returning from loops is another, and that looks very weird: `break <expr>`. I can see how if/else as expressions is nice shorthand in a ternary kind of way, but I wouldn't (and don't, in any other systems language) mind just initializing my variables with zero values and updating them inside those blocks. Generally I like destructuring, but I think Rust's struct destructuring is a little much. I wouldn't mind having... I don't know 4 more lines of variable initialization the 0.0008% of the time I need to do this. I would prefer that ranges weren't special syntax and instead worked just like any other iterator. This also avoids weird parens in range-based pipelines. 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 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. 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. 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. I wish closures taking no arguments didn't look like the boolean or operator. Using `cfn()` (closure function) here wouldn't have been terrible, I don't think. `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. I would prefer `impl <trait> on <struct>` instead of `impl <trait> for <struct>` because `for` already means something else. 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. I wish the "new type idiom" didn't reuse `struct`. Probably `type` goes there. 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. --- Probably these seem like really small issues, but I really think that all these things together would make Rust much more consistent and clear at a very slight cost to ergonomics.
- 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.