The user specifically asked about stackfull coroutines, which is not the direction we're going. But I think the pain point the user talked about (futures right now are difficult to deal with) will be resolved by our stackless coroutine approach, and more in line with Rust's values around "zero cost abstractions."
Signal's use of SGX was to increase users' trust in Signal - OpenWhisperSystems couldn't log your contacts even if they wanted to. Its up to potential users to decide if they think OpenWhisperSystems will surreptitiously perform an attack on their own secure enclave to secretly log your contact information.
My apologies if I misunderstood you - I read your comment as suggesting that safe abstractions are "leaky" and therefore create additional responsibilities for users to validate that they are using them safely when composing them together. This is not the case unless those abstractions are incorrect - which is the same situation you are with any language, just most languages those abstractions exist within the language runtime & not in libraries.
Proving the correctness of unsafe code is totally different from what you talked about, which was composing different abstractions with unsafe internals together.
Users of safe Rust do not need to worry about whether the composition of two safe interfaces that use unsafe internally is safe unless one of those interfaces is incorrect. Your comment would suggest that users need to think about the untyped invariants of each library they use, but this is not correct, libraries are not allowed to rely on untyped invariants for the correctness of their safe APIs.
There is a conflation happening here. What is the nature of this bug when you compose these two libraries together?
If it is a violation of Rust's safety guarantees, then at least one of those libraries has a bug, it is exposes a safe abstraction which is not actually safe. One could not argue that the safe Rust program is wrong; the library exposing an unsafe interface as safe is unarguably wrong.
If the library just behaves incorrectly in a manner disconnected from the type system because some global state was changed in a way it doesn't expect ("the hardware" in this case), then that's a normal bug & it is not connected to unsafe code at all.
> The major difficulty here is that in general, unsafe pieces of code cannot be safely composed, even if the unsafe pieces of code are individually safe. This allows you to bypass runtime safety checks without unsafe code just by composing "safe" modules that internally use unsafe code in their implementation.
This comment suggests you don't have much domain knowledge about how `unsafe` in Rust works, so I'm surprised you speak with such confidence. Your comment is flatly wrong: users using only safe code are not responsibility for guaranteeing the composed safety of the components they use (whether or not they are implemented with unsafe code).
Interfaces marked safe must uphold Rust's safety guarantees, or they are incorrect. They are just wrong if they have additional untyped invariants that need to be maintained to guarantee their safety; interfaces like this must be marked `unsafe`.
Because they cannot depend on untyped invariants, any correct implementation with a safe interface can be composed with any other. This ability to create safe abstractions over unsafe code which extend the reasoning ability of the type system is a fundamental value proposition of Rust.
- We are directing material resources (as in, hours of paid work) to improving compiler performance. We consider this a serious problem, and "the compiler performance is always improving" is not an accurate gloss of the amount of work people are putting into solving it.
- We would love to have a REPL but there are nontrivial technical challenges, and it has not been a major requested feature by our users.
- I have never heard the complaint about the size of packages before now; I have no idea how we compare to other languages in this regard.
I've witnessed some of the conversations you're talking about (I think I engaged with you about REPLs in the past), and I think the "true-believer syndrome" has more to do with how you interpret the answers you receive than with the behavior of anyone else.
What we could do better than we do today - and your comment about ergonomics alludes to our work to improve this - is ease the onboarding of that complexity we have to be "honest" about. Its a design constraint of Rust that it must maximize user control, but that does not imply that users have to be faced with all of those choices as soon as they first try to write Rust. In some respects I think this article is an attempt at a counterargument to that work (suggesting that it is "dishonest") but I fundamentally do not believe that there is a contradiction between giving advanced users control and making it easier for new users to write correct code before they have a full understanding of the entire system.
This is not my experience using Rust. I had six months of programming experience when I first tried Rust. I definitely got confusing errors at first, but I grasped the system within a month or two. And that was in 2014 - the borrowchecker and especially its errors have way improved since then!
But this is also a very different objection from your initial objection, which was just a statement of fact about type systems for turing complete languages.
Do you stay away from all type systems? This would be an accurate negative framing for any type system, which all necessarily reject some correct programs.
I understood your point. As my comment alluded to, the criticisms of tokio this blog post is attempting to address have to do with the implementation complexity - abstractions do not reduce the implementation complexity of themselves.
There's another reason Rust's ecosystem uses futures (as opposed to having some library-based greenthreading system): each future is like the stack of a userspace thread, but perfectly sized (it will be as large as the largest stack space needed at a yield point). This reduces the memory footprint of services using futures.
What's missing here is that Go has a userspace scheduler and greenthreads. Despite presenting the user with something that looks like old school blocking IO, because you are only blocking a greenthread it is actually asynchronous IO under the hood.
And of course there is a huge amount of abstraction to build this up. It's just all hidden in the language runtime, so people can say with a straight face that there is "no abstraction and no cruft."
There are various criticisms of tokio, coming from different directions. Some have to do with the fact that some abstractions in the futures ecosystem are leaky today and that makes them less easy to use than they could be (though they won't always be leaky[]). But others have to do with understanding the internal implementation of these abstractions - people who feel they must understand how their library works internally before using it.
Of course, schedulers are just complicated. Most of the time you don't think about how complicated your scheduler is, because its either an OS primitive in the kernel or a language primitive in your language's runtime. But since tokio is a library - and modular - it gets criticism for being complex that in my opinion is unfair.
[] To be more concrete: a future is essentially a state machine representing the stack state at any yield point; it can't (currently) contain lightweight references into itself because they'd be invalidated when the future is move around. This means using borrowing in futures programs is often infeasible today. Solutions are in the works.
I think this was a very bad move, because Mozilla installed adware in all of its browsers. The fact that it was installed through an add on, though, seems irrelevant. Mozilla developers can distribute arbitrary code to all users because they write the browser. The add on just makes this particular bit of code user visible.