It seems to have a fair slew of type-system features, so how does it manage to compile so fast, compared to for instance the Rust compiler, which doesn't do global type inference?
The current slowness of the Rust compiler is largely due to putting more developer attention into improving the runtime speed of fully-optimized binaries. Hence, regardless of which mode it's in, the compiler does a lot of unnecessary work that a debug-mode binary doesn't need or benefit from. There's also simply a ton of low-hanging fruit lying around: we're only two weeks out from 1.0 and the nightly compiler is already reportedly 30% faster for code compiling in the wild. There's another 10% compilation speed reduction coming from work around optimizing linking. There's also work towards parallel codegen coming along nicely (https://github.com/rust-lang/rust/pull/26018) which reports a 30% reduction in compilation time. In the longer run there are plans for incremental compilation which should drastically reduce the amount of work that gets unnecessarily repeated with each compilation cycle (https://github.com/rust-lang/rfcs/pull/594). In the even longer run there are plans to fully parallelize the compiler phases and also to pre-optimize code before it gets to LLVM in order to reduce the sheer quantity of IR that we shovel into it.
TL;DR: Crystal manages much faster compilation than Rust because Rust's compiler developers may have deprioritized optimizing the compiler for a bit too long. :)
It's because we spent some time thinking the algorithms and optimizing them, and whenever we make changes to the compiler we make sure the times remain pretty much the same (it's hard because the compiler's size grows so the times inevitably grow, at least for the compiler). And from time to time we profile and optimize further, we like speed.
I believe with time Rust can achieve a similar performance, maybe even better because they will be able to do incremental compilation (maybe, I'm not sure how will they do that with parameterized types).
There's also the thing that most of the things in Crystal are lazy: if you don't invoke a method there are no type checks to be done for it. This means that you only pay for what you use (in terms of compile speed) but also what you don't use doesn't end up being in the resulting executable.
Almost. You at least need to exercise the code at compile time. For example you could write dummy usage tests like this:
typeof(MyClass.new.some_method(1, 2, 3))
That basically says "the above compiles and has some type", so you don't have to test what that method really does, just that it compiles.
I don't think it's a big problem, though. In Ruby it's the same: unless you exercise your code you don't know if it works. Now, think of a classically compiled language like C and C++: if it compiles, does it mean that it works? I doubt you'd release your code without at least a few tests (or a small test-app) to try it.