Live data from Hacker News

Flattening Rust’s learning curve

corrode.dev

341–350 of 405 posts

Re: Flattening Rust’s learning curve

#341
post #18

It's like reading "A Discipline of Programming", by Dijkstra. That morality play approach was needed back then, because nobody knew how to think about this stuff. Most explanations of ownership in Rust are far too wordy. See [1]. The core concepts are mostly there, but hidden under all the examples. - Each data object in Rust has exactly one owner. - Ownership can be transferred in ways that preserve the one-owner ru…

Maybe it's my learning limitations, but I find it hard to follow explanations like these. I had similar feelings about encapsulation explanations: it would say I can hide information without going into much detail. Why, from whom? How is it hiding if I can _see it on my screen_. Similarly here, I can't understand for example _who_ is the owner. Is it a stack frame? Why would a stack frame want to move ownership to it…

> Why would a stack frame want to move ownership to its callee

Happens all the time in modern programming:

callee(foo_string + "abc")

Argument expression foo_string + "abc" constructs a new string. That is not captured in any variable here; it is passed to the caller. Only the caller knows about this.

This situation can expose bugs in a run-time's GC system. If callee is something written in a low level language that is resposible for indicating "nailed" objects to the garbage collector, and it forgets to nail the argument object, GC can prematurely collect it because nothing else in the image knows about that object: only the callee. The bug won't surface in situations like callee(foo_string) where the caller still has a reference to foo_string (at least if that variable is live: has a next use).

Re: Flattening Rust’s learning curve

#342
post #206

Earlier quoted context omitted.

Maybe it's my learning limitations, but I find it hard to follow explanations like these. I had similar feelings about encapsulation explanations: it would say I can hide information without going into much detail. Why, from whom? How is it hiding if I can _see it on my screen_. Similarly here, I can't understand for example _who_ is the owner. Is it a stack frame? Why would a stack frame want to move ownership to it…

> Why can mutable reference be only handed out once? Here's a single-threaded program which would exhibit dangling pointers if Rust allowed handing out multiple references (mutable or otherwise) to data that's being mutated: let mut v = Vec::new(); v.push(42); // Address of first element: 0x6533c883fb10 println!("{:p}", &v[0]); // Put something after v on the heap // so it can't be grown in-place let v2 = v.clone();…

The analogous program in pretty much any modern language under the sun has no problem with this, in spite of multiple references being casually allowed.

To have a safe reference to the cell of a vector, we need a "locative" object for that, which keeps track of v, and the offset 0 into v.

Re: Flattening Rust’s learning curve

#343

Earlier quoted context omitted.

> quite a similar model Yeah, and that model is rather old: https://en.wikipedia.org/wiki/Greenspun%27s_tenth_rule In practice, complex software systems have been written in multiple languages for decades. The requirements of performance-critical low-level components and high-level logic are too different and they are in conflict. > you give the compiler an opportunity to optimise across those unsafe usages One worka…

> LTO On some workloads (think calls not possible to inline within a hot loop), I found LTO to be a requirement for C code to match C# performance, not the other way around. We've come a long way! (if you ask if there are any caveats - yes, JIT is able to win additional perf. points by not being constrained with SSE2/4.2 and by shipping more heavily vectorized primitives OOB which allow doing single-line changes that…

> on some workloads, I found LTO to be a requirement for C code to match C# performance

Yeah, I observed that too. As far as I remember, that code did many small memory allocations, and .NET GC was faster than malloc.

However, last time I tested (used .NET 6 back then), for code which churches numbers with AVX, my C++ with SIMD intrinsics was faster than C# with SIMD intrinsics. Not by much but noticeable, like 20%. The code generator was just better in C++. I suspect the main reason is .NET JIT compiler doesn’t have time for expensive optimisations.

Re: Flattening Rust’s learning curve

#344
post #18

It's like reading "A Discipline of Programming", by Dijkstra. That morality play approach was needed back then, because nobody knew how to think about this stuff. Most explanations of ownership in Rust are far too wordy. See [1]. The core concepts are mostly there, but hidden under all the examples. - Each data object in Rust has exactly one owner. - Ownership can be transferred in ways that preserve the one-owner ru…

I like how you phrase it, but it's missing the mutable XOR shared for references.

Re: Flattening Rust’s learning curve

#345
post #296

Earlier quoted context omitted.

I believe you can do that in C pretty easily with a void pointer, someone correct me if I'm mistaken. Should you? Different question entirely.

But you can't add 2 void pointers and seamlessly get integer addition if they point at integers or concatenation if they point at strings. (You could build your own custom data types that have type metadata in a shared header and an addition function that uses it, but then you're building your own custom language on top which isn't really the same thing.) So yes C really does restrict you in some ways that Javascript…

That wasn't the constraint I was responding to, you moved the goalposts! :)

Re: Flattening Rust’s learning curve

#346

Earlier quoted context omitted.

> LTO On some workloads (think calls not possible to inline within a hot loop), I found LTO to be a requirement for C code to match C# performance, not the other way around. We've come a long way! (if you ask if there are any caveats - yes, JIT is able to win additional perf. points by not being constrained with SSE2/4.2 and by shipping more heavily vectorized primitives OOB which allow doing single-line changes that…

> on some workloads, I found LTO to be a requirement for C code to match C# performance Yeah, I observed that too. As far as I remember, that code did many small memory allocations, and .NET GC was faster than malloc. However, last time I tested (used .NET 6 back then), for code which churches numbers with AVX, my C++ with SIMD intrinsics was faster than C# with SIMD intrinsics. Not by much but noticeable, like 20%.…

> The code generator was just better in C++. I suspect the main reason is .NET JIT compiler doesn’t have time for expensive optimisations.

Yeah, there are heavy constraints on how many phases there are and how much work each phase can do. Besides inlining budget, there are many hidden "limits" within the compiler which reduce the risk of throughput loss.

For example - JIT will only be able to track so many assertions about local variables at the same time, and if the method has too many blocks, it may not perfectly track them across the full span of them.

GCC and LLVM are able to leisurely repeat optimization phases where-as RyuJIT avoids it (even if some phases replicate some optimizations happened earlier). This will change once "Opt Repeat" feature gets productized[0], we will most likely see it in NativeAOT first, as you'd expect.

On matching codegen quality produced by GCC for vectorized code - I'm usually able to replicate it by iteratively refactoring the implementation and quickly testing its disasm with Disasmo extension. The main catch with this type of code is that GCC, LLVM and ILC/RyuJIT each have their own quirks around SIMD (e.g. does the compiler mistakenly rematerialize vector constant construction inside the loop body, undoing you hosting its load?). Previously, I thought it was a weakness unique to .NET but then I learned that GCC and LLVM tend to also be vulnerable to that, and even regress across updates as it sometimes happens in SIMD edge cases in .NET. But it is certainly not as common. What GCC/LLVM are better at is if you start abstracting away your SIMD code in which case it may need more help as once you start exhausting available registers due to sometimes less than optimal register allocation you start getting spills or you may be running in a technically correct behavior around vector shuffles where JIT needs to replicate portable behavior but fails to see your constant does not need it so you need to reach out for platform-specific intrinsics to work around it.

[0]: https://github.com/dotnet/runtime/issues/108902

Re: Flattening Rust’s learning curve

#347
post #324

Earlier quoted context omitted.

It's not "usually right" though. Rust can't compile a doubly-linked list[1] without unsafe! And people trip over this immediately when they start writing Rust, because that kind of code is pervasive in other environments. Thus statements like "Rust just doesn't like dangling pointers" are unhelpful, because while it's true it's not sufficient to write anything but the most trivial code. [1] Or basically any graph-lik…

People write non-trivial code all the time without worrying about that sort of thing. Quite a lot can be done with plain tree structures. In the real world, your data is flat and even your conventions for interpreting it as non-flat (such as, say, JSON) only create trees that perhaps simulate back-links with another informal protocol.

Sigh. The whole premise of the linked article is, in fact, that people hit validation problems with the borrow checker early on when learning rust and that attention is needed to "flatten the learning curve" to assist their understanding of what we all agree is a unique and somewhat confusing set of semantics relative to competing languages.

Rust flaming is just so terribly exhausting. No matter how reasonable and obvious a point is there's always someone willing to go to the mattresses in a fourty-comment digression about how Rust is infallible.

Re: Flattening Rust’s learning curve

#348
post #192
post #183

Earlier quoted context omitted.

"raw pointers are one of the most important concepts in CS" that's a reach and a half, I don't remember the last time I've used one

The concept of being able to reference a raw memory address and then access the data at that location directly feels pretty basic computer science. Perhaps you do software engineering in a given language/framework? A clutch is fundamental to automotive engineering even if you don’t use one daily.

It all depends on how you define computer science vs computer engineering. In all my CS classes, not once did I need to deal with pointer arithmetic or memory layout. That's because my CS classes were all theoretical, concerning pseudocode and algorithmic complexity. Mapping the pseudocode onto actual hardware was never a consideration.

In contrast, there was hardly ever a computer engineering class where I could ignore raw memory addresses. Whether it was about optimizing a memory structure for cache layout or implementing some algorithm efficiently on a resource-anemic (mmu-less) microcontroller, memory usage was never automatic.

Re: Flattening Rust’s learning curve

#349
post #60

Earlier quoted context omitted.

And he's telling other people they should like it as well, because he has seen the light. My gut feeling says that there's a fair bit of Stockholm Syndrome involved in the attachments people form with Rust. You could see similar behavioral issues with C++ back in the days, but Rust takes it to another level.

I think most of us enamoured with rust are c++ refugees glad the pain is lessened. The tooling including the compiler errors really are great though. I like the simplicity of c, but I would still pick rust for any new project just for the crates and knowing I'll never have to debug a segfault. I like pytorch and matlab fine for prototyping. Not much use for in-between languages like go or c# but I like the ergonomics…

Yes! 100% this!

For me, programming with C++ was like building castles out of sand. I could never make them tall enough before they would collapse under their own weight.

But with Rust, I leveled up my abilities and built a program larger than I ever thought possible. And for that I'm thankful to Rust for being a language that actually makes sense to me.

Re: Flattening Rust’s learning curve

#350

Rust has a few big hurdles for new users: - it's very different from other languages. That's intentional but also an obstacle. - it's a very complex language with a very terse syntax that looks like people are typing with their elbows and are hitting random keys. A single character can completely change the meaning of a thing. And it doesn't help that a lot of this syntax deeply nested. - a lot of its features are ha…

>it's very different from other languages. That's intentional but also an obstacle. It's very different from a lot of the languages that people are typically using, but all the big features and syntax came from somewhere else. See: >The type system and the borrowing mechanism are good examples. Unless you are a type system nerd a lot of that is just gobblygook to the average Python or Javascript user. Well, yeah, but…

Python is growing type annotations at a brisk pace though, and Typescript is cannibalizing Javascript at an incredible speed. Between that and even Java getting ADTs, I suspect the people who whine about "type nerds" are in for some rough years as dynamic languages lose popularity.

And I suspect the people who are familiar with seeing something like `dict[str, int]` can map that onto something like `HashMap` without actually straining their brains, and grow from there.

Post reply on HN