ChatGPT解决这个技术问题 Extra ChatGPT

C++ 的概念和 Rust 的 trait 有何异同?

In Rust, the main tool for abstraction are traits. In C++, there are two tools for abstractions: abstract classes and templates. To get rid of some of the disadvantages of using templates (e.g. hard to read error messages), C++ introduced concepts which are "named sets of requirements".

Both features seem to be fairly similar:

Defining a trait/concept is done by listing requirements.

Both can be used to bound/restrict generic/template type parameters.

Rust traits and C++ templates with concepts are both monomorphized (I know Rust traits can also be used with dynamic dispatch, but that's a different story).

But from what I understand, there are also notable differences. For example, C++'s concepts seem to define a set of expressions that have to be valid instead of listing function signatures. But there is a lot of different and confusing information out there (maybe because concepts only land in C++20?). That's why I'd like to know: what exactly are the differences between and the similarities of C++'s concepts and Rust's traits?

Are there features that are only offered by either concepts or traits? E.g. what about Rust's associated types and consts? Or bounding a type by multiple traits/concepts?

To acknowledge this up front: I know this question is in danger of being closed as "too broad". And if the vast majority of you think so, well so be it. But: I think the question is still fine for this site (it's a fairly specific question about two features) and I don't think it's useful to break this question into multiple sub-questions.
I was one of them and I think this kind of question is just not really constructive, cannot be answered properly and in general it does not fit SO's format very well. I'd like to see a detailed comparison of the above mentioned features of the two languages -- as an article somewhere perhaps, but not as a short answer. I think @Shepmaster's link sums this up pretty well
Thanks for your answers! @NeilButterworth Note that I didn't ask why they are different, though. I just asked for the factual differences.
how you think this question could be asked in a better way — right now, it appears that any answerer would have to be quite expert in Rust and C++. For example, you off-handedly mention "Rust's associated types and consts", but a C++ expert might not know all the details of such. This means that the pool of qualified people will be very small. It's possible that if you describe all the specifics of the technology you know, the question may be easier.
As a relevant parallel question, What is the difference between traits in Rust and typeclasses in Haskell? has a score of 130 at the moment and is relatively well viewed.

M
Matthieu M.

Disclaimer: I have not yet used concepts, all I know about them was gleaned from the various proposals and cppreference, so take this answer with a grain of salt.

Run-Time Polymorphism

Rust Traits are used both for Compile-Time Polymorphism and, sometimes, Run-Time Polymorphism; Concepts are only about Compile-Time Polymorphism.

Structural vs Nominal.

The greatest difference between Concepts and Traits is that Concepts use structural typing whereas Traits use nominal typing:

In C++ a type never explicitly satisfies a Concept; it may "accidentally" satisfy it if it happens to satisfy all requirements.

In Rust a specific syntactic construct impl Trait for Type is used to explicitly indicates that a type implements a Trait.

There are a number of consequences; in general Nominal Typing is better from a maintainability point of view -- adding a requirement to a Trait -- whereas Structural Typing is better a bridging 3rd party libraries -- a type from library A can satisfy a Concept from library B without them being aware of each other.

Constraints

Traits are mandatory:

No method can be called on a variable of a generic type without this type being required to implement a trait providing the method.

Concepts are entirely optional:

A method can be called on a variable of a generic type without this type being required to satisfy any Concept, or being constrained in any way.

A method can be called on a variable of a generic type satisfying a Concept (or several) without that method being specified by any Concept or Constraint.

Constraints (see note) can be entirely ad-hoc, and specify requirements without using a named Concept; and once again, they are entirely optional.

Note: a Constraint is introduced by a requires clause and specifies either ad-hoc requirements or requirements based on Concepts.

Requirements

The set of expressible requirements is different:

Concepts/Constraints work by substitution, so allow the whole breadth of the languages; requirements include: nested types/constants/variables, methods, fields, ability to be used as an argument of another function/method, ability to used as a generic argument of another type, and combinations thereof.

Traits, by contrast, only allow a small set of requirements: associated types/constants, and methods.

Overload Selection

Rust has no concept of ad-hoc overloading, overloading only occurs by Traits and specialization is not possible yet.

C++ Constraints can be used to "order" overloads from least specific to most specific, so the compiler can automatically select the most specific overload for which requirements are satisfied.

Note: prior to this, either SFINAE or tag-dispatching would be used in C++ to achieve the selection; calisthenics were required to work with open-ended overload sets.

Disjunction

How to use this feature is not quite clear to me yet.

The requirement mechanisms in Rust are purely additive (conjunctions, aka &&), in contrast, in C++ requires clauses can contain disjunctions (aka ||).


"Structural Typing is better a bridging 3rd party libraries" - Only if the requirements are formulated in a way that the type can be adapted. If there's a requirement to be able to call a read member on a type, but the library only provides Read, there's very little you can do. The Rust impl blocks can adapt.
@LukasKalbertodt: There is no Hindley Milner type inference ongoing with C++ concepts; so no.
@SebastianRedl: Sure. However that's the difference between always needing an adapter struct (Rust) vs only needing an adapter struct to smooth away differences (C++). Furthermore, using free functions rather than member functions allows only needing to add one function (read here, which delegates to Read).
Is there anything in C++ that can be compared to other kinds of associated items, such as associated constants or associated types?
@RalfJung: In C++ this would static member variables (const ones) and nested typedefs or nested classes.