Live data from Hacker News

Rust project goals: Immobile types and guaranteed destructors

github.com

101–110 of 112 posts

Re: Rust project goals: Immobile types and guaranteed destructors

#101
post #90

Earlier quoted context omitted.

Sure, thats one reason why IntoFuture and Future exist. Imo, in hindsight this is also main mistake in aysnc Rust: The whole async system should be build around IntoFuture rather than Future (async fn should return impl IntoFuture). That way you could pass around IntoFutures without being affected by auto traits leaking. Only when you actually call .await() or .poll() would the immovable Future materialize.

You're right that this is an important issue. I wrote a post explaining this in some detail, so at the very least we avoid having the same problem with generator functions: https://blog.yoshuawuyts.com/gen-auto-trait-problem

Oh yeah, I've read about all your blog post on this topic :)

To dump some ideas on you: I think one missing piece might be that FnOnce() -> impl Future should implement IntoFuture. Async runtimes would then use IntoFuture in their APIs agressively.

I call this a "workload blueprint" at work. Its a closure/type that contains all the info to start the workload, but in a minimal form. In Rust terms this would be a buildprint that is ideally Send + Move + Forget + 'static, even if the actual work (and the backing struct of the Future) is !Send (e.g. It holds an Rc across await points).

Runtimes could use this for their advantage: There would be a global pool of "workload blueprint" that can be stolen by any executer thread, but once a !Send workload has started on one thread it can't be migrated to another.

This in combination with matklad's ideas about seperating TaskSend from ThreadSend (https://matklad.github.io/2023/12/10/nsfw.html) would solve most of my async pain points.

Re: Rust project goals: Immobile types and guaranteed destructors

#102
post #75

Earlier quoted context omitted.

How do native exceptions work in Delphi?

Very similar to Windows Structured Exception Handling, which the Win32 implementation used, and also looks similar to Java, but without checked exceptions. try/except/end and try/finally/end blocks. The major differences with C++: * objects are references not values, cutting out all the copy constructor, assignment operator, destruction on out of scope etc. * objects are zero-initialized after allocation and before c…

Not all objects are references, because Delphi still supports the Turbo Pascal object model for compatibility, regardless of how many years they are deprecated now.

Agree with the rest, I always loved how Borland picked Object Pascal extensions from Apple, merged them with what was going on with Modula languages and C++ during the 1990's, while coming out with something saner.

While at the same time keeping C++ around, as market value when buying the whole package.

What everyone is going crazy regarding Zig, Odin, Jai, C3, whatever improvements over C and C++, where already available in Delphi, Modula-2, Ada and co, with Delphi being the most affordable option until Borland decided to pivot to big corp.

Re: Rust project goals: Immobile types and guaranteed destructors

#103

Earlier quoted context omitted.

C++26 adopted senders/receivers (std::execution) as its official concurrency model, with the explicit aim of supporting structured concurrency. See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p23...

Skimming the proposal, that looks like the equivalent of the "executor" (scheduler) and "task" (sender) implemented in Rust async runtimes. I could have missed it, but I don't see anything in particular to help with managing structured concurrency? Like, the central challenge is cancellation: In structured concurrency, subtasks spawned from a parent task must finish cancelling before the parent task can be cancelled.

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p23...

Re: Rust project goals: Immobile types and guaranteed destructors

#104
post #22

Earlier quoted context omitted.

Things you'd expect to work already. It doesn't really add anything new and flashy, but removes some annoying warts. Sync code has scoped threads that enable multi-threaded execution within a function, without having to ensure the data outlives the function call. Async can't do that while guaranteeing safety. This makes tokio::spawn awkward and annoying, and is a major source why people dislike Rust's async. Low-leve…

> and is a major source why people dislike Rust's async It's worth mentioning that there is, in fact, no language out there other than Rust that can even do this in the first place. Some languages give the illusion that they support it by boxing the stack frame of async functions and letting a garbage collector deal with the consequences, but that comes with significant drawbacks too (additional GC pressure, heap all…

>You can do it with C++ coroutines, but it's much harder to do correctly than in Rust if you want to maintain any sense of conviction that the system is correct.

Could you elaborate on this?

Re: Rust project goals: Immobile types and guaranteed destructors

#105

Earlier quoted context omitted.

But also come with a plausible implementation strategy, that from what I know is not currently known for no-panic.

Out of curiosity, is there some reason it would be harder than I'm imagining? It seems like a fairly straightforward type-system feature. (I.e., it would be a lot of work, but only because any significant new language feature is a lot of work.)

The problem with the trivial implementation is that it's barely useful. You cannot use indexing, for example.

Not to mention it's still not easy: to have any usefulness, there will need to be a way to be generic over it (similar to keyword generics), and that's far from trivial.

Re: Rust project goals: Immobile types and guaranteed destructors

#106
post #9

Although not part of the goal, it also mentions `!Destruct`/"must-move types", aka linear types: Instead of there always being a way to drop values without providing any arguments, if you wanna get rid of a value of a linear type you have to call a function that takes it by value.

For context, the reason this would be really nice is that it would enable API designs that catch certain kinds of errors. let txn = create_transaction(); // do something with the transaction txn.commit(); // consume the txn Right now, you can't implement this API without choosing between either silently rolling back unless the user calls `commit()`, or panicking in the Drop impl for the transaction if the user didn't…

The question (one question, at least) is what to do with unwinding. If we consider every function to be able to unwind, that means you basically need to have a lot of boilerplate, and it's also not clear how to express that syntactically. Even differentiating `panic = "abort"` is not simple, as there is no language feature currently that does that.

Re: Rust project goals: Immobile types and guaranteed destructors

#107

Earlier quoted context omitted.

> and is a major source why people dislike Rust's async It's worth mentioning that there is, in fact, no language out there other than Rust that can even do this in the first place. Some languages give the illusion that they support it by boxing the stack frame of async functions and letting a garbage collector deal with the consequences, but that comes with significant drawbacks too (additional GC pressure, heap all…

>You can do it with C++ coroutines, but it's much harder to do correctly than in Rust if you want to maintain any sense of conviction that the system is correct. Could you elaborate on this?

C++ just lets you pass a reference or pointer from one task's stack to another task or thread, with no assurance that the callee task terminates before the caller's stack is deallocated (i.e. the caller returns before its children terminate).

This is a compile error in Rust, and you need unsafe code to achieve it. But then the real power is that you can construct a safe API that lets you do it safely, fully checked at compile time.

See `std::thread::scope` in the standard library, or `rayon::scope` if you want an implementation based on thread pools.

Re: Rust project goals: Immobile types and guaranteed destructors

#108
post #49
post #42

Earlier quoted context omitted.

This is the opposite, it is further opting out of flexibility.

I guess the point is that the concepts are needed, which is true. But the Rust way (more explicit and targeted) of dealing with these concepts seems better than how either C++ or D handle it.

Yes, I meant the concepts needed for low level language and tweaking when high performance is of concern.

Re: Rust project goals: Immobile types and guaranteed destructors

#109

Earlier quoted context omitted.

>You can do it with C++ coroutines, but it's much harder to do correctly than in Rust if you want to maintain any sense of conviction that the system is correct. Could you elaborate on this?

C++ just lets you pass a reference or pointer from one task's stack to another task or thread, with no assurance that the callee task terminates before the caller's stack is deallocated (i.e. the caller returns before its children terminate). This is a compile error in Rust, and you need unsafe code to achieve it. But then the real power is that you can construct a safe API that lets you do it safely, fully checked a…

Thanks!

>See `std::thread::scope` in the standard library, or `rayon::scope` if you want an implementation based on thread pools.

These seem to be about multu-threaded code if I got it right, but how does async work?

Re: Rust project goals: Immobile types and guaranteed destructors

#110

Earlier quoted context omitted.

Out of curiosity, is there some reason it would be harder than I'm imagining? It seems like a fairly straightforward type-system feature. (I.e., it would be a lot of work, but only because any significant new language feature is a lot of work.)

The problem with the trivial implementation is that it's barely useful. You cannot use indexing, for example. Not to mention it's still not easy: to have any usefulness, there will need to be a way to be generic over it (similar to keyword generics), and that's far from trivial.

Well, yeah, I would not expect indexing (on the standard library types) to be usable within a no-panic function, because panicking on out-of-range inputs is what those operations do. Admittedly, probably lots of people would initially think "oh, being sure never to panic sounds useful", look into it more, realize what's actually involved, and decide "never mind, I'll stick with the risk of panicking" (which is the correct decision for almost all software). But there'd still be use cases for no-panic.

If this is the "trivial" implementation, then I'm not sure what a "nontrivial" implementation would look like, unless it means adding a proof-tactics language to Rust to allow statically checked proofs of arbitrary program properties. Which would be really cool and all kinds of useful, but which I think everyone realizes would be a gargantuan project even to design, let alone implement.

Genericity is not strictly required for no-panic to be useful (lots of type-system features still don't have it), but yes, it would be very nice to have. (Though one might hope that, once they figure out genericity for one keyword (probably const), that'd make it easier to add for others.)

Post reply on HN