29

It looks to me that Rust's trait is the same thing as Java's interface - a set of functions that needs to be implemented on object.

Is there a technical reason for naming it trait not interface or is it just some preference?

smokku
  • 1,256
  • 13
  • 22
  • 3
    It is not exactly the same. Although they serve for the "same" abstraction idea. – Netwave Oct 07 '21 at 08:02
  • No, Rust is not Java and the concepts don't map exactly. In particular AFAIK Java interfaces don't have an equivalent for Rust's [extension traits](http://xion.io/post/code/rust-extension-traits.html). – Jmb Oct 07 '21 at 08:09
  • 1
    `interface` also historically implied a complete lack of behaviour (only members specification) and mixins could provide that behaviour, but rust traits are, well, [traits](https://en.wikipedia.org/wiki/Trait_\(computer_programming\)), which allow for both (as well as non-inheriting composition, which @jmb mentions). – Masklinn Oct 07 '21 at 08:11
  • 3
    “It looks to me that Java's interface is the same thing as Haskell's type classes - a set of functions that needs to be implemented on object. Is there a technical reason for naming it interface not type class or is it just some preference?” – mcarton Oct 07 '21 at 09:04

2 Answers2

51

Rust traits and Java interfaces both address the problem of having multiple possible implementations that adhere to some convention/protocol/interface for interacting with a value/object, without constraining the implementation details as a Java superclass does. They can be used in many of the same situations. However, they are different in many details, mostly meaning that Rust traits are more powerful:

  • Java interfaces require that the implementing object have methods with specific names. Each Rust trait has a completely separate namespace. In Java, two interfaces might be impossible to implement together:

    interface Foo {
      void someMethod();
    }
    interface Bar {
      int someMethod();
    }
    class TwoInterfaces implements Foo, Bar {
      public int someMethod();  // The return type must be void and also must be int
    }
    

    In Rust, when you implement a trait for a type, you do so with a separate impl block specifying that trait, so it is explicit which trait each method/function belongs to. This means that implementing a trait for a type cannot conflict with a different trait (except at call sites that have both traits in scope, which must disambiguate by using function syntax instead of method syntax).

  • Java traits can have generics (type parameters), SomeInterface<T>, but an object can implement an interface only once.

    class TwoGenerics implements Foo<Integer>, Foo<String> {
      public void someMethod(??? value) {}  // Can't implement, must be one method
    }
    interface Foo<T> {
      void someMethod(T value);
    }
    

    In Rust, a trait may be implemented for many types, and these are basically considered just like different traits:

    struct TwoGenerics;
    
    trait Foo<T> {
        fn some_method(&self, input: T);
    }
    
    impl Foo<i32> for TwoGenerics {
        fn some_method(&self, input: i32) {}
    }
    impl Foo<String> for TwoGenerics {
        fn some_method(&self, input: String) {}
    }
    

    To get Java-like behavior of requiring there to be one particular type for any implementing type, you can define an associated type instead of a generic:

    struct Thing;
    
    trait Foo {
        type Input;
        fn some_method(&self, input: Self::Input);
    }
    
    impl Foo for Thing {
        type Input = i32;
        fn some_method(&self, input: i32) {}
    }
    
  • In Rust, a trait may be implemented for a type you didn't define, if you defined the trait. Thus, you can define a trait in your crate and then implement it for relevant types in the standard library (or other libraries you depend on), such as for serialization, random generation, traversal, etc. In Java, workarounds such as run-time type checks are required to achieve similar results.

  • In Rust, a trait may have a generic implementation that applies to all types meeting some criteria (bounds), or an implementation that applies to a particular generic type only when its parameters are suitable. For example, in the standard library, there is (approximately)

    impl<T> Clone for Vec<T> where T: Clone {...}
    

    so a Vec is clonable if and only if its contents are. In Java, a class cannot conditionally implement an interface, which is problematic for any recursive property: for example, list instanceof Serializable might be true while the list will fail to serialize because one or more of its elements is not serializable.

  • Rust traits may have associated constants, types, and non-method functions (analogous to Java static methods), all of which may be different for each implementing type. When Java interfaces have static members, they have only one implementation for the entire interface.

    For example, the Default trait in Rust allows constructing a new instance of any implementing type by calling Default::default() or T::default(). In Java, you need to create an interface implemented by separate “factory” classes to do this (but then the factories can have their own state/data, so you might still choose to use a factory trait and types in Rust).

  • Rust traits can have functions which accept multiple inputs (or produce outputs) that are also of the implementing type. Java interfaces cannot refer to the implementing type; they can only add a type parameter that isn't required to be the same. (On the other hand, Java has subclassing (subtyping) where Rust doesn't, and so the situation is necessarily more complicated when you have a collection of instances of different concrete type but the same supertype.)

There's probably many more details that could be mentioned, but I think these cover a lot of the ways in which you can or must use them differently even though they are meant for the same tasks.


As to the name “trait” versus “interface”, this is due to an existing concept of traits in computer science which are considered to have several specific properties, mostly that there is no inheritance and no overriding involved in implementing and using traits. This is closely related to the “separate namespace” I mentioned above.

Kevin Reid
  • 37,492
  • 13
  • 80
  • 108
10

Interface is a concept of Object Oriented Programming. When you say that an object type implements an interface, you are talking about a property of that type. It says that the type follows certain contract.

Rust is not an object oriented language. And traits are not exactly interfaces. When you say, that a struct has some trait, it does not necessarily mean, that it is a property of the struct, but rather that struct maps with the functionality of the trait.

For example, you can have a built-in rust type [f32; 2] - an array with two values. And you can have a trait Point:

trait Point {
  fn x(&self) -> f32;
  fn y(&self) -> f32;
}

As the author of the trait, you can decide that the type [f32; 2] maps well to the functionality of this trait. And you can implement this trait for the type:

impl Point for [f32; 2] {
  fn x(&self) -> f32 { self[0] }
  fn y(&self) -> f32 { self[1] }
}

By doing so you didn't change the type itself, but rather said that it has your trait.

This is one thing you cannot do with interfaces but can do with traits. I believe that the name trait was chosen rather then interface for rust to emphase this distinction and prevent confusion of concepts.

musicformellons
  • 12,283
  • 4
  • 51
  • 86
Maxim Gritsenko
  • 2,396
  • 11
  • 25
  • So, what you are saying is that interfaces are intrinsic to the type (declared when defining a type), but traits are externally provided for a type? But Go has interfaces that are not part of the type and are still called interface. – smokku Oct 07 '21 at 14:55
  • 6
    Not exactly. Interface is a contract that the type must adhere to. Trait is some functionality associated with the type. – Maxim Gritsenko Oct 07 '21 at 15:05
  • 3
    I think when trait is being used as a generic argument or `impl trait` it is effectively a contract that the value must adhere to. – smokku Oct 08 '21 at 11:19
  • 1
    This answer is completely wrong. Rust is an Object Oriented programming language and traits can be think as Interfaces. "Traits are Rust's sole notion of interface. A trait can be implemented by multiple types, and in fact new traits can provide implementations for existing types." - Rust Website – Nam Duong Oct 02 '22 at 04:19