4 ms·
From the naive lens of someone writing C++, this all looks... extremely elementary. The first half of the blog post touts monomorphization, which (to my otherwi
by MauranKilom 4y ago
From the naive lens of someone writing C++, this all looks... extremely elementary. The first half of the blog post touts monomorphization, which (to my otherwise uneducated eyes) is nothing other than what template instantiation does in C++, no? And the second half is dedicated to the technicalities of why it's hard to make the compiler support the equivalent of the following C++ signature:
template<int N>
std::array<int, N+1> foo();
I suppose there's some unmentioned "we don't want Rust generics to be Turing complete" in the deeper reasons, but... It sure makes me feel like either I'm taking a lot of things for granted in C++ or Rust painted itself into a strange corner.
Am I missing something that makes Rust generics way more complicated than C++ templates? Or is retrofitting them into Rust really such a hellish adventure?
- Salamander9384 4y agoI think that this is a great example of some of the tradeoffs between Rust and C++! The main difference is that the C++ code will check everything at instantiation time, and C++ templates can lead to nasty error messages because it's hard to assign _blame_ to either the caller or the callee. As an example, consider the following code: template<int N> std::array<int, N> foo() { std::array<int, (N / 2) * 2> out; return out; } This code will only typecheck if N is even. But that's not written down anywhere, and if I tried to pass in an odd N, then I'd get some template error telling me that _something_ went wrong, and it may even give me a trace. But that error doesn't tell me whether it's a bug in foo, or a bug in how I'm using foo. If it's a bug in using foo, it certainly doesn't tell me how to fix things. Rust takes the approach of requiring that you specify constraints on generic parameters up front. It then checks whether the function's body will compile _for all_ possible generic parameters, given the constraints. This is much easier to use from a usability perspective, since you know whether to blame the caller or the callee, and it explicitly enumerates the requirements that you need to meet (i.e. the constraints on the generic parameter) in order to get your code to compile. This is why C++ lets you do many more crazy things with templates than Rust currently allows you to with generics (and why is moving more slowly to add things to its type system). C++'s alternative approach gives up usability for extra flexibility. Neither approach is "right." They're just different trade-offs. (As an aside, it turns out that the Rust type system was Turing complete even before const generics were added.)
- MauranKilom 4y agoThank you for the explanation, that makes it clear!
- tialaramex 4y agohttps://godbolt.org/z/roGxc69qh https://godbolt.org/z/roGxc69qh Provides you with an example to play with, as described this is broken for foo<5> but works fine with say, foo<6>
- cttet 4y ago"we don't want Rust generics to be Turing complete" Has good implications, like good IDE support