Yep, 100%. The difference is conceptual, not practical. The Rust Node<T> is not generic over any type T, it is only generic over types T that implement ==. Any attempt to instantiate it with a type that does not is actually a type error. Traits further qualify and constrain types. In C++ it's not a type error, it's a method resolution error. As a programmer, when building this object, I do really mean to say "generic over types that can be compared for equality", not "generic in general and I promise later I'll tell you how to do it", even though where we get in the end is functionally equivalent. As it's being used, an inadequate T class is actually not part of the "type" of this object.
That feels to me "more correct", or at least more expressive. Practically, though, yeah, maybe it doesn't make a difference.
C++ is kinda simple like thta: if you don't use it, you don't pay for it
Rust strives for this as well, the difference here being that any type used in this object will use it, and the compiler is capable of making sure everything is in order before even starting down that road.
I don't think this is completely true. It is at least simpler to constrain generic types more strictly than the current implementation requires in Rust. You may wish to add extra constraints on the type to give yourself implementation flexibility without needing to break callers. For example, if the current implementation only requires equality but I only want to accept types that implement ordering, that's pretty trivial in Rust. I would guess you could use some if 0 trick in C++ to use the operator but not impact the implementation, but that obscures intent at a minimum.
Conversely, (stable) Rust does not have the equivalent of SFINAE. Specialization is available in nightly, which is pretty similar. I've seen this come up comparing Abseil Swiss Map and Hashbrown maps. The former will usually do the most efficient thing by default, but the latter requires the subtle RawEntry API to do things like lookup or insert without copying unless I'm actually inserting.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
Yep, 100%. The difference is conceptual, not practical. The Rust
Node<T>
is not generic over any typeT
, it is only generic over typesT
that implement==
. Any attempt to instantiate it with a type that does not is actually a type error. Traits further qualify and constrain types. In C++ it's not a type error, it's a method resolution error. As a programmer, when building this object, I do really mean to say "generic over types that can be compared for equality", not "generic in general and I promise later I'll tell you how to do it", even though where we get in the end is functionally equivalent. As it's being used, an inadequateT
class is actually not part of the "type" of this object.That feels to me "more correct", or at least more expressive. Practically, though, yeah, maybe it doesn't make a difference.
Rust strives for this as well, the difference here being that any type used in this object will use it, and the compiler is capable of making sure everything is in order before even starting down that road.
I don't think this is completely true. It is at least simpler to constrain generic types more strictly than the current implementation requires in Rust. You may wish to add extra constraints on the type to give yourself implementation flexibility without needing to break callers. For example, if the current implementation only requires equality but I only want to accept types that implement ordering, that's pretty trivial in Rust. I would guess you could use some
if 0
trick in C++ to use the operator but not impact the implementation, but that obscures intent at a minimum.Conversely, (stable) Rust does not have the equivalent of SFINAE. Specialization is available in nightly, which is pretty similar. I've seen this come up comparing Abseil Swiss Map and Hashbrown maps. The former will usually do the most efficient thing by default, but the latter requires the subtle
RawEntry
API to do things like lookup or insert without copying unless I'm actually inserting.