Уроки · 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, чтобы не забирать значение в собственность у вызывающего (как в уроке «Владение и ссылки») — отдельная тема, здесь достаточно знать, что такая запись существует.