Donât 99% of people donât need the efficiency boost of not having a garbage collector. Instead learn a Functional Programming Language like Scala, OCaml or F#
TehPers@beehaw.org
on 03 Sep 2025 22:13
nextcollapse
Or both? Functional languages are good to be familiar with, but so is Rustâs strict ownership model. Both lead to better code in all languages.
Itâs not only the efficiency boost. What about safe concurrency? Or about the tooling - does sbt really compare favorably to cargo? (Iâd say ânoâ, having used both over multiple years)
Most web applications donât even need concurrency, what they need is a language thatâs easy to read with as little cruft as possible, rust is not that.
Iâm saying the best web language would be one that is as simple as python or js with the type security of an ml language. Rescript comes to mind as well.
FizzyOrange@programming.dev
on 01 Sep 2025 16:44
nextcollapse
One mistake they did unfortunately ship though is bind patterns that look like variable names.
TehPers@beehaw.org
on 01 Sep 2025 20:17
nextcollapse
As in using consts (or variables you think are consts) as refutable patterns? Yeah this was an oversight Iâm sure.
One option is an edition change requiring a const keyword, so
match foo {
const BAR => {},
baz => {},
}
Right now they use a lint to try to warn the dev though.
shape_warrior_t@programming.dev
on 02 Sep 2025 00:58
collapse
Suppose we require a keyword â say var â before all binding patterns. This results in having to write things like for (&(var x1, var y1, var z1), &(var x2, var y2, var z2)) in points.iter().tuple_windows() {},
which is quite a bit more verbose than the current for (&(x1, y1, z1), &(x2, y2, z2)) in points.iter().tuple_windows() {}.
Not to mention youâll have to write let var x = 0; just to declare a variable, unless you redesign the language to allow you to just write var x = 0 (and if you do that, youâll also have to somehow support a coherent way to express if let Some(x) = arr.pop() {} and let Some(x) = arr.pop() else {todo!()}).
Suppose we require a keyword â say const â before all value-matching patterns that look like variables. Then, whatâs currently
TehPers@beehaw.org
on 02 Sep 2025 22:40
nextcollapse
I completely forgot that unit structs/variants define their own associated consts. I wonder if in patterns the type can be used instead of the associated const though? That might resolve a lot of the headache. Itâd mean changing the way the ident is resolved to looking in the type namespace though.
const <block> already works as a pattern I believe? That could be used instead for constants.
Literals would always work in-place as constant expressions.
I went the âonly let introduces bindingsâ route, and Iâm pretty happy so far:
if (left.next(), right.next())
... is (Some(let l), Some(let r)) { /* use l and r */ }
... is (Some(let l), None ) { /* use l */ }
... is (None, Some(let r)) { /* use r */ }
... is (None, None ) { /* use nothing */ }
}
sugar_in_your_tea@sh.itjust.works
on 05 Sep 2025 14:47
collapse
Yeah, they could literally have the same syntax as now, but w/ let when introducing a variable. So:
Or you could put the let before the Some(âŚ) as let Some(l), which allows us to keep the current if let Some(âŚ) = ⌠syntax. Either of those would feel more consistent than the current implementation.
TehPers@beehaw.org
on 02 Sep 2025 22:34
nextcollapse
Interesting perspective. Not sure I agree with most of the suggestions though.
Some of the earlier ones remind me of C#'s records. Were they inspired from them?
Some of the later ones just feel like Go to me.
I like the idea of dropping syntax for ranges. It does feel like the syntax just leads to confusion.
Named parameters are problematic because of parameter names becoming significant to the API. See Pythonâs * and / in parameter lists (like def foo(a, *, b) for example).
Some of the earlier ones remind me of C#'s records. Were they inspired from them?
No, that stuff is much much older.
Named parameters are problematic because of parameter names becoming significant to the API. See Pythonâs * and / in parameter lists (like def foo(a, *, b) for example).
I think the name problem is overblown, you can always have an annotation to facilitate name changes.
azdle@news.idlestate.org
on 03 Sep 2025 15:24
nextcollapse
Iâm curious, have you used Rust much? Most of those changes just feel like ârust should be more familiar to meâ changes.
Also:
As Rust 2.0 is not going to happen, Rust users will never get these language design fixes
Isnât necessarily true for most of your suggestions. Since most of them are just changes to syntax semantics and not language semantics they could be made in an edition.
calcopiritus@lemmy.world
on 04 Sep 2025 12:00
nextcollapse
The âdrop the array and slice syntaxâ is just nuts. With 0 justification.
sugar_in_your_tea@sh.itjust.works
on 05 Sep 2025 15:38
nextcollapse
Drop struct initialization syntax
âŚ
Named parameters using =
I would like to take this a step further: support default arguments (fn func(a: int = 0); func(); func(a=0)), and then have struct initialization work like that. Itâs really nice in Python, though Iâd like some rules to require args w/ default values to use the assignment syntax, and _require args w/o default values to be in positional order (our codebase is littered with long_func_name(long_var_name=long_var_name, âŚ) where function calls are incredibly long and redundant). That does a few things for us:
make defaults more obvious - no more T::new() since now you can just do T() if you just want defaults
make special cases obvious - func(1, 2, some_arg=value) makes it obvious that value is special
eliminates the T{âŚ} syntax, since you can just use the T(âŚ) syntax
We probably disagree about requiring positional args to use the assignment syntax, but both your preference and mine can be enforced w/ conventions (i.e. give your variables relevant names to the function).
Replace impl and eliminate the impl X for Y syntax
I disagree, though I can see where youâre coming from. Many OOP languages use the first option, and the second try to reuse keywords.
But youâre also missing one use of the impl keyword: fn func() -> impl Trait.
I come from Go and I honestly like the syntax there:
func (T self) fn_name(args...) {
}
Many donât. I like it because it shows that these methods arenât part of the type T, theyâre âattachedâ to it. Thatâs much closer to how this actually works that how Java represents it. Itâs a small thing, but for something low-level like Rust, I think that makes sense.
Also, it makes room for implementing additional functionality on existing types outside of that package. This doesnât work exactly as Iâd prefer, but the syntax opens the door to that.
Stop using macros to emulate varargs
Iâm on the fence about this. The println!(â{}â, val) syntax is really nice, and thatâs one of the common uses for varargs. If your varargs are all the same type, you can use slices: fn func(varargs: &[T]); func(&[1, 2, 3]) and avoid macros entirely. If your types are inconsistent (i.e. Câs varargs), youâre going to have a rough time w/ any vararg syntax and will need a macro anyway.
I agree that macros shouldnât be abused to emulate varargs, but I donât think we actually need a vararg syntax and can use existing tools.
Drop range syntax
Agree. Use a built-in like Pythonâs range(âŚ) instead of fancy syntax, and have the type ([T], &[T], or vec<T>) be inferred at compile time. This can probably just drop-in to everywhere the range syntax is being used currently. Itâs a little more verbose, but itâs way clearer.
Drop array and slice syntax
Are you suggesting using keywords/methods instead? So [T].slice(âŚ) or even Array<T>.slice(âŚ)? I think thatâs reasonable, and it can work similarly to Vec, but it would complicate the syntax a bit for my vararg proposal. But yeah, [T, count] is pretty ugly, but Iâm not convinced Array<T, count> is all that much better.
I think your post could benefit from a couple examples.
Make generics use [] instead of <>/::<>
I prefer Dâs ! approach: T!U or T!(U). I would switch macros to T#(âŚ) instead, since # is already used for things adjacent to macros anyway. But to minimize impact, we could just use # for generics instead.
Iâm not a fan of the [] for generics, and I disagree w/ Go using that. <> is awkward due to conflicts with comparison operators, so I agree that it needs to go.
Fold Index and IndexMut into Fn trait family
Eh, Iâm less interested in this one, but I donât have a strong opinion. Calling *T.index(âŚ) isnât a big askâŚ
Remove the hierarchy between Eq/Ord and PartialEq/PartialOrd traits
This is certainly annoying, but surely the right solution is to just make floats implement Eq/Ord, no? I can understand someone prefe
requiring positional args to use the assignment syntax
Not sure, maybe my wording isnât clear enough.
What I intended to say is that arguments can be named, not that they have to.
In any case, the order of arguments must match the order of parameters, named or not.
But youâre also missing one use of the impl keyword: fn func() -> impl Trait.
That removal could actually happen, so I didnât list it. (Rust started requiring dyn and disallowed naked trait returns with edition 2018. So dropping the impl in that position might not be completely impossible like the other uses of impl.)
Are you suggesting using keywords/methods [for array and slice syntax] instead?
Yes, just methods.
I can understand someone preferring the PartialEq/PartialOrd behavior
You can have both â thatâs whatâs being made possible by them not being in a hierarchy.
I think if-let makes sense, but donât expand it.
Itâs a bit late for that, isnât it? ;-)
Why? What value does -> () provide? Why not elide that?
What value is provided by keeping it? Why a syntactic special-case for exactly that type and not any other random type?
languages w/o them feel awkward since youâre generally limited to one statement per line
But youâre also missing one use of the impl keyword: fn func() -> impl Trait.
[âŚ] So dropping the impl in [return position] might not be completely impossible like the other uses of impl.
But the impl markes that it is a trait to the programmers.
Take the following functions:
func1()->A{...}
func2()->A{...}
Does the following snippet compile?
let mut thing = func1();
thing = func2();
Under the current rules we know it will. But if A could be a trait, the functions could return different types. We currently mark that with the impl.
Why? What value does -> () provide? Why not elide that?
What value is provided by keeping it?
What value does cluttering up your code with -> () provide?
Why a syntactic special-case for exactly that type and not any other random type?
Because the unit type is special, just like the never ! type. () also has the special importance of being the return value of an empty statement and some other stuff.
languages w/o [semicolons] feel awkward since youâre generally limited to one statement per line
Then fixing that might make sense. :-)
Itâs fixed with semicolons ;-)
smiletolerantly@awful.systems
on 05 Sep 2025 15:48
collapse
Drop if-let
Over my cold dead body. if-let-else is such a fantastic pattern. Makes everything an order of magnitude more readable. Works so nicely for unwrapping opts/errs.
threaded - newest
I really should learn some Rust đ
Donât 99% of people donât need the efficiency boost of not having a garbage collector. Instead learn a Functional Programming Language like Scala, OCaml or F#
Or both? Functional languages are good to be familiar with, but so is Rustâs strict ownership model. Both lead to better code in all languages.
Itâs not only the efficiency boost. What about safe concurrency? Or about the tooling - does sbt really compare favorably to cargo? (Iâd say ânoâ, having used both over multiple years)
Most web applications donât even need concurrency, what they need is a language thatâs easy to read with as little cruft as possible, rust is not that.
Iâm saying the best web language would be one that is as simple as python or js with the type security of an ml language. Rescript comes to mind as well.
One mistake they did unfortunately ship though is bind patterns that look like variable names.
As in using consts (or variables you think are consts) as refutable patterns? Yeah this was an oversight Iâm sure.
One option is an edition change requiring a
const
keyword, soRight now they use a lint to try to warn the dev though.
There was a recent langdev Stack Exchange question about this very topic. Itâs a bit trickier to design than it might seem at first.
Suppose we require a keyword â say
var
â before all binding patterns. This results in having to write things likefor (&(var x1, var y1, var z1), &(var x2, var y2, var z2)) in points.iter().tuple_windows() {}
,which is quite a bit more verbose than the current
for (&(x1, y1, z1), &(x2, y2, z2)) in points.iter().tuple_windows() {}
.Not to mention youâll have to write
let var x = 0;
just to declare a variable, unless you redesign the language to allow you to just writevar x = 0
(and if you do that, youâll also have to somehow support a coherent way to expressif let Some(x) = arr.pop() {}
andlet Some(x) = arr.pop() else {todo!()}
).Suppose we require a keyword â say
const
â before all value-matching patterns that look like variables. Then, whatâs currentlyturns into either the inconsistently ugly
or the even more verbose
I completely forgot that unit structs/variants define their own associated consts. I wonder if in patterns the type can be used instead of the associated const though? That might resolve a lot of the headache. Itâd mean changing the way the ident is resolved to looking in the type namespace though.
const <block>
already works as a pattern I believe? That could be used instead for constants.Literals would always work in-place as constant expressions.
I went the âonly
let
introduces bindingsâ route, and Iâm pretty happy so far:Yeah, they could literally have the same syntax as now, but w/
let
when introducing a variable. So:Or you could put the
let
before theSome(âŚ)
aslet Some(l)
, which allows us to keep the currentif let Some(âŚ) = âŚ
syntax. Either of those would feel more consistent than the current implementation..
In addition to that, I have my own list of things Rust should not have shipped with, but did.
Interesting perspective. Not sure I agree with most of the suggestions though.
Some of the earlier ones remind me of C#'s records. Were they inspired from them?
Some of the later ones just feel like Go to me.
I like the idea of dropping syntax for ranges. It does feel like the syntax just leads to confusion.
Named parameters are problematic because of parameter names becoming significant to the API. See Pythonâs
*
and/
in parameter lists (likedef foo(a, *, b)
for example).No, that stuff is much much older.
I think the name problem is overblown, you can always have an annotation to facilitate name changes.
Iâm curious, have you used Rust much? Most of those changes just feel like ârust should be more familiar to meâ changes.
Also:
Isnât necessarily true for most of your suggestions. Since most of them are just changes to syntax semantics and not language semantics they could be made in an edition.
The âdrop the array and slice syntaxâ is just nuts. With 0 justification.
I would like to take this a step further: support default arguments (
fn func(a: int = 0); func(); func(a=0)
), and then have struct initialization work like that. Itâs really nice in Python, though Iâd like some rules to require args w/ default values to use the assignment syntax, and _require args w/o default values to be in positional order (our codebase is littered withlong_func_name(long_var_name=long_var_name, âŚ)
where function calls are incredibly long and redundant). That does a few things for us:T::new()
since now you can just doT()
if you just want defaultsfunc(1, 2, some_arg=value)
makes it obvious thatvalue
is specialT{âŚ}
syntax, since you can just use theT(âŚ)
syntaxWe probably disagree about requiring positional args to use the assignment syntax, but both your preference and mine can be enforced w/ conventions (i.e. give your variables relevant names to the function).
I disagree, though I can see where youâre coming from. Many OOP languages use the first option, and the second try to reuse keywords.
But youâre also missing one use of the
impl
keyword:fn func() -> impl Trait
.I come from Go and I honestly like the syntax there:
Many donât. I like it because it shows that these methods arenât part of the type T, theyâre âattachedâ to it. Thatâs much closer to how this actually works that how Java represents it. Itâs a small thing, but for something low-level like Rust, I think that makes sense.
Also, it makes room for implementing additional functionality on existing types outside of that package. This doesnât work exactly as Iâd prefer, but the syntax opens the door to that.
Iâm on the fence about this. The
println!(â{}â, val)
syntax is really nice, and thatâs one of the common uses for varargs. If your varargs are all the same type, you can use slices:fn func(varargs: &[T]); func(&[1, 2, 3])
and avoid macros entirely. If your types are inconsistent (i.e. Câs varargs), youâre going to have a rough time w/ any vararg syntax and will need a macro anyway.I agree that macros shouldnât be abused to emulate varargs, but I donât think we actually need a vararg syntax and can use existing tools.
Agree. Use a built-in like Pythonâs
range(âŚ)
instead of fancy syntax, and have the type ([T]
,&[T]
, orvec<T>
) be inferred at compile time. This can probably just drop-in to everywhere the range syntax is being used currently. Itâs a little more verbose, but itâs way clearer.Are you suggesting using keywords/methods instead? So
[T].slice(âŚ)
or evenArray<T>.slice(âŚ)
? I think thatâs reasonable, and it can work similarly toVec
, but it would complicate the syntax a bit for my vararg proposal. But yeah,[T, count]
is pretty ugly, but Iâm not convincedArray<T, count>
is all that much better.I think your post could benefit from a couple examples.
I prefer Dâs
!
approach:T!U
orT!(U)
. I would switch macros toT#(âŚ)
instead, since#
is already used for things adjacent to macros anyway. But to minimize impact, we could just use#
for generics instead.Iâm not a fan of the
[]
for generics, and I disagree w/ Go using that.<>
is awkward due to conflicts with comparison operators, so I agree that it needs to go.Eh, Iâm less interested in this one, but I donât have a strong opinion. Calling
*T.index(âŚ)
isnât a big askâŚThis is certainly annoying, but surely the right solution is to just make floats implement Eq/Ord, no? I can understand someone prefe
Thanks for your reply, some replies below!
Not sure, maybe my wording isnât clear enough. What I intended to say is that arguments can be named, not that they have to. In any case, the order of arguments must match the order of parameters, named or not.
That removal could actually happen, so I didnât list it. (Rust started requiring
dyn
and disallowed naked trait returns with edition 2018. So dropping theimpl
in that position might not be completely impossible like the other uses ofimpl
.)Yes, just methods.
You can have both â thatâs whatâs being made possible by them not being in a hierarchy.
Itâs a bit late for that, isnât it? ;-)
What value is provided by keeping it? Why a syntactic special-case for exactly that type and not any other random type?
Then fixing that might make sense. :-)
But the impl markes that it is a trait to the programmers.
Take the following functions:
Does the following snippet compile?
Under the current rules we know it will. But if A could be a trait, the functions could return different types. We currently mark that with the
impl
.What value does cluttering up your code with
-> ()
provide?Because the unit type is special, just like the never
!
type.()
also has the special importance of being the return value of an empty statement and some other stuff.Itâs fixed with semicolons ;-)
Over my cold dead body. if-let-else is such a fantastic pattern. Makes everything an order of magnitude more readable. Works so nicely for unwrapping opts/errs.
Their suggested replacement is closer to C#'s
is
keyword:The issue here is that we still have let-else, which canât be translated as easily:
I donât think the alternative to let-else is too bad.
Thatâs not an alternative, itâs removing
let-else
entirely. Itâs equivalent to this:let-else
is specifically a feature that allows you to use a refutable pattern to deconstruct a value by providing a divergingelse
branch.Removing
let-else
is the whole point of the linked article series:Being able to do to everything Rust does, but without Rustâs zoo of
if-then-else
,match
,if-let
,let-else
etc.