<p>In Rust, structs and enums may implement any number of traits, which specify certain functions. When a struct/enum implements a trait, this is where the functions are defined:</p>
<pre><code>impl Foo for Thing {
    fn bar() { ... }
}
</code></pre>
<p>This allows one type to implement two or more traits with methods with conflicting names/signatures, since a trait needs to be imported for its methods to be used.</p>
<p>In comparison, many OOP languages have interfaces, which serve a similar purpose. When a class implements an interface, it must have every method the interface requires, but the method doesn't &quot;belong&quot; to the interface in the way that it does with Rust's traits, among other differences. Additionally, a new trait can be implemented on an existing class, whereas interfaces cannot.</p>
<p>What are the pros and cons of these two approaches?</p>

<design-choice-comparison><traits><typeclasses><interfaces>
What are the pros and cons of traits in comparison with interfaces?
CC BY-SA 4.0
2023-05-18T15:07:16.873Z
There is no real hard line between traits and interfaces. In C# for example you can choose to implement a method differently with the same name for 2 different interfaces.
mousetail 'he-him'
CC BY-SA 4.0
2023-05-18T15:11:36.297Z
I would say the main difference is that it's possible to implement a trait for a third-party type, but you cannot implement an interface for a third-party class, at least not in the languages I'm used to (instead you would use the [adapter pattern](https://en.wikipedia.org/wiki/Adapter_pattern)).
kaya3
CC BY-SA 4.0
2023-05-18T15:11:59.803Z
@kaya3 Oh this is an important one. Lemme add that to the question.
rydwolf
CC BY-SA 4.0
2023-05-18T15:58:42.927Z
Note that "traits" can mean something different. The [traits in MLIR](https://mlir.llvm.org/docs/Traits/#) refer to properties, like how an operation is commutative.
chrisaycock
<p>Traits:</p>
<ul>
<li>Are not types. You need something like Rust's <code>impl Trait</code> or <code>dyn Trait</code>.</li>
<li>Are usually resolved implicitly. OCaml's modules is a counterexample.</li>
<li>Can have overlapping implementations. For instance, implementing <code>Display&lt;(Vec&lt;T&gt;, Vec&lt;char&gt;)&gt;</code> and <code>Display&lt;(Vec&lt;char&gt;, Vec&lt;T&gt;)&gt;</code> at the same time may cause issues.</li>
<li>Needs a prolog-style resolution, whose time complexity can be high (depending on how rich your type theory is).</li>
<li>Multiple trait impl is not a problem, can be added freely (Rust's orphan rule is not mandatory) if we don't care about clashes.</li>
<li>Can have an <code>impl</code> for a special case of a type. For instance, <code>impl ToString for Vec&lt;char&gt;</code>.</li>
<li>Identically named functions in different traits are possible.</li>
</ul>
<p>Interfaces:</p>
<ul>
<li>Are types. Put them directly in the signatures.</li>
<li>Requires subtyping (and probably subtyping polymorphism) to work properly. This is horrible for type theory, because <span class="math-container">$(a : A) \in \Gamma$</span> does not mean <span class="math-container">$a : A$</span> in the context <span class="math-container">$\Gamma$</span>! It only means <span class="math-container">$a : B$</span> for some <span class="math-container">$B :&lt; A$</span>.</li>
<li>Resolution is trivial. Lookup the inheritance tree!</li>
<li>Is tied to the class system. We cannot, say, implement <code>ToString</code> for the type <code>List&lt;char&gt;</code>. We may only do so for the generic <code>List&lt;T&gt;</code>.</li>
<li>Implementations of methods are tied to the subclass. They must not clash.</li>
</ul>

CC BY-SA 4.0
2023-05-18T15:31:35.663Z
2023-08-29T15:38:02.263Z
2023-08-29T15:38:02.263Z
ice1000
CC BY-SA 4.0
2023-05-18T16:39:19.547Z
It can be argued that `List` in signatures is syntax sugar for `impl List` in interface-based languages, as I was discussing with the OP a while back before this site existed
Seggan
CC BY-SA 4.0
2023-05-18T16:45:32.810Z
@Seggan In Java you're free to have `Vec<List>` with each member of a subtype of `List`, but `Vec<impl List>` seems much harder to implement
ice1000
CC BY-SA 4.0
2023-05-18T17:50:35.030Z
Because `impl List` is pretty much the same as `L: List; L`, you can just write `L: List; Vec<L>` in both languages
Seggan
CC BY-SA 4.0
2023-05-18T19:14:07.527Z
In Java List is actually closer to dyn List, and they can afford to have it be implicit, as every object is already stored and passed by reference
abel1502
CC BY-SA 4.0
2023-08-29T17:45:27.380Z
I think the part about type theory is backwards. If $(a : A) \in \Gamma$ then trivially $\Gamma \vdash a : A$, but the reverse does not hold; if $\Gamma \vdash a : A$ then this doesn't necessarily mean that $(a : A) \in \Gamma$. That said, it doesn't necessarily mean $(b : B) \in \Gamma$ for some $B <: A$ either; it might be entailed by some other derivation in the given type system. I also don't really understand why subtyping would be considered "horrible for type theory".
kaya3
CC BY-SA 4.0
2023-09-08T13:25:47.517Z
@ice1000: Traits and interfaces are types... if the language wishes so. In fact, the `impl` and `dyn` are relatively recent additions in the Rust language made for the sake of explicitness, prior to this addition you could just say `Box<Debug>`.
Matthieu M.
CC BY-SA 4.0
2023-09-08T13:27:18.627Z
@ice1000: Interfaces can definitely be implemented for specific classes. C++ allows it with specialization (of classes).
Matthieu M.
CC BY-SA 4.0
2023-08-09T10:13:14.687Z
Can you explain your notation ("`(a : A) ∈ Γ`", "`B :< A`", etc) on the "Requires subtyping..." bullet?
Dai
CC BY-SA 4.0
2023-08-09T14:35:34.653Z
@Dai these are hard to explain in a few words. I found https://www.hedonisticlearning.com/posts/understanding-typing-judgments.html which might be helpful.
ice1000
<h1>Organization</h1>
<p>There’s a chance I misunderstand what you’re getting at, but Swift blurs the line between the two by supporting both syntaxes:</p>
<pre><code>protocol P {
  var x: Int { get }
}

struct Foo: P {
  var x: Int /* stored */
}

struct Bar {}
extension Bar: P {
  var x: Int { return 3 }
}
</code></pre>
<p>The latter is commonly used as an organizational tool, as it segregates methods for different protocols and even allows them to live in different files. This is especially important for types like <code>Array</code> that conform to a laundry list of protocols.</p>

CC BY-SA 4.0
2023-05-18T15:33:06.760Z
2023-05-18T15:33:06.760Z
Bbrk24
CC BY-SA 4.0
2023-07-16T21:28:07.413Z
Swift and Scala both followed the path of "start with `interface` but make it powerful enough to be a typeclass", and it's super interesting that they did it in such different ways. In Scala, you land on implicits and a `trait` keyword which is basically a super-interface. In Swift, you get extension implementations that are as powerful as typeclasses.
Silvio Mayolo
CC BY-SA 4.0
2023-09-06T22:25:28.267Z
@SilvioMayolo - Swift's protocols are *not* as powerful as typeclasses.  AIUI, they do not provide a way of specifying that the type of a function argument, return value or property is the type of the implementing class, which is supported by typeclasses (and is required, for example, to describe the `Monad` typeclass, as if a type `T<A>` implements `Monad` then it must have a function that takes a `T<A>` and a function from `A` to `B` and returns a `T<B>`, which may not be a different Monad implementation).  I don't know enough about Scala's traits to be sure whether this applies there too.
occipita
CC BY-SA 4.0
2023-09-06T22:26:41.173Z
@occipita They do support `Self` for "the exact implementing type", but they don't support generic associatedtypes which is necessary for `Monad`.
Bbrk24
<p>I think you can have a language that has both the traditional OOP interfaces <em>and</em> traits. Anyway, let's look at how we can use either to store a vector of animals. In Rust:</p>
<pre><code>trait Animal {…}
struct Dog;
impl Animal for Dog {…}
struct Cat;
impl Animal for Cat {…}
let animals: Vec&lt;Box&lt;dyn Animal&gt;&gt; = Vec::new();
</code></pre>
<p>This vector is now storing two pointers for each animal it contains: one pointer points to the <code>Dog</code> or <code>Cat</code> object (which is holding the data members), the other points to the <code>impl Animal</code> for that struct (which thus is holding the function members).</p>
<p>In C++ you would probably write:</p>
<pre><code>struct Animal {…};
struct Dog: public Animal {…};
struct Cat: public Animal {…};
auto animals = std::vector&lt;std::unique_ptr&lt;Animal&gt;&gt;;
</code></pre>
<p>That vector only holds one pointer per animal, and that pointer points to the <code>Dog</code> or <code>Cat</code> object. However, apart from the data members, those objects are also containing a pointer to a <a href="https://en.wikipedia.org/wiki/Virtual_method_table" rel="nofollow noreferrer">vtable</a>.</p>
<p>So you could say that in C++, the <code>impl</code> pointer is stored in the objects themselves, whereas in Rust it's not in the objects, but stored along with the pointer to those objects.</p>
<p>The drawback of C++'s way is that you can't add another trait for a <code>Dog</code> or <code>Cat</code> after the fact, because the layout of the objects, including that of the vtables, is fixed when those structs have been declared. With traits you can add new ones and still have a vector of runtime polymorphic objects.</p>
<p>The drawback of traits is that the vector <code>animals</code> needs to store twice as much. If you have multiple data structures pointing to the same objects, that will increase memory usage compared to OOP interfaces. On the other hand, with C++ every object that is dereived from a base class with virtual member fucntions contains a vtable pointer, even if you don't need it.</p>
<p>I'm pretty sure that you can emulate OOP interfaces in Rust, and traits in C++, but it's of course nicer if the language has a built-in and concise way for doing what you want.</p>

CC BY-SA 4.0
2023-07-13T12:15:00.323Z
2023-07-13T12:15:00.323Z
G. Sliepen
# What are the pros and cons of traits in comparison with interfaces?
Posted by: username_0 on 2023-05-18
In Rust, structs and enums may implement any number of traits, which specify certain functions. When a struct/enum implements a trait, this is where the functions are defined:
impl Foo for Thing {
    fn bar() { ... }
}

This allows one type to implement two or more traits with methods with conflicting names/signatures, since a trait needs to be imported for its methods to be used.
In comparison, many OOP languages have interfaces, which serve a similar purpose. When a class implements an interface, it must have every method the interface requires, but the method doesn't "belong" to the interface in the way that it does with Rust's traits, among other differences. Additionally, a new trait can be implemented on an existing class, whereas interfaces cannot.
What are the pros and cons of these two approaches?
**Tags:** design-choice-comparison traits typeclasses interfaces
> **Comments:**
> **username_1**: There is no real hard line between traits and interfaces. In C# for example you can choose to implement a method differently with the same name for 2 different interfaces.  
> **username_2**: I would say the main difference is that it's possible to implement a trait for a third-party type, but you cannot implement an interface for a third-party class, at least not in the languages I'm used to (instead you would use the [adapter pattern](https://en.wikipedia.org/wiki/Adapter_pattern)).  
> **username_0**: @kaya3 Oh this is an important one. Lemme add that to the question.  
> **username_3**: Note that "traits" can mean something different. The [traits in MLIR](https://mlir.llvm.org/docs/Traits/#) refer to properties, like how an operation is commutative.  
---
## 3 answers
### Answer by username_4 on 2023-05-18. Score: 6
Traits:

* Are not types. You need something like Rust's `impl Trait` or `dyn Trait`.
* Are usually resolved implicitly. OCaml's modules is a counterexample.
* Can have overlapping implementations. For instance, implementing `Display<(Vec, Vec)>` and `Display<(Vec, Vec)>` at the same time may cause issues.
* Needs a prolog-style resolution, whose time complexity can be high (depending on how rich your type theory is).
* Multiple trait impl is not a problem, can be added freely (Rust's orphan rule is not mandatory) if we don't care about clashes.
* Can have an `impl` for a special case of a type. For instance, `impl ToString for Vec`.
* Identically named functions in different traits are possible.

Interfaces:

* Are types. Put them directly in the signatures.
* Requires subtyping (and probably subtyping polymorphism) to work properly. This is horrible for type theory, because $(a : A) \in \Gamma$ does not mean $a : A$ in the context $\Gamma$! It only means $a : B$ for some $B :< A$.
* Resolution is trivial. Lookup the inheritance tree!
* Is tied to the class system. We cannot, say, implement `ToString` for the type `List`. We may only do so for the generic `List`.
* Implementations of methods are tied to the subclass. They must not clash.
> **Comments:**
> **username_5** (2023-05-18): It can be argued that `List` in signatures is syntax sugar for `impl List` in interface-based languages, as I was discussing with the OP a while back before this site existed  
> **username_4** (2023-05-18): @Seggan In Java you're free to have `Vec<List>` with each member of a subtype of `List`, but `Vec<impl List>` seems much harder to implement  
> **username_5** (2023-05-18): Because `impl List` is pretty much the same as `L: List; L`, you can just write `L: List; Vec<L>` in both languages  
> **username_6** (2023-05-18): In Java List is actually closer to dyn List, and they can afford to have it be implicit, as every object is already stored and passed by reference  
> **username_8** (2023-08-09): Can you explain your notation ("`(a : A) ∈ Γ`", "`B :< A`", etc) on the "Requires subtyping..." bullet?  
> **username_4** (2023-08-09): @Dai these are hard to explain in a few words. I found https://www.hedonisticlearning.com/posts/understanding-typing-judgments.html which might be helpful.  
> **username_2** (2023-08-29): I think the part about type theory is backwards. If $(a : A) \in \Gamma$ then trivially $\Gamma \vdash a : A$, but the reverse does not hold; if $\Gamma \vdash a : A$ then this doesn't necessarily mean that $(a : A) \in \Gamma$. That said, it doesn't necessarily mean $(b : B) \in \Gamma$ for some $B <: A$ either; it might be entailed by some other derivation in the given type system. I also don't really understand why subtyping would be considered "horrible for type theory".  
> **username_7** (2023-09-08): @ice1000: Traits and interfaces are types... if the language wishes so. In fact, the `impl` and `dyn` are relatively recent additions in the Rust language made for the sake of explicitness, prior to this addition you could just say `Box<Debug>`.  
> **username_7** (2023-09-08): @ice1000: Interfaces can definitely be implemented for specific classes. C++ allows it with specialization (of classes).  
---
### Answer by username_9 on 2023-05-18. Score: 4
Organization
There’s a chance I misunderstand what you’re getting at, but Swift blurs the line between the two by supporting both syntaxes:
protocol P {
  var x: Int { get }
}

struct Foo: P {
  var x: Int /* stored */
}

struct Bar {}
extension Bar: P {
  var x: Int { return 3 }
}

The latter is commonly used as an organizational tool, as it segregates methods for different protocols and even allows them to live in different files. This is especially important for types like Array that conform to a laundry list of protocols.
> **Comments:**
> **username_10** (2023-07-16): Swift and Scala both followed the path of "start with `interface` but make it powerful enough to be a typeclass", and it's super interesting that they did it in such different ways. In Scala, you land on implicits and a `trait` keyword which is basically a super-interface. In Swift, you get extension implementations that are as powerful as typeclasses.  
> **username_11** (2023-09-06): @SilvioMayolo - Swift's protocols are *not* as powerful as typeclasses.  AIUI, they do not provide a way of specifying that the type of a function argument, return value or property is the type of the implementing class, which is supported by typeclasses (and is required, for example, to describe the `Monad` typeclass, as if a type `T<A>` implements `Monad` then it must have a function that takes a `T<A>` and a function from `A` to `B` and returns a `T<B>`, which may not be a different Monad implementation).  I don't know enough about Scala's traits to be sure whether this applies there too.  
> **username_9** (2023-09-06): @occipita They do support `Self` for "the exact implementing type", but they don't support generic associatedtypes which is necessary for `Monad`.  
---
### Answer by username_12 on 2023-07-13. Score: 3
I think you can have a language that has both the traditional OOP interfaces and traits. Anyway, let's look at how we can use either to store a vector of animals. In Rust:
trait Animal {…}
struct Dog;
impl Animal for Dog {…}
struct Cat;
impl Animal for Cat {…}
let animals: Vec> = Vec::new();

This vector is now storing two pointers for each animal it contains: one pointer points to the Dog or Cat object (which is holding the data members), the other points to the impl Animal for that struct (which thus is holding the function members).
In C++ you would probably write:
struct Animal {…};
struct Dog: public Animal {…};
struct Cat: public Animal {…};
auto animals = std::vector>;

That vector only holds one pointer per animal, and that pointer points to the Dog or Cat object. However, apart from the data members, those objects are also containing a pointer to a vtable.
So you could say that in C++, the impl pointer is stored in the objects themselves, whereas in Rust it's not in the objects, but stored along with the pointer to those objects.
The drawback of C++'s way is that you can't add another trait for a Dog or Cat after the fact, because the layout of the objects, including that of the vtables, is fixed when those structs have been declared. With traits you can add new ones and still have a vector of runtime polymorphic objects.
The drawback of traits is that the vector animals needs to store twice as much. If you have multiple data structures pointing to the same objects, that will increase memory usage compared to OOP interfaces. On the other hand, with C++ every object that is dereived from a base class with virtual member fucntions contains a vtable pointer, even if you don't need it.
I'm pretty sure that you can emulate OOP interfaces in Rust, and traits in C++, but it's of course nicer if the language has a built-in and concise way for doing what you want.
---
