Live data from Hacker News

Why Rust closures are somewhat hard

stevedonovan.github.io

61–70 of 92 posts

Re: Why Rust closures are somewhat hard

#61
post #14

Earlier quoted context omitted.

Trait aliases will make some of those unwieldly declarations a bit easier on the eyes #![feature(trait_alias)] trait Map = Fn(T) -> T; fn compose (f1: impl Map , f2: impl Map ) -> impl Map { |n| f1(f2(n)) }

This is a nice feature, do you know any other language that has something similar?

looks doable with C++ using :

template using Map = std::function;

Re: Why Rust closures are somewhat hard

#62
post #51

Earlier quoted context omitted.

> You just limited the set of programs you can write to those that can be written in the block-lifetime-scoped manner, which is smaller than the set of good programs (see: pretty much any graph-heavy data structures). Given that most of my Rust programs have involved graph-heavy data structures, I'd like to see you explain why it's impossible for me to have written what I have written.

Not impossible, just harder. Ceteris paribus, it would have been easier to write those programs in Python or Java. It's not like this is not well-known. Rust users admit it. GUIs are hard to write in Rust because of the limitations of RAII+borrow checker based memory. You have to rely on ref counted pointers which can become very complicated to deal with in cyclic dependencies and long-lived references common in GUIs…

Note that moc does not do anything related to ownership, its main point is to provide runtime reflection abilities to your own classes. If you write Qt code without creating your own types you don't even need it.

Re: Why Rust closures are somewhat hard

#63
post #51

Earlier quoted context omitted.

Not impossible, just harder. Ceteris paribus, it would have been easier to write those programs in Python or Java. It's not like this is not well-known. Rust users admit it. GUIs are hard to write in Rust because of the limitations of RAII+borrow checker based memory. You have to rely on ref counted pointers which can become very complicated to deal with in cyclic dependencies and long-lived references common in GUIs…

This indictment makes little sense to me considering that, outside the Rust compiler itself, the largest early and still ongoing use case for Rust was/is Servo. A web browser engine, which is predominantly a thing that's dealing with multitudes of graph-shaped problems. :confused:

It’s a real issue. Consider (as you say) the Rust compiler. It has an IR phase called MIR, which is a tree, and elements in the tree need to know how to find themselves in the parent. For example, a function has a set of basic blocks, and each BB needs to know about its function. This is a very typical IR; LLVM is the same.

Backreferences are hard in Rust, so a BB instead maintains its index into a Vec, owned by the function. But this index is just a number: ownership is not modeled, it is not statically checked, and it may fall out of sync. It is effectively a slow, weird (though sandboxed) raw pointer.

You can write this stuff in Rust, but it is awkward and Rust cannot bring its strengths to bear. Rust assumes a tree-like ownership model and if you fall off that path, it can’t help much. Graphs aren’t trees so Rust is less helpful here.

Re: Why Rust closures are somewhat hard

#64
post #48

Everything that's hard in Rust could be solved by GC. With Rust, programmers have to spend most of their mental energy worrying about management of memory, which has largely been automated already. I always go back to this quote from Andrei Alexandrescu (creator of D): A disharmonic personality. Reading any amount of Rust code evokes the joke "friends don't let friends skip leg day" and the comic imagery of men with…

Gargabe collection is a much bigger crutch. Once you use it your entire program is contaminated by the performance characteristics of a single improperly written function. You cannot mix real time code with non real time code. If you do your real time code will be delayed by the non real time code. The unpredictability of the GC has become a property of your entire application.

Re: Why Rust closures are somewhat hard

#65
post #59
post #48

Everything that's hard in Rust could be solved by GC. With Rust, programmers have to spend most of their mental energy worrying about management of memory, which has largely been automated already. I always go back to this quote from Andrei Alexandrescu (creator of D): A disharmonic personality. Reading any amount of Rust code evokes the joke "friends don't let friends skip leg day" and the comic imagery of men with…

Every GC is a trade off. You can't replace RAII with GC without losing some control. No matter how much you innovate in it, you have to pay with something. That's the whole point of GC after all. Here is a good read about it: https://blog.plan99.net/modern-garbage-collection-911ef4f8bd...

GC enabled systems programing languages don't replace RAII with GC, they offer both and its up to the developer to make the best use of them.

Re: Why Rust closures are somewhat hard

#66
post #65
post #59

Earlier quoted context omitted.

Every GC is a trade off. You can't replace RAII with GC without losing some control. No matter how much you innovate in it, you have to pay with something. That's the whole point of GC after all. Here is a good read about it: https://blog.plan99.net/modern-garbage-collection-911ef4f8bd...

GC enabled systems programing languages don't replace RAII with GC, they offer both and its up to the developer to make the best use of them.

If the language works without GC, you can implement optional GC for it. Even Rust had something like that in the early days (though it was dropped). But languages where GC is expected by design, don't allow you to disable it (like Java).

Re: Why Rust closures are somewhat hard

#67

The fact that you can even do stuff like this in Rust is amazing. It’s simultaneously: relatively clean code, very efficient and type-safe. I like it.

Not sure I agree with "relatively clean code" when one of the examples show "fn compose (f1: impl Fn(T)->T, f2: impl Fn(T)->T) -> impl Fn(T)->T {" which is just a mix-match of keywords and other things, with tons of syntax embedded in just one line. But as always, depends on where you come from. I mostly deal with lisp languages nowadays, so guessing it's just my view that the line quoted above seems complex enough t…

Perhaps you should pick your examples more carefully. That function just repeats impl Fn(T)->T three times which is exactly what yo would expect from the compose function. If there was a mix of multiple different types of functions you could demonstrate the additional complexity of Rust. What you did is just prove that Rust is the same as most languages.

Re: Why Rust closures are somewhat hard

#68
post #66
post #65

Earlier quoted context omitted.

GC enabled systems programing languages don't replace RAII with GC, they offer both and its up to the developer to make the best use of them.

If the language works without GC, you can implement optional GC for it. Even Rust had something like that in the early days (though it was dropped). But languages where GC is expected by design, don't allow you to disable it (like Java).

Except I wasn't speaking about Java, rather D, Nim, Modula-3, Active Oberon, System C#, Swift.

Re: Why Rust closures are somewhat hard

#69

Earlier quoted context omitted.

This indictment makes little sense to me considering that, outside the Rust compiler itself, the largest early and still ongoing use case for Rust was/is Servo. A web browser engine, which is predominantly a thing that's dealing with multitudes of graph-shaped problems. :confused:

It’s a real issue. Consider (as you say) the Rust compiler. It has an IR phase called MIR, which is a tree, and elements in the tree need to know how to find themselves in the parent. For example, a function has a set of basic blocks, and each BB needs to know about its function. This is a very typical IR; LLVM is the same. Backreferences are hard in Rust, so a BB instead maintains its index into a Vec , owned by the…

> It is effectively a slow ... raw pointer.

In my experience (unless I've misunderstood what you're trying to say) this is the fastest way to write graphs because of how much more cache-friendly it is than having to chase a load of pointers.

Re: Why Rust closures are somewhat hard

#70

I feel most things in Rust are harder to do, at least at first sight. A compromise for the enhanced safeness I guess. Could give it a try again sometime.

It seems to me that it's not that Rust is hard, it's that all programming is hard but Rust exposes the complexity so we can make a more informed decision about the trade offs of safety and performance etc, while other languages tend to hide the complexity (null reference, race conditions, etc).

I've found that once you fully get this, Rust is actually easier to write code with. Especially refactoring. You can make sweeping changes to complex code and be satisfied that once it compiles it will most likely work.

Fearless refactoring is one of the prime benefits of Rust.

Post reply on HN