C++20 concepts are not like Rust traits

At writing, Rust's Wikipedia currently says the following:

Functions can be given generic parameters, which usually require the generic type to implement a certain trait or traits. Within such a function, the generic value can only be used through those traits. This means that a generic function can be type-checked as soon as it is defined. This is in contrast to C++ templates, which are fundamentally duck typed and cannot be checked until instantiated with concrete types. C++ concepts address the same issue and are expected to be part of C++20 (2020).

But C++ concepts (as currently proposed) are very different from Rust traits, and do not allow for the parameterised function to be type-checked only once.

Rust traits are implemented explicitly, whereas C++ concept constraints are implicitly met. This means that concept-constrained templates can legally invoke behaviour not defined by the concept. So, the following code compiles in g++ v9.2 with flags --std=c++2a -fconcepts:1

 1 #include <string>
 3 template<typename T>
 4 concept bool Stringable = requires(T a) {
 5     {a.stringify()} -> std::string;
 6 };
 8 class Cat {
 9  public:
10     std::string stringify() {
11         return "meow";
12     }
14     void pet() {
15     }
16 };
18 template<Stringable T>
19 void f(T a) {
20     a.pet();
21 }
23 int main() {
24     f(Cat());
25     return 0;
26 }

The Rust equivalent would not compile:

 1 trait Stringable {
 2     fn stringify() -> String;
 3 }
 5 struct Cat {
 6 }
 8 impl Cat {
 9     fn pet() {}
10 }
12 impl Stringable for Cat {
13     fn stringify() -> String {
14         "meow".to_string()
15     }
16 }
18 fn f<T: Stringable>(a: T) {
19     a.pet();  // error[E0599]: no method named `pet` found for type `T` in the current scope
20 }
22 fn main() {
23     let cat = Cat{};
24     f(cat);
25 }

C++ concept-constrained templates are still only type checked when concrete instantiation is attempted – they just give better, sooner error messages for types that don't comply with the constraint, rather than the long stream of nonsense that failed template instantiations output in C++ without concepts.

I think it's quite common for people to think that concepts are the same as traits. They look similar syntactically, and also the realities of them aren't well known because they aren't yet in a standard. I hope this can clarify things for anyone curious, and help anyone adjust expectations before the C++20 standard is released.

Rust traits comparisons with other language constructs

Although Rust traits are very different from C++20 concepts, they have similarities to a lot of other language constructs for polymorphism: