Live data from Hacker News

Rust's Ugly Syntax (2023)

matklad.github.io

141–150 of 171 posts

Re: Rust's Ugly Syntax (2023)

#141

Earlier quoted context omitted.

I always, always forget what `'a: 'b` means, because I remember it always being the opposite of what I think it is, but memorizing that obviously doesn't work because then it will just flip again the next time. It's so annoying.

It was a good signal to me that you are overthinking into the architecture if that is really required. Rust makes something pretty much impossible in C/C++ possible, but not necessarily easy, and that would be one such example.

> It was a good signal to me that you are overthinking into the architecture if that is really required.

Sure, maybe I don't need to statically guarantee the correct execution of code that could easily just throw at runtime instead, but it sure is a fun hobby.

Re: Rust's Ugly Syntax (2023)

#142
post #139
post #2

I think the article makes a good point, but the actual example isn’t Rust’s worst, not even close. It gets really hard to follow code when multiple generic types are combined with lifetime markers. Then it truly becomes a mess.

That's why I am a huge fan of Rust but at the same time at the end of the day all I want is the features of the language that Rust has minus the memory management and a GC. That would be my dream language. If only ReasonML/Rescript were more popular... Or I guess Elixir

check out Google’s Carbon :)

Re: Rust's Ugly Syntax (2023)

#143
post #136

Here's the cleaned up version of Rust from the OP: pub fn read(path: Path) -> Bytes { let file = File::open(path); let bytes = Bytes::new(); file.read_to_end(bytes); bytes } Here is is in raku ( https://raku.org ): sub read(Str:D $path --> Buf:D) { $path.IO.slurp: :bin } [the `--> Buf:D` is the raku alternative to monads]

Then it’s just this with C#: public byte[] Read(string path) => File.ReadAllBytes(path); I think the article’s trying to explain a concept using an arbitrary piece of code from stdlib, not necessarily that specific scenario (opening and reading all bytes from a file).

well no need for the sub wrapper really

  say $path.IO.slurp: :bin

Re: Rust's Ugly Syntax (2023)

#144
post #15

Earlier quoted context omitted.

Throw an exception proving the point of the article even further

You would actually use a Result type: use std::io; pub fn read(path: Path) -> io::Result { File::open(path)?.read_to_end() }

Sure, if you are allowed to change the signature, makes it look more ugly than just returning Bytes though

Re: Rust's Ugly Syntax (2023)

#145
post #139
post #2

I think the article makes a good point, but the actual example isn’t Rust’s worst, not even close. It gets really hard to follow code when multiple generic types are combined with lifetime markers. Then it truly becomes a mess.

That's why I am a huge fan of Rust but at the same time at the end of the day all I want is the features of the language that Rust has minus the memory management and a GC. That would be my dream language. If only ReasonML/Rescript were more popular... Or I guess Elixir

Scala

Re: Rust's Ugly Syntax (2023)

#146
post #2

I think the article makes a good point, but the actual example isn’t Rust’s worst, not even close. It gets really hard to follow code when multiple generic types are combined with lifetime markers. Then it truly becomes a mess.

Here's a nice example of a Trait that has async functions: fn list_items ( &'life0 self, collection_href: &'life1 str, ) -> Pin , Error>> + Send + 'async_trait>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait, Rendered docs: https://mirror.whynothugo.nl/vdirsyncer/v2.0.0-beta0/vstorag... Source: https://git.sr.ht/~whynothugo/vdirsyncer-rs/tree/v2.0.0-beta...

or just

    async fn list_items(&self, colletion_href: &str) -> Result, Error>

Re: Rust's Ugly Syntax (2023)

#147
post #127

Someone needs to tell them about async Rust. Big yikes.

I'm a big Rust fan, but async Rust is an abomination.

I love async Rust. Its implementation is marvelous. Sueper plasant to write now that

* async closures

* async trait fns,

* `impl Trait` everywhere

are in place.

Re: Rust's Ugly Syntax (2023)

#148
post #2

I think the article makes a good point, but the actual example isn’t Rust’s worst, not even close. It gets really hard to follow code when multiple generic types are combined with lifetime markers. Then it truly becomes a mess.

I always, always forget what `'a: 'b` means, because I remember it always being the opposite of what I think it is, but memorizing that obviously doesn't work because then it will just flip again the next time. It's so annoying.

You have to first remember what lifetimes are there for in the first place: to indicate that values live at least a certain duration. That is what the type &'a means. Then it becomes clear what the relation 'a: 'b means

Re: Rust's Ugly Syntax (2023)

#149
post #139
post #2

I think the article makes a good point, but the actual example isn’t Rust’s worst, not even close. It gets really hard to follow code when multiple generic types are combined with lifetime markers. Then it truly becomes a mess.

That's why I am a huge fan of Rust but at the same time at the end of the day all I want is the features of the language that Rust has minus the memory management and a GC. That would be my dream language. If only ReasonML/Rescript were more popular... Or I guess Elixir

It's not necessarily identical to what you are looking for per se but a mix of C# and F# will be the closest overall to Rust in terms of performance, access to systems programming features, language expressiveness and tooling experience.

Cons are:

- C# has OOP (you don't have to use it heavily)

- No Hindler-Milner type inference in C#, nested generic arguments may need to be specified by hand

- Smaller amount of supported targets by CoreCLR: x86, x86_64, arm, arm64 for ISAs and Linux, Windows, macOS and FreeBSD for OSes. NativeAOT-based support is experimentally available on iOS, and is undergoing further work. As you can imagine, LLVM targets absolutely everything under the sun and above it too. no-std story in Rust is first-class. C# has bflat and zerosharp but they are niche.

- In C#, type unions will only be available in one of the coming versions. F# to the rescue

- Error handling is a combination of exceptions and e.g. int.TryParse patterns, there is no implicit returns with ? like in Rust

- Lack of associated types and the types own their interface implementations unlike traits which you can introduce without control over the source. This results in having to implement wrapper types (even if they are structs) if you want to modify their interface implementations

- You only control shallow immutability - readonly struct will not prohibit modification of the contents of a Dictionary that it holds

- async/await is more expensive

- Big popular libraries often have features or implementation incompatible or not guaranteed to work with native compilation via NativeAOT

- Object reference nullability (e.g. 'T?') happens at the level of static analysis i.e. does not participate in type system the same way Option does in Rust

Pros are:

- Has FP features like in Rust: high order functions, pattern matching (match val -> val switch { , also 'is'), records, tuples, deconstruction

- Fast to compile and run, AOT not so fast to compile but tolerable

- If you know how to use Cargo, you know how to use .NET CLI: cargo init -> dotnet new {console, classlib, etc.}, cargo run -> dotnet run, cargo build -> dotnet build/publish, cargo add {package} -> dotnet add package {package}

- Monomorphized structs generics with the same zero-cost abstraction assurance like in Rust, &mut T -> ref T, &T -> ref readonly T, sometimes in T but with caveats

- Box/Arc -> class or record, Arc> -> class + lock (instance) { ... }

- Easy to use async/await but without ever having to deal with borrow checker and misuse-resistant auto-scaling threadpool. Tasks are hot started. Simply call two task-returning network calls and await each one when you need to. They will run in background in parallel while you do so. While they are more expensive than in Rust, you can still do massive concurrency and spawn 1M of them if you want to

- Built-in Rayon - Parallel.For and PLINQ, there is Channel too, you can e.g. 'await foreach (var msg in chReader) { ... }'

- Iterator expressions -> LINQ, seq.filter(...).map(...).collect() -> seq.Where(...).Select(...).ToArray(), unfortunately come with fixed cost but improve in each version

- Rust slice that wraps arbitrary memory -> Span, e.g. can write the same fast idiomatic text parsing on top of them quite easily

- Stupid fast span routines like .IndexOf, .Count, .Fill, .CopyTo which use up to AVX512

- Compiler can devirtualize what in Rust is Box

- Can make small native or relatively small JIT single-file executables that don't require users to install runtime

- Rich and fast FFI in both directions, can statically link into Rust, can statically link Rust components into itself (relatively new and advanced)

- Great tooling, Rider is very good, VSCode + base C# extension about as good as rust-analyzer

- Controversial but powerful runtime reflection and type introspection capability, can be used in a very dynamic way with JIT and compile additional code on the fly

- A bit easier to contribute to, depending on area owner and project (runtime, roslyn, aspnetcore, ...)

- CoreLib has full-blown portable SIMD API that is years ahead of portable-simd initiative in Rust

Because I occasionally use Rust and prefer its formatting choices, I carry this .editorconfig around: https://gist.github.com/neon-sunset/c78174b0ba933d61fb66b54d... to make formatting terser and more similar. Try it out if K&R and `I` prefix on interfaces annoy you.

Re: Rust's Ugly Syntax (2023)

#150
post #132

Earlier quoted context omitted.

I always, always forget what `'a: 'b` means, because I remember it always being the opposite of what I think it is, but memorizing that obviously doesn't work because then it will just flip again the next time. It's so annoying.

I am reminded of many instances over my life of: 1) Every time I go through a particular door I try to push it when it's a pull door. 2) I notice this and ensconce in my brain that it's the opposite of what I think it is. 3) After a while, my brain actually starts to remember it the correct way around to start with. 4) But my brain doesn't then drop the 'opposite' rule so now I remember it the correct way around, the…

I don't identify human, but you're absolutely right. For me, this also happens with the direction of hot vs cold on faucets/showers. I can never ever understand. Sometimes they're relative to the top edge and sometimes to the bottom edge. Sometimes they are on the wall/sink and sometimes they are on the actual handle. Because everything is always completely impossible to define proper rules for, the only thing I can rely on is "it's different than you thought it was before" but relying on that also doesn't work!
Post reply on HN