- Lack of restrictions
- Safety
- Performance
If you choose safety and no restriction, you pay the price in performance (for GC etc.)
- Lack of restrictions
- Safety
- Performance
If you choose safety and no restriction, you pay the price in performance (for GC etc.)
It's not "2 out of 3", it's a triangle where a language can pick a sweet spot anywhere within the triangle (and ideally, it's not a "sweet spot" either, but more like a "sweet area" where the programmer can pick an actual spot within that area defined by the language).
fn setData(comptime pins: anytype, bus: anytype, data: u8) @TypeOf(bus)
In practice this looks and feels like dynamic typing, yet when looking at the compiler output it still resolves to optimal code (since it's "compile-time dynamic typing" not "run-time dynamic typing", but the difference in practice is surprisingly small).That's why I claimed that no such language exists.
Rust is quite the opposite, more primitives that carry semantics (most of those in the stdlib though), a stronger but also more rigid type system, but those are pretty much needed for the compiler to guarantee safety.
The million dollar question is of course, can there be a more relaxed Rust with the same safety and performance guarantees, maybe by "squeezing the type inference engine" even more (and letting Rust look or somehow extract information across crate boundaries)? And do Rust programmers even see this as desirable, or are they mostly comfortable in their current sweet spot of the triangle?
Rust absolutely is not the end state of programming languages but I feel in many ways it's quite ahead than most of everything else. But yes, we still need something a tad better than it.
in rust, the P and B are resolved at compile time, not at runtime. If you wanted dynamic dispatch the types would be Box<dyn B>
The borrow checker works on "struct granularity", but it would be much more flexible and convenient if borrowing would work on memory location granularity (for instance passing a struct reference into a function "taints" the entire struct as borrowed, even if that function only accesses a single item in the borrowed struct - this 'coarse borrowing' restriction then may lead to all sorts of workarounds to appease the compiler, from 'restructuring' your structs into smaller pieces (which then however may fit one borrowing situation, but not another), or using 'semantic crutches' like Rc, Cell or Box.
There are also related restrictions about function call barriers. AFAIK the Rust compiler cannot "peek into" called function bodies to figure out what's actually going on inside those functions (and that information would be very valuable for fine-grained borrow checking), it can only work with the information in the function signature.
Again, disclaimer: take this with a grain of salt since I'm not a daily Rust user, but this is how I understood why Rust feels so restrictive.
That's valid, thanks for pointing it out. I seem to recall the team lately mentioning they are starting to consider fixing that. And yes that's a real productivity killer, happened to me as well in the past.
I don't think it's so much "can not" as it is "will not". Allowing the function signature to be determined by the body can lead to accidentally breaking callers by changing the body.
That, at least, is consistent with other parts of the signature: the input/output types and how the lifetimes of input/output references are related.