New Mojo language feature: default trait methods!

We unveiled this live at the Modular community meeting last night: default methods can now be defined on traits! This lets a trait define an implementation of a method that is provided to all conforming structs. These default methods can be explicitly overridden in conforming structs, as needed.

As an example:

# Any struct conforming to EqualityComparable now only needs to define one of
# __ne__ ~or~ __eq__ and will get a definition of the other with no
# additional code!

# For instance:
trait EqualityComparable:
    fn __eq__(self, other: Self) -> Bool:
        ...

    fn __ne__(self, other: Self) -> Bool:
        return not self.__eq__(other)

@value
struct Point(EqualityComparable):
    var x: Int
    var y: Int

    fn __eq__(self, other: Self) -> Bool:
        # Since __eq__ is implemented we now get __ne__ defined for free!
        return self.x == other.x and self.y == other.y

    # Defaulted methods can also be overriden if we want different behavior.
    # fn __ne__(self, other: Self) -> Bool:
    #     return self.x != other.x or self.y != other.y

Currently a trait method is considered to be non-defaulted if the first thing in it’s body is either a ‘…’ or a ‘pass’. In the future, only ‘…’ will mark a trait method as not defaulted. For example:


trait Foo:
  # Either of the following are non-defaulted
  # fn foo(self):
  #   ...
  #
  # fn foo(self):
  #   pass

  # While this is not:
  fn foo(self):
    print("Foo.foo")

We’re excited to see how much code we can clean up as a result of this new feature. Check out all that red in this commit due to being able to define __ne__ in terms of __eq__ within EqualityComparable. And that’s just one case!

7 Likes

I’ve been using this feature a lot lately, for what it’s worth. It was nice to remove a lot of boilerplate code by switching to default trait methods! :+1:

2 Likes