Docs/Types

Traits

Share method implementations between types.

On this page

A trait is a set of methods that classes and structs copy into their own body with use. Use it to share an implementation; use an interface when callers need to accept different classes.

Use a trait#

Valk
trait Labeled {
    fn label() String {
        return "Item: " + this.name
    }
}

class Folder {
    name: String
    use Labeled
}

struct Tag {
    name: String
    use Labeled
}

fn main() {
    println(Folder { name: "Reports" }.label()) // Item: Reports
    println(Tag { name: "urgent" }.label())     // Item: urgent
}

The trait's methods are checked inside each type that uses them, so this.name works because both types have a name property.

Generic traits#

A trait can take type parameters, given at the use:

Valk
trait Wrapper[T] {
    fn wrap(value: T) Array[T] {
        return Array[T]{ value }
    }
}

class Box {
    use Wrapper[String]
}

fn main() {
    println(Box{}.wrap("x").length) // 1
}

Replace a trait method#

A method the type defines itself replaces the trait's method with the same name. The trait's other methods then call the type's version:

Valk
trait Greeter {
    fn name() String { return "someone" }
    fn greet() String { return "Hello " + this.name() }
}

class Person {
    first: String
    use Greeter
    fn name() String { return this.first }
}

fn main() {
    println(Person { first: "Ada" }.greet()) // Hello Ada
}

When two traits define the same method, the type must define that method itself.

Traits and interfaces together#

A trait can supply the methods an interface asks for:

Valk
interface Named {
    fn display() String;
}

trait DisplayName {
    fn display() String { return "<" + this.name + ">" }
}

class User is Named {
    name: String
    use DisplayName
}

fn show(item: Named) {
    println(item.display())
}

fn main() {
    show(User { name: "ada" }) // <ada>
}