Like struct, trait defines a new type composed of named fields and methods. For example:

trait Id:
  id u32
  fn print()  // No implementation provided

Traits differ from structs in two important ways:

Traits are versatile, facilitating many useful roles:

Traits as mixins

This page focuses on the mixin role of traits. This is effectively a different form of inheritance than field-based delegated inheritance. When a trait is used as a mixin by some other struct or trait, its fields and methods are added to that type as if part of the type.

Mixing in Fields

Suppose we have a trait that defines some fields:

trait LexerInfo:
  line i32
  col i32

A struct may include these fields as part of its definition by using mixin:

struct Node:
  type u8
  mixin LexerInfo
  count i32

The end result is that the Node struct ends up with four fields, with 'line' and 'col' located in between 'type' and 'count'. 'line' and 'col' are accessible the same way as 'type' and 'count', just as if they were explicitly declared by Node. No two fields may share the same name, no matter where they come from.

A struct may mix in multiple traits. Similarly, trait definitions may mix in one or more traits.

Mixing in Default Methods

In addition to its fields, a struct will also mix in the trait's methods. However, the mechanism here is slightly different. Any method implemented by a trait mixin will only be inherited by the struct if it has not already been implemented by the struct. This is done by matching the number and types of all method parameter types. Because the struct can effectively override the mixin trait's implementation of a method, we call the trait's implementation a default method.

Importantly, if a trait mixin includes methods without an implementation, the struct must define and implement these methods.

Self

Traits, like structs, may specify Self as an alias for the trait type itself. This is the default type used for the self parameter. When a trait is mixed into a struct, Self is understood to refer to that struct. Where this behavior is not desired, one should use the trait's name instead of Self.

Knowing when to choose Self vs. the trait's name depends on what other roles the trait may play. Typically, self on every method should use Self and everywhere else (field types and other method parameters) should use the trait's name. This makes the trait suitable for virtual references and generic bounds.

Marker traits

A marker trait defines no fields nor methods. Its value comes from other trait or struct types inheriting it. Its use then becomes a testable attribute of that type (e.g., by generics), allowing one to distinguish between types that support a certain behavior and those that don't.

_