Time and time again I read something along the lines of this quote. I don't really understand where this is coming from. Do you agree with this assertion? And if yes what are the distinctions to say Java interfaces that make rust traits so different?
Just to name one case where they differ, I believe in Java a class can't implement the same interface with different generic parameters.
I'm not familiar with Java interfaces, but in my head traditional interfaces are just a blueprint for a set of associated functions, while Rust traits can have associated types so that you can differentiate distinct trait implementations.
I believe many instances of “Interface” language features, especially in OOP languages, don't support naming your Self
type in the interface (to use it as a return type, for example). This is somewhat similar to “object safety” restrictions in Rust, and understandable insofar that interfaces in such languages do typically always support polymorphism (and virtual calls) for all interfaces, but in the cases where you don’t want that, and need to name Self
, you work around it with CRTP-style constructions.
Also many instances of “Interface” language features require you to list all interfaces that are being implemented when defining the type. Whereas traits can be defined later, even in a different crate (as long as orphan rules are respected).
Ultimately, the word “interface” is just a word without a specific definition, and used in quite different meaning between different programming languages. If I’m not mistaken, Go uses the word to mean a collection of methods (with signatures) that you adhere to implicitly in a sort-of duck-typing manner. (Which brings back the ability for downstream interfaces to be implemented by upstream types, I suppose.) On the other hand, any system of “interfaces” that’s a nominal system (explicit implementations are necessary) and supports downstream implementations, while still enforcing coherence, would arguably be pretty close to Rust’s traits or Haskell’s type classes (the two are generally considered relatively “the same”) already, even if it still called itself “interface” or any other term. Note that the original “type classes” system of Haskell without extensions doesn’t even come with type features like Rust’s associated types (“associated type synonyms”) or generic traits (“multi-parameter type classes”[1]). They do however [excuse my usage of Rust terminology for simplicity] allow the Self
type to appear anywhere in method signatures, like return types, as parameter to other generic types, or inside of further trait bounds for the individual method.
since in Haskell,
Self
does not exist as a special thing but it’s just a type parameter, too, so only multiple parameters is the equivalent to Rust’s traits with generic type arguments ↩︎
A couple of important differences have already been named.
Another one is that the methods in a trait, as well as the trait itself, can have trait bounds of their own.
While these may seem like small differences, they have a huge impact on the applicability of traits vs interfaces. If you have the time, try and create a Collection
trait, then implement it for various collections. You'll quickly notice that it becomes more trouble than it's worth, which is why Rust doesn't define one.
To be effective with traits when someone is used to interfaces, one needs to unlearn a fair amount of their interface knowledge.
This is why I called them distant cousins: one can clearly see that they're related, but the situations that they're applicable in are different enough that it warrants its own syntax to alert the user to the fact that they're more different than one might expect.
A nest analogy might be functions VS closures: one can clearly see they're related, but they diverge enough in feature set and applicability that giving them the same syntax would have created far more confusion than clarity, and unnecessarily so.
The way I think of it is that "interfaces" are a way to erase a concrete type and call a bunch of methods via a stable, while traits let you write something closer to "type level equations".
For example, think about things like where Foo: Add<Rhs=T, Output=Foo>
. There's no way you could do something like that with traditional interfaces.
And if yes what are the distinctions to say Java interfaces that make rust traits so different?
I wrote a detailed comparison of Rust traits and Java interfaces in particular:
Interfaces, in Java, are types. That is, given
public interface Storage {}
you can define variables, arguments, fields, &c of type Storage
, whose values (other than null
) are pointers to objects that implement the Storage
interface.
Rust traits, in and of themselves, are not types. That is, given std::cmp::PartialEq
, you cannot hold a value with the type PartialEq
.
I tend to think that this is a relatively minor distinction, since in the situations where you'd want to abstract over values whose types share a trait, you can use dyn T
(for trait T
) to get nearly the same capabilities that a Java interface type provides. But it is a distinction, and does affect how I write code, so I hope it helps in your search for understanding.
The same is true in C#, Go, and a lot of other contemporary languages with interfaces/protocols, incidentally, though not in all of them. Rust's traits share more with ML type classes than with interfaces, but all three things are primarily doing the same job of providing commonality so that you can abstract over similar types.
I don't really understand where this is coming from.
Programmers coming from other languages need a handle, and using what they know is a good one. Rust doesn't exist in a vacuum.
Thanks everyone for your answers. What I gathered so far, is that traits try to solve the same kind of problem as Java Interfaces but differ in so many details that their use can look very different from one another.
Yes. I appreciate all this info, I still have so much to learn about Rust traits! But if a Java person new to Rust said "What do I use in Rust instead of Java interfaces?", I would definitely say traits, and just add that there are some differences. And now I can point them to @kpreid's SO post.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.