1. 1. Introduction
  2. 2. Введение
  3. 3. C чего начать
  4. 4. Изучение Rust
    1. 4.1. Угадайка
    2. 4.2. Обедающие философы
    3. 4.3. Вызов кода на Rust из других языков
  5. 5. Синтаксис и семантика
    1. 5.1. Связывание имён
    2. 5.2. Функции
    3. 5.3. Простые типы
    4. 5.4. Комментарии
    5. 5.5. Конструкция `if`
    6. 5.6. Циклы
    7. 5.7. Владение
    8. 5.8. Ссылки и заимствование
    9. 5.9. Время жизни
    10. 5.10. Изменяемость
    11. 5.11. Структуры
    12. 5.12. Перечисления
    13. 5.13. Конструкция `match`
    14. 5.14. Шаблоны сопоставления `match`
    15. 5.15. Синтаксис методов
    16. 5.16. Вектора
    17. 5.17. Строки
    18. 5.18. Обобщённое программирование
    19. 5.19. Типажи
    20. 5.20. Типаж `Drop`
    21. 5.21. Конструкция `if let`
    22. 5.22. Типажи-объекты
    23. 5.23. Замыкания
    24. 5.24. Универсальный синтаксис вызова функций
    25. 5.25. Контейнеры и модули
    26. 5.26. `const` и `static`
    27. 5.27. Атрибуты
    28. 5.28. Псевдонимы типов
    29. 5.29. Приведение типов
    30. 5.30. Ассоциированные типы
    31. 5.31. Безразмерные типы
    32. 5.32. Перегрузка операций
    33. 5.33. Преобразования при разыменовании
    34. 5.34. Макросы
    35. 5.35. Сырые указатели
    36. 5.36. Небезопасный код
  6. 6. Эффективное использование Rust
    1. 6.1. Стек и куча
    2. 6.2. Тестирование
    3. 6.3. Условная компиляция
    4. 6.4. Документация
    5. 6.5. Итераторы
    6. 6.6. Многозадачность
    7. 6.7. Обработка ошибок
    8. 6.8. Выбор гарантий
    9. 6.9. Интерфейс внешних функций
    10. 6.10. Типажи `Borrow` и `AsRef`
    11. 6.11. Каналы сборок
    12. 6.12. Using Rust without the standard library
  7. 7. Нестабильные возможности Rust
    1. 7.1. Плагины к компилятору
    2. 7.2. Встроенный ассемблерный код
    3. 7.3. Без stdlib
    4. 7.4. Внутренние средства
    5. 7.5. Элементы языка
    6. 7.6. Продвинутое руководство по компоновке
    7. 7.7. Тесты производительности
    8. 7.8. Синтаксис упаковки и шаблоны `match`
    9. 7.9. Шаблоны `match` для срезов
    10. 7.10. Ассоциированные константы
    11. 7.11. Пользовательские менеджеры памяти
  8. 8. Глоссарий
  9. 9. Syntax Index
  10. 10. Библиография

Синтаксис методов

Функции — это хорошо, но если вы хотите вызвать несколько связных функций для каких-либо данных, то это может быть неудобно. Рассмотрим этот код:

baz(bar(foo));Run

Читать данную строку кода следует слева направо, поэтому мы наблюдаем такой порядок: «baz bar foo». Но он противоположен порядку, в котором функции будут вызываться: «foo bar baz». Было бы классно записать вызовы в том порядке, в котором они происходят, не так ли?

foo.bar().baz();Run

К счастью, это возможно! Rust предоставляет возможность использовать такой синтаксис вызова метода с помощью ключевого слова impl.

Вызов методов

Вот как это работает:

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

fn main() {
    let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
    println!("{}", c.area());
}Run

Этот код напечатает 12.566371.

Мы создали структуру, которая представляет собой круг. Затем мы написали блок impl и определили метод area внутри него.

Методы принимают специальный первый параметр, &self. Есть три возможных варианта: self, &self и &mut self. Вы можете думать об этом специальном параметре как о x в x.foo(). Три варианта соответствуют трем возможным видам элемента x: self — если это просто значение в стеке, &self — если это ссылка и &mut self — если это изменяемая ссылка. Мы передаем параметр &self в метод area, поэтому мы можем использовать его так же, как и любой другой параметр. Так как мы знаем, что это Circle, мы можем получить доступ к полю radius так же, как если бы это была любая другая структура.

По умолчанию следует использовать &self, также как следует предпочитать заимствование владению, а неизменные ссылки изменяемым. Вот пример, включающий все три варианта:

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn reference(&self) {
       println!("принимаем self по ссылке!");
    }

    fn mutable_reference(&mut self) {
       println!("принимаем self по изменяемой ссылке!");
    }

    fn takes_ownership(self) {
       println!("принимаем владение self!");
    }
}Run

Цепочка вызовов методов

Итак, теперь мы знаем, как вызвать метод, например foo.bar(). Но что насчет нашего первоначального примера, foo.bar().baz()? Это называется «цепочка вызовов», и мы можем сделать это, вернув self.

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }

    fn grow(&self, increment: f64) -> Circle {
        Circle { x: self.x, y: self.y, radius: self.radius + increment }
    }
}

fn main() {
    let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
    println!("{}", c.area());

    let d = c.grow(2.0).area();
    println!("{}", d);
}Run

Проверьте тип возвращаемого значения:

fn grow(&self) -> Circle {Run

Мы просто указываем, что возвращается Circle. С помощью этого метода мы можем создать новый круг, площадь которого будет в 100 раз больше, чем у старого.

Статические методы

Вы также можете определить методы, которые не принимают параметр self. Вот шаблон программирования, который очень распространен в коде на Rust:

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn new(x: f64, y: f64, radius: f64) -> Circle {
        Circle {
            x: x,
            y: y,
            radius: radius,
        }
    }
}

fn main() {
    let c = Circle::new(0.0, 0.0, 2.0);
}Run

Этот статический метод, который создает новый Circle. Обратите внимание, что статические методы вызываются с помощью синтаксиса: Struct::method(), а не ref.method().

Шаблон «строитель» (Builder Pattern)

Давайте предположим, что нам нужно, чтобы наши пользователи могли создавать круги и чтобы у них была возможность задавать только те свойства, которые им нужны. В противном случае, атрибуты x и y будут 0.0, а radius будет 1.0. Rust не поддерживает перегрузку методов, именованные аргументы или переменное количество аргументов. Вместо этого мы используем шаблон «строитель». Он выглядит следующим образом:

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

struct CircleBuilder {
    x: f64,
    y: f64,
    radius: f64,
}

impl CircleBuilder {
    fn new() -> CircleBuilder {
        CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
    }

    fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
        self.x = coordinate;
        self
    }

    fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
        self.y = coordinate;
        self
    }

    fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
        self.radius = radius;
        self
    }

    fn finalize(&self) -> Circle {
        Circle { x: self.x, y: self.y, radius: self.radius }
    }
}

fn main() {
    let c = CircleBuilder::new()
                .x(1.0)
                .y(2.0)
                .radius(2.0)
                .finalize();

    println!("площадь: {}", c.area());
    println!("x: {}", c.x);
    println!("y: {}", c.y);
}Run

Всё, что мы сделали здесь, — это создали ещё одну структуру, CircleBuilder. В ней мы определили методы строителя. Также мы определили метод area() в Circle. Мы также сделали еще один метод в CircleBuilder: finalize(). Этот метод создаёт наш окончательный Circle из строителя. Таким образом, мы можем использовать методы CircleBuilder, чтобы уточнить создание Circle.