3 ms·
Is this a scrappy version of type classes (Haskell) or traits (Rust)?
by pharmakom 5y ago
Is this a scrappy version of type classes (Haskell) or traits (Rust)?
- steveklabnik 5y agoSort of, in that they are a thing you do to constrain generic parameters. A significant difference in my understanding is that type classes and traits are required, but concepts are not. That is, using the example from the article, a concept can tell you if you're passing something that doesn't add, but you can add inside a function without using a concept. In other words: fn add<T>(x: T, y: T) -> T { x + y } This won't compile in Rust: error[E0369]: cannot add `T` to `T` --> src/lib.rs:2:7 | 2 | x + y | - ^ - T | | | T | help: consider restricting type parameter `T` | 1 | fn add<T: std::ops::Add<Output = T>>(x: T, y: T) -> T { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ This suggestion works, but you don't have to write it this way. Once the constraints get more complex than T: Foo, I personally switch to this form: use std::ops::Add; fn add<T>(x: T, y: T) -> T where T: Add<Output = T>, { x + y } I find it a little easier to read. YMMV. Whereas in C++, this does compile: template<typename T> T add(T a, T b) { return a + b; } If you try to add something that doesn't have + defined: int main(void) { add("4", "5"); } you get this <source>:4:12: error: invalid operands to binary expression ('const char *' and 'const char *') return a + b; ~ ^ ~ <source>:8:5: note: in instantiation of function template specialization 'add<const char *>' requested here add("4", "5"); ^ Whereas, if you do what the article does (though I'm using char* instead of std::string, whatever) #include <concepts> template<typename _Tp> concept integral = std::is_integral_v<_Tp>; template<std::integral T> T add(T a, T b) { return a + b; } int main(void) { add("4", "5"); } you get <source>:12:5: error: no matching function for call to 'add' add("4", "5"); ^~~ <source>:6:3: note: candidate template ignored: constraints not satisfied [with T = const char *] T add(T a, T b) ^ <source>:5:15: note: because 'const char *' does not satisfy 'integral' template<std::integral T> ^ /opt/compiler-explorer/gcc-11.1.0/lib/gcc/x86_64-linux-gnu/11.1.0/../../../../include/c++/11.1.0/concepts:102:24: note: because 'is_integral_v<const char *>' evaluated to false concept integral = is_integral_v<_Tp>; ^ This doesn't feel like a huge change because add is such a small function, but if it were larger and more complicated, the error with a concept is significantly better.