| > What's the difference between a standard library and a runtime? In the three decades I've been programming, they've been used interchangeably. First of all, you're right. But despite its definition I think people tend to look at it differently. A runtime is generally thought of as a platform on top of which your code runs on; it needs to start first, and it manages your code. Or perhaps it runs in a side thread. A language that has a runtime is hard to embed into something via just the C ABI, because a function call wouldn't use just the standard platform calling convention; it would have to start that runtime, perhaps marshal the parameters into something supported by that runtime, and then finally the runtime runs your function's code. Take for example cgo, for which you'd need to start the garbage collector first (among other things), hence why the cgo FFI is expensive. Take as another example an async Rust function, which would require e.g. a Tokio runtime to be started first. Another example is Java, for which you'd have to start the whole JVM first. A language that has no runtime, or a minimal runtime, can be called via the C ABI directly. All the function needs is to follow the calling convention, and then its code starts running immediately. This is just my opinion of other people's opinions, I may be wrong. |
That's not a well-defined thing.
> A language that has a runtime is hard to embed into something via just the C ABI, because a function call wouldn't use just the standard platform calling convention
But Java can be embedded in native code or embed native code. It has a specified FFI in both directions.
> Take for example cgo, for which you'd need to start the garbage collector first (among other things), hence why the cgo FFI is expensive.
Well, Java doesn't quite work like that, and its (new) FFI is free in most important cases (i.e. same as a non-inlined C-to-C call). Also, "starting the garbage collector" is not well-defined. What "starts" Rust's garbage collector?
I understand what you're trying to get at, but things aren't so simple. There are, indeed, differences especially around JIT vs AOT, but it's not as simple as saying "having a runtime" or not, nor is everything similar in all languages (Rust and C don't work the same vis-a-vis the C ABI, and Java, C#, and Go interop with native code are all quite different from each other).
> A language that has no runtime, or a minimal runtime, can be called via the C ABI directly.
A Java program can easily expose any method via the C ABI to be called directly if the process has been started from Java -- i.e. it's easy for Java code to give native code a function pointer to Java code. Going the other way, i.e. embedding Java in a C program, is somewhat more involved, but even C++'s interop with C, not to mention Rust or Zig, is not always straightforward. Like in Java, certain functions need to be marked as "C-interopable".