4 ms·
> Rust’s type system is powerful, with a generics system that’s in some ways more limited than C++ templates but much easier to grok. I'm definitely not experi
by squiguy7 8y ago
> Rust’s type system is powerful, with a generics system that’s in some ways more limited than C++ templates but much easier to grok.
I'm definitely not experienced enough in C++ to know the details around this but I would love to hear about it from someone who is. I know some of the limitations of Rust's current type system but there is a decent amount of work underway to implement things like constant generics.
- steveklabnik 8y agoThere's two core differences. The first is what happens when you get something wrong. Let's say you write a templated function. This is Rust syntax, mapping it to C++ is left as an exercise for the reader: fn foo<T>(x: T) { x.bar(); } Rust checks the types before expansion, not after. So you get this error: error[E0599]: no method named `bar` found for type `T` in the current scope --> src/lib.rs:2:9 | 2 | x.bar(); | ^^^ In C++, this stuff is checked after expansion, so if you only pass things that have bar to foo, you're all good! It will compile. But when you pass something that doesn't, you'll get an error then. This is a restriction, but one that leads to better error messages, and stronger checks. You'd need to write fn foo<T: Bar>(x: T) where Bar is a trait that provides a bar method. The second difference is what is allowed in generics: Rust only lets you use type parameters. We have accepted an RFC to allow constant expressions (the most straightforward of which is 'integers'), but it hasn't been implemented yet. C++ lets you do this today https://stackoverflow.com/questions/499106/what-does-template-unsigned-int-n-mean https://stackoverflow.com/questions/499106/what-does-templat... and https://en.cppreference.com/w/cpp/language/template_parameters#Non-type_template_parameter https://en.cppreference.com/w/cpp/language/template_paramete... (they also have "template template parameters" aka higher kinded types https://en.cppreference.com/w/cpp/language/template_parameters#Template_template_arguments https://en.cppreference.com/w/cpp/language/template_paramete...)
- petters 8y agoIt is worth adding that the C++ community has wanted to add something called "concepts" since a very long time. Rust traits sounds similar. So C++ will likely move in the direction of Rust here.
- steveklabnik 8y agoYes. They're similar in ways, but also very different. I know that they have been added to the C++20 draft, but haven't gotten a chance to really dig in yet.
- masklinn 8y agohttps://stackoverflow.com/questions/47675493/equivalent-of-specific-template-usage-in-c-for-rust https://stackoverflow.com/questions/47675493/equivalent-of-s... notes const generics, const specialisation, associated type constructors, type-quantified higher-rank trait bounds and variadic generics.
- klodolph 8y agoC++ templates are weird. They are kind of like a mix between macros and the kind of generic types you would see in other languages. As a macro system, you can use templates to "generate code" but in a very limited way, it's nowhere near as expressive as a full macro system like you'd see in Lisp, Rust, or Haskell. As system for creating generic types, templates are too general-purpose... they give you a lot of power to underspecify things, and you can't typecheck a template until you instantiate it (like a dynamically typed program). It interacts in strange ways with operator overloading, namespace resolution, and other language features like constructors. There are all sorts of template helpers that are necessary but leave you scratching your head until you actually see them in use and find the corner case that they're needed for, like std::declval. There are also all the rules about ADL that interact with templates, and then the cherry on top is SFINAE which lets you control whether a problem with a template is an error or not. Simple templates are not too hard to grok, but writing actual generic types ends up being painful due to the proliferation of corner cases you have to consider, and there are a lot of things that you could do with a macro system that are frustrating and difficult with templates, but since they're "almost possible" you end up contorting the code a little bit and sprinkling on some ad-hoc helper definitions until it works. It's better than the C preprocessor but worse than almost everything else. Languages like Lisp have proper macros, so anything you want to do with a template you can just achieve by defining it in a macro... you have more rope to shoot yourself in the foot with, but the rules for Lisp macros are straightforward and easier to understand (you just write a function that spits out code, basically). Languages like C#, Java, Haskell, etc. have better generic types, so you can do something like say "this function takes an array of type T[], and T must implement the Widget interface, it will give you back a single T". In C++ you can accomplish this goal but you can't as easily encode the constraint that "T must be a Widget", you have to do something weird like put a static assert in the function body, or put a std::enable_if in the type, or accept that there will be template substitution errors with possibly long and difficult-to-read error messages. Mind you, this has really underscored (for me) the amazing developments in our understanding of language design over the past decades. C has preprocessor macros which are barely serviceable and impossible to use for anything complicated, C++ has templates which are more powerful but complicated, Java generics with type erasure are very clean but interact in subtle ways with e.g. covariance and contravariance, C# addresses the covariance and contravariance problems but interacts poorly with operator overloading. TBH I think Go made the right decision to keep both generics and operator overloading out of the core language at the beginning, since not having it at all is in many ways preferable to having a bad version.