Traits VS. Interfaces

Traits vs. interfaces

An interface defines a set of methods that the implementing class must implement.

When a trait is use'd the implementations of the methods come along too--which doesn't happen in an Interface.

That is the biggest difference.

From the Horizontal Reuse for PHP RFC:

Traits is a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

Is Rust trait the same as Java interface

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. Rust traits have 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, every implementation of a trait lives in its own impl block and they are considered distinct. 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 a separate “factory” interface to do this.

  • 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.

Is Traits in Scala an Interface or an Abstract Class?

Being a java developer I would have not provided body inside interface
but scala traits allowed this

A trait in Scala provides means of defining a default implementation, and so does Java starting version 8. In Scala, they are primarily used as mixins.

In regards to the compilation error, this arises from the fact that you have declared both Vehicle and Motor with a startEngine method, and that creates an ambiguity from the compilers point of view, since you're mixing in both implementations. To overcome this, you need to explicitly override the method in the implementing class/trait:

override def startEngine: Unit = super.startEngine

One important thing to note is that super here is referring to the last trait in the mixin chain which supplies a startEngine method, in this case Motor. This means that you'll see:

Motor Start
Stop Engine

Which I'd argue is not what you want.

What can be done is to require Vehicle to have a mixed in Motor implementation using self types:

trait Vehicle {
self: Motor =>

def startEngine: Unit = {
println("Vehicle Start")
startMotor
}

def stopEngine: Unit = {
println("Stopping Engine")
stopMotor
}
}

And define your Motor methods in terms of motor only:

trait Motor {
def startMotor: Unit = {
println("Motor Start")
}

def stopMotor: Unit = {
println("Stop Motor")
}
}

And then you mixin everything:

object Car extends Vehicle with Motor

And call:

def main(args: Array[String]): Unit = {
Car.startEngine
Car.stopEngine
}

And you get:

Vehicle Start
Motor Start
Stopping Engine
Stop Motor

Why PHP Trait can't implement interfaces?

The really short version is simpler because you can't. That's not how Traits work.

When you write use SomeTrait; in PHP you are (effectively) telling the compiler to copy and paste the code from the Trait into the class where it's being used.

Because the use SomeTrait; is inside the class, it can't add implements SomeInterface to the class, because that has to be outside the class.

"why aren't Traits types in PHP? "

Because they can't be instantiated. Traits are really just a language construct (telling the compiler to copy and paste the trait code into this class) as opposed to an object or type that can be referenced by your code.

So, i want to "design" in the code that every class that want to use
my trait have to implement the interface.

That can be enforced using an abstract class to use the trait and then extending classes from it.

interface SomeInterface{
public function someInterfaceFunction();
}

trait SomeTrait {
function sayHello(){
echo "Hello my secret is ".static::$secret;
}
}

abstract class AbstractClass implements SomeInterface{
use SomeTrait;
}

class TestClass extends AbstractClass {
static public $secret = 12345;

//function someInterfaceFunction(){
//Trying to instantiate this class without this function uncommented will throw an error
//Fatal error: Class TestClass contains 1 abstract method and must therefore be
//declared abstract or implement the remaining methods (SomeInterface::doSomething)
//}
}

$test = new TestClass();

$test->sayHello();

However - if you do need to enforce that any class that uses a Trait has a particular method, I think you may be using traits where you should have been abstract classes in the first place.

Or that you have your logic the wrong way round. You're meant to require classes that implement interfaces have certain functions, not that if they have certain functions that they must declare themselves as implementing an interface.

Edit

Actually you can define abstract functions inside Traits to force a class to implement the method. e.g.

trait LoggerTrait {

public function debug($message, array $context = array()) {
$this->log('debug', $message, $context);
}

abstract public function log($level, $message, array $context = array());
}

However this still doesn't allow you to implement the interface in the trait, and still smells like a bad design, as interfaces are much better than traits at defining a contract that a class needs to fulfill.

Difference between Trait and an Abstract Class in PHP

Traits allow you to share code between your classes without forcing you into a specific class hierarchy. Say you want all your classes to have the convenient utility method foo($bar); without traits you have two choices:

  • implement it individually with code redundancy in each class
  • inherit from a common (abstract) ancestor class

Both solution aren't ideal, each with their different tradeoffs. Code redundancy is obviously undesirable, and inheriting from a common ancestor makes your class hierarchy design inflexible.

Traits solve this problem by letting you implement foo($bar) in a trait which each class can "import" individually, while still allowing you to design your class hierarchy according to business logic requirements, not language necessities.

Understanding C++ Traits and Making Them Efficient

That looks like an adapter, not a trait as is used in C++.

Traits in C++ is like std::numeric_limits or std::iterator_traits. It takes a type and returns some information about that type. The default implementation handles a certain number of cases, and you can specialize it to handle other cases.


The code he wrote has a few issues.

  1. In Rust, this is used for dynamic dispatch. The template version is not dynamic.

  2. C++ thrives on value types. With the embedded reference, this cannot be a value type.

  3. The checking is late, at duck typing time, and the errors show up in the trait code, not at the call site.

An alternative approach to this is to use free functions and concepts and ADL.

turn_light_on(foo) and turn_light_off(foo) can be defaulted and found via ADL allowing customization of existing types. If you want to avoid the "one namespace" problem you can include an interface tag.

namespace Light {
struct light_tag{};
template<class T>
concept LightClass = requires(T& a) {
{ a.on() };
{ a.off() };
};
void on(light_tag, LightClass auto& light){ light.on(); }
void off(light_tag, LightClass auto& light){ light.off(); }
// also, a `bool` is a light, right?
void on(light_tag, bool& light){ light=true; }
void off(light_tag, bool& light){ light=false; }
template<class T>
concept Light = requires(T& a) {
{ on( light_tag{}, a ) };
{ off( light_tag{}, a ) };
};
void lightController(Light auto& l) {
on(light_tag{}, l);
off(light_tag{}, l);
}
struct SimpleLight {
bool bright = false;
void on() { bright = true; }
void off() { bright = false; }
};
}

then we have our OddLight:

namespace Odd {
class OddLight {
public:
void set(bool state);
};
}

we want it to be a Light, so we do this:

namespace Odd {
void on(::Light::light_tag, OddLight& odd){ odd.set(true); }
void off(::Light::light_tag, OddLight& odd){ odd.set(false); }
}

then

struct not_a_light{};

if we have test code:

int main() {
Light::SimpleLight simple;
Odd::OddLight odd;
not_a_light notLight;
Light::lightController(simple);
Light::lightController(odd);
// Light::lightController(notLight); // fails to compile, error is here
}

note that the concept map:

namespace Odd {
void on(::Light::light_tag, OddLight& odd){ odd.set(true); }
void off(::Light::light_tag, OddLight& odd){ odd.set(false); }
}

can be defined in either namespace Odd or namespace Light.

If you want to extend this to dynamic dispatch, you'd have to manually write the type erasure.

namespace Light {
struct PolyLightVtable {
void (*on)(void*) = nullptr;
void (*off)(void*) = nullptr;
template<Light T>
static constexpr PolyLightVtable make() {
using Light::on;
using Light::off;
return {
[](void* p){ on( light_tag{}, *static_cast<T*>(p) ); },
[](void* p){ off( light_tag{}, *static_cast<T*>(p) ); }
};
}
template<Light T>
static PolyLightVtable const* get() {
static constexpr auto retval = make<T>();
return &retval;
}
};
struct PolyLightRef {
PolyLightVtable const* vtable = 0;
void* state = 0;

void on() {
vtable->on(state);
}
void off() {
vtable->off(state);
}
template<Light T> requires (!std::is_same_v<std::decay_t<T>, PolyLightRef>)
PolyLightRef( T& l ):
vtable( PolyLightVtable::get<std::decay_t<T>>() ),
state(std::addressof(l))
{}
};
}

now we can write:

void foo( Light::PolyLightRef light ) {
light.on();
light.off();
}

and we get dynamic dispatch; the definition of foo can be hidden from the caller.

Extending PolyLightRef to PolyLightValue isn't that tricky -- we just add in assign(move/copy)/construct(move/copy)/destroy to the vtable, and either stuff the state onto the heap in the void* or use small buffer optimization in some cases.

And now we have a full rust-esque system of dynamic "trait" based dispatch, the traits are tested at the point of ingress (when you pass them either as Light auto or PolyLightYYY), customization in either the trait namespace or in the type's namespace, etc.

I, personally, am looking forward to c++23 having reflection, and the possibility of automating some of the above boilerplate.


There is actually a grid of useful variants:

RuntimePoly        CompiletimePoly     Concepts
PolyLightRef LightRef<T> Light&
PolyLightValue LightValue<T> Light

you can write to solve this problem in a Rust-like way.

c++17 deduction guides can be used to make the CompiletimePoly a bit less annoying to use:

LightRef ref = light;

can deduce T for you with

template<class T>
LightRef(T&)->LightRef<T>;

(this may be written for you), and at call site

LightRefTemplateTakingFunction( LightRef{foo} )

Live example with error messages



Related Topics



Leave a reply



Submit