Talking about foundational software but no mention of the biggest missing part of rust IMO: an ABI.
If you want to write an OS in rust and provide rich services for applications to use, you need to offer libraries they can call without needing to recompile when the OS is upgraded. Windows mostly does this with COM, Apple historically had ObjC’s dynamic dispatch (and now Swift’s ABI), Android does this with a JVM and bytecode… Rust can only really offer extern "C", and that really limits how useful dynamic libraries can be.
Doing an ABI without a VM-like layer (JVM, .NET) is really difficult though, and requires you to commit to certain implementation details without ever changing them, so I can understand why it’s not a priority. To my knowledge the only success stories are Swift (which faced the problem head-on) and COM (which has a language-independent IDL and other pain points.) ObjC has such extreme late binding that it almost feels like cheating.
If rust had a full-featured ABI it would be nearly the perfect language. (It would improve compile times as well, because we’d move to a world where dependencies could just be binaries, which would cut compile times massively.)
I don't think Rust ever wants to do ABI stability that isn't opt-in, because Rust is about zero-cost abstractions and a stable ABI is not zero-cost. See this explanation of how C++'s de facto stable ABI significantly reduces performance: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p20... [PDF]
For the use case of letting Rust programs dlopen plugins that are also written in Rust, the current solutions are stabby and abi_stable. They're not perfect but they seem to work fairly well in practice.
For more general use cases like cross-language interop, the hope is to get somewhere on crABI: https://github.com/joshtriplett/rfcs/blob/crabi-v1/text/3470... This is intended to be a superior successor to C's ABI, useful for all the same use cases (at least if the code you want to talk to is new enough to support it). Note that this isn't something the Rust maintainers can do unilaterally; there's a need to get buy-in from maintainers of other languages, Linux distros, etc.
I'm not sure that the "everything is a shared library" approach is workable in a language where heap allocation is generally explicit and opt-in. While Swift has some support for stack allocation, most types are heap-allocated, and I think that reduces the extent to which the rest of the language has to be warped to accommodate the stable ABI.
> While Swift has some support for stack allocation, most types are heap-allocated, and I think that reduces the extent to which the rest of the language has to be warped to accommodate the stable ABI.
Stack vs heap in swift is simple: structs are stack, classes are heap.
This is what makes Swift’s effort so impressive: it works with stack data just as well as heap. Stack allocated types being used across library boundaries has a ton of plumbing to make it work, there’s the concept of a value witness table (like a vtable but for size/stride information) that can allow looking up sizes at runtime. Stack allocation makes heavy use of alloca(), which most languages shy away from (since it means stack frames aren’t of a static size.) It was difficult but swift managed to do it.
For rust to do something similar it would have to fix a lot of cases where object safety is broken, and yeah, this may require changing the language, particularly to support dynamic/runtime size information. (It would also make the dyn keyword effectively obsolete, because to make generics work across library boundaries, any type has to be able to “become” dynamic if the linker needs it to be.)
It would definitely require changes to the language, but Swift provides some inspiring prior art here.
We are more or less trying to solve this exact problem with Wasm Components [1]. If core WebAssembly is a portable instruction format, WebAssembly Components are a portable executable/linkable format.
Using them is unfortunately not yet quite as easy as using repr(wasm)/extern "wasm". But with wit-bindgen [2] and the wasm32-wasip2 target [3], it's not that hard either.
I love WASM and WASI, but it's not nearly the same, unfortunately. Performance takes a hit, you can't use async in a straightforward way, launching hundreds of thousands of tasks is problematic etc. WASM is great for allowing to extend your app, but I don't see it as a replacement for an ABI anytime soon
Not sure if this is really needed though. I mean, it would be more convenient to be able to pass more "things" around interfaces (slices, trait objects, ...), but you can still do everything you need to with an explicit extern "C" abi.
(And I've seen proposal to extend extern ABI to more things)
I've seen talks on this topic at Rust conferences which seemed strongly influenced by Swift's approach, so that will probably be the direction this ends up going in.
It wouldn’t, there would have to be dynamic dispatch. Swift provides some prior art for “monomorphization in the same binary, dynamic dispatch across linker boundaries” as a general approach, and it works pretty well.
I don't really understand the point for writing an "OS in rust and provide rich services for applications to use".
You can use any serialisation method you want (C structs, Protobuf, JSON, ...).
An ABI is for passing data in the programming language and trusting it blindly, e.g. what functions use when calling each other.
Any sane OS still has to validate the data that crosses its boundaries. For example, if you make a Linux system call, it doesn't blindly use a pointer you pass it, but instead actually checks if the pointer belongs to the calling process's address space.
It seems pointless to pick Rust's internal function-calling data serialisation for the OS boundary; that wouldn't make it easier for any userspace programs running in that OS than explicitly defining a serialisation format.
Why would an OS want to limit itself to Rust's internal representation, or why would Rust want to freeze its internal representation, making improvements impossible on both sides? This seems to bring only drawbacks.
The only benefit I can see so far for a stable ABI is dynamic linking (and its use in "plugins" for programs where the code is equally trusted, e.g. writing some dlopen()able `.so` files in Rust, as plugins for Rust-only programs), where you can really "blindly call the symbol". But even there it's a bit questionable, as any mistake or mismatch in ABI can result in immediately memory corruption, which is something that Rust users rather dislike.
I really like Rust but there are some quite frustrating core paper cuts that I wish would get more attention:
1. Self-referencing structs. Especially where you want to have something like a source file and the parsed AST in the same struct. You can't easily do that at the moment. It would be nice if there was something like an offset reference that made it work. Or something else...
2. The orphan rule. I get it, but it's still annoying. We can do better than newtype wrappers (which sometimes have to be nested 2 or 3 levels deep!).
3. The fact that for reasonable compile time you need to split projects into lots of small crates. Again, I understand the reasons, but the result sucks and we can definitely do better. As I understand it this is because crates are compiled as one compilation unit, and they have to be because circular dependencies are allowed. While that is true, I expect most code doesn't actually have circular dependencies so why not make those opt-in? Or even automatically split items/files within a crate into separate compilation units based on the dependency graph?
There's probably more; this is just what I can remember off the top of my head.
Hopefully that's constructive criticism. Rust is still my favourite programming language by far.
> As I understand it this is because crates are compiled as one compilation unit, and they have to be because circular dependencies are allowed
Rust allows circular dependencies between modules within a single compilation unit, but crates can't be circular dependencies.
> I expect most code doesn't actually have circular dependencies
Not true, most code does have benign circular dependencies, though it's not common to realize this. For example, consider any module that contains a `test` submodule, that's a circular dependency, because the `test` submodule imports items from the parent (it has to, because it wants to test them), but also the parent can refer to the submodule, because that's how modules and submodules fundamentally work. To eliminate the circular dependency you would need to have all test functions within every compilation unit defined within the root of the crate (not in a submodule off of the root; literally defined in the root namespace itself).
Would add 4: partial self borrows (ability for methods to only part of their struct).
For 3, I think the low hanging fruit is probably better support for just using multiple crates (support for publishing them as one package for example).
Huge agree with the orphan rule. We should be able to disable this in application crates, or do away with it when we can prove certain hygiene, like proc macros.
The emphasis on cross-language compatibility may be misplaced. You gain complexity and lose safety. If we have to do that, it would help if it were bottom-up, replacing libc, for example.
Go doesn't do cross-language calling much.
Google paid people to build the foundations, the low-level libraries right. Rust tends toward many versions of the basics, most flawed.
One of the fundamental problems in computing for about two decades now has been getting programs on the same machine to efficiently talk to each other. Mostly, people have hacked on the Microsoft .DLL concept, giving them state. Object request brokers, like CORBA, never caught on.
Nor did good RPC, like QNX. So people are still trying to link stuff together that doesn't really want to be linked.
Or you can do everything with sockets, even on the same machine. They're the wrong abstraction, raw byte streams, but they're available.
My read, which is based on context from elsewhere but could be wrong, is that the post is not really talking about creating a replacement for traditional shared libraries that works across language boundaries (like COM or Wasm components). Rather, "other languages" here is a euphemism for C++ in particular, and the goal is to enable incremental migration of brownfield C++ codebases to Rust. The goal of pervasive memory safety everywhere, especially in the kinds of contexts where C++ has traditionally been used, requires some kind of answer to "what about all the existing programs?", and the current interop between Rust and C++ is not good enough to be a satisfying answer. If it gets improved to the point where the two languages can interoperate smoothly at the source level, then I think the possibility of Rust displacing C++ for most use cases becomes something like realistic.
Sometimes I think sockets with a spec for what's on the "wire" is about as good an abstraction as you can get for arbitrary cross-language calling. If you could have your perfect abstraction for cross-language calling what would it be?
Not sure, but it should be message-oriented, rather than stream-oriented. You have to put a framing protocol on top before you can do anything else. Then you have to check that framing is in sync and have some recovery.
I'm currently struggling with the connection between Apache mod_fcgid (retro) and a Rust program (modern). Apache launches FCGI programs as subprocesses, with stdin and stdout connected to the parent via either pipes or UNIX local sockets. There's a binary framing protocol, an 8-byte header with a length. You can't transmit arbitrarily large messages; those have to be "chunked". There's a protocol for that. You can have multiple transactions in progress. The parent can make out of band queries of the child. There's a risk of deadlock if you write too much and fill the pipe when the other end is also writing. All that plumbing is specific to this application.
(Current problem: Rust std::io appears to not like stdin being a UNIX socket. Trying to fix that.)
You could definitely do JSON / msgpack and have like 5 C API functions like, read, write, wake_on_readable, and it wouldn't be the worst thing, and it wouldn't incur any IPC overhead.
> You have to avoid abstractions and conveniences and minimizing allocations so as not to trigger the garbage collector.
That's not really how modern GCs work, and not how abstractions work when you have a good JIT. The latency impact of modern GCs is now often effectively zero (there are zero objects processed in a stop-the-world pause, and the overall CPU utilisation of the GC is a function of the ratio between the size of the resident set and the size of the heap) and a JIT can see optimisation opportunities more easily and exploit them more aggressively than an AOT compiler (thanks to speculative optimisations). The real cost is in startup/warmup time and memory overhead, as well as optimising amortised performance rather than worst-case performance. Furthermore, how much those tradeoffs actually cost can be a very different matter from what they are (e.g. 3x higher RAM footprint may translate to zero additional cost, and doing manual memory management may actually cost more), as brilliantly explored in this recent ISMM talk: https://youtu.be/mLNFVNXbw7I
> C++’s innovations in zero-cost abstractions
I think that the notion of zero-cost abstractions - i.e. the illusion of "high" [1] abstraction when reading the code with the experience of "low" abstraction when evolving it - is outdated and dates from an era (the 1980s and early '90s) when C++ believed it could be both a great high-level and great a low-level language. Since then, there's generally been a growing separation rather than unification of the two domains. The portion of software that needs to be very "elaborate" (and possibly benefit from zero-cost abstractions) and still low-level has been shrinking, and the trend, I think, is not showing any sign of reversing.
[1]: By high/low abstraction I mean the number of subroutine implementations (i.e. those that perform the same computational function) that could be done with no impact at all on the caller. High abstraction means that local changes are less likely to require changing remote code and vice-versa, and so may have an impact on maintenance/evolution costs.
I've got a nice one recently: A pipeline has bytes being transformed to chars by a user selectable encoding, then the chars go to a next step.
In an AOT language, this must be dynamic dispatch, as there are multiple algorithms. The JDK, however, notices how the encoding is basically always UTF-8 and does an 'if utf8 then do utf8code else do dynamic dispatch'. Then the inliner comes along, pushes that if outside a loop, and merges the byte reading, encoding and char processing to 1 big code block, all optimized together.
Any optimisation transforms the program in some way that has to preserve its meaning. Generally, to do that, AOT compilers need to prove that the transformation is always correct; that can be difficult for some deep optimisations. OTOH, JITs can assume that some transformation is correct, aggressively apply it, and if it turns out they're wrong, the code will trigger some signal that will have the runtime deoptimise (some modern AOT compilers have some speculative and deoptimisation capabilities, but not as general as JITs').
Java has an excellent GC, but a horrible runtime. .net is probably the best GC integrated into a language with decent memory layout ability. If all you want is the GC without a language attached, LXR is probably the most interesting. it's part of MMTK which is a rust library for memory allocation and GC that Java, Ruby, and Julia are all in the process of adding options for.
The currently flagged comment and the discussion below it actually do have a salient point.
Comment: How about get an actual published language standard? How about get more implementations of it?
In the discussion, @infogulch: If you are aiming to be the foundation of an entire industry it doesn't seem unreasonable to ask for some specifications. https://news.ycombinator.com/item?id=44926375
I think it’s flagged (although I don’t have an opinion on whether it should be) because it’s not clear at all whether it’s actually a reasonable thing to expect: plenty of industries are built on programming languages that don’t have formal specifications, much less natural language ones.
To argue by example: Ruby has an ISO standard, but that standard is for a very old version of the language. Python doesn’t have an independent standard at all; it’s like Rust in that the reference implementation is itself the standard. But nobody is champing at the bit to replace their Ruby or Python stack for standards reasons.
This is an ambitious and difficult project that is a work in progress. They’re continuing to work hard at it. There were 2 PRs merged to master less than 2 hours ago when I wrote this comment. On a Saturday, that’s commitment right there.
The plan described in "Our Vision for the Rust Specification", and the linked RFC3355, were abandoned early in 2024.
The team that was formed to carry out RFC3355 still exists, though it's had an almost complete turnover of membership.
They're currently engaged in repeating the process of deciding what they think a spec ought to be like, and how it might be written, from scratch, as if the discussion and decisions from 2023 had never happened.
The tracking issue for the RFC was https://github.com/rust-lang/rust/issues/113527 . You can see the last update was in 2023, at the point where it was time to start the actual "write the spec" part.
That's when it all fell apart, because most of the people involved were, and are, much more interested in having their say on the subject of what the spec should and shouldn't be like than they are in doing the work to write it.
I hope this is clear: the published language standard the previous poster was asking about does not currently exist.
You linked to efforts to create a specification, but they aren't done or close to done.
It's also not clear the goal is to create a standard. E.g., according to the spec-vision document, the spec isn't planned to be authoritative (the implementation is). If that doesn't change the spec can't really function as a standard. (A standard would allow independent implementations and independent validation of an implementation. A non-authoritative spec doesn't.)
Because I’m a cynical person, I view LLMs like I’ve viewed rust for years: a neat tool that makes a small subset of people happy and a touch more productive.
The evangelical defense of either topic is very off-putting, to say the least.
@infogulch really nailed it with the comment about how the rust community wants influence/power, and ignores the responsibility that comes with said influence/power.
Python still has no standard and open antagonistic towards refactoring their standard library so that the language and the library aren't shipped as a monolith.
The most difficult part for alternative Python implementations is supporting the same stdlib.
I do find it curious that the people who write comments demanding a spec and alternate implementations aren’t aware of the work-in-progress spec and alternate implementations.
I don't have gripes with Rust per se, but I do take issue with the contingent of commenters that seem to not understand the value of a formal specification in the construction of formal computational systems in the first place. Basically every computational system lives and dies on its degree of formal specifiability, and having more formal specification is always better unless you are cool with being beholden to the fact that some particular implementation of some program happened to work at some point in time for some input once. That insecure ground is what you're standing on if you don't think formal specifications have any value. In the world of computation, the specification is the system, modulo incidental properties of implementation like performance.
> in the construction of formal computational systems
> Basically every computational system lives and dies on its degree of formal specifiability
How much software development work would you say qualifies as "construction of formal computational systems"? I feel like I have to be thinking of something different than you because to a first approximation I think ~no software has much in the way of formal specification done on it, if any at all.
I feel like there's a bit of black-and-white thinking here as well. It's not as if you pick either "full formal specification with bonus Lean/Coq/etc." or "YOLO" - there's shades of grey in between where you can nail down different bits with more or less specificity/formality/etc. depending on feasibility/priorities/etc. Same with support for/against a formal spec - there's more nuance than "an absolute necessity" or "a waste of time".
People often cast doubts by asking why Rust needs a spec (spec is not the same as standard), and this proves there is still too little engineering in so-called "software engineering".
Software engineering is not engineering. No need to pretend that it should be. We dont rebuild a bridge 3 times before we get it right, but in SE thats a pretty good approach
If you want to write an OS in rust and provide rich services for applications to use, you need to offer libraries they can call without needing to recompile when the OS is upgraded. Windows mostly does this with COM, Apple historically had ObjC’s dynamic dispatch (and now Swift’s ABI), Android does this with a JVM and bytecode… Rust can only really offer extern "C", and that really limits how useful dynamic libraries can be.
Doing an ABI without a VM-like layer (JVM, .NET) is really difficult though, and requires you to commit to certain implementation details without ever changing them, so I can understand why it’s not a priority. To my knowledge the only success stories are Swift (which faced the problem head-on) and COM (which has a language-independent IDL and other pain points.) ObjC has such extreme late binding that it almost feels like cheating.
If rust had a full-featured ABI it would be nearly the perfect language. (It would improve compile times as well, because we’d move to a world where dependencies could just be binaries, which would cut compile times massively.)
For the use case of letting Rust programs dlopen plugins that are also written in Rust, the current solutions are stabby and abi_stable. They're not perfect but they seem to work fairly well in practice.
For more general use cases like cross-language interop, the hope is to get somewhere on crABI: https://github.com/joshtriplett/rfcs/blob/crabi-v1/text/3470... This is intended to be a superior successor to C's ABI, useful for all the same use cases (at least if the code you want to talk to is new enough to support it). Note that this isn't something the Rust maintainers can do unilaterally; there's a need to get buy-in from maintainers of other languages, Linux distros, etc.
I'm not sure that the "everything is a shared library" approach is workable in a language where heap allocation is generally explicit and opt-in. While Swift has some support for stack allocation, most types are heap-allocated, and I think that reduces the extent to which the rest of the language has to be warped to accommodate the stable ABI.
Stack vs heap in swift is simple: structs are stack, classes are heap.
This is what makes Swift’s effort so impressive: it works with stack data just as well as heap. Stack allocated types being used across library boundaries has a ton of plumbing to make it work, there’s the concept of a value witness table (like a vtable but for size/stride information) that can allow looking up sizes at runtime. Stack allocation makes heavy use of alloca(), which most languages shy away from (since it means stack frames aren’t of a static size.) It was difficult but swift managed to do it.
For rust to do something similar it would have to fix a lot of cases where object safety is broken, and yeah, this may require changing the language, particularly to support dynamic/runtime size information. (It would also make the dyn keyword effectively obsolete, because to make generics work across library boundaries, any type has to be able to “become” dynamic if the linker needs it to be.)
It would definitely require changes to the language, but Swift provides some inspiring prior art here.
This article goes into the details: https://faultlore.com/blah/swift-abi/
Using them is unfortunately not yet quite as easy as using repr(wasm)/extern "wasm". But with wit-bindgen [2] and the wasm32-wasip2 target [3], it's not that hard either.
[1]: https://youtu.be/tAACYA1Mwv4
[2]: https://github.com/bytecodealliance/wit-bindgen
[3]: https://doc.rust-lang.org/nightly/rustc/platform-support/was...
(And I've seen proposal to extend extern ABI to more things)
You can use any serialisation method you want (C structs, Protobuf, JSON, ...).
An ABI is for passing data in the programming language and trusting it blindly, e.g. what functions use when calling each other.
Any sane OS still has to validate the data that crosses its boundaries. For example, if you make a Linux system call, it doesn't blindly use a pointer you pass it, but instead actually checks if the pointer belongs to the calling process's address space.
It seems pointless to pick Rust's internal function-calling data serialisation for the OS boundary; that wouldn't make it easier for any userspace programs running in that OS than explicitly defining a serialisation format.
Why would an OS want to limit itself to Rust's internal representation, or why would Rust want to freeze its internal representation, making improvements impossible on both sides? This seems to bring only drawbacks.
The only benefit I can see so far for a stable ABI is dynamic linking (and its use in "plugins" for programs where the code is equally trusted, e.g. writing some dlopen()able `.so` files in Rust, as plugins for Rust-only programs), where you can really "blindly call the symbol". But even there it's a bit questionable, as any mistake or mismatch in ABI can result in immediately memory corruption, which is something that Rust users rather dislike.
1. Self-referencing structs. Especially where you want to have something like a source file and the parsed AST in the same struct. You can't easily do that at the moment. It would be nice if there was something like an offset reference that made it work. Or something else...
2. The orphan rule. I get it, but it's still annoying. We can do better than newtype wrappers (which sometimes have to be nested 2 or 3 levels deep!).
3. The fact that for reasonable compile time you need to split projects into lots of small crates. Again, I understand the reasons, but the result sucks and we can definitely do better. As I understand it this is because crates are compiled as one compilation unit, and they have to be because circular dependencies are allowed. While that is true, I expect most code doesn't actually have circular dependencies so why not make those opt-in? Or even automatically split items/files within a crate into separate compilation units based on the dependency graph?
There's probably more; this is just what I can remember off the top of my head.
Hopefully that's constructive criticism. Rust is still my favourite programming language by far.
Rust allows circular dependencies between modules within a single compilation unit, but crates can't be circular dependencies.
> I expect most code doesn't actually have circular dependencies
Not true, most code does have benign circular dependencies, though it's not common to realize this. For example, consider any module that contains a `test` submodule, that's a circular dependency, because the `test` submodule imports items from the parent (it has to, because it wants to test them), but also the parent can refer to the submodule, because that's how modules and submodules fundamentally work. To eliminate the circular dependency you would need to have all test functions within every compilation unit defined within the root of the crate (not in a submodule off of the root; literally defined in the root namespace itself).
Yes, that's what I was saying.
> that's a circular dependency, because the `test` submodule imports items from the parent
This isn't a circular dependency because the parent doesn't import anything from the test module.
It would mean your compilation unit might have to be a subset of a module, but I think that's fine?
Would add 4: partial self borrows (ability for methods to only part of their struct).
For 3, I think the low hanging fruit is probably better support for just using multiple crates (support for publishing them as one package for example).
By that do you mean that there are better alternatives that Rust could adopt or that we need such alternatives (but they could not exist)?
The emphasis on cross-language compatibility may be misplaced. You gain complexity and lose safety. If we have to do that, it would help if it were bottom-up, replacing libc, for example.
Go doesn't do cross-language calling much. Google paid people to build the foundations, the low-level libraries right. Rust tends toward many versions of the basics, most flawed.
One of the fundamental problems in computing for about two decades now has been getting programs on the same machine to efficiently talk to each other. Mostly, people have hacked on the Microsoft .DLL concept, giving them state. Object request brokers, like CORBA, never caught on. Nor did good RPC, like QNX. So people are still trying to link stuff together that doesn't really want to be linked.
Or you can do everything with sockets, even on the same machine. They're the wrong abstraction, raw byte streams, but they're available.
I'm currently struggling with the connection between Apache mod_fcgid (retro) and a Rust program (modern). Apache launches FCGI programs as subprocesses, with stdin and stdout connected to the parent via either pipes or UNIX local sockets. There's a binary framing protocol, an 8-byte header with a length. You can't transmit arbitrarily large messages; those have to be "chunked". There's a protocol for that. You can have multiple transactions in progress. The parent can make out of band queries of the child. There's a risk of deadlock if you write too much and fill the pipe when the other end is also writing. All that plumbing is specific to this application.
(Current problem: Rust std::io appears to not like stdin being a UNIX socket. Trying to fix that.)
That's not really how modern GCs work, and not how abstractions work when you have a good JIT. The latency impact of modern GCs is now often effectively zero (there are zero objects processed in a stop-the-world pause, and the overall CPU utilisation of the GC is a function of the ratio between the size of the resident set and the size of the heap) and a JIT can see optimisation opportunities more easily and exploit them more aggressively than an AOT compiler (thanks to speculative optimisations). The real cost is in startup/warmup time and memory overhead, as well as optimising amortised performance rather than worst-case performance. Furthermore, how much those tradeoffs actually cost can be a very different matter from what they are (e.g. 3x higher RAM footprint may translate to zero additional cost, and doing manual memory management may actually cost more), as brilliantly explored in this recent ISMM talk: https://youtu.be/mLNFVNXbw7I
> C++’s innovations in zero-cost abstractions
I think that the notion of zero-cost abstractions - i.e. the illusion of "high" [1] abstraction when reading the code with the experience of "low" abstraction when evolving it - is outdated and dates from an era (the 1980s and early '90s) when C++ believed it could be both a great high-level and great a low-level language. Since then, there's generally been a growing separation rather than unification of the two domains. The portion of software that needs to be very "elaborate" (and possibly benefit from zero-cost abstractions) and still low-level has been shrinking, and the trend, I think, is not showing any sign of reversing.
[1]: By high/low abstraction I mean the number of subroutine implementations (i.e. those that perform the same computational function) that could be done with no impact at all on the caller. High abstraction means that local changes are less likely to require changing remote code and vice-versa, and so may have an impact on maintenance/evolution costs.
In an AOT language, this must be dynamic dispatch, as there are multiple algorithms. The JDK, however, notices how the encoding is basically always UTF-8 and does an 'if utf8 then do utf8code else do dynamic dispatch'. Then the inliner comes along, pushes that if outside a loop, and merges the byte reading, encoding and char processing to 1 big code block, all optimized together.
Deleted Comment
Deleted Comment
Comment: How about get an actual published language standard? How about get more implementations of it?
In the discussion, @infogulch: If you are aiming to be the foundation of an entire industry it doesn't seem unreasonable to ask for some specifications. https://news.ycombinator.com/item?id=44926375
I agree with @infogulch
To argue by example: Ruby has an ISO standard, but that standard is for a very old version of the language. Python doesn’t have an independent standard at all; it’s like Rust in that the reference implementation is itself the standard. But nobody is champing at the bit to replace their Ruby or Python stack for standards reasons.
One of the founding members wrote a blog post & RFC which gives some good reasons why a spec is needed https://blog.m-ou.se/rust-standard https://rust-lang.github.io/rfcs/3355-rust-spec.html
I’m curious, did you Google before writing this comment? I did just now and the first result I found is https://github.com/rust-lang/reference.
This is something the Rust project is taking very seriously. You can read up on their approach in this detailed blog post from November 2023 - Our Vision for the Rust Specification (https://blog.rust-lang.org/inside-rust/2023/11/15/spec-visio...).
This is an ambitious and difficult project that is a work in progress. They’re continuing to work hard at it. There were 2 PRs merged to master less than 2 hours ago when I wrote this comment. On a Saturday, that’s commitment right there.
> How about get more implementations of it?
Here’s what another quick google search yielded. From the official Rust blog, published November 2024: gccrs: An alternative compiler for Rust (https://blog.rust-lang.org/2024/11/07/gccrs-an-alternative-c...)
Hope that helps dispel the confusion around the lack of spec or alternate implementation.
The team that was formed to carry out RFC3355 still exists, though it's had an almost complete turnover of membership.
They're currently engaged in repeating the process of deciding what they think a spec ought to be like, and how it might be written, from scratch, as if the discussion and decisions from 2023 had never happened.
The tracking issue for the RFC was https://github.com/rust-lang/rust/issues/113527 . You can see the last update was in 2023, at the point where it was time to start the actual "write the spec" part.
That's when it all fell apart, because most of the people involved were, and are, much more interested in having their say on the subject of what the spec should and shouldn't be like than they are in doing the work to write it.
You linked to efforts to create a specification, but they aren't done or close to done.
It's also not clear the goal is to create a standard. E.g., according to the spec-vision document, the spec isn't planned to be authoritative (the implementation is). If that doesn't change the spec can't really function as a standard. (A standard would allow independent implementations and independent validation of an implementation. A non-authoritative spec doesn't.)
Because I’m a cynical person, I view LLMs like I’ve viewed rust for years: a neat tool that makes a small subset of people happy and a touch more productive.
The evangelical defense of either topic is very off-putting, to say the least.
@infogulch really nailed it with the comment about how the rust community wants influence/power, and ignores the responsibility that comes with said influence/power.
The most difficult part for alternative Python implementations is supporting the same stdlib.
I do find it curious that the people who write comments demanding a spec and alternate implementations aren’t aware of the work-in-progress spec and alternate implementations.
> Basically every computational system lives and dies on its degree of formal specifiability
How much software development work would you say qualifies as "construction of formal computational systems"? I feel like I have to be thinking of something different than you because to a first approximation I think ~no software has much in the way of formal specification done on it, if any at all.
I feel like there's a bit of black-and-white thinking here as well. It's not as if you pick either "full formal specification with bonus Lean/Coq/etc." or "YOLO" - there's shades of grey in between where you can nail down different bits with more or less specificity/formality/etc. depending on feasibility/priorities/etc. Same with support for/against a formal spec - there's more nuance than "an absolute necessity" or "a waste of time".
True, all other things being equal. But your logic falls down when all other things aren't equal.
Rust is a more "secure ground" than C even though C has an official specification and Rust doesn't really.
Also you shouldn't say "formal specification" in this context because I don't think you really mean https://en.wikipedia.org/wiki/Formal_specification