Methods

Any function can be marked as a method by writing a . before the name in the definition:

fn .sum(list: List[N32]) -> N32 {
  let sum = 0;
  while list.pop_front() is Some(value) {
    sum += value;
  }
  sum
}

Methods must take at least one argument, called the receiver. Methods can still be called like normal functions, but they can also be called using method syntax:

[1, 2, 3].sum() // 6

A method call can refer to any method candidate with the appropriate receiver types. Method candidates include:

  • any method that is in scope
  • any method defined in the module of the receiver type

This means that if you define a custom type, and declare methods in its module, anything using that type can call those methods, without needing to explicitly import them:

enum Shape {
  Circle(F32),
  Rect({ width: F32, height: F32 }),
}

mod Shape {
  pub fn .perimeter(shape: Shape) -> F32 {
    match shape {
      Shape::Circle(radius) {
        6.28 * radius
      }
      Shape::Rect({ width, height }) {
        2.0 * (width + height)
      }
    }
  }
}

let shape = Shape::Circle(1.0);
shape.perimeter() // 6.28

The first parameter of a methods can also be a reference, allowing the method to mutate the value it is called upon.

mod Shape {
  pub fn .scale(&shape: &Shape, factor: F32) {
    match &shape {
      &Shape::Circle(radius) {
        radius *= factor;
      }
      &Shape::Rect({ width, height }) {
        width *= factor;
        height *= factor;
      }
    }
  }
}

let shape = Shape::Rect({ width: 4.0, height: 6.0 });
shape.perimeter() // 20.0
shape.scale(2.3);
shape.perimeter() // 46.0