Interfaces

Compile-time capability interfaces, bounds, fields, default methods, and marker interfaces.

Interfaces describe the fields and methods a type must provide. They are most often used as generic bounds, so a generic function can call required methods or access required fields.

Interfaces are static contracts in the current language. They are not runtime value types: there is no dyn Interface, trait object, existential interface value, fat pointer, vtable layout, dynamic-dispatch object model, or runtime interface ABI.

interface HasValue<T> {
    value: T,
}

struct Box: HasValue<i32> {
    value: i32,
}

fn read_value<T: HasValue<i32>>(item: T) -> i32 {
    return item.value;
}

fn main() {
    let box = Box { value: 42 };

    assert read_value<Box>(box) == 42;
}

Methods

Interface methods use normal method syntax. A struct lists implemented interfaces after :.

interface Writer {
    fn write(self, value: i32);
}

struct Counter: Writer {
    total: i32,

    fn write(self, value: i32) {
        self.total += value;
    }
}

fn write_three<W: Writer>(writer: &W) {
    writer.write(3);
}

fn main() {
    let counter = Counter { total: 0 };
    write_three(&counter);

    assert counter.total == 3;
}

The implementation must match the interface member names, parameter types, self reference shape, and return type.

Default Methods

An interface method with a body is a default method. Implementing structs can use it without declaring their own method.

interface CounterSink {
    fn write(self, value: i32);

    fn write_twice(self, value: i32) {
        self.write(value);
        self.write(value);
    }
}

struct Counter: CounterSink {
    total: i32,

    fn write(self, value: i32) {
        self.total += value;
    }
}

fn main() {
    let counter = Counter { total: 0 };
    counter.write_twice(7);

    assert counter.total == 14;
}

If a struct provides a method with the same name as a default method, its signature must still match the interface.

Inheritance

Interfaces can inherit other interfaces. A declaration can either provide a body or end with a semicolon when it only combines other interfaces.

interface Readable {
    fn read(self) -> i32;
}

interface Writable {
    fn write(self, value: i32);
}

interface Stream: Readable, Writable;

A struct that implements Stream must satisfy both Readable and Writable.

Operators

Interfaces can require operator methods. Generic code can then use the operator through an interface bound.

interface Addable<T> {
    +(self, other: const &T) -> T;
}

struct Number: Addable<Number> {
    value: i32,

    +(self, other: const &Number) -> Number {
        return Number { value: self.value + other.value };
    }
}

fn add<T: Addable<T>>(a: T, b: T) -> T {
    return a + b;
}

fn main() {
    let a = Number { value: 10 };
    let b = Number { value: 32 };
    let sum = add<Number>(a, b);

    assert sum.value == 42;
}

See Operators for the full overloadable operator list.

Not Value Types

An interface name may appear as a generic constraint, a struct conformance entry, a base interface, or the compiler-created self contract inside an interface method. It is invalid as a concrete value/storage type, including for locals, fields, parameters, returns, arrays, slices, nullable forms, references, raw pointers, aliases, and generic arguments.

Use a generic parameter constrained by the interface instead:

fn read_from<R: Reader>(reader: &R) -> usize {
    return reader.read();
}

Builtin Markers

std.builtin declares marker interfaces that the compiler treats specially.

InterfaceImplemented by
Copycopyable built-ins and structs that explicitly implement Copy
Integerprimitive signed and unsigned integer types except bool
Signedi8, i16, i32, i64, i128, isize
Unsignedu8, u16, u32, u64, u128, usize

Signed and Unsigned imply Integer. Structs can explicitly implement Copy when all fields are copyable, but structs cannot implement Integer, Signed, or Unsigned.

See Move, Copy, and Clone for copy eligibility, move-only values, clone APIs, and drop behavior.

Rules

  • Interface fields require matching field names and exact field types.
  • Interface methods require matching method names, parameters, self shape, and return type.
  • Default methods count as implemented unless a struct declares a mismatched method with the same name.
  • Generic interfaces can be used as bounds, for example T: HasValue<i32>.
  • Interface bounds are checked during overload resolution and specialization.
  • Default methods are statically resolved; they are not virtual calls.

See Generics for bounds and numeric marker constraints.