Перейти к содержимому

Уроки · 54

Методы

Вызовы функций в уроках выглядели по-разному, а разницу мы никогда не объясняли. Мы уже писали свои функции — в уроке «Свои функции» — и выглядели они вот так: имя, потом скобки, а внутри через запятую — аргументы, например direction(center_x, center_y, mouse_x, mouse_y). А angle.cos() из урока «Вращение» выглядит совсем по-другому: сначала само значение, потом точка, потом имя, и скобки почти всегда пустые. Разберёмся, что это за второй вид вызова и откуда он берётся.

Тут всё знакомо: пишем имя, потом скобки, и внутри них по порядку идут все входы — ровно как уже было в уроке «Свои функции»:

fn distance(from: Point, to: Point) -> f32 {
let dx = to.x - from.x;
let dy = to.y - from.y;
(dx * dx + dy * dy).sqrt()
}

Вызов: distance(from, to) — оба входа внутри скобок, порядок и есть весь смысл.

Метод — функция, которая «принадлежит» значению

Заголовок раздела «Метод — функция, которая «принадлежит» значению»

angle.cos() — та же самая идея вызова функции, просто записанная по-другому: не имя со скобками, а сначала само значение (angle), потом точка, потом имя функции (cos), потом скобки. Такую функцию называют методом. angle тут не что-то отдельное от скобок — это и есть единственный вход, просто написанный перед точкой, а не внутри скобок. Поэтому скобки у angle.cos() пустые: всё, что нужно методу, уже стоит перед точкой.

Если входов у метода больше одного, лишние идут в скобки — ровно так, как уже встречалось в уроке «Точная остановка»:

let step = SPEED.min(distance);

SPEED здесь не какой-то особенный первый вход — просто один из двух: SPEED.min(distance) значит «наименьшее из SPEED и distance», только одно число записано перед точкой, а второе — в скобках. .sqrt(), .len(), .push(...) — та же самая запись, просто у разных значений: f32, Vec, массив. Устройство каждого уже разбиралось по отдельности раньше — здесь общее только то, как их вызывают.

Методы можно вешать не только на встроенные типы вроде f32, но и на свой собственный — через блок impl (от английского implementation). Возьмём уже знакомую функцию distance из урока «Точка и прямоугольник» и повесим её как метод на Point:

impl Point {
// self — это и есть то значение Point, что стоит перед точкой.
fn distance_to(self, other: Point) -> f32 {
let dx = other.x - self.x;
let dy = other.y - self.y;
(dx * dx + dy * dy).sqrt()
}
}

Внутри impl Point { ... } — почти обычная функция, только первый параметр называется self и означает то значение Point, у которого вызвали этот метод. Было distance(from, to) — стало from.distance_to(to): from больше не входит в скобки, оно и так уже здесь, перед точкой, а внутри метода видно под именем self.

  • Обычная функция: имя, скобки, все входы внутри них по порядку — как distance(from, to).
  • Метод: значение, точка, имя, скобки — но значение перед точкой и есть один из входов, просто записанный по-другому, а не что-то отдельное от остальных.
  • angle.cos(), .sqrt(), .len(), .push(...) — всё это методы: пустые скобки значат, что весь нужный вход уже стоит перед точкой; непустые, как у SPEED.min(distance), — что входов больше одного, а лишние легли в скобки.
  • impl Point { fn distance_to(self, other: Point) -> f32 { ... } } — вешает метод на свой тип: внутри self значит то значение, которое стоит перед точкой при вызове.
  • В реальном коде вместо self почти всегда пишут &self, чтобы не забирать значение в собственность у вызывающего (как в уроке «Владение и ссылки») — отдельная тема, здесь достаточно знать, что такая запись существует.